Wie strukturiere ich ein Template-System mit PHP?

15

Ich habe diese Frage über Stackoverflow gelesen:

/programming/104516/calling-php-functions-within-heredoc-strings

und die akzeptierte Antwort lautet:

template.php:

<html>
    <head>
        <title> <? = $ title?> </ title>
    </ head>
    <body>
        <? = getContent ()?>
    </ body>
</ html>

index.php:

<? php

$ title = 'Demo Title';

Funktion getContent () {
    return '<p> Hallo Welt! </ p>';
}

include ('template.php');

?>

Für mich ist das oben Genannte in dem Sinne nicht gut strukturiert, dass template.php von Variablen abhängt, die in anderen Skripten definiert sind. Und Sie verwenden ein include (), um Code auszuführen, wenn Sie dies tun include('template.php')(im Gegensatz zu include (), um eine Klasse oder eine Funktion einzuschließen, die nicht sofort ausgeführt wird).

Ich denke, ein besserer Ansatz ist es, Ihre Vorlage in eine Funktion zu packen:

template.php:

<? php

Funktionsvorlage ($ title, $ content) {
    ob_start (); ?>

<html>
    <head>
        <title> <? = $ title?> </ title>
    </ head>
    <body>
        <? = $ content?>
    </ body>
</ html>

    <? php return ob_get_clean ();
}

?>

index.php:

<? php

require_once ('template.php');

Druckvorlage ('Demo-Titel', '<p> Hallo Welt! </ p>');

?>

Ist der zweite Ansatz besser? Gibt es einen noch besseren Weg, dies zu tun?

Ryan
quelle

Antworten:

12

Ich würde den zweiten Ansatz nicht machen, da die Parameter nicht benannt sind.

Ein gut geschriebener Aufsatz über die Funktionsweise eines Vorlagensystems stammt von Parr und wird oft von Leuten zitiert, die Vorlagensysteme und / oder Web-MVC-Frameworks schreiben.

Ich persönlich bevorzuge normalerweise die Implementierung einer ArrayObject-Klasse mit einem Eigenschaftenarray, auf das sich die Vorlage beziehen würde, $this->propertyNameund das tatsächlich das Vorlagenobjekt ist $this->property['name']. Dies könnte auch einfach durch die Verwendung von __setund __geterreicht werden.

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

und eine Vorlage würde so aussehen:

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

und das Aufrufen würde so aussehen:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

Soweit ich mich erinnere, sehen die alten Symfony 1.x und die Zend_View Template Engines so aus, und für mich ist das in Ordnung.

Aadaam
quelle
Ähnliches macht auch
TinyButStrong
Wie gehst du mit Looping um, wenn es um Partituren geht? Verwenden Sie eine separate "Sub" -Vorlage?
Ryan
Ich frage mich, ob es eine Möglichkeit gibt, $ this in der Vorlage loszuwerden, zum Beispiel nur $ title oder $ name.
Ryan
1
@Ryan Sie können extract($this->properties);die Eigenschaften direkt vor der include-Anweisung in Variablen extrahieren.
Joyce Babu
1

Eine Vorlage hängt immer von externen Variablen ab, und da PHP eine dynamische Sprache ist, kann beim Kompilieren nicht erzwungen werden, dass sie vorhanden ist. Der erste Ansatz ist also wahrscheinlich in Ordnung. es gibt jedoch noch ein paar probleme:

  • In diesem Beispiel wird die HTML-Codierung der Ausgabe nicht berücksichtigt, was bedeutet, dass Sie potenzielle XSS-Schwachstellen haben.
  • Wenn eine dieser Variablen nicht gesetzt ist, gibt der Code eine Warnung aus. Sie werden nicht sehen, ob die Warnung sinnvoll ist oder nicht (möglicherweise ist die Variable optional).

Ein sehr einfacher Ansatz besteht darin, eine Funktion, vorzugsweise mit einem sehr kurzen Namen, zu schreiben, die sich um beide kümmert. es zu nennen _()scheint eine übliche Konvention zu sein. Eine einfache Version könnte so aussehen:

function _($raw) {
    if (isset($raw)) {
        return htmlspecialchars($raw);
    }
}

Und dann würden Sie in Ihrer Vorlage Folgendes tun:

<title><?= _($title) ?></title>

Weitere Dinge, die mit der _()Funktion zu tun haben :

  • Geben Sie ein zweites Argument an, um das Überschreiben des htmlspecialcharsAufrufs zuzulassen , wenn Sie HTML-Code anstelle von unformatierten Zeichenfolgen übergeben möchten.
  • In Typprüfung , so dass Arrays und Objekte werden nicht nachgeben Arrayund Object, sondern etwas Sinnvolles (oder leere Zeichenkette).
  • Unterstützung für Internationalisierung hinzufügen (ein weiteres Argument).
  • Drucken Sie im Debug-Modus den Namen der Variablen mit einer speziellen Formatierung aus, falls diese nicht definiert ist. Auf diese Weise können Sie auf einen Blick erkennen, ob die Vorlage fehlerhaft ist.
tdammers
quelle
1

Das erste template.phpist einfacher zu laden als es ist in Dreamweaver / Bluegriffon / Was auch immer und wird von einem serverseitigen, nicht versierten Webdesigner bearbeitet, das zweite ... nun, es könnte immer noch irgendwie sein, aber es ist wahrscheinlicher, dass es während des Bearbeitungsprozesses kaputt geht .

Ich würde mit dem ersten Schritt fortfahren , um das Erscheinungsbild von HTML mit Vorteilen zu erhalten , wie es Designer von Drittanbietern normalerweise bevorzugen.

ZJR
quelle
in welchem ​​Sinne? Meinst du, es kann nicht per Drag & Drop geändert werden? Der zweite Text ist derselbe wie der erste, jedoch mit ein paar Zeilen PHP umschlossen. Unter der Annahme, dass es immer diese Struktur geben wird, ist es für sie nicht schwieriger, sie von Hand zu bearbeiten.
Ryan
1
Eine gute Vorlage ist für Entwickler sinnvoll, eine gute Vorlage für Designer.
Citricguy
1

Da Codeigniter mein bevorzugtes Framework ist, schlage ich Ihnen eine Codeigniter-Lösung vor:

class Template {
    protected $path, $data;

    public function __construct($path, $data = array()) {
        $this->path = $path;
        $this->data = $data;
    }

    public function render() {
        if(file_exists($this->path)){
            //Extracts vars to current view scope
            extract($this->data);

            //Starts output buffering
            ob_start();

            //Includes contents
            include $this->path;
            $buffer = ob_get_contents();
            @ob_end_clean();

            //Returns output buffer
            return $buffer;
        } else {
            //Throws exception
        }
    }       
}

Ihre Vorlage würde so aussehen:

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

Und Sie würden es rendern, indem Sie die Klasse instanziieren, Daten in einem Array als Konstruktorparameter übergeben und die Methode 'render' aufrufen:

$data = array('title' => 'My title', 'content' => 'My content');
$tmpl = new Template('template.php', $data);
echo $tmpl->render();

Auf diese Weise führen Sie den enthaltenen Code nicht direkt aus, und Sie behalten auch die Lesbarkeit Ihrer Vorlage bei, sodass die Designer zufrieden sind.

lortabac
quelle
-3

PHP ist keine Programmiersprache. Daher ist das Schreiben mit starker Typisierung nicht möglich, wenn Ihre Sprache kein echtes Typensystem hat. Aber Sie können Ihrer IDE wie PHP-Storm helfen, den gewünschten Trick zu machen ...

Ihre Ansichtsdefinition:

interface mySimpleView { 
  public function getTitle(); 
} 

Ihre Vorlagendatei:

<?php
/**
* @var $this mySimpleView  <== Your IDE will try to use this type.
*/
?>

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->
Roger Keulen
quelle
Ich mag diese Antwort (obwohl klar ist, dass "PHP keine Programmiersprache ist" immer umstritten sein wird.
Ronnie