Inhaltsverzeichnis
PHPTAL ist eine Implementierung des hervorragenden »Zope Page Template«- (ZPT-)Systems in PHP. PHPTAL unterstützt die TAL-, METAL- und I18N-Namensräume.
PHPTALES ist das Äquivalent zu TALES, der »Template Attribute Language Expression Syntax«. Es legt fest, wie XML-Attributwerte behandelt werden.
Da PHPTALES TALES sehr stark ähnelt, sollte es leicht sein, Python-TAL- Vorlagen in solche für PHP beziehungsweise andersherum zu übertragen.
Um TAL-konform zu sein, implementiert PHPTAL einen XPath-artigen Datenzugriff.
PHPTAL ist unter der LGPL frei verfügbar; es wurde von Laurent Bedubourg <lbedubourg@motion-twin.com>
entwickelt und wird von Kornel Lesiński betreut.
Wir benutzen XML-/HTML-Vorlagen (»Templates«), um Logik (weshalb wird was ausgegeben) und Darstellung (wie sieht es im Brauser aus) voneinander zu trennen. Diese Trennung hat mehr als einen Vorteil:
besseres Anwendungsdesign
Umstrukturierungen werden einfacher
bessere Wartbarkeit
das Design lässt sich leicht ändern
Die meisten Vorlagensysteme (»Template-Systeme«) nutzen <? ?>
-, <% %>
- oder <xxx:yyy></xxx:yyy>
-Auszeichnungen, um die Stellen zu finden, die verarbeitet werden müssen. Dadurch wird die Entwicklung des Vorlagensystems einfacher, dem (Vorlagen-) Designer wird die Arbeit aber unnötig erschwert.
TAL versteckt seine Logik größtenteils in XML-Attributen und erhält dabei die Syntax und Struktur von XHTML. Dadurch ist eine direkte Betrachtung von TAL-Vorlagen in Brausern möglich (WYSIWYG-Editoren, unmittelbare Vorschau). Die HTML-Syntax-Hervorhebung (»Syntax Highlighting«) im Editor wird nicht gestört.
Falls Sie schon einmal mit einem einfachen Vorlagensystem gearbeitet haben, werden Ihnen bereits Codeschnipsel begegnet sein, die ungefähr wie folgt aussehen:
<table>
<%loop myarray as myitem %>
<tr>
<td><% myitem %></td>
</tr>
<%/loop%>
</table>
Mit PHPTAL können Sie das folgendermaßen schreiben:
<table>
<tr tal:repeat="myitem myarray">
<td tal:content="myitem">
text replaced by the item value
</td>
<td tal:replace="">sample 1</td>
<td tal:replace="">sample 2</td>
<td tal:replace="">sample 3</td>
</tr>
</table>
Der obige Code wird samt der enthaltenen Beispieldaten in einem normalen Brauser angezeigt werden, das heißt, dass Sie die Seite Ihren Kunden zeigen können, ohne dass auch nur eine Zeile des Codes, der das Feld myarray
erzeugt, schon existieren muss.
Ein weiterer großer Vorteil von PHPTAL ist, daß Sie von der mehr als 3-jährigen Erfahrung, der Dokumentation und der Hilfe der Zope-Gemeinschaft profitieren. PHPTAL verlässt sich auf diese Gemeinschaft, um seinen Nutzern eine große Menge nützlicher Informationen zu bieten.
PHPTAL ist so aufgebaut, dass es für fortgeschrittene Entwickler und anspruchsvolle (d.h. ressourcenintensive) Systeme so anpassbar wie möglich ist, aber dennoch für den Anfänger durch bequeme und einfache Grundeinstellungen benutzbar bleibt (Wir haben es zumindestens probiert :).
PHPTAL wird als PEAR-Paket verteilt (siehe pear.php.net). Sie können die PHPTAL-Bibliothek aber auch von der PHPTAL-Webseite (phptal.org) herunterladen.
So installieren Sie PHPTAL via PEAR:
pear install http://phptal.org/latest.tar.gz
Einmal installiert, können Sie PHPTAL via PEAR leicht auf aktuellem Stand halten:
pear upgrade http://phptal.org/latest.tar.gz
Nutzen Sie PEAR nicht, oder ist es auf ihrem Rechner nicht installiert, können Sie auf das komprimierte Archiv ausweichen:
tar zxvf PHPTAL-X.X.X.tar.gz
cp -r PHPTAL-X.X.X/PHPTAL* /path/to/your/lib/folder
Damit wird die Datei PHPTAL.php
und der zugehörige PHPTAL-Ordner in /path/to/your/lib/folder
installiert.
Um einen ersten Eindruck von PHPTAL zu gewinnen, hier ein einfaches Beispiel statt vieler Worte.
Ihre Vorlage ist ein gültiges XML-/HTML-Dokument (samt Wurzelelement). Die Datei nennen wir my_template_file.xhtml
:
<?xml version="1.0"?>
<html>
<head>
<title tal:content="title">
Platz für den Seitentitel
</title>
</head>
<body>
<h1 tal:content="title">Beispieltitel</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Telefonnummer</th>
</tr>
</thead>
<tbody>
<tr tal:repeat="person people">
<td tal:content="person/name">Name der Person</td>
<td tal:content="person/phone">Telefonnummer der Person</td>
</tr>
<tr tal:replace="">
<td>Beispielname</td>
<td>Beispielnummer</td>
</tr>
<tr tal:replace="">
<td>Beispielname</td>
<td>Beispielnummer</td>
</tr>
</tbody>
</table>
</body>
</html>
In php müssen Sie lediglich die PHPTAL-Bibliothek einbinden und eventuell eine Variable anpassen, um ihre Vorlage zu individualisieren.
<?php
require_once 'PHPTAL.php';
// create a new template object
$template = new PHPTAL('my_template_file.xhtml');
// the Person class
class Person {
public $name;
public $phone;
function Person($name, $phone) {
$this->name = $name;
$this->phone = $phone;
}
}
// let's create an array of objects for test purpose
$people = array();
$people[] = new Person("bla", "0162/4578930");
$people[] = new Person("blabla", "0714/87629");
$people[] = new Person("blubb", "0102/6637912");
$people[] = new Person("blablubb", "0190/123456");
// put some data into the template context
$template->title = 'Der Titeltext';
$template->people = $people;
// execute the template
try {
echo $template->execute();
}
catch (Exception $e){
echo $e;
}
?>
Wenn sie das PHP Script ausführen, werden sie etwas ähnlich dem Folgenden erhalten.
<?xml version="1.0"?>
<html>
<head>
<title>Der Titeltext</title>
</head>
<body>
<h1>Der Titeltext</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Telefonnummer</th>
</tr>
</thead>
<tbody>
<tr>
<td>bla</td>
<td>0162/4578930</td>
</tr><tr> <td>blabla</td>
<td>0714/87629</td>
</tr><tr> <td>blubb</td>
<td>0102/663791</td>
</tr><tr> <td>blablubb</td>
<td>0190/123456</td>
</tr>
</tbody>
</table>
</body>
</html>
PHPTAL schert sich nicht besonders um Zeilenumbrüche und Einrückungen in den Dateien, die es liest bzw. erzeugt. Möchten Sie, dass der erzeugte HTML-Code hübsch wird, d.h mit Zeilenumbrüchen und perfekten Einrückungen, müssen Sie ihn eventuell mit HTML-Tidy nachbearbeiten.
Inhaltsverzeichnis
»Template Attribute Language« (TAL) bedeutet ungefähr soviel wie attributbasierte Vorlagensprache. Dieser Abschnitt beschreibt TAL und seine Erweiterungen. Er zielt hauptsächlich auf Template-Designer ab, sollte aber auch von PHP-Programmierern gelesen werden.
Es ist wichtig zu wissen, dass die Reihenfolge der TAL-Attribute innerhalb einer Auszeichnung bedeutungslos ist.
Zum Beispiel ist
<span tal:define="usersList application/listUsers"
tal:condition="somecondition"
tal:repeat="user usersList"
>…</span>
genau dasselbe wie:
<span tal:repeat="user usersList"
tal:condition="somecondition"
tal:define="usersList application/listUsers"
>…</span>
Die PHPTAL-Rangfolge enspricht der in der TAL-Beschreibung festgelegten Abarbeitungsreihenfolge:
define
condition
repeat
content oder replace
attributes
omit-tag
Der URI für diesen Namensraum lautet http://xml.zope.org/namespaces/tal
. Um das tal:
Attribut Präfix in XML zu verwenden, müssen Sie es deklarieren:
<html xmlns:tal="http://xml.zope.org/namespaces/tal" …>
PHPTAL erzwingt diese Angabe nicht.
tal:define
Dieses Attribut definiert eine oder mehrere Variablen, die im weiteren Verlauf in der Vorlage verwendet werden können.
Es können eine oder mehrere durch Semikolon getrennte Variablen angegeben werden.
Definition eines Kürzels für einen langen Pfad:
<span tal:define="global destname pfad/zu/einer/Variable">…</div>
Mehrere Variablen zugleich definieren:
<span tal:define="global vname string:Hans; nname string:Meier">…</span>
Beginnt eine Definition mit global
Schlüsselwort ist die Variable dannach überall in der Vorlage und in allen Makros sichtbar. Eine globale Variablen darf später in der Vorlage auch neu definiert werden.
<span tal:define="global hello string:Hallo Welt"/>
<p tal:content="hello"/>
Im Gegensatz dazu ist eine lokale Variable nur innerhalb der Auszeichnung sichtbar, in der Sie definiert ist.
<span tal:define="hello string:Hallo Welt"/>
<p tal:content="hello"/>
Dieser Code liefert einen 'undefined variable'-Fehler.
Sie können tal:define
auch zusammen mit anderen Attributen verwenden. Es wird in jedem Fall vor allen anderen Attributen ausgewertet.
Definition einer Zeichenkette innerhalb einer Vorlage:
<span tal:define="global destname string:irgendetwas">…</span>
Defining a string containing another variable:
<span tal:define="fname string:Paul; hello string:Hello $fname! Welcome to this page" />
Definition einer Zeichenkette, die eine weitere Variable enthält:
<span tal:define="global hello string:Hallo $fname, willkommen auf dieser Seite" />
Ein kleiner Trick, der den Inhalt einer Auszeichnung verwendet (für sehr komplexe Werte hilfreich):
<span tal:define="global hello">Hallo ${fname}, willkommen auf dieser Seite</span>
Dieser Spezialfall funktioniert nur bei Verwendung des global
Schlüsselwortes.
In obigen Beispielen wird die <span>
-Auszeichnung in der Ausgabe nicht auftauchen, da sie weder druckbaren Inhalt noch Attribute enthält. Selbst die Meldung im letzten Beispiel wird nicht erscheinen. Sie wird von der Variablen hello
geschluckt.
Andererseits wird durch
<span tal:define="hello string:Hallo ${fname}, willkommen auf dieser Seite" tal:content="hello" />
sowohl die Variable hello
gesetzt, als auch der Text ausgegeben.
Der folgende Code aber ist nicht erlaubt, da tal:define
der Variablen »hello« den Inhalt des Knotens zuweist. Der Inhalt des Knotens ist allerdings zu diesem Zeitpunkt noch unklar, da tal:content
ihn erst später festlegt. Der tatsächliche Inhalt wird dabei wie Beispielinhalt ignoriert. hello
wird nicht definiert, und Sie erhalten eine Fehlermeldung.
<span tal:define="hello" tal:content="hello">
Hallo ${fname}, willkommen auf dieser Seite
</span>
tal:condition
Das Element und sein Inhalt werden nur angezeigt, wenn das Ergebnis der Bedingung wahr ist.
<p tal:condition="identified"> Welcome member … </p>
<p tal:condition="not: identified">
Please login before accessing this page
</p>
Wenn ihr PHP Unterbau ihrer Vorlage nicht genügend Methoden zu Verfügung stellt, werden Sie des öfteren auf PHP zurückgreifen müssen, um spezielle Bedingungen zu prüfen:
<span tal:comment="show only if more than five items in the cart"
tal:condition="php: cart.countItems() GT 5">…</span>
Dadurch kann unerwünscht viel Logik in der Vorlage landen. Manchmal ist es daher besser, der Vorlage boolsche Ausdrücke oder Methoden anzubieten.
<span tal:condition="cart/hasEnoughItems">…</span>
tal:repeat
Dieses Attribut arbeitet auf abzählbaren Objekten wie Feldern, assoziativen Feldern oder Objekten, die die PHP5 Iterator
Klasse implementiert.
Das tal:repeat
Attribut wiederholt seine Auszeichnung und seinen Inhalt solange, bis es am Ende der angegebenen Quelle (Feld, Objekt) angekommen ist.
<tr tal:repeat="item some/result">
<td tal:content="item">text replaced by item</td>
</tr>
Innerhalb einer solchen Schleife können Sie mit speziellen repeat/
Pfaden auf aktuelle Schleifenzustände (und die ihrer Eltern für verschachtelte Schleifen) zugreifen.*
In obigen Beispiel liefert
repeat/item/key
: den Elementenschlüssel, wenn some/result eine assoziative Quelle ist (sonst den Index)
repeat/item/index
: den Elementenindex (0 bis Gesamtanzahl-1)
repeat/item/number
: die Elementennummer (1 bis Gesamtanzahl)
repeat/item/even
: wahr, wenn der Index des Elementes gerade ist
repeat/item/odd
: wahr, wenn der Index des Elementes ungerade ist
repeat/item/start
: wahr, wenn das Element das erste ist
repeat/item/end
: wahr, wenn das Element das letzte ist
repeat/item/length
: die Anzahl der Elemente in some/result
item
ist die Variable, die im tal:repeat
Ausdruck definiert wird.
tal:repeat
wird in den meisten Fällen auf das Ergebnis einer SQL Datenbankabfrage angewendet werden. Der folgende Code funktioniert wenn playersRanking
ein Objekt enthält, das das PHP Iterator
Interface implementiert:
<table>
<thead>
<tr>
<th>Position</th>
<th>Player</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr tal:repeat="ranking playersRanking">
<td tal:content="ranking/position"/>
<td tal:content="ranking/player"/>
<td tal:content="ranking/score"/>
</tr>
</tbody>
</table>
tal:omit-tag
Dieses Attribut gibt dem PHPTAL Parser auf, die Anfangs- und Endauszeichnung des umgebenden Elementes zu ignorieren und nur den Inhalt auszugeben.
<span tal:omit-tag="condition">
if the condition is true, then only this text will appear and span open and close will be removed
</span>
Ergibt:
only this text will appear, span open and close will be removed
Dieses Attribut ist nützlich, wenn Sie ein Auszeichnung wahlweise darstellen möchten oder nicht. Z.B. kann ein Text in Abhängigkeit von einer Bedingung einmal als Text und einmal als Verweis ausgezeichnet werden.
Wenn Sie ein Element benötigen, das niemals ausgegeben wird, können Sie dazu tal:block
verwenden
<tal:block tal:repeat="x php:range(1,10)">only this text will appear, ten times.</tal:block>
tal:replace
Dieses Attribut ersetzt die gesamte Auszeichnung durch den angegebenen Wert, oder durch nichts, wenn kein Wert agegeben wird.
<span tal:replace="string:this beautiful string">
this ugly string and span
</span>
Ergibt:
this beautiful string
tal:replace
kann auch verwendet werden und Beispiele in Vorlagen zu schreiben die in der endgültigen Ausgabe nicht enhalten sein sollen.
<table>
<tr tal:repeat="item myresult">
<td tal:content="item">item value</td>
</tr>
<tr tal:replace="">
<td>sample 1</td>
</tr>
<tr tal:replace="">
<td>sample 2</td>
</tr>
</table>
tal:content
Dieses Attribut ersetzt den Auszeichnungsinhalt durch das Ergebnis des enthaltenen Ausdrucks.
<span tal:define="myvar string:my string"/>
<span tal:content="myvar">will be replaced</span>
Ergibt:
<span>my string</span>
tal:attributes
Mit tal:attributes
können HTML Attribute gesetzt oder verändert werden.
<a href="http://www.foo.com" title="some foo link"
tal:attributes="href somelink/href; title somelink/title"
tal:content="somelink/text"
>sample link</a>
Wobei 'somelink
' aus folgendem besteht:
$somelink->href = "http://www.google.com";
$somelink->title = "google search engine";
$somelink->text = "the google search engine";
Ergibt:
<a href="http://www.google.com"
title="google search engine">the google search engine</a>
Das Semikolon (;
) trennt einzelne Attribute. Möchten Sie ein Semikolon ausgeben, so müssen Sie es verdoppeln (;;
).
Ein etwas komplexeres Beispiel zu tal:repeat
:
<tr tal:repeat="ranking playerRankings"
tal:attributes="class php: repeat.ranking.odd ? 'odd' : NULL">
…
</tr>
Der php:
Operator wird später genauer erklärt werden. Hier wird, wenn die Zeilennummer ungerade ist, tr
ein class
Attribut mit 'odd' als Wert zugewiesen sonst wird kein class
Attribut gesetzt.
"condition ? then : else
" ist ein normales PHP Konstrukt, daß vorsichtig verwendet werden muß, sich aber in mehr als einer Situation als nützlich erweist.
Ein besserer Weg um dasselbe Ergebnis zu erhalten ist es, den PHP Programmierer um einen maßgeschneiderten Operator zu bitten (siehe PHP Integration / maßgeschneiderte Operatoren), der dann wie folgt genutzt wird:
<tr tal:repeat="ranking playerRankings"
tal:attributes="class css-odd:repeat/ranking/odd">
…
</tr>
Der Operator sollte "odd" zurückgeben, wenn repeat/ranking/odd
wahr ist, sonst NULL
.
Verwenden Sie in tal:attributes
TALES-Alternativen (die a|b|c Notation) und ist nothing
(oder NULL
in PHP) die letzte der Alternativen, wird das Attribut gar nicht ausgegeben wenn kein Wert dafür vorhanden ist. (Hierdurch werden leere Attribute vermieden.):
… tal:attributes="title object/tooltip | nothing">
XHTML-Attribute wie selected
, checked
usw. werden automatisch richtig verwendet.
<input type="checkbox" tal:attributes="checked object/isChecked"/>
Beachten Sie, daß XHTML Groß- und Kleinschreibung unterscheidet. SELECTED
ist in XHTML ein Fehler. Verwenden Sie selected
.
tal:on-error
Wenn ein Pfadfehler oder irgendeine PHP-Ausnahme in der Auszeichnung auftritt, ersetzt dieses Attribut die Auszeichnung durch das Ergebnis des tal:on-error
-Ausdrucks.
<span tal:on-error="string:No username defined here"
tal:content="user/name">the user name here</span>
Tritt beim Zugriff auf name
oder user
ein Fehler auf, wird die Fehlermeldung anstelle der Auszeichnung ausgegeben.
Dies funktioniert auch bei mehreren Vorlagenebenen:
<span tal:on-error="string:error occurred somewhere">
<span tal:content="user/firstname"/>
<span tal:content="user/lastname"/>
<span metal:use-macro="userMenu" />
</span>
Der URI für diesen Namensraum lautet http://xml.zope.org/namespaces/metal
. Um das metal:
Attribut Präfix in XML zu verwenden, müssen Sie es deklarieren:
<html xmlns:tal="http://xml.zope.org/namespaces/metal" …>
PHPTAL erzwingt diese Angabe nicht.
METAL ist die Kurzform für „Macro Extension for TAL“. Dieser Namensraum erlaubt es Vorlagendesignern, Makros zu definieren und aufzurufen. Makros können für die rekursive Ausgabe von Daten oder zum Einbinden von Code aus anderen Vorlagen verwendet werden.
metal:define-macro
Dieses Attribut deklariert ein Makro. Mit Makros lassen sich Bibliotheken aus kleinen Vorlagen erstellen, die in anderen Vorlagen wiederverwendet werden können.
<div metal:define-macro="main_menu">
<ul>
<li><a href="/">home</a></li>
<li><a href="/products">products</a></li>
<li><a href="/contact">contact</a></li>
</ul>
<div>
Last modified:
<span tal:content="mdate">page modification date</span>
</div>
</div>
Makros erben den Variablenkontext des Aufrufers. In obigem Beispiel hängt die Variable „mdate
“ von der aufrufenden Vorlage ab.
metal:use-macro
Dieses Attribut ruft ein Makro auf und setzt das Ergebnis an seiner statt in die aktuelle Vorlage ein.
<span
tal:comment="main_menu template requires 'mdate' variable"
tal:define="mdate page/last_modified"
metal:use-macro="main_menu"
/>
Makros lassen sich über die Angabe des Dateinamens auch aus anderen Vorlagen ansprechen.
<span metal:use-macro="site_macros.xhtml/main_menu"/>
Der PHPTAL-Ersetzungsmechanismus läßt sich auch innerhalb von metal:use-macro
Werten nutzen:
<span metal:use-macro="${design}/site_macros.xhtml/main_menu"/>
Ein Makro darf sich selbst aufrufen. Auf diese Weise können Sie Felder rekursiv ausgeben:
<ul metal:define-macro="show-list">
<li tal:repeat="item list">
<tal:block tal:condition="php:is_array(item)" tal:define="list item" metal:use-macro="show-list" />
<tal:block tal:condition="php:!is_array(item)" tal:content="item" />
</li>
</ul>
metal:define-slot
Dieses Attribut darf nur innerhalb einer Auszeichnung mit metal:define-macro
auftreten.
Slots können von der aufrufenden Vorlage mit, auch dynamisch generiertem, eigenem XML/XHTML-Inhalt gefüllt werden.
Slots können als eine Art rückwirkende Einfügungen gesehen werden; ein Makro kann eine ganze Seite erzeugen, die durch Slots in Abhängigkeit vom URL individualisiert wird.
<span metal:define-slot="news_place">
<table>
<tr tal:repeat="item php:latestNews()">
<td tal:content="item/value">news description</td>
</tr>
</table>
</span>
Obiges Beispiel definiert eine Stelle 'news_place', die durch die aufrufende Vorlage überschrieben werden kann. Im nächsten Abschnitt wird dieses Beispiel fortgeführt.
metal:fill-slot
Dieses Attribut darf nur innerhalb eines metal:use-macro
-Blocks verwendet werden.
Hiermit wird PHPTAL angewiesen, einen bestimmten Slot mit dem Inhalt innerhalb der metal:fill-slot
-Auszeichnung zu ersetzen.
<span tal:condition="logged" metal:fill-slot="news_place">
<h2>user menu</h2>
<ul>
<li><a href="/user/action/inbox">inbox</a></li>
<li><a href="/user/action/new">new mail</a></li>
<li><a href="/user/action/disconnect">disconnect</a></li>
</ul>
</span>
Slots ermöglichen durch ein einfaches Einsetzverfahren tatsächlich wiederverwendbare und individualisierbare Vorlagen.
Der URI für diesen Namensraum lautet http://xml.zope.org/namespaces/i18n
. Um das i18n:
Attribut Präfix in XML zu verwenden, müssen Sie es deklarieren:
<html xmlns:i18n="http://xml.zope.org/namespaces/i18n" …>
PHPTAL erzwingt diese Angabe nicht.
„i18n“ ist eine Kurzform für das englische 'internationalization' (i, 18 Buchstaben, n). Dieser XML-Namensraum erlaubt es Template-Designern, Textbereiche anzugeben, die in die jeweilige Ausgabesprache übersetzt werden sollen.
i18n:translate
Dieses Attribut definiert Text, der durch das PHPTAL-Übersetzungssystem übersetzt werden soll.
<div i18n:translate="string:welcome_message">Welcome here</div>
Im obigen Beispiel wird PHPTAL nach einem Übersetzungsschlüssel 'welcome_message' suchen und den Auszeichnungsinhalt durch die Übersetzung in der gerade aktuellen Sprache ersetzen.
<div i18n:translate="">Welcome here</div>
Hier ist die Verwendung ein wenig anders, da kein Übersetzungsschlüssel angegeben worden ist. PHPTAL wird den Auszeichnungsinhalt 'Welcome here' als Schlüssel benutzen. Kennt das Übersetzungssystem den Schlüssel 'Welcome here', ergibt das eine regelgerechte Übesetzung.
Wird keine Übersetzung gefunden, wird der Schlüssel als Ergebnis benutzt. Darum ist es sinnvoll, lesbare Texte statt Kürzel als Schlüssel zu verwenden.
Beachten Sie bitte, daß der Schlüssel, um eine dynamische Schlüsselwahl zu ermöglichen, auch in einer Variablen enthalten sein kann.
<div tal:define="welcome random_welcome_message">
<div i18n:translate="welcome">…</div>
</div>
i18n:attributes
Dieses Attribut legt fest, welche HTML-Attribute übersetzt werden sollen. Ähnlich zu i18n:translate
verlangt i18n:attributes
eine durch Semikola getrennte Liste aus Attribut-/Schlüssel-Paaren.
<img i18n:attributes="alt 'picture alternative text';title thetitle" alt="Picture" title="${thetitle}" />
i18n:name
Dieses Attribut weist einer Übersetzungsvariablen einen Wert zu.
Übersetzungen können ${xxx}
Zeichenketten enthalten, in denen "xxx
" den Namen einer Variablen bezeichnet, die dynamisch eingefügt werden soll.
Diese Variable enthält die Auszeichnung und ihren Inhalt. Wird die Auszeichnung um den Inhalt herum nicht benötigt, benutzen Sie tal:replace
anstatt tal:content
. Falls der Wert eine Verknüpfung von Zeichenketten ist kann tal:omit-tag
hilfreich sein.
<span i18n:name="myVar" tal:content="some/path"/>
<!-- <span>${some/path}</span> -->
<span i18n:name="myVar" tal:replace="some/path"/>
<!-- ${some/path} -->
<span i18n:name="myVar">foo</span>
<!-- <span>foo</span> -->
<span i18n:name="myVar" tal:omit-tag="">foo</span>
<!-- foo -->
Ein Beispiel zur Benutzung von i18n:
<div i18n:translate="">
Welcome <span i18n:name="user" tal:replace="user/name"/>,
you have <span i18n:name="mails" tal:replace="user/nbrMails"/>
unread mails.
</div>
Der Übersetzungsschlüssel lautet hier:
"Welcome ${user}, you have ${mails} unread mails."
PHPTAL wird in ihrer Übersetzung ${user}
durch ${user/name}
und ${mails}
durch ${user/nbrMails}
ersetzen.
Weitere Informationen zu I18N und PHPTAL finden sich im PHP-Kapitel dieses Buches.
In der Grundeinstellung wird angenommen, daß Übersetzungen nur Text enthalten; daher maskiert PHPTAL sämtliche "<" Zeichen.
In i18n:translate
können Sie das structure
-Schlüsselwort verwenden, um die Maskierung zu unterbinden und den übersetzten Text unverändert zu übernehmen.
<div i18n:translate="structure '<b>bold text</b>'" />
Ergibt:
<div><b>bold text</b></div>
Vorbehalt: Dies funktioniert nur in den einfachsten Fällen – TAL-Attribute innerhalb der Übersetzungen werden ignoriert. Nicht wohlgeformtes XHTML in Übersetzungen zerstört die Wohlgeformtheit der ganzen Ergebnisseite.
Der URI für diesen Namensraum lautet http://phptal.org/ns/phptal
. Um das phptal:
Attribut Präfix in XML zu verwenden, müssen Sie es deklarieren:
<html xmlns:phptal="http://phptal.org/ns/phptal" …>
PHPTAL erzwingt diese Angabe nicht.
Diese Attribute sind in den TAL-Spezifikationen nicht enthalten, aber sehr nützlich für die Arbeit mit PHPTAL.
phptal:debug
Dieses Attribut schaltet für den Inhalt der Auszeichnung innerhalb derer es definiert ist das PHPTAL Debugging ein.
Der Debugmodus speichert Informationen wie den Dateinamen und die Quellcodezeilennummer in der Vorlage, so daß Ausnahmemeldungen über fehlerhafte Pfadzugriffen mehr Information über das 'Wo' enthalten.
<html>
<head>
…
</head>
<body>
<div id="menu">
…
</div>
<div id="leftPane" phptal:debug=""
tal:comment="this div seems buggy, keep
trace of where errors are thrown">
…
</div>
</body>
</html>
phptal:cache
Durch dieses Attribut wird ein komplettes Element, also Auszeichnung samt Attributen und Inhalt, auf der Festplatte gepuffert und erst dann wieder neu interpretiert, wenn die Pufferzeit abgelaufen ist.
Eine solche Pufferung lohnt sich nur für Elemente, die sehr komplexe Ausdrücke, Makros aus externen Dateien, oder PHP-Ausdrücke/Objekte mit Datenbankzugriffen enthalten. Ansonsten sind nichtgepufferte Vorlagen genauso schnell.
Der Inhalt dieses Attributs ist eine Zeitspanne (wie lange soll das Element gepuffert werden), die als Zahl mit 'd
', 'h
', 'm
' oder 's
' als Einheit geschrieben wird.
<div class="footer" phptal:cache="3h">…</div>
<div>
wird maximal alle 3 Stunden einmal ausgeführt.
Der Zeitspanne kann wahlweise ein "per
"-Parameter folgen, der festlegt, ob der Puffer geteilt wird. In der Grundeinstellung wird ein Elementpuffer von allen Seiten genutzt, die die entsprechende Vorlage nutzen. Sie können ein "per url
" hinzufügen, so daß jede URL einen eigenen Puffer für das betreffende Element erhält.
<ol id="breadcrumbs" phptal:cache="1d per url">…</ol>
<ol>
wird für jede Seite separat einen Tag lang gepuffert.
Um für jeden einzelnen Wert eines Ausdrucks (der im Ergebnis eine Zeichenkette liefern muß) einen eigenen Puffer zu erhalten, können Sie "per expression
" verwenden.
Dabei dürfen sich die Ausdrücke nicht auf Variablen beziehen, die via tal:define
in der selben Auszeichnung definiert werden.
<ul id="user-info" phptal:cache="25m per object/id">…</ul>
<ul>
wird für jede Object-ID einzeln 25 Minuten lang gepuffert.
Seien Sie vorsichtig damit private Daten Ihrer Nutzer zu puffern. Der Zwischenspeicher wird von allen Nutzern geteilt, wenn Sie ihn nicht durch per user/id
oder ähnliche Ausdrücke benutzerspezifisch machen.
Es ist eine gute Idee, einen Puffer nicht händisch zu löschen, sondern in den per
-Parameter eine Versionsnummer oder einen Zeitstempel einzufügen. Dadurch wird die gepufferte Vorlage erneuert, sobald sich die Version oder der Zeitstempel ändert. Ein separates Pufferlöschen ist dann nicht nötig.
<div phptal:cache="100d per php:news.id . news.last_modified_date">…</div>
phptal:cache
-Blöcke können geschachtelt werden, aber der äußere Block puffert die inneren unabhängig von ihrem Alter.
metal:fill-slot
können Sie in Auszeichnungen mit phptal:cache
nicht verwenden.
phptal:tales
Mit diesem Attribut läßt sich das Verhalten von PHPTALES verändern. In der Grundeinstellung werden PHPTAL-Attribute eng an ZPT angelehnt interpretiert. In manchen Fällen bräuchte man aber nur PHP und findet sich bei der ständigen Verwendung des php:
- Operators wieder.
Ein weiterer Punkt ist die Art und Weise, in der PHPTAL Pfade auswerten muß. Zum Beispiel dauert die Auswertung von myobject/mymethod/property/10/othermethod/hashkey
relativ lange (denken Sie darüber aber nicht zuviel nach - optimieren Sie erst, wenn Sie wirklich ein Problem mit dem Durchsatz haben!).
Zur Laufzeit nimmt sich PHPTAL myobject
und findet heraus, daß es ein Objekt ist; stellt dann fest, daß mymethod
eine Methode dieses Objektes (und keine Variable) ist und ruft sie auf; untersucht das Ergebnis, um festzustellen, daß dies ein Objekt mit einer Eigenschaft ist; sieht, daß sein Wert ein Feld ist; greift sich das 10
-Element dieses Feldes und bestimmt, daß das ein Objekt ist; entscheidet, daß othermethod
eine Methode dieses Objektes (und keine Variable) ist und erhält das Ergebnis ihrer Ausführung; um dieses wiederum als Objekt zu erkennen und sich den Wert für den Schlüssel hashkey
zu holen.
Natürlich ist das ein extremes Beispiel, und da das Ganze schnell genug ist, interessiert es zumeist nicht. Was geschieht aber, wenn ein solcher Pfad innerhalb eines großen tal:repeat
aufgerufen wird? Hmm… Hier kann phptal:tales
hilfreich sein:
<html>
<body>
<table phptal:tales="php">
<tr tal:repeat="myobject document.getChildren()">
<td
tal:content="myobject.mymethod().property[10].otherMethod().hashkey"></td>
</tr>
</table>
</body>
</html>
Beachten Sie, daß obiges Beispiel dasselbe tut wie:
<html>
<body>
<table>
<tr tal:repeat="myobject php:document.getChildren()">
<td
tal:content="php:myobject.mymethod().property[10].otherMethod().hashkey"></td>
</tr>
</table>
</body>
</html>
Der php:
-Operator wird in einem eigenen Kapitel erläutert.
tal:block
tal:block
ist ein syntaktisches Zückerchen für Auszeichnungen, die viele TAL Attribute enthalten, die nicht ausgegeben werden sollen.
<tal:block define="myvar string:Some value"/>
ist dasselbe wie:
<span tal:define="myvar string:Some value" tal:omit-tag=""/>
Ein weiteres Beispiel:
<tal:block condition="someCondition" repeat="item someRepeat">
<div metal:use-macro="x"/>
</tal:block>
ist dasselbe wie:
<div tal:omit-tag=""
tal:condition="someCondition"
tal:repeat="item someRepeat">
<div metal:use-macro="x"/>
</div>
Zum Formulieren von Ausdrücken innerhalb von TAL-, METAL-, PHPTAL-Attributen wird PHPTALES verwendet. In den vorhergehenden Beispielen sind ihnen schon einige PHPTALES Beispielverwendungen (string:
, php:
, not:
, …) begegnet. Dieses Kapitel beschreibt nun die Verwendung von PHPTALES in Vorlagen.
Der Wert eines TAL-Attributs darf mehr als einen Ausdruck enthalten (z. B: tal:define
). Dabei müssen die einzelnen Ausdrücke durch ';
' getrennt werden.
Das ist der Standardoperator, der innerhalb eines TAL-Ausdrucks verwendet wird, wenn kein anderer Operator angegeben wird.
Folgende Zeilen liefern dasselbe Ergebnis:
<span tal:content="data/user/name"/>
<span tal:content="path:data/user/name"/>
<span>${data/user/name}</span>
In der Vorlage oder in einem Ausdruck können Sie auf eine bekannte Variable zugreifen, indem Sie ihren Pfad in der Form ${path/to/my/variable}
angeben.
<h1>${document/title}</h1>
<span tal:replace="string:welcome ${user/name},
this page has been readed ${page/countRead} times"/>
Falls Sie versuchen eine Variable zu lesen, die nicht existiert, wirft PHPTAL eine Ausnahme aus. Verwenden Sie exists: um zu überprüfen, ob eine Variable lesbar ist.
Da '<
', '>
' und '&
' in XML nur umständlich zu verwenden sind, bietet PHPTAL eine alternative PHP-Operator Syntax für diese und, aus Konsistenzgründen, ein paar weitere Zeichen.
Diese Syntax kann nur in php:
-Ausdrücken verwendet werden.
<
: LT
(kleiner als)
>
: GT
(größer als)
<=
: LE
(kleiner oder gleich)
>=
: GE
(größer oder gleich)
==
: EQ
(gleich)
!=
: NE
(ungleich)
&&
: AND
||
: OR
Da Ausdrücke mit ';
' getrennt werden, und weil '$
' Zeichen den Anfang eines Pfades markieren, müssen Sie
';;
' verwenden, wenn Sie ein ';
' in eine Zeichenkette einfügen wollen,
'$$
' verwenden, wenn Sie ein '$
' Zeichen in eine Zeichenkette einfügen wollen.
<span tal:replace="string:this is a $$100 page"/>
string:foo $bar baz <!-- $bar wird ersetzt -->
string:foo $$bar baz <!-- keine Ersetzung -->
string:foo ; php:doFoo() <!-- zwei einzelne Ausdrücke -->
string:foo ;; php:doFoo() <!-- eine einzige Zeichenkette -->
php:
Dieser Operator erwartet als Argument einen regulären PHP-Ausdruck, wobei er '->
' durch einen Punkt '.
' ersetzt, und Variablennamen mit einem vorangestellen Dollarzeichen '$
' versieht.
Ein vom Rest des Ausdrucks durch Leerzeichen getrennter Punkt '.
' wird als Verkettungszeichen behandelt.
php:htmlentities(foo)
php:'string ${varReplaced}'
php:'string ${some.path().to[0].var}'
php:NOT foo OR (bar GT baz)
php:a + b
php:array('a', 'b', 'c')
php:range(0, 90)
php:foo . a.b.c(e) . htmlentities(SomeClass::staticMethod())
php:SomeClass::ConstOfClass
php:SomeClass::$staticVar
Verwenden Sie php:
sparsam. In 80% ihrer Vorlagen werden Sie diesen Operator nicht benötigen, aber manchmal werden Sie eine spezielle PHP-Methode aufrufen müssen, z.B. um sich zu vergewissern, daß ein Benutzer angemeldet ist, oder um spezielle, komplexe Daten, in Abhängigkeit von einigen Bedingungen, innerhalb der Vorlage dynamisch zu laden.
not:
Dieser Operator bewirkt eine boolsche Negation. Er wird in tal:condition
Ausdrücken verwendet.
<span tal:condition="not: logged">not logged</span>
exists:
Dieser boolsche Operator gibt wahr
(true
) zurück, wenn der getestete Pfad existiert und falsch
(false
) sonst. Er funktioniert analog zur PHP-Funktion isset().
Normalerweise liefert die Verwendung eines nicht existierenden Pfades eine Fehlermeldung wie "Cannot find variable 'foo
' in current scope". Darum sollten unsichere Pfade vor der Verwendung immer geprüft werden:
<span tal:condition="exists: user/preferences"
tal:content="user/preferences">
user preferences here if defined
</span>
Benutzen Sie in PHPTALES stattdessen die Funktion isset()
.
default
Dies ist kein Operator, sondern ein Schlüsselwort, das es Vorlagenentwicklern erlaubt, im Falle eines Fehlers, oder wenn etwas nicht definiert ist, den Inhalt einer Auszeichnung als Ersatzwert zu verwenden.
<span tal:define="myVar path/to/possible/var | default">
default my var value
</span>
<span tal:content="some/var | other/path | default">
no some/var and no other/path found here
</span>
<a href="unknown.xhtml" title="Unknown page"
tal:attributes="href item/href | default; title item/title | default"
tal:content="item/title | default">Unknown page</a>
Das obige Beispiel führt das '|
'-Zeichen ein, das für Definitionen oder Ausgaben die Festlegung von Alternativen ermöglicht.
structure
Dies ist kein Operator, sondern ein Schlüsselwort.
Bei der Ausgabe von Variablen innerhalb von PHPTAL-Vorlagen kodiert PHPTAL alle HTML/XML-eigenen Zeichen, um sicherzustellen, daß das Ausgabedokument gültig ist.
Es kommt vor, daß Sie HTML/XML-Variablen verwenden, die unverändert ausgegeben werden sollen:
<h1 tal:content="structure document/title"/>
<span tal:replace="structure document/content"/>
Im obigen Beispiel wird angenommen, daß $document->title
und $document->content
Variablen sind, die vorformatiertes HTML enthalten, das unverändert ausgegeben werden soll.
Ein Ausdruckskette ist eine durch das '|
'-Zeichen getrennte Liste von Ausdrücken.
PHPTAL wertet die Ausdrücke in einer solchen Kette von vorne nach hinten aus, bis der erste Ausdruck ein Nicht-Null-Ergebnis zurückliefert, das keine Fehlermeldung ist. Danach bricht die Auswertung ab.
Da ein string:
-Ausdruck immer wahr ist, wird die Auswertung durch einen string:
-Ausdruck immer beendet.
Innerhalb von Ausdrucksketten können Sie, wie jeden anderen Ausdruck, auch Ausdrücke mit dem php:
-Operator verwenden:
<h1 tal:content="page/title | page/alternativeTitle | default>
untitled page
</h1>
Inhaltsverzeichnis
Dieser Abschnitt richtet sich an PHP-Entwickler und erläutert die Verwendung und Anpassung von PHPTAL bei einfacher und fortgeschrittener Nutzung.
PHPTAL
: die PHPTAL-Hauptklasse, zum Laden und Ausführen von Vorlagen.
PHPTAL_Filter
: filtert Vorlagenquellen und PHPTAL Ausgabe.
PHPTAL_Trigger
: behandelt die Ausgabe von Elementen mit phptal:id
.
PHPTAL_TranslationService
: erlaubt das Ersetzen der eingebauten gettext™-Unterstützung durch ein eigenes Internationalisierungssystem.
Die einzige Konstante, die in der Datei PHPTAL.php
definiert ist, ist PHPTAL_VERSION
. Sie enthält die Version der PHPTAL-Bibliothek (Format: X.X.X
) die auf dem Rechner installiert ist.
Die Konfigurationskonstanten aus älteren Versionen sind durch Methoden ersetzt worden.
PHPTAL versucht die bestmöglichen Grundeinstellungen zu verwenden. Eine Änderung sollte nicht nötig sein.
All of these are methods of the PHPTAL
class. set
methods return instance of their class, so you can chain them:*
<?php
echo $phptal->setPhpCodeDestination('/tmp/phptal')->setOutputMode(PHPTAL::XML)->setTemplate('tpl.zpt')->execute();
?>
is the same as:
<?php
$phptal->setPhpCodeDestination('/tmp/phptal');
$phptal->setOutputMode(PHPTAL::XML);
$phptal->setTemplate('tpl.zpt');
echo $phptal->execute();
?>
There are other set
methods for filters, internationalisation, etc. They have been described in other sections of this manual.*
setOutputMode(mode
)
Changes what syntax is used when generating elements. Valid modes are:
PHPTAL::XHTML
In this mode (which is default) PHPTAL will output XHTML in a way that is backwards-compatible with HTML browsers.
Empty elements will be forced to use self-closing form (<img/>
, <link/>
), and non-empty elements will always have closing tag.
XHTML output mode changes <link>
element in way that is incompatible with RSS. Use XML output mode to generate RSS feeds or use Atom.
Boolean attribtes (checked
, selected
, etc.) will always have value required by the XHTML specification (it simplifies use of tal:attributes
).
<![CDATA[
blocks will be added or removed automatically and will use special escaping syntax that is safe in both XML and HTML.
If you're always sending XHTML as application/xhtml+xml
, then it's better to use XML output mode.
PHPTAL::HTML5
This mode generates documents that have best compatibility with text/html
parsing in current web browsers, but are not XML.
PHPTAL will change DOCTYPEs to <!DOCTYPE html>
. Namespace declarations, name prefixes, explicit CDATA
sections and other HTML-incompatible constructs will be ommited.
This mode is not a "tag soup". PHPTAL will close all elements properly and quote attributes when it's necesary. Output will be properly formatted HTML 5, and fully backwards-compatible with current HTML 4 browsers.
PHPTAL::XML
This mode outputs "pure" XML without compatibility with text/html
parsing. Use this mode when generating feeds, SVG, MathML, RDF and other XML files.
setEncoding(encoding
)
Gibt die in Ihren Vorlagen verwendete Kodierung an. Die Grundeinstellung ist UTF-8.
PHPTAL geht davon aus, das die Kodierung aller Vorlagen und erzeugten Dokumente die gleiche ist. Der BOM (Byte Order Marker) wird aus UTF-8 Dokumenten entfernt.
PHPTAL liest keine Zeichenkodierungen aus XML-Dateien und verändert niemals eine Kodierung.
Ersparen Sie sich den Ärger und verwenden Sie immer und für alles UTF-8.
setTemplateRepository(string_or_array
)
Specifies where to look for templates. If given a string, it adds it to the list of paths searched. If given array, it replaces the list.
This doesn't mean all your files need to be in the root directory, you can use sub folders to organize your template designer's work. It's just a shortcut which will allow you to reference templates without specifying the real path, but instead their relative path within the repository.
It's like include_path
, but for PHPTAL templates only.
setPhpCodeDestination(path
)
To tell PHPTAL where to store its intermediate (temporary) PHP files. By default it uses directory given by PHP's sys_get_tmp_dir()
, which usually is '/tmp/
' directory.
setPhpCodeExtension(string
)
What filename extension should be used for intermediate PHP files. The default is php
and frankly, there's no reason to change it.
setCacheLifetime(num_days
)
Maximum number of days intermediate files and fragments cached with phptal:cache
should be kept.
setForceReparse(boolean
)
Forces reparsing of all templates all the time. It should be used only for testing and debugging. It's useful if you're testing prefilters or changing code of PHPTAL itself.
This slows down PHPTAL very much. Never enable this on production servers!
PHPTAL
Dies ist die Hauptklasse der Bibliothek.
Ihre gebräuchlichste Verwendung:
<?php
// include the library
require_once 'PHPTAL.php';
// instantiate a new PHPTAL object using specified template file
$tpl = new PHPTAL('mytemplate.xhtml');
// setting some template context variables
$tpl->title = 'my title';
$tpl->values = array(1,2,3,4);
$tpl->user = new User('Joe');
// execute the template and echo the result in a 'secure' way
try{
echo $tpl->execute();
}
catch (Exception $e){
echo "Exception thrown while processing template\n";
echo $e;
}
?>
You can perfectly well choose to specify the template source after setting context variables.
<?php
…
$tpl = new PHPTAL();
// it is a matter of taste but you can use the set() method instead of
// setting context using PHPTAL::__set() like above
$tpl->set('title', 'my title');
$tpl->set('values', array(1,2,3,4));
$tpl->set('user', new User('Joe'));
$tpl->setTemplate('mytemplate.xhtml');
…
?>
You can also decide to use a generated string as the template source instead of using an existing template file:
<?php
$src = <<<EOS
<html>
<head>
<title tal:content="title">my title</title>
</head>
<body>
<h1 tal:content="title">my title</h1>
</body>
</html>
EOS;
require_once 'PHPTAL.php';
$tpl = new PHPTAL();
$tpl->setSource($src);
$tpl->title = 'this is my title';
try {
echo $tpl->execute();
}
catch (Exception $e){
echo $e;
}
?>
In the above example, because PHPTAL requires a template source identifier (usually the template file realpath), PHPTAL will use the md5 of the $src
parameter as a unique identifier. You may decide to force the identifier using a second setSource()
argument:
<?php
$src = <<<EOS
<html>
<head>
<title tal:content="title">my title</title>
</head>
<body>
<h1 tal:content="title">my title</h1>
</body>
</html>
EOS;
require_once 'PHPTAL.php';
$tpl = new PHPTAL();
// If you specify where the source comes from (second argument to setSource),
// PHPTAL will be able to generate more helpful error messages.
$tpl->setSource($src, __FILE__);
$tpl->title = 'this is my title';
try {
echo $tpl->execute();
}
catch (Exception $e){
echo $e;
}
?>
PHPTAL_Filter
This interface allows you to automatically filter templates sources (pre-filters) or PHPTAL result (post-filters).
Pre filters are invoked before the template parsing and won't be invoked until the source template file is modified.
Post filters are invoked after each template execution.
<?php
require_once 'PHPTAL.php';
class MyPreFilter implements PHPTAL_Filter {
public function filter($source){
return $source;
}
}
class MyPostFilter implements PHPTAL_Filter {
public function filter($xhtml){
return $xhtml;
}
}
$tpl = new PHPTAL('mytemplate.xhtml');
$tpl->setPostFilter(new MyPostFilter());
echo $tpl->execute();
?>
You can set only one post filter using setPostFilter()
. If you have more than one filter to chain, you can wrap them into a single class, implementing the PHPTAL_Filter
interface, which would invoke the filter's chain.
<?php
require_once 'PHPTAL.php';
class FilterChain implements PHPTAL_Filter {
private $_filters = array();
public function add(PHPTAL_Filter $filter){
$this->_filters[] = $filter;
}
public function filter($source){
foreach($this->_filters as $filter){
$source = $filter->filter($source);
}
return $source;
}
}
$myfilter = new FilterChain();
$myfilter->add(new CommentFilter()); // imaginary filter
$myfilter->add(new TidyFilter()); // imaginary filter
$tpl = new PHPTAL('mytemplate.xhtml');
$tpl->setPostFilter($myFilter);
echo $tpl->execute();
?>
PHPTAL_Trigger
The phptal:id
attribute was added into the PHPTAL for the PHP5 version to replace the old PHPTAL_Cache
interface and to abstract it a little more.
When a phptal:id
is reached, PHPTAL will look in its triggers list for a matching id and will invoke the trigger start()
and end()
methods before entering the element, and just after it.
If the PHPTAL_Trigger::start()
methods returns PHPTAL_Trigger::SKIPTAG
, PHPTAL will ignore the element and its content (start()
may echo something to replace it).
If your trigger wants the element and its content to be executed, you'll have to return PHPTAL_Trigger::PROCEED
.
The PHPTAL_Trigger::end()
will be called after the element (whether it has been executed or not). This allows you to build cache systems using ob_start()
in start()
and ob_get_contents()
, ob_end_clean()
in end()
.
<html>
…
<div>
foo bar baz <span tal:replace="id"/> foo bar baz
…
</div>
…
</html>
For some reason we decide the <div>
block requires to be cached. We introduce a phptal:id
into the template:
<html>
…
<div phptal:id="somePossiblyUniqueKeyword">
…
foo bar baz <span tal:replace="id"/> foo bar baz
…
</div>
…
</html>
Then we write our trigger which will cache the <div>
content:
<?php
require_once 'PHPTAL.php';
require_once 'PHPTAL/Trigger.php';
class CacheTrigger implements PHPTAL_Trigger
{
public function start($phptalid, $tpl)
{
// this cache depends on 'id' which must appears in
// the template execution context
$this->_cachePath = 'cache.' . $tpl->getContext()->id;
// if already cached, read the cache and tell PHPTAL to
// ignore the tag content
if (file_exists($this->_cachePath)){
$this->_usedCache = true;
readfile($this->_cachePath);
return self::SKIPTAG;
}
// no cache found, we start an output buffer and tell
// PHPTAL to proceed (ie: execute the tag content)
$this->_usedCache = false;
ob_start();
return self::PROCEED;
}
// Invoked after tag execution
public function end($phptalid, $tpl)
{
// end of tag, if cached file used, do nothing
if ($this->_usedCache){
return;
}
// otherwise, get the content of the output buffer
// and write it into the cache file for later usage
$content = ob_get_contents();
ob_end_clean();
echo $content;
$f = fopen($this->_cachePath, 'w');
fwrite($f, $content);
fclose($f);
}
private $_cachePath;
private $_usedCache;
}
?>
The key here is to return from start()
with either SKIPTAG
or PROCEED
.
When SKIPTAG
is returned, PHPTAL will just ignore the tag and call end()
. This usually means that the trigger takes the hand in deciding what to show there.
When PROCEED
is returned, PHPTAL will execute the tag and its content as usual, then call end(). This allows our cache class to play with output buffers to execute the tag once and to store the result in a file which will be used in later calls.
To install our trigger we use:
<?php
require_once 'PHPTAL.php';
require_once 'CacheTrigger.php'; // our custom trigger
$trigger = new CacheTrigger();
$tpl = new PHPTAL('test.xhtml');
// this trigger will only be called for phptal:id="triggerId"
$tpl->addTrigger('somePossiblyUniqueKeyword', $trigger);
$tpl->id = 1;
echo $tpl->execute();
?>
You can add as many triggers as you like to your templates. A generic cache trigger may also handle more than one phptal:id
… etc…
PHPTAL_TranslationService
PHPTAL comes with a default gettext™ translation service, as shown in another section. For some reason you may prefer to implement your own service of translation.
The PHPTAL_TranslationService
interface is here to serve your needs.
The usage of your service will be the same as the PHPTAL_GetTextTranslator
.
$tpl->setTranslator($yourOwnTranslatorInstance);
Ihre Implementierung muß die folgenden Methoden definieren:
setLanguage(…
)
Diese Methode kann durch die Vorlage aufgerufen werden, um die aktuelle Ausgabesprache zu ändern (z.B. de_CH
).
Die Argumente sind eine Liste möglicher Sprachen (verwenden Sie func_get_args()
, um die Argumentliste zu erhalten). Ihr Dienst sollte dann die erste bekannte Sprache wählen.
<?php
require_once 'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
…
public function setLanguage(){
$langs = func_get_args();
foreach($langs as $lang){
// if $lang known use it and stop the loop
$this->_currentLang = $lang;
break;
}
return $this->_currentLang;
}
…
private $_currentLang;
}
?>
useDomain($domain
)
If you decided to store your translations into separate files, one for each application, for example, this method allows you to select the translation domain from your templates (i18n:domain
).
<?php
require_once 'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
…
public function useDomain($domain){
if (!array_key_exists($domain, $this->_domains)){
$file = "domains/$this->_currentLang/$domain.php";
$this->_domains[$domain] = include($file);
}
$this->_currentDomain = $this->_domains[$domain];
}
…
private $_currentDomain;
private $_domains = array();
}
?>
Das obige Beispiel ist eine möglich Übersetzungslösung, bei der die Schlüssel in PHP-Dateien gespeichert werden, die ein assoziatives key=>translation-Feld zurückgeben.
setVar($key
,$value
)
This method matches i18n:name
calls. It builds an interpolation context for later translate calls.
<?php
require_once 'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
…
public function setVar($key, $value){
$this->_context[$key] = $value;
}
…
private $_context = array();
}
?>
translate($key
)
The last and most important method to implement, it asks your service to translate the specified key for the currently selected language.
<?php
require_once 'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
…
public function translate($key){
$value = $this->_currentDomain[$key];
// interpolate ${myvar} using context associative array
while (preg_match('/\${(.*?)\}/sm', $value, $m)){
list($src,$var) = $m;
if (!array_key_exists($var, $this->_context)){
$err = sprintf('Interpolation error, var "%s" not set',
$var);
throw new Exception($err);
}
$value = str_replace($src, $this->_context[$var], $value);
}
return $value;
}
…
}
?>
gettext™ ist der GNU-Standard für das Internationalisierungs- und Übersetzungssystem. Es kann im Zusammenspiel mit PHP benutzt werden und wird von PHPTAL unterstüzt.
Die Benutzung von gettext™ ist einfach, aber Sie sollten einige Tests durchführen, um sicherzustellen, daß auf ihrem Rechner alles richtig zusammenspielt.
PHP muß mit dem --with-gettext
Schalter kompiliert werden. Näheres dazu findet sich in der PHP-Dokumentation.
Mit dem folgenden Stückchen Code können Sie ihre PHP-Installation daraufhin prüfen:
//
// test if gettext extension is installed with php
//
if (!function_exists("gettext"))
{
echo "gettext is not installed\n";
}
else
{
echo "gettext is supported\n";
}
Die PHP-gettext™-Erweiterung verlangt nach einer bestimmten Verzeichnisstruktur, die die Übersetzungsdateien enthält.
/path/to/your/translation_root/en_US/LC_MESSAGES/
/path/to/your/translation_root/en_GB/LC_MESSAGES/
/path/to/your/translation_root/fr_FR/LC_MESSAGES/
/path/to/your/translation_root/es_ES/LC_MESSAGES/
… and so on …
Die Sprachkode besteht aus zwei Zeichen, die die eigentliche Sprache (fr, de, it, …) und zwei Zeichen, die das Land (FR, CH, DE, IT, …) festlegen.
Das Verzeichnismuster sieht so aus:
<path_to_where_you_want>/<ll_CC>/LC_MESSAGES/
PO-Dateien sind Klartextdateien, die die Übersetzungen enthalten. Sie können sie problemlos händisch editieren.
minimalistisches po-Beispiel (en_US/LC_MESSAGES/mydomain.po
):
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Simple test"
msgstr "A small sentence in english"
Einmal bearbeitet, muß jede PO-Datei indiziert werden:
msgfmt mydomain.po -o mydomain.mo
Dieser Aufruf funktioniert nur, wenn Sie die gettext™-Werkzeuge auf ihrem System installiert haben.
Hierdurch wird eine MO-Datei (machine object) erzeugt, in der Ihre Übersetzungen für einen schnellen Zugriff indiziert vorliegen.
Nun müssen Sie diese Datei in die anderen gewünschten Sprachen übersetzen.
Minimalistisches PO-Beispiel (fr_FR/LC_MESSAGES/mydomain.po
):
msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Simple test"
msgstr "Une petite phrase en français"
Auch diese Übersetzungsdatei muß indiziert werden:
msgfmt mydomain.po -o mydomain.mo
The domain is matched against your translation file names. In above examples we used 'mydomain' as domain name.
You can have more than one domain for the same application, it can enhance gettext™'s performance to split your application translations in more than one file.
<?php
require_once 'PHPTAL.php';
require_once 'PHPTAL/GetTextTranslator.php';
try {
$tr = new PHPTAL_GetTextTranslator();
// set language to use for this session (first valid language will
// be used)
$tr->setLanguage('en_GB.utf8', 'en_GB');
// register gettext domain to use
$tr->addDomain('mydomain', '/path/to/your/translation_root');
// specify current domain
$tr->useDomain('mydomain');
$tpl = new PHPTAL('mytemplate.xhtml');
// tell PHPTAL to use our translator
$tpl->setTranslator($tr);
// output translated template
echo $tpl->execute();
}
catch (Exception $e){
echo $e;
}
If you need to translate some other text, that is not in the template (e.g. plaintext e-mail message), you can reuse PHPTAL's translator:
$tr = $tpl->getTranslator();
$subject = $tr->translate("Registration information");
$tr->setVar("user",$username);
$message = $tr->translate("Dear ${user}, thanks for registering!");
mail($email, $subject, $message);
If you're using PHPTAL's standard gettext™ translator, you can use gettext()
too.
The I18N Namensraum allows some variable interpolation in your translations.
# english
msgid "welcome"
msgstr "Welcome ${name} you have ${n} mails!"
# french
msgid "welcome"
msgstr "Bienvenue ${name} vous avez recu ${n} messages!"
A template can use this interpolation as follows:
<span i18n:translate="welcome">
Welcome
<span i18n:name="name" tal:replace="user/name"/>
you currently have
<span i18n:name="n" tal:replace="user/unreadeMails"/>
unread messages!
</span>
Because i18n:translate
contains a value 'welcome'
, the template data will be ignored and the message given by gettext™ will be used instead.
PHPTAL enthält einige grundlegende Operatoren: „not:“, „exists:“, „string:“, „php:“, „path:“.
Diese Operatoren sind in den ZPT-Spezifikationen definiert. PHPTALES kann mit eigenen Operatoren zur Bearbeitung von Zeichenketten, Kalenderdaten, Währungen, oder was auch immer erweitert werden.
Ziel der Anwendung jedweden Operators ist es, ein Stückchen PHP-Code zurückzugeben, der in den aus der Vorlage generierte PHP-Code integriert wird.
Operatoren werden während der Vorlagenzerlegung angewendet. Wenn Sie das Verhalten eines Operators verändern, müssen Sie alle vorher erzeugten PHP-Dateien löschen und die Vorlagen neu zerlegen lassen.
Beachten Sie, daß Operatoren Code und keine Daten ausgeben!
Jede PHP-Funktion, die mit "phptal_tales_
" beginnt, definiert einen Operator.
Operatoren erwarten zwei Argumente::
$src
: die Quellzeichenkette nach dem "modifier:" Schlüsselwort
$nothrow
: eine boolsche Variable, die entscheidet ob durch die phptal_path()
Auflösung eine Ausnahme ausgeworfen werden soll. Diese Variable muß immer an innerhalb Ihres eigenen Operators aufgerufene weitere phptal_tales_
-Operatoren durchgereicht werden.*
For example, in the following TAL template,
<span tal:replace="some-modifier: my/path/value"/>
The src argument will be "my/path/value
", and the $nothrow
boolean will be false, because tal:replace
requires the path to be fully resolvable.
Ein Ausdruck wie:
<span tal:replace="some-modifier: my/path/value | other/path"/>
nutzt zwei Operatoren:
some-modifier: with "my/path/value
" as $src
argument and $nothrow
set to true because an alternative exists
path: with "other/path
" as $src
, and $nothrow
set to false because in case the alternative is not found, tal:replace
will be in trouble.
Zur Erinnerung: path:
wird implizit verwendet, sollte kein anderer Operator angegeben sein.
Operatoren können andere Operatoren verwenden, um einfacheren PHP-Code zu erhalten; siehe das folgende Beispiel:
//
// This modifier will return a money formated string (XXX.XX)
//
// usage:
//
// money: path/to/my/amount
//
// this modifier uses phptal_tales() function to generate the
// PHP code that will return the value of the modifier argument.
//
// in the example:
//
// money: path/to/my/amount
//
// the produced code will be something looking like:
//
// sprintf("%01.2f", phptal_path($ctx->path, "to/my/amount"))
//
// This code will be included right into the template where needed.
//
// @param string $src
// The expression string
// @param string $nothrow
// A boolean indicating if exceptions may be throw by phptal_path if
// the path does not exists.
// @return string
// PHP code to include in the template
//
function phptal_tales_money( $src, $nothrow )
{
// remove spaces we do not require here
$src = trim($src);
return 'sprintf("%01.2f", '.phptal_tales($src, $nothrow).')';
}
PHPTAL arbeitet, indem es PHP-Dateien aus der Vorlagenlogik generiert; das bedeutet, daß es ein Verzeichnis benötigt, in dem die erzeugten Dateien gespeichert und durch den PHP Interpreter zerlegt werden können.
In der Grundeinstellung verwendet PHPTAL, falls vorhanden, die PHP-Funktion sys_get_temp_dir()
, um das temporäre Verzeichnis zu bestimmen, in dem die generierten PHP-Dateien gespeichert werden. Sonst wird auf unixartigen Systemen /tmp
und auf Microsoft Systemen c:\windows\temp
verwendet. Sie können die Grundeinstellung durch den Aufruf von setPhpCodeDestination()
mit einem passenden Pfad nach ihren Wünschen ändern. Sei es das übliche temporäre Verzeichnis, sei es ein eigenes: seine Zugriffsrechte müssen so gesetzt sein, daß der PHP ausführenden Prozess (d.h. der Apache Betreiber wenn das mod_php Modul verwendet wird, sonst der cgi/fastcgi Betreiber) Dateien anlegen und verändern kann.
PHPTAL erzeugt zu jeder Vorlagendatei und, falls phptal:cache
verwendet wird, auch für jedes Element eine (php) Datei. Für Makros werden keine separaten Dateien angelegt. (Sie werden einfach als PHP-Funktionen in die erzeugte Datei eingefügt.) Diese Dateien werden ab und an automatisch gelöscht, genauer: jedes mal, wenn eine Vorlage bearbeitet wird, wird die alte Datei mit einer gewissen durch setCachePurgeFrequency()
bestimmten Wahrscheinlichkeit gelöscht, falls ihre durch setCacheLifetime()
festgelegte Lebensdauer abgelaufen ist.
Natürlich kann man alte bzw. unbenutzte Dateien auch via cron-Job und Shell löschen (hier: UNIXartige Shell):
find /tmp/ -name tpl_\* \( -atime +1 -o -mtime +14 \) -delete
Vielen Dank an:
Das ZPT-Team, das diese nützlichen Spezifikationen entwickelt hat,
die PHPTAL-Gemeinschaft für ihre Unterstützung, Hilfe und ihre Hinweise,
Jean-Michel Hiver, der mich zum Draufschauen 'zwang',
Olivier Parisy, den ersten enthusiastischen PHPTAL-Benutzer und Bug Finder,