PHPTAL Handbuch

PHP Template Attribute Language

Laurent Bédubourg

Kornel Lesiński

Dan Sheppard

Anton Valeriyevich Andriyevskyy

Axel Zöllich

Versionsgeschichte

Inhaltsverzeichnis

  1. Einleitung
  2. Warum PHPTAL?
  3. Installation
  4. Ein erstes Beispiel
  5. Die »Template Attribute Language«
    1. Attributrangfolge
    2. TAL-Namensraum
      1. tal:define
      2. tal:condition
      3. tal:repeat
      4. tal:omit-tag
      5. tal:replace
      6. tal:content
      7. tal:attributes
        1. Optionale Attribute
      8. tal:on-error
    3. METAL Namensraum
      1. metal:define-macro
      2. metal:use-macro
      3. metal:define-slot
      4. metal:fill-slot
    4. I18N Namensraum
      1. i18n:translate
      2. i18n:attributes
      3. i18n:name
      4. XHTML in Übersetzungen
    5. PHPTAL Namensraum
      1. phptal:debug
      2. phptal:cache
        1. Gesteuerte Auffrischung
        2. Begrenzungen:
      3. phptal:tales
    6. tal:block
    7. PHPTALES
      1. path:
      2. Alternative PHP-Operator Syntax
      3. string:
      4. php:
      5. not:
      6. exists:
      7. default
      8. structure
      9. Ausdrucksketten
  6. PHP Integration
    1. Konstanten
    2. Konfigurationsmethoden
      1. setOutputMode(mode)
      2. setEncoding(encoding)
      3. Other methods
    3. class PHPTAL
    4. interface PHPTAL_Filter
    5. interface PHPTAL_Trigger
    6. interface PHPTAL_TranslationService
      1. method setLanguage()
      2. method useDomain($domain)
      3. method setVar($key,$value)
      4. method translate($key)
    7. Die Arbeit mit gettext
      1. Erzeugung der Übersetzungsverzeichnisstruktur (Hmpf)
      2. Portable Object files
      3. Translation Domain
      4. Using Translator in PHP
      5. Variable interpolation
    8. Maßgeschneiderte Operatoren entwickeln
  7. A. Hinweis für Systembetreuer
  8. B. Nützliche Verweise
  9. C. Danksagungen

Einleitung


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 entwickelt und wird von Kornel Lesiński betreut.

Warum PHPTAL?


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 :).

Installation


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.

Ein erstes Beispiel


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.

Die »Template Attribute Language«


»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.

Attributrangfolge

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:

  1. define

  2. condition

  3. repeat

  4. content oder replace

  5. attributes

  6. omit-tag

TAL-Namensraum

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" …>

Anmerkung

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.

Tipp

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>

Anmerkung

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.

Optionale Attribute

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"/>

Warnung

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>

METAL Namensraum

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" …>

Anmerkung

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.

I18N Namensraum

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" …>

Anmerkung

PHPTAL erzwingt diese Angabe nicht.

Anmerkung

„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.

XHTML in Übersetzungen

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>

Warnung

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.

PHPTAL Namensraum

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" …>

Anmerkung

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.

Anmerkung

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.

Anmerkung

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.

Warnung

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.

Gesteuerte Auffrischung

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>

Begrenzungen:

  • 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>

Anmerkung

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>

PHPTALES

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.

path:

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"/>

Warnung

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.

Alternative PHP-Operator Syntax

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

string:

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>

Tipp

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.

Ausdrucksketten

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>

PHP Integration


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.

Konstanten

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.

Konfigurationsmethoden

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.

    Warnung

    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.

    Tipp

    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.

Anmerkung

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.

Anmerkung

PHPTAL liest keine Zeichenkodierungen aus XML-Dateien und verändert niemals eine Kodierung.

Tipp

Ersparen Sie sich den Ärger und verwenden Sie immer und für alles UTF-8.

Other methods

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.

Tipp

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.

Warnung

This slows down PHPTAL very much. Never enable this on production servers!

class 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;
}

?>

interface 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();
?>

Multiple post filters

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();
?>

interface 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…

interface 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:

method 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;
}
?>

method 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.

method 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();
}
?>

method 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;
    }
    
}
?>

Die Arbeit mit gettext

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";
}

Erzeugung der Übersetzungsverzeichnisstruktur (Hmpf)

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/

Portable Object files

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

Translation Domain

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.

Using Translator in PHP

<?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.

Variable interpolation

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.

Maßgeschneiderte Operatoren entwickeln

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).')';
}

Anhang A. Hinweis für Systembetreuer

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

Anhang C. Danksagungen

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,