dedlfix: Templates

Beitrag lesen

echo $begrüßung;

Templates sind aber wohl kein Thema hier im Forum?

Nicht dass ich wüsste. Ich las schon des öfteren Beiträge, mit Fragen zum Thema, die auch beantwortet wurden. Es ist nur keine Technik mit der beginnende PHP-Programmierer bewusst arbeiten. Unbewusst arbeiten sie damit, weil PHP an sich schon ein Template-System ist, jedoch wird die oft propagierte Trennung von Eingabe-, Verarbeitungs und Ausgabe-Logik mangels besseren Wissens vernachlässigt.

Kannst du mir ein geeignetes Forum für dieses Thema nennen? Bin Anfänger.

(Die Ausrede, man sei Anfänger, wird gern benutzt, um irgendwelche Vorteile zu erlangen, die sich mir nicht erschließen. Ob jemand wenig Kenntnisse besitzt, merkt man meist schon an der Art der Fragestellung und dem Wissen bzw. Nichtwissen, das sich im Laufe des Thread herausstellt. Oft (nicht immer) ist so eine Aussage gleichzusetzen mit einem Nichtwirklichbeschäftigenwollen mit dem Thema, was bei mir gelegentlich die Antwortlust dämpft. Deswegen ist eine solche Aussage in meinen Augen weniger hilfreich als sich der sie verwendende erhofft. Aber das nur nebenbei.)

Ein wohl recht beliebtes Templatesystem ist Smarty. Dazu gibt es sicher auch ein eigenes Forum. Mir persönlich gefällt diese Art von Template-System jedoch nicht sonderlich.

Es gibt verschiedene Ansätze, die Trennung von Verarbeitung und Ausgabe anzugehen. Ein Ansatz ist, dass man die Ausgabelogik zur Ausgabe zählt. Wenn also x Datensätze auszugeben sind, wird die Schleife zum Ausgeben als Aufgabe des Template-Systems angesehen. Dafür finden sich dann Elemente (oder Nachbildungen davon) wie for-Schleifen im Template-System. Derjenige, der das Template schreibt, muss dann neben dem HTML (oder XML oder was auch immer) auch diese Elemente kennen und richtig einsetzen können.

HTML_Template_IT geht den anderen Weg. Es kennt nur die Elemente Platzhalter und Block. Sämtliche Ausgabelogik steht im Programm. Ein Template-Schreiber für dieses System hat also deutlich weniger Elemente dazuzulernen.
HTML_Template_Sigma basiert auf HTML_Template_IT und fügt zwei Funktionalitäten hinzu: Includes und Funktionsaufrufe. Für mein Verständnis ist das für ein Template-System ausreichend.

Die Frage nach dem besseren System ist philosophischer Natur. Vermutlich gibt es ebenso viele Gründe für den einen Ansatz wie gegen den anderen und umgekehrt.

Ein Template-System ist natürlich auch wieder ein Stück Code, das Zeit bei der Abarbeitung benötigt. Wenn man diese sparen möchte und trotzdem nicht auf PHP verzichten will, kann man auch gleich bei "reinem" PHP bleiben, vielleicht ergänzt um ein wenig Datenverwaltung. Außerdem spart man sich dabei das Hantieren mit RegExps. Hier ein Beispiel wie so etwas aussehen könnte (einsetzbar ab PHP4 (wegen Verwendung von Referenzen und statischen Variablen)):

template.php

<?php  
/**  
 * data - kümmert sich um die Ausgabedatenhaltung  
 *  
 * schreibender Zugriff - Variante 1  
 *   data('placeholdername', 'value');  
 * schreibender Zugriff - Variante 2  
 *   $placeholder =& data('placeholdername');  
 *   $placeholder = 'value';  
 * lesender Zugriff  
 *   echo data('placeholdername');  
 *  
 * @param string/int $placeholder Name des Platzhalters  
 * @param mixed $value (optional) Wert für den Platzhalter  
 * @return mixed  Referenz auf Platzhalterwert  
 */  
function &data($placeholder, $value = null) {  
  // Statische Variable. Behält ihren Inhalt auch bei mehrfachem Aufruf.  
  static $data = array();  
  
  // gegebenenfalls einen übergebenen Wert eintragen  
  if (!is_null($value))  
    $data[$placeholder] = $value;  
  
  // als Leerstring anlegen, wenn placeholder nicht vorhanden ist.  
  if (!isset($data[$placeholder]))  
    $data[$placeholder] = '';  
  
  // Referenz auf anfgeforderten Wert zurückgeben.  
  // Damit kann auch der Rest des Programmes den Wert direkt beeinflussen.  
  return &$data[$placeholder];  
}  
  
/**  
 * p - Platzhalterwert-Ausgabe  
 *  
 * @param string $placeholder Name des Platzhalters  
 * @return void  
 */  
function p($placeholder) {  
  echo data($placeholder);  
}  
?>

Der verarbeitende Teil des Programms übergibt die auszugebenden Daten der Funktion data() zur Aufbewahrung, beispielsweise so:

data('title', 'hello world');
  data('name', 'Welt');

Das Template wird mit einem der include-Befehle eingeladen (oder auch direkt an Eingabe- und Verarbeitungsteil angehängt) und könnte so aussehen:

<!DOCTYPE...>  
<html>  
<head>  
  <title><?php p('title') ?></title>  
</head>  
<body>  
  <p>Hallo <?php p('name')?>!</p>  
</body>  
</html>

Dieses System ist für Projekte geeignet bei denen der Programmierer auch die Templates schreibt oder wenn der Template-Schreiber PHP-Grundkenntnisse hat (oder man ihm diese beibringen kann).

Um wiederholte Daten ausgeben zu können, folgt hier noch eine Ergänzung. Die Funktion p() wurde erweitert und eine Funktion namens loop() kommt hinzu.

  
/**  
 * p - Platzhalterwert-Ausgabe  
 *  
 * @param string $placeholder Name des Platzhalters  
 * @param boolean $global (optional) Loop ignorieren, globale Ausgabedaten verwenden  
 * @return void  
 */  
function p($placeholder, $global = false) {  
  // Loop nicht ignoriert und Aufruf innerhalb eines Loops?  
  if (!$global and $loop = loop(false)) {  
    $data =& data($loop);  
    if (!is_array($data)) {  
      // keine Loopdaten vorhanden  
      echo '';  
      return;  
    }  
    $record = current($data);  
    echo isset($record[$placeholder]) ? $record[$placeholder] : '';  
    return;  
  }  
  
  echo data($placeholder);  
}  
  
/**  
 * loop - startet einen Loop oder setzt den Loopdatenzeiger weiter  
 *  
 * Anwendungsbeispiel:  
 *   Datenverarbeitung:  
 *     $records =& data('records', array());  
 *     $records[] = array('col1' => 'value11', 'col2' => 'value12'); // ein Datensatz  
 *     $records[] = array('col1' => 'value21', 'col2' => 'value22'); // noch ein Datensatz  
 *   Ausgabe:  
 *     <table>  
 *     <?php while (loop('records')): ?>  
 *       <tr><td><?php p('col1');?></td><td><?php p('col2');?></td></tr>  
 *     <?php endwhile;?>  
 *     </table>  
 *  
 * nichtöffentliche Verwendung:  
 *   Wird loop(false) aufgerufen, wird der aktuelle Loopname oder null zurückgegeben  
 *  
 * @param string $loop Name des Loops  
 * @return boolean  true wenn Loopdaten vorhanden  
 */  
function loop($loop) {  
  static $loopnamestack = array();  
  static $currentloopname = null;  
  
  // interne Nutzung: p() frag den aktuellen Loopnamen ab  
  if (!$loop)  
    return $currentloopname;  
  
  // neuer Loop fängt an  
  if ($loop != $currentloopname) {  
    // ggf. aktuellen Loopnamen sichern; neuen merken  
    if ($currentloopname)  
      $loopnamestack[] = $currentloopname;  
    $currentloopname = $loop;  
  
    // Loop-Daten vorbereiten  
    $data =& data($loop);  
    // kein Array => false => keine Schleife  
    if (!is_array($data))  
      return false;  
    // leeres Array => false => keine Schleife  
    return (boolean) reset($data);  
  }  
  // weiter im aktuellen Loop  
  else {  
    $data =& data($loop);  
    // kein Array => false => keine Schleife  
    if (!is_array($data))  
      return false;  
    // Array-Ende => false => Schleifenende  
    if (!next($data)) {  
      // gesicherten Loopnamen ggf. wiederherstellen  
      $currentloopname = array_pop($loopnamestack); // null wenn $loopnamestack leer ist  
      return false;  
    }  
    return true;  
  }  
}

Man kann sogar Loops verschachteln, wie das folgende Anwendungsbeispiel zeigt.

Datenverarbeitung:

<?php  
data('title', 'hello world'); // Variante 1  
$name =& data('name');  
// Variante 2  
$name = 'Welt';  
  
$dbdata =& data('dbdata', array());  
$dbdata[] = array('name' => 'foo'); // ein Datensatz  
$dbdata[] = array('name' => 'bar'); // noch ein Datensatz  
  
data('loop', array(array(1), array(2), array(3)));  
?>

Template:

<!DOCTYPE...>  
<html>  
<head>  
  <title><?php p('title') ?></title>  
</head>  
<body>  
  <p>Hallo <?php p('name')?>!</p><!-- globaler Zugriff -->  
  
  <?php while (loop('loop')):?>  
    <!-- numerisch indexierte Daten und globaler Zugriff aus einem Loop heraus -->  
    <p>Loop <?php p(0)?> (<?php p('name', true)?>):</p>  
    <?php while (loop('dbdata')):?>  
      <p>Hallo <?php p('name')?>!</p>  
    <?php endwhile; ?>  
  <?php endwhile; ?>  
  
  <p>Hallo <?php p('name')?>!</p><!-- wieder globaler Zugriff -->  
</body>  
</html>

Es wäre auch möglich, dieses System als Klasse zu schreiben, was aber Mehraufwand beim Template-Schreiben bedeutet, da man dann entweder Objektmethoden ($template->methode()) oder Klassenmethoden (Template::methode()) statt einfacher Funktionsaufrufe (funktion()) notieren muss.

Wenn du dich nun fragst, warum ich trotz meiner Abneigung gegen Code im Template den Nachmittag damit verbracht habe, solch ein System zu schreiben, dann versuche ich das so zu begründen: Man muss manchmal Kompromisse schließen. Dieses System habe ich mit dem Ziel entwickelt, geringen Aufwand für Abarbeitung durch die PHP-Maschine mit möglichst einfacher Template-Syntax zu verbinden. Letztere ist sogar im Rahmen der PHP-Möglichkeiten beliebig ausbaubar. (Habe ich etwa grade Smarty nacherfunden?)

echo "$verabschiedung $name";

Und wenn jemand Lust hat, einen Feature-/Tipps&Tricks-/wasAnderes-Artikel mit diesem Template-System schreiben, nur zu ... :-)