Dokumentation

Separate Template Engine
Version 1.5 (09.01.2013)

Was ist Separate Template Engine?

Separate Template Engine, oder kurz Separate, ist eine objektorientierte Template Engine für PHP, die im Entwicklungsprozess von webbasierten Anwendungen große Abhilfe schafft. Mit Separate ist es möglich, Business Logik (z.B. das Auslesen der Daten aus einer Datenbank) von der Darstellung (HTML-Quelltext) zu trennen und somit einen sauberen, übersichtlichen und gut strukturierten Quelltext zu produzieren. Die Generierung von fertigen HTML Seiten wird dabei automatisch von der Template Engine gemanagt.
 

Architektur von Separate

Wichtige Eigenschaften der Architektur

Während der Entwicklung wurde großen Wert auf die dynamische Funktionserweiterung (wie z.B. Value Formatter oder die Vererbungs-Möglichkeit der SeparateTemplate-Klasse) gelegt. Insgesamt wurden folgende, für die Entwicklung wichtige, Eigenschaften umgesetzt:
  • Minimale funktionale Beschränkungen (Erweiterung mit eigenen Funktionalitäten ist möglich)
  • Einfache API (z.B. nur die SeparateTemplate.php Datei wird benötigt)
  • Unabhängig von anderen Bibliotheken (wie z.B. Pear,...)

Aufbau einer Separate-basierten Anwendung

Anwendungen, die auf Separate basieren, bestehen aus zwei Schichten – der Darstellungsschicht (Presentation Layer) und der Business Logik. Die Darstellungsschicht besteht aus HTML-Quelltext und aus Template-Variablen bzw. sonstigen Separate-relevanten Konstrukten. In der Business Logik werden Berechnungen durchgeführt (z. B. das Auslesen der Daten aus der Datenbank) und anschließend die daraus generierten Werte bestimmten Template Variablen zugewiesen.
 

 
Die obere Abbildung zeigt die Business Logik sowie die Darstellungsschicht (Presentation Layer), welche von Separate verarbeitet wird. Als Ergebnis wird ein HTML-Quelltext zurückgegeben.
 

Integration in das eigene Projekt

Separate benötigt PHP5 oder höher. Um Separate zu benutzen, muss die bereitgestellte SeparateTemplate-Klasse in Ihr Projekt eingefügt werden.
require_once './SeparateTemplate.php';

Als nächstes muss eine Templatedatei geladen werden. Dazu wird die Methode SeparateTemplate.loadSourceFromFile(...) benutzt.
$t = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');

Nach einem erfolgreichen Laden der Templatedatei kann die eigentliche Implementierung, wie z. B. die Zuweisung von Werten zu bestimmten Template Variablen, stattfinden.
$t->assign('MY_VARIABLE', 'my value');

Die anschließende Generierung der HTML-Seite erfolgt mit der SeparateTemplate.display() Methode.
$t->display();

Der folgende Quelltext fasst alle oben erwähnten Schritte noch einmal zusammen.
<?php

require_once './SeparateTemplate.php';

$t = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');
$t->assign('MY_VARIABLE', 'my value');
$t->display();

Template-Variablen

Für jede dynamisch generierte Informationseinheit muss in der Templatedatei eine Template-Variable angelegt werden. Während der Seitengenerierung ersetzt die Template Engine diese Variablen durch einen zuvor zugewiesenen Wert. Die Syntax sieht wie folgt aus:
${<Variable Name>}

Dynamische Blöcke

Mit dynamischen Blöcken können Sie HTML-Quelltext in Abschnitte aufteilen. Diese Abschnitte können während der Laufzeit beliebig oft wiederholt werden. Das ist sinnvoll, um z.B. eine Tabelle mit dynamischem Inhalt zu generieren. Die Syntax sieht wie folgt aus:
<!-- BEGIN <Block Name> -->
    HTML-Quelltext...
<!-- END <Block Name> -->

Im PHP-Quelltext wird jeder Block durch ein SeparateTemplate-Objekt repräsentiert. Mit der SeparateTemplate.fetch(...)-Methode wird ein neuer Block erstellt. Diesem können anschließend beliebige Werte zugewiesen werden. Mit der SeparateTemplate.assign(...)-Methode wird dann der modifizierte Block dem Root-Template übergeben. Abhängig von der Datensatz-Anzahl muss dieser Vorgang mehrmals wiederholt werden.
 
Das untenstehende Beispiel zeigt eine einfache Implementierung von dynamischen Blöcken.

PHP-Quelltext:
<?php

require_once './SeparateTemplate.php';

$rootTemplate = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');

for($row = 1; $row <= 3; $row++)
{
    //fetch row block from root template
    $rowBlock = $rootTemplate->fetch('row');
    
    for($column = 1; $column <= 3; $column++)
    {
        //fetch column block from row block
        $columnBlock = $rowBlock->fetch('column');
        
        $columnBlock->assign('ROW_NUMBER', $row);
        $columnBlock->assign('COLUMN_NUMBER', $column);
        
        //assign modified column block back to row block
        $rowBlock->assign('column', $columnBlock);
    }
    
    //assign modified row block back to root template
    $rootTemplate->assign('row', $rowBlock);
}

$rootTemplate->display();

?>

Template-Quelltext (index.htm):
<html>
    <body>
        <table>
            <!-- BEGIN row -->
                <tr>
                    <!-- BEGIN column -->
                        <td>Row: ${ROW_NUMBER} Column: ${COLUMN_NUMBER}</td>
                    <!-- END column -->
                </tr>
            <!-- END row -->
        </table>
    </body>
</html>

Wie Sie im oberen Beispiel sehen, können in Blöcken auch weitere Blöcke definiert werden. Der daraus generierte HTML-Quelltext sieht dann wie folgt aus:
<html>
    <body>
        <table>
            <tr>
                <td>Row: 1 Column: 1</td>
                <td>Row: 1 Column: 2</td>
                <td>Row: 1 Column: 3</td>
            </tr>
            <tr>
                <td>Row: 2 Column: 1</td>
                <td>Row: 2 Column: 2</td>
                <td>Row: 2 Column: 3</td>
            </tr>
            <tr>
                <td>Row: 3 Column: 1</td>
                <td>Row: 3 Column: 2</td>
                <td>Row: 3 Column: 3</td>
            </tr>
        </table>
    </body>
</html>

Zuweisung von Platzhalterwerten

Werte für vordefinierte Template-Variablen werden mit bestimmten Methoden im PHP-Quelltext zugewiesen. Es existieren drei unterschiedliche Arten von Zuweisungen:
 

1. Einfache Zuweisung von Platzhalterwerten

Bei dieser Zuweisungsart sind die zugewiesenen Werte auf einen Template-Block oder einen Root-Template beschränkt. Template-Platzhalter, die außerhalb von diesem Bereich liegen, werden einfach ignoriert. Die Zuweisung erfolgt mit einer SeparateTemplate.assign(...)-Methode.
$t->assign('MY_VARIABLE', 'my value');

2. Block-relevante Zuweisung von Platzhalterwerten

Mit Block-relevanter Platzhalterzuweisung kann ein Wert einer Variable zugewiesen werden, die sich innerhalb eines Blocks (inkl. des Unterblocks) befindet. Variablen, die sich in anderen Blöcken befinden, werden dagegen ignoriert.
$t->assignForBlock('MY_BLOCK_VARIABLE', 'my block value');

3. Globale Zuweisung von Platzhalterwerten

Bei dieser Zuweisungsart können Werte zu allen Template-Variablen zugewiesen werden. Es spielt also keine Rolle, ob sich eine Template Variable in irgendwelchen Blöcken befindet oder direkt auf Root-Ebene des Templates. Die Verwendung von dieser Zuweisungsart ist nur bei Template-Variablen sinnvoll, die häufig in mehreren Quelltextbereichen vorkommen. Für die Zuweisung von globalen Werten wird die SeparateTemplate.assignForGlobal(...)-Methode benutzt.
$t->assignForGlobal('MY_GLOBAL_VARIABLE', 'my global value');

Formatierung von Platzhalterwerten

Standardmäßig werden alle Werte ohne Veränderung im HTML-Quelltext dargestellt. Manchmal ist es jedoch notwendig, diese vorher zu verändern. Zum Beispiel dann, wenn auf einer Seite bestimmter Text dargestellt wird, der HTML-Steuerzeichen (<, >, &, ...) enthält. Diese Zeichen müssen beim Generieren der Seite umgewandelt werden. Um das Ganze möglichst elegant und dynamisch zu machen, unterstützt Separate sogenannte Value Formatter.
 
Ein Value Formatter besteht aus Anweisungen, die eine Zeichenkette umwandeln. Wenn ein Wert einer Template-Variable zugewiesen wird, die Value Formatter verwendet, dann wird der zugewiesene Wert vor der Darstellung im generierten HTML-Quelltext durch den jeweiligen Value Formatter umgewandelt. Die Syntax für eine Template-Variable mit festgelegtem Value Formatter sieht wie folgt aus:
${(<Value Formatter Name>)<Variable Name>}

Folgendes Beispiel demonstriert eine einfache Value-Formatter-Implementierung, welche den Unix Timestamp in ein lesbares Datum bzw. Zeit umwandelt.
 
PHP-Quelltext:
<?php

require_once './SeparateTemplate.php';
require_once './formatter/DateFormatter.php';
require_once './formatter/TimeFormatter.php';

$t = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');
$t->assign('UNIX_TIMESTAMP', time());
$t->display();

?>

Template-Quelltext (index.htm):
<html>
    <body>
        Timestamp: ${UNIX_TIMESTAMP}<br>
        Date: ${(Date)UNIX_TIMESTAMP}<br>
        Time: ${(Time)UNIX_TIMESTAMP}
    </body>
</html>

Der daraus generierte HTML-Quelltext sieht dann wie folgt aus:
<html>
    <body>
        Timestamp: 1354175751<br>
        Date: 29.11.2012<br>
        Time: 08:55
    </body>
</html>

Default Value Formatter

Mit der Methode SeparateTemplate.setDefaultFormatter(...) kann ein Value Formatter gesetzt werden, welcher standardmäßig bei allen Template-Variablen verwendet wird.
$t->setDefaultFormatter(new TextFormatter());

Vorhandene Value Formatter

Mit Separate werden mehrere Value Formatter bereits mitgeliefert.
 
Formatter Klasse Name Beschreibung
DateFormatter Date Konvertiert Unix Timestamp in ein lesbares Datum, z.B. 21.11.2012
TimeFormatter Time Konvertiert Unix Timestamp in eine lesbare Zeitangabe, z.B. 12:30
DateTimeFormatter DateTime Konvertiert Unix Timestamp in ein lesbares Datum mit zusätzlicher Zeitangabe, z.B. 21.11.2012 12:30
TextFormatter Text Ersetzt alle HTML-Steuerzeichen (z.B. <, > oder &) durch dafür vorgesehene HTML-Zeichen (z.B. &lt;, &gt; oder &amp;).
UrlFormatter Url Erstellt eine URL-konforme Zeichenkette, welche
primär für Hyperlinks vorgesehen ist.
HashFormatter Hash Hash wird in einem IF-Konstrukt benutzt, um zwei unbekannte Zeichenketten zu vergleichen.
IsEmptyFormatter IsEmpty IsEmpty wird in einem IF-Konstrukt benutzt, um zu prüfen, ob ein Platzhalter leer ist.

Eigene Value Formatter erstellen

Neben bereits vorhandenen Value Formatter können auch eigene Value Formatter erstellt werden. Dazu muss eine Klasse angelegt werden, die AbstractValueFormatter Klasse vererbt. Anschließend muss die Methode AbstractValueFormatter.formatValue(...) mit eigener Business Logik überschrieben werden.
 
Bei der Benennung von benutzerdefinierten Formatter-Klassen muss eine bestimmte Schreibweise eingehalten werden. Der Klassenname fängt mit einem Formatter Namen an, welcher bei Template-Variablen verwendet wird, und endet immer mit dem Wort "Formatter".
 
Syntax der Klassenbezeichnung für einen benutzerdefinierten Value Formatter:
<Formatter Name>Formatter

Folgendes Beispiel zeigt eine Implementierung von Value Formatter, welcher den Platzhalterwert in Großbuchstaben umwandelt.
 
PHP-Quelltext:
<?php

require_once './SeparateTemplate.php';

class ToUpperFormatter extends AbstractValueFormatter
{
    public function formatValue($value)
    {
        return strtoupper($value);
    }
}


$t = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');
$t->assign('MY_VARIABLE', 'my value');
$t->display();

?>

Template-Quelltext (index.htm):
<html>
    <body>
        ${(ToUpper)MY_VARIABLE}
    </body>
</html>

Der daraus generierte HTML-Quelltext sieht dann wie folgt aus:
<html>
    <body>
        MY VALUE
    </body>
</html>

Platzhalter ohne Formatierungsmöglichkeit

Manchmal ist eine maximal mögliche Geschwindigkeit der HTML-Quelltext Generierung sehr wichtig. Für diese Anforderung existiert eine spezielle Platzhalter-Art, welche im Rendering sehr schnell ist, dafür aber keine Formatierung durch Value Formatter unterstützt.
 
Im Template-Quelltext beginnen Platzhalter dieser Art mit einem #-Zeichen:
#{<Variable Name>}

Im PHP-Quelltext müssen hierfür spezielle Assign-Methoden benutzt werden. Diese Methoden sind mit normalen Assign-Methoden identisch, fangen jedoch mit einem x-Zeichen an.
$t->xassign('MY_VARIABLE', 'my value');
$t->xassignForBlock('MY_BLOCK_VARIABLE', 'my block value');
$t->xassignForGlobal('MY_GLOBAL_VARIABLE', 'my global value');

Template Including

In eine Templatedatei können auch andere Templatedateien eingebunden werden.
 
Syntax des Include-Befehls:
<!-- INCLUDE <File Name> -->

Folgendes Beispiel zeigt eine Implementierung des Include-Befehls im Template-Quelltext.
 
Header-Datei (header.htm):
<html>
    <body>

Footer-Datei (footer.htm):
    </body>
</html>

Template-Quelltext (index.htm):
<!-- INCLUDE header.htm -->
    <p>Hallo</p>
<!-- INCLUDE footer.htm -->

IF-Konstrukt

Manchmal ist es notwendig, bestimmte Entscheidungen auf die Darstellungsschicht zu verlagern. Um dies möglich zu machen, werden IF-Konstrukte unterstützt. Die Syntax eines IF-Konstrukts sieht wie folgt aus:
<!-- IF <Bedingung 1> -->
    HTML-Quelltext...
<!-- ELSE IF <Bedingung 2> -->
    HTML-Quelltext...
<!-- ELSE -->
    HTML-Quelltext...
<!-- END IF -->

Die Bedingung kann aus mehreren Template-Variablen zusammengestellt werden. Es können alle Vergleichsoperatoren verwendet werden, die auch im PHP existieren. Im Folgenden ist eine einfache Beispielimplementierung von einem IF-Konstrukt abgebildet.
 
PHP-Quelltext:
<?php

require_once './SeparateTemplate.php';

$t = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');
$t->assign('MY_VARIABLE', 'hallo');
$t->display();

?>

Template-Quelltext (index.htm):
<html>
    <body>
        <!-- IF '${MY_VARIABLE}' == 'hallo' -->
            The value of MY_VARIABLE is 'hallo'
        <!-- ELSE -->
            The value of MY_VARIABLE is '${MY_VARIABLE}'
        <!-- END IF -->
    </body>
</html>

Besonderheiten beim Vergleich von Werten

Um eine maximale Geschwindigkeit zu ermöglichen, werden von der Template Engine bestimmte Validierungen nicht durchgeführt. Aus diesem Grund muss der Entwickler einige Besonderheiten beim Erstellen einer IF-Abfrage beachten.
 
Um zwei Variablen mit unbekannten Werten zu vergleichen, muss ein Hash Formatter benutzt werden, welcher die Werte beim Vergleich in einen Hash-String umwandelt.
<!-- IF '${(Hash)MY_VARIABLE_A}' == '${(Hash)MY_VARIABLE_B}' -->
    ...
<!-- END IF -->

Um zu überprüfen, ob eine Variable einen leeren String enthält, muss der IsEmpty Formatter benutzt werden. Wenn die Variable einen leeren String enthält, wird der Wert durch den Formatter in "TRUE" umgewandelt. Ansonsten wird der Wert in "FALSE" umgewandelt.
<!-- IF '${(IsEmpty)MY_VARIABLE}' == 'TRUE' -->
    ...
<!-- END IF -->

Um boolesche Vergleiche zu vereinfachen, steht die SeparateTemplate::booleanToString(...) Methode zur Verfügung. Diese wandelt einen booleschen Wert in "TRUE" oder "FALSE" um.
 
PHP-Quelltext:
$t->xassign('LOGGED_IN_FLAG', SeparateTemplate::booleanToString(...));

Template-Quelltext:
<!-- IF '#{LOGGED_IN_FLAG}' == 'TRUE' -->
    ...
<!-- END IF -->

Template Parameter

Mit Template Parametern können beliebige Einstellungen im Template definiert werden. Diese Parameter können dann im PHP-Quelltext ausgelesen werden. Aber wofür braucht man das? Hier ist ein Beispiel aus der Praxis:
 
Manchmal kommt es vor, dass eine Webanwendung unterschiedliche Layouts besitzt. Zum Beispiel wenn neben dem normalen Layout ein weiteres Layout für mobile Endgeräte angeboten wird. In der Praxis werden für dieselbe Business-Logik zwei unterschiedliche Templates entwickelt - eins für Standard-Layout, und eins für mobile Endgeräte. Nehmen wir mal an, dass wir eine dynamisch generierte Tabelle mit vielen Datensätzen haben, die wiederum auf mehrere Seiten verteilt sind. Bei dem Standard-Layout werden auf jeder Seite 100 Datensätze angezeigt. Im Layout für mobile Endgeräte sollen aber max. 10 Datensätze pro Seite angezeigt werden, da es ansonsten zu viel wird. Um es zu realisieren, ohne irgendwelche Änderungen im PHP-Quelltext zu tätigen, wurden sogenannte Template-Parameter entwickelt.
 
Template-Parameter Syntax:
<!-- PARAMETER <Parameter Name> '<Parameter Wert>' -->

Beispiel:
<!-- PARAMETER NUMBER_OF_ROWS '100' -->

Diese Parameter können mit der SeparateTemplate.getParameters()-Methode ausgelesen werden. Mit der SeparateTemplate.getParameterValue(...)-Methode kann der Inhalt von einem beliebigen Parameter ermittelt werden. Falls ein Parameter nicht existiert, wird eine Exception geschmissen. Es macht also Sinn, den Aufruf mit der SeparateTemplate.isParameterSet(...)-Methode vorher zu überprüfen. Außerdem können auch eigene Parameter während der Ausführung festgelegt werden oder der Inhalt von bereits vorhandenen Parametern geändert werden. Dazu steht die SeparateTemplate.setParameterValue(...)-Methode zur Verfügung.
 

Template-Kommentare

Jeder weiß, dass im HTML eine Möglichkeit existiert, Quelltext Kommentare zu schreiben. Die Kommentare sehen dabei wie folgt aus:
<!-- HTML-Kommentar... -->

Solche Quelltext-Kommentare haben aber zwei Nachteile: Diese werden an den Client gesendet und erzeugen unnötigen Traffic. Ein weiterer Nachteil besteht darin, dass solche Kommentare auf der Client-Seite (im HTML-Quelltext) sichtbar sind. Aus diesem Grund bietet Separate eine Möglichkeit an, spezielle Template-Kommentare zu erstellen. Diese Template-Kommentare sind im generierten HTML-Quelltext nicht sichtbar und werden somit auch nicht an den Client weitergeleitet.
 
Die Syntax sieht wie folgt aus:
<!--- Template-Kommentar... --->

Empfehlungen

Die Benennung von vielen Quelltext-Komponenten ist dem Programmierer selbst überlassen. Trotzdem wird empfohlen, eine vorgegebene Namensgebung zu verwenden.
 

Benennung von SeparateTemplate-Variablen im PHP-Quelltext

Es wird empfohlen, alle SeparateTemplate-Objekte in einer Array (z. B. $t) abzuspeichern. Der Blockname wird dabei einfach als Key übergeben. Im unteren Beispiel steht $t['.'] für das Root-Template und $t['block']['.'] bzw. $t['block']['sub_block']['.'] für jeweilige Blöcke.
$t['.'] = SeparateTemplate::instance()->loadSourceFromFile('./index.htm');

for(...)
{
    $t['block']['.'] = $t['.']->fetch('block');
    
    for(...)
    {
        $t['block']['sub_block']['.'] = $t['block']['.']->fetch('sub_block');
        
        //...
        
        $t['block']['.']->assign('sub_block', $t['block']['sub_block']['.']);
    }
    
    $t['.']->assign('block', $t['block']['.']);
}

$t['.']->display();

Benennung von Template-Variablen in großen Projekten

In großen Projekten kann es vorkommen, dass ein Variablenname mehrere fachliche Bedeutungen hat. Zum Beispiel kann bei einer mehrsprachigen Anwendung die Template-Variable NAME für einen Benutzernamen (wie z.B. "Max Mustermann") sowie für die Aufschrift in aktueller Sprache (z.B. "Name") stehen. Um diese Konflikte zu vermeiden, macht es Sinn, einen Präfix für jede fachliche Bedeutung zu definieren:
#{LANGUAGE.NAME}: ${VALUE.NAME}

Im oberen Beispiel beginnen alle Template-Variablen für Phrasen in aktueller Sprache mit LANGUAGE und alle Variablen für dynamische Werte mit VALUE.
 
Ein weiterer Vorteil besteht darin, dass es bei jeder Seitengenerierung möglich ist, Werte für die LANGUAGE-Variablen automatisch zu setzen. Um alle LANGUAGE-Variablen zu ermitteln, die im aktuellen Template existieren, kann die Methode SeparateTemplate.getVariableNames() verwendet werden. Anschließend können die Werte mit der Methode SeparateTemplate.xassignForGlobal(...) gesetzt werden.
 

Lizenz

Separate Template Engine wurde von Eduard Sudnik entwickelt und ist ein open-source Framework für PHP, welches unter der MIT-Lizenz zur Verfügung steht.
© 2004—2017 Eduard Sudnik E-Mail: separate@esud.info