PHPTAL Manual PHP Template Attribute Language Laurent Bedubourg Kornel Lesinski Dan Sheppard Anton Valeriyevich Andriyevskyy Revision History ---------------------------------------------------------------------- Table of Contents 1. Introduction 2. Why use PHPTAL 3. Installation 4. First example 5. Template Attribute Language 1. Referencing variables with ${} 2. Attribute priority 3. TAL namespace 1. tal:define 2. tal:condition 3. tal:repeat 4. tal:omit-tag 5. tal:replace 6. tal:content 7. tal:attributes 1. Optional attributes 8. tal:on-error 4. METAL namespace 1. metal:define-macro 2. metal:use-macro 3. metal:define-slot 4. metal:fill-slot 5. I18N namespace 1. i18n:translate 2. i18n:attributes 3. i18n:name 4. XHTML in translations 6. PHPTAL namespace 1. phptal:debug 2. phptal:cache 1. Instant refreshing 2. Limitations: 3. phptal:tales 7. tal:block 8. PHPTALES 1. path: 2. Alternative PHP operator syntax 3. string: 4. php: 5. not: 6. exists: 7. true: 8. default 9. structure 10. Expression chains 6. PHP Integration 1. Constants 2. class PHPTAL 1. Configuration methods 1. setOutputMode(mode) 2. setEncoding(encoding) 3. Other methods 2. execute() method 3. echoExecute() method 4. addPreFilter() method 3. class PHPTAL_PreFilter 4. PHPTAL DOM 5. interface PHPTAL_Filter 6. interface PHPTAL_Trigger 7. interface PHPTAL_TranslationService 1. method setLanguage(...) 2. method useDomain($domain) 3. method setVar($key,$value) 4. method translate($key) 5. method setEncoding($encoding) 8. Working with gettext 1. Creating the translation directory structure 2. Portable Object files 3. Translation Domain 4. Using Translator in PHP 5. Variable interpolation 9. Creating custom expression modifiers 7. A. Note for system administrators 8. B. Useful links 9. C. Greetings Introduction ---------------------------------------------------------------------- PHPTAL is an implementation of the excellent Zope Page Template (ZPT) system for PHP. PHPTAL supports TAL, METAL, I18N namespaces. PHPTALES is the equivalent of TALES, the Template Attribute Language Expression Syntax. It defines how XML attribute values are handled. As PHPTALES is similar to TALES, it should be easy to port python TAL templates into PHP ones (and vice versa). To be TAL compliant, PHPTAL implements XPath-like access to data. PHPTAL is freely distributed under the LGPL license, it is developed by Laurent Bedubourg and maintained by Kornel Lesinski. Why use PHPTAL ---------------------------------------------------------------------- XML/HTML templates exist to separate logic from presentation in web services. This separation brings more than one accompanying benefit. o better application design o easier task repartition o better maintainability o easy web skins Most template systems uses , <% %> or tags to find their sections. It allows easier template system development but doesn't really help template designers. TAL hides most of its logic in XML attributes, preserving syntax and structure of XHTML. This allows previewing of TAL templates in web browser (WYSIWYG editors, live previews) and doesn't break HTML syntax highlighting in programmers' editors. If you have already worked with a simple template system, then you must have encountered something looking like: <%loop myarray as myitem %> <%/loop%>
<% myitem %>
Well, with PHPTAL you now can write:
text replaced by the item value sample 1 sample 2 sample 3
The above code will render correctly with the sample text in normal web browser, so you can present it to your clients even if the code required to get 'myarray' values doesn't yet exist. Another big advantage of PHPTAL is that you benefit from more than 3 years of Zope community experience, documentation, examples, and help. PHPTAL relies on this community to provide its users a great deal of useful information. PHPTAL is designed to be as customizable as possible for advanced developers and performance-eating systems, but still be easy to use for beginners, with a comfortable and simple default behavior (at least we tried :) Installation ---------------------------------------------------------------------- PHPTAL is released as a PEAR package (see pear.php.net). You can download the PHPTAL library on the PHPTAL website: phptal.org. You can install it using the PEAR utility: pear install http://phptal.org/latest.tar.gz Once installed, you can upgrade PHPTAL easily on each PHPTAL update using PEAR: pear upgrade http://phptal.org/latest.tar.gz If you do not use PEAR or do not have it installed on your system, you can still install PHPTAL by unzipping the downloaded archive. tar zxvf PHPTAL-X.X.X.tar.gz cp -r PHPTAL-X.X.X/PHPTAL* /path/to/your/lib/folder This will install the PHPTAL.php file and the associated PHPTAL folder in /path/to/your/lib/folder. First example ---------------------------------------------------------------------- To get a first impression of PHPTAL usage, a simple example is better than many words. Your template is a valid XML/HTML document (with a root element). Here's a file named 'my_template_file.xhtml'. Place for the page title

sample title

Name Phone
person's name person's phone
sample name sample phone
sample name sample phone
In PHP, you just have to include the PHPTAL library, and maybe configure a few variables to customize the template system. name = $name; $this->phone = $phone; } } // let's create an array of objects for test purpose $people = array(); $people[] = new Person("foo", "01-344-121-021"); $people[] = new Person("bar", "05-999-165-541"); $people[] = new Person("baz", "01-389-321-024"); $people[] = new Person("quz", "05-321-378-654"); // put some data into the template context $template->title = 'The title value'; $template->people = $people; // execute the template try { echo $template->execute(); } catch (Exception $e){ echo $e; } ?> If you execute the PHP script, you will obtain something similar to what follows. The title value

The title value

Name Phone
foo 01-344-121-021
bar 05-999-165-541
baz 01-389-321-024
quz 05-321-378-654
PHPTAL doesn't care much about line breaks and indentation in files it reads and generates. If you want source code of generated HTML files to be pretty (with line breaks and perfect indentation), then you might need to postprocess it with HTML Tidy. Template Attribute Language ---------------------------------------------------------------------- Table of Contents 1. Referencing variables with ${} 2. Attribute priority 3. TAL namespace 1. tal:define 2. tal:condition 3. tal:repeat 4. tal:omit-tag 5. tal:replace 6. tal:content 7. tal:attributes 1. Optional attributes 8. tal:on-error 4. METAL namespace 1. metal:define-macro 2. metal:use-macro 3. metal:define-slot 4. metal:fill-slot 5. I18N namespace 1. i18n:translate 2. i18n:attributes 3. i18n:name 4. XHTML in translations 6. PHPTAL namespace 1. phptal:debug 2. phptal:cache 1. Instant refreshing 2. Limitations: 3. phptal:tales 7. tal:block 8. PHPTALES 1. path: 2. Alternative PHP operator syntax 3. string: 4. php: 5. not: 6. exists: 7. true: 8. default 9. structure 10. Expression chains This section describes TAL and its extensions. It mainly targets template designers but must be read by PHP integrators as well. Referencing variables with ${} When working with templates and you want to reference a variable within the text, you can use the ${variableName} notation. For example, You have ${count} credits left. You can also use the same PHPTALES paths as in other PHPTAL constructs: Your best friend is: ${php: !some.other() ? "me" : "your dog"} Tip If you have to exscape the ${var} notation, write $${var}. Attribute priority It is important to note that the order of declaration of attributes is irrelevant. For example, ... Is exactly the same as: ... Priority is the same as described by the TAL specification: 1. define 2. condition 3. repeat 4. content or replace 5. attributes 6. omit-tag TAL namespace 1. tal:define 2. tal:condition 3. tal:repeat 4. tal:omit-tag 5. tal:replace 6. tal:content 7. tal:attributes 1. Optional attributes 8. tal:on-error URI for this namespace is http://xml.zope.org/namespaces/tal. To use tal: attribute prefix in XML you're required to declare it: Note PHPTAL does not enforce this requirement. tal:define Defines variables in the template. It takes one or multiple semicolon-separated variable definitions. Making a shortcut to a long path:
...
Defining more than one variable at the same time: ... Each definition may start with global keyword which makes variable available everywhere later in the template and all macros. Global variables can be redefined.

On the contrary, a local variable is only available inside the element it is defined in (and inside macros that are called in this element):

Tip You may also use tal:define with other attributes, it will be executed before any other attributes on the same element. Creating a string inside the template: Defining a string containing another variable: A small trick which uses content of the element (useful if you need to define very complex value): Hello ${fname}! Welcome to this page In above example, the tag won't show up because it has no printable content (hello variable grabs it) nor attributes. Note This is a special case. It only works if you use the global keyword. tal:condition The element and its content will be shown only if the condition is evaluated to true.

Welcome member ...

Please login before accessing this page

If the PHP backend does not provide your templates with enough methods, you will often have to fall back to PHP for special conditions: ... This may put too much logic inside the template and it is sometimes preferable to provide boolean attributes or accessible methods to the template: ... tal:repeat This attribute handles iterable objects like arrays, associative arrays, and objects implementing the PHP5 Iterator class. The repeat attribute repeats its element and its content until the end of the specified resource. text replaced by item Within a loop, you can access the current loop information (and that of its parent for nested loops) using specific repeat/* paths. In the above example: o repeat/item/key : returns the item's key if some/result is an associative resource (index otherwise) o repeat/item/index : returns the item index (0 to count-1) o repeat/item/number : returns the item number (1 to count) o repeat/item/even : returns true if item index is even o repeat/item/odd : returns true if item index is odd o repeat/item/start : returns true if item is the first one o repeat/item/end : returns true if item is the last one o repeat/item/length : returns the number of elements in some/result "item" depends on the receiver variable defined in tal:repeat expression. The most common usage of tal:repeat is in using some SQL database result. The following code will work if playersRanking contains object that implements PHP's Iterator interface:
Position Player Score
tal:omit-tag This attribute asks the PHPTAL parser to ignore the elements's open and close tag, its content will still be evaluated. if the condition is true, then only this text will appear and span open and close will be removed Will produce: only this text will appear, span open and close will be removed This attribute is useful when you want to create element optionally, e.g. hide a link if certain condition is met. If you want element that is never output, you can use tal:block only this text will appear, ten times. tal:replace This attribute will replace the entire tag with a value, or by nothing if no value is given. this ugly string and span Will produce: this beautiful string tal:replace can also be used to create samples in source templates, but remove them from final output.
item value
sample 1
sample 2
tal:content This attribute replaces the tag content with the evaluation of its expression. will be replaced Will produce: my string tal:attributes This attribute changes tag attribute(s) value(s). sample link With a 'somelink' having: $somelink->href = "http://www.google.com"; $somelink->title = "google search engine"; $somelink->text = "the google search engine"; Will produce: the google search engine Semicolon (;) separates attributes. If you want semicolon to be output in an attribute, you have to double it (;;). A somewhat complicated example involving tal:repeat: ... The php: modifier will be explained later, basically if the line is odd then tr will have a class attribute with "odd" as value, otherwise, no class will be set. The "condition ? then : else" is a regular PHP expression which must be used with care but has proven to be useful on more than one occasion. A better way to achieve the same result would be to ask your PHP coder to create a custom modifier for your needs (see PHP integration / custom modifiers) which would be used as follows: ... The modifier would return "odd" if repeat/ranking/odd is true, NULL otherwise. Optional attributes If you use TALES alternatives in tal:attributes and use nothing (or NULL in PHP) as last alternative, attribute won't be added at all if there's no value for it (this avoids adding empty attributes): ... tal:attributes="title object/tooltip | nothing"> XHTML attributes like selected, checked, etc. are properly handled automatically. Warning Remember that XHTML is case-sensitive, so SELECTED attribute is an error in XHTML. Use selected. tal:on-error This attribute replaces the element with the tal:on-error content if any runtime error is detected while generating the element, such as reference to non-existent variable or macro. the user name here If an error occurs accessing 'name' or 'user', the error string will be shown at the tag's place. This also works on more than one level of template: Note Due to optimizations performed by PHPTAL, tal:on-error on element containing an element with metal:fill-slot may not catch errors that happened inside the slot. METAL namespace 1. metal:define-macro 2. metal:use-macro 3. metal:define-slot 4. metal:fill-slot URI for this namespace is http://xml.zope.org/namespaces/metal. To use metal: attribute prefix in XML you're required to declare it: Note PHPTAL does not enforce this requirement. METAL stands for 'Macro Extension for TAL'. This namespace allows template designers to define and call macros. Macros can be used to output data recursively or to include code from other template files. metal:define-macro This attribute declares a macro. Think of macros as library of small templates which can be reused in any other template.
Last modified: page modification date
Macros inherit all variables from their caller. In the above example, the variable 'mdate' depends on the template that calls the macro. metal:use-macro This attribute calls a macro and includes its result in the current template. You can refer to external macros defined in other templates by specifying the template source file. It is interesting to note that you can also use the PHPTAL inline replacement feature inside the use-macro attribute value: Macro can call itself. This way you can output arrays recursively:
Callbacks Since you can use variables in macro names, you can create macros that call back other macros. This is useful in cases where slots are not enough.
this will be called every time
metal:define-slot This attribute must appear inside a metal:define-macro tag. Slots can be replaced by caller template with some custom dynamically-generated XML/XHTML. Slots can be thought of like reverse includes, a macro can be an entire page and slots customize this page depending on the URL. For instance, a slot may contain the latest news in the home page or user actions when the member is logged in.
news description
The above example defines a place called 'news_place' which can be overwritten by caller templates. See next section for the continuation of this example. Warning Slots work like variables, not like callbacks. metal:define-slot in tal:repeat will always repeat the same value over and over again. metal:fill-slot This attribute can occur only inside metal:use-macro element. This explicitly tells PHPTAL to replace a defined slot with the content provided inside the metal:fill-slot attribute.

user menu

Slots give the opportunity to define really customizable and reusable page templates with a simple push technology. Warning Slots work like variables, not like callbacks. metal:fill-slot in tal:repeat will keep overwriting same slot over and over again. I18N namespace 1. i18n:translate 2. i18n:attributes 3. i18n:name 4. XHTML in translations URI for this namespace is http://xml.zope.org/namespaces/i18n. To use i18n: attribute prefix in XML you're required to declare it: Note PHPTAL does not enforce this requirement. Note i18n is a short name for 'internationalization'. This namespace allow template designers to specify some text zones that must be translated during template evaluation. i18n:translate This attribute defines some text part that must be translated using PHPTAL's translation system.
Welcome here
In the above example, PHPTAL will looks for a translation key named 'welcome_message' and will replace the content of the tag with the equivalent in currently requested language.
Welcome here
This usage is a little different, no translation key is given, thus PHPTAL will use the content of the tag 'Welcome here' as the translation key. This is a regular translation if translation system knows the key 'Welcome here'. If no translation is found, the key will be used as the translation result. That's why using readable message instead of keys may be a good choice. Please note that the key to translate may be contained in a variable, to allow dynamic key selection.
...
i18n:attributes Defines which attributes should be translated. Takes semicolon-separated list of attributes and keys similar to those for i18n:translate. Picture i18n:name This attribute sets a translation variable value. Translations may contain ${xxx} strings where "xxx" is the name of a variable that needs to be interpolated dynamically. The value of this variable will be set to the tag and its content. If you don't need the tag around the value, use tal:replace instead of tal:content. tal:omit-tag may help if the value is a concatenation of strings. foo foo An example of i18n usage:
Welcome , you have unread mails.
The translation key of this example will be: "Welcome ${user}, you have ${mails} unread mails." PHPTAL will replace ${user} with ${user/name} and ${mails} with ${user/nbrMails} in translation. More information about I18N with PHPTAL is available in the PHP section of this book. XHTML in translations By defaults translations are assumed to contain only text, so PHPTAL escapes all "<" characters. You can use structure keyword in i18n:translate to disable escaping and use translated text as-is:
Gives:
bold text
Warning Caveats: This will only work in simplest cases - TAL attributes inside translated strings are ignored. Ill-formed XHTML in translations will break page well-formedness. PHPTAL namespace 1. phptal:debug 2. phptal:cache 1. Instant refreshing 2. Limitations: 3. phptal:tales URI for this namespace is http://phptal.org/ns/phptal. To use phptal: attribute prefix in XML you're required to declare it: Note PHPTAL does not enforce this requirement. These attributes are not defined in TAL specifications, but are useful when working with PHPTAL. phptal:debug This attribute toggles the activation of PHPTAL debugging for the content of the tag it is defined in. Note To debug errors in macros called across templates you need to add phptal:debug in template which defines the macro, not the one which uses it. The debug mode stores information like filename and source line number in the template, so exceptions thrown by incorrect path access will contain more information about where they where thrown. ...
...
phptal:cache This attribute causes output of entire element (including its tag) to be cached on disk and not re-evaluated until cache expires. Note Use of cache is beneficial only for elements that use very complex expressions, macros from external files or PHP expressions/objects that access the database. Otherwise uncached templates will be just as fast. Content of this attribute is a duration (how long element should be kept in cache) written as number with 'd', 'h', 'm' or 's' suffix.
will be evaluated at most once per 3 hours. Duration can be followed by optional "per" parameter that defines how cache should be shared. By default cache is shared between all pages that use that template. You can add "per url" to have separate copy of given element for every URL.
    will be cached for one day, separately for each page. You can add "per expression" to have different cache copy for every different value of an expression (which MUST evaluate to a string). Note Expression cannot refer to variables defined using tal:define on the same element.
      ...
      will be cached for 25 minutes, separately for each object ID. Warning Be careful when caching users' private data. Cache will be shared with everyone unless you make it user-specific with per user/id or similar expression. Instant refreshing Instead of clearing cache, it might be a better idea to put version or last modification timestamp in the per parameter. This will cause cached template to be refreshed as soon as version/timestamp changes and no special cache clearing will be necessary.
      ...
      Limitations: o phptal:cache blocks can be nested, but outmost block will cache other blocks regardless of their freshness. o You cannot use metal:fill-slot inside elements with phptal:cache. phptal:tales This attribute allows us to change the behavior of PHPTALES. The default behaviors is to interpret attribute expressions in a very ZPT way. But sometimes you just would like to have PHP there, and you end up using php: modifier everywhere. Another problem concerning PHPTALES is the way PHPTAL has to interpret paths. For example, myobject/mymethod/property/10/othermethod/hashkey takes relatively long to interpret (but don't worry about this too much - don't optimize until you find that it is really a problem with performance!) PHPTAL has (at runtime) to take myobject and discover that it is an object; find out that 'mymethod' is a method of this object (rather than a variable), and then to call it; explore the result to determine that it is an object with a property; find that its value is an array; find the 'ten' element of this array, and determine that it is an object; decide that othermethod is a method of this object (rather than a variable), and get the result of its execution; find that it is an object, and then retrieve the value for the key 'hashkey'. Of course this was an extreme example and most of the time we don't care, because the process is fast enough. But what if this very long path is called inside a big tal:repeat? D'oh! phptal:tales can help us here:
      Please note that the above example does the same as:
      Note php: modifier is explained in its own chapter. tal:block tal:block is a syntactic sugar for elements which contains many TAL attributes which are not to be echoed. is the same as: Another example:
      is the same as:
      PHPTALES 1. path: 2. Alternative PHP operator syntax 3. string: 4. php: 5. not: 6. exists: 7. true: 8. default 9. structure 10. Expression chains PHPTALES is the equivalent of TALES, the Template Attribute Language Expression Syntax which is the syntax used inside TAL, METAL, PHPTAL attributes and ${...} inline expressions. In previous examples, you should have seen some PHPTALES examples (string:, php:, not:, ...). This chapter describes the usage of PHPTALES in templates. The value of a TAL attribute may contain more than one expression (ex: tal:define), in which case each expression must be separated from the next one with a ';' character. path: This is the default modifier used in TAL expression when no other modifier is specified. The following lines will give the same result: ${data/user/name} Inside the template or inside expression strings, you can refer to a context variable using its path in the form ${path/to/my/variable}

      ${document/title}

      Warning If you try to read variable that does not exist, PHPTAL will throw an exception. Use exists: to check if variable can be read Alternative PHP operator syntax Because '<', '>' and '&' characters are cumbersome to use in XML, PHPTAL provides alternative syntax for PHP operators using these characters, and a few more for consistency. These operators can be used only in php: expressions. o < : LT (less than) o > : GT (greater than) o <= : LE (less or equal) o >= : GE (greater or equal) o == : EQ (equal) o != : NE (not equal) o && : AND o || : OR string: Because expressions are separated by a ';' character, and because '$' marks the start of a path, you must use: o ';;' when you want to insert a real ';' character in a string, o '$$' when you want to insert a real '$' character in a string. string:foo $bar baz string:foo $$bar baz string:foo ; php:doFoo() string:foo ;; php:doFoo() php: This expression evaluates what follows as a regular PHP expression except that '->' is replaced by dots '.' and variable names are prefixed with a dollar '$' sign. A dot '.' separated from the rest of expression by spaces is assumed to be a concatenation sign. 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 php: should be used with care and won't be needed in 80% of your templates but sometimes you will need to invoke some special PHP method to be certain whether a user is logged in, or to retrieve specific complex data depending on some conditions, dynamically inside the template. not: This expression is a boolean one, useful in tal:condition statements. not logged exists: This expression returns true if the path specified after it exists, and false otherwise. It is analogous to PHP's isset(). Normally using a path which doesn't exist throws an error like "Cannot find variable 'foo' in current scope". Thus, uncertain paths must be checked first: Use user/preferences here if defined Tip In PHPTALES use isset() instead. true: This expression returns true if the path specified after it exists and has value that evaluates to true (the value can be of any type). It is analogous to PHP's !empty() construct. Tip In PHPTALES use !empty() instead. default This is not an expression but a keyword, allowing template designers to keep the content of a tag as an alternative value if an error occurs, or if something is not defined. default my var value no some/var and no other/path found here Unknown page Above examples introduce the '|' character that allows the definition of alternatives for defines or prints. structure This is not an expression modifier but a keyword. While printing variables inside PHPTAL templates, you will have noticed that PHPTAL encodes each variable to ensure the validity of the output document. Sometimes, you may use HTML/XML variables which must be echoed as is.

      In above examples, we assume that $document->title and $document->content are variables containing preformatted HTML which must be echoed as is. Expression chains An expression chain is a list of expressions separated by '|' characters. While evaluating expressions separated by '|', PHPTAL will stop its evaluation when an expression value is not null and no error was raised while evaluating the expression. As a string: expression is always true, string: always terminates an expression chain whatever expression may follow. You can use php: expressions inside expression chains, like any other expression.

      PHP Integration ---------------------------------------------------------------------- Table of Contents 1. Constants 2. class PHPTAL 1. Configuration methods 1. setOutputMode(mode) 2. setEncoding(encoding) 3. Other methods 2. execute() method 3. echoExecute() method 4. addPreFilter() method 3. class PHPTAL_PreFilter 4. PHPTAL DOM 5. interface PHPTAL_Filter 6. interface PHPTAL_Trigger 7. interface PHPTAL_TranslationService 1. method setLanguage(...) 2. method useDomain($domain) 3. method setVar($key,$value) 4. method translate($key) 5. method setEncoding($encoding) 8. Working with gettext 1. Creating the translation directory structure 2. Portable Object files 3. Translation Domain 4. Using Translator in PHP 5. Variable interpolation 9. Creating custom expression modifiers This section is aimed at PHP developers and explains how to use and customize PHPTAL behaviors for simple and advanced usage. o PHPTAL: the main PHPTAL class. It is used to load and execute templates. o PHPTAL_Filter: filtering template sources and PHPTAL output. o PHPTAL_Trigger: handles output of elements with phptal:id. o PHPTAL_TranslationService: for replacing the built-in gettext support with your own internationalization system. Constants The only constant defined in PHPTAL.php is PHPTAL_VERSION. It contains version of PHPTAL library installed on your system (in format: X.X.X). In older versions of there were constants for configuration. They have been removed in favor of methods. class PHPTAL 1. Configuration methods 1. setOutputMode(mode) 2. setEncoding(encoding) 3. Other methods 2. execute() method 3. echoExecute() method 4. addPreFilter() method This is the main library class for you to use. The most common method of use: 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. 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: my title

      my title

      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: my title

      my title

      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; } ?> Configuration methods PHPTAL tries to use best defaults possible and you shouldn't need to change any of the settings. All of these are methods of the PHPTAL class. set* methods return instance of their class, so you can chain them: setPhpCodeDestination('/tmp/phptal')->setOutputMode(PHPTAL::XML)->setTemplate('tpl.zpt')->execute(); ?> is the same as: setPhpCodeDestination('/tmp/phptal'); $phptal->setOutputMode(PHPTAL::XML); $phptal->setTemplate('tpl.zpt'); echo $phptal->execute(); ?> There are other set* methods for filters, internationalization, 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. o Empty elements will be forced to use self-closing form (, ), and non-empty elements will always have closing tag. Warning XHTML output mode changes element in way that is incompatible with RSS. Use XML output mode to generate RSS feeds or use Atom. o Boolean attributes (checked, selected, etc.) will always have value required by the XHTML specification (it simplifies use of tal:attributes). o . Namespace declarations, name prefixes, explicit CDATA sections and other HTML-incompatible constructs will be omitted. Note This mode is not a "tag soup". PHPTAL will close all elements properly and quote attributes when it's necessary. 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) Specify character encoding used by your templates. The default is UTF-8. PHPTAL assumes that encoding of all templates and generated documents is the same. BOM (Byte Order Marker) is removed from UTF-8 documents. Note PHPTAL does not read encoding from XML files and never converts character encodings. Tip Save yourself trouble and always use UTF-8 for everything. 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. Tip 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. The default is 30 days. Cache is scanned and cleaned up only when PHPTAL recompiles a file, and only (on average) once per 30 recompiles. You can simply delete cached files if you don't want to wait until PHPTAL clears them. 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 pre filters or changing code of PHPTAL itself. Warning This slows down PHPTAL very much. Never enable this on production servers! execute() method Executes the template and returns final markup. PHPTAL keeps all variables after execution and this method can be called multiple times. You can use it to create multiple versions of the same page (by changing only some variables between executions) or to generate multiple different templates with same variables (by calling setTemplate() between executions). Note If you need "fresh" copy of PHPTAL, just create a new object. echoExecute() method Since most common use of execute() method is to echo its output, PHPTAL offers convenience method that echoes output immediately without buffering. This enables streaming of arbitrarily large output without hitting memory limit. Note Fragments of template that use tal:on-error or phptal:cache are buffered regardless. The code: echoExecute(); ?> is same as: execute(); ?> but little faster. Warning Currently echoExecute() method has some unexpected limitations. Limitations When echoExecute() is used, PHPTAL will throw exception if you call any macro defined in a file that has XML declaration or DOCTYPE. Typically PHPTAL allows templates to "inherit" DOCTYPE from another file (useful when subpage is calling main layout template), however that is not possible without buffering. To work around this you can: o Keep using echo $tpl->execute() until this limitation is lifted. o Remove all DOCTYPEs and XML declarations from templates and echo them from PHP before calling echoExecute(). addPreFilter() method Note In PHPTAL 1.2.1 this method replaced older setPreFilter() method, which is now deprecated. Adds new pre filter that will be applied to the template. Pre filters can modify source code and parsed DOM nodes of the template. Pre filters are applied in other they've been set. See description of PHPTAL_PreFilter for more information how to write your own pre filter. class PHPTAL_PreFilter Pre filters are executed only once before template is compiled. Pre filters operate on template's source code, so they are not able to access value of any template variables. However pre filters can "see" and modify TAL markup. To create pre filter extend PHPTAL_PreFilter class and implement only filter*() methods you need. filter() Receives template source code as string and is expected to return new source. You can use it to simply find'n'replace fragments of source code. Be careful not to introduce syntax errors in the template. Warning PHPTAL's error messages will refer to line numbers after filtering, which may be confusing if your prefilter adds or remove lines from the source code. filterDOM() Receives root PHPTAL DOM node of parsed file and should edit it in place. Example pre filter that removes all comments: function filterDOM(PHPTAL_Dom_Element $element) { foreach($element->childNodes as $node) { if ($node instanceof PHPTAL_Dom_Comment) { $node->parentNode->removeChild($node); } else if ($node instanceof PHPTAL_Dom_Element) { $this->filterDOM($node); /* recursively filter all elements */ } } } getCacheId() Should return (any) string that uniquely identifies this filter and its settings, which is used to (in)validate template cache. Each time you return different string, template will be recompiled. Implement this method if result of the filter depends on its configuration. Unlike other filter methods, this one is called on every execution. Tip When developing and testing your filter, set setForceReparse(true) to force PHPTAL to update templates every time. Otherwise result of your filter will be cached and you won't see the changes. getPHPTAL() Returns instance of PHPTAL class that uses this prefilter. You can query it to check current encoding or other settings. PHPTAL DOM Internally PHPTAL represents documents using it's own document object model, a little bit similar to W3C's DOM. However, PHPTAL's API has only few basic methods for DOM manipulation. PHPTAL_Dom_Element class has following properties and methods: o array childNodes ; Numerically indexed array containing children of the element. Warning Please don't edit this array directly. Use appendChild(), etc. Note PHPTAL does not implement nextSibling, firstChild, etc. o PHPTAL_Dom_Element parentNode ; Parent node (element) of the current element. Use it to traverse tree upwards. o void appendChild($node); Appends node to the element. Node will be removed from its current element and added at the end of this element. Warning PHPTAL does not manage namespace declarations. Moving nodes between elements in different namespaces will change meaning of the document. o void replaceChild($new_node, $old_node); Old node will be replaced with new node. o void removeChild($node); Remove node from its parent. o string getAttributeNS($namespace_uri, $local_name); Returns unescaped (without entities) value of a specific attribute. $a->getAttributeNS('','href') Tip In XML attributes don't inherit element's namespace, and all XHTML attributes are in the default namespace. o array getAttributeNodes(); Returns array of PHPTAL_Dom_Attr objects, which represent all element's attributes. You can modify attributes' values without using setAttributeNodes(). o void setAttributeNodes(array $attrs); Replaces all elements attributes with the given ones. o string getLocalName(); Returns local name of the element, e.g. has local name title. o string getNamespaceURI(); Returns namespace URI of the the element, e.g. has namespace http://www.w3.org/2005/Atom. Tip XHTML namespace is http://www.w3.org/1999/xhtml. Text, CDATA and attribute nodes have getValueEscaped() and setValueEscaped() methods which allow reading/setting of their text with entities preserved. interface PHPTAL_Filter This interface allows you to create filters for processing result of template execution. Post filters are set using setPostFilter() method. Post filters are invoked after each template execution. Tip If your filter is slow, try using pre filter instead, which is executed only once before template is compiled. Result of template processing (with values of all variables and no TAL markup) will be passed to your filter's filter() method: 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. _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() method 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(). ...
      ... foo bar baz foo bar baz ...
      ... For some reason we decide the
      block requires to be cached. We introduce a phptal:id into the template: ...
      ... foo bar baz foo bar baz ...
      ... Then we write our trigger which will cache the
      content: _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: 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 1. method setLanguage(...) 2. method useDomain($domain) 3. method setVar($key,$value) 4. method translate($key) 5. method setEncoding($encoding) 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); Your implementation must define the following methods: method setLanguage(...) This method may be called by the template to change the current output language and/or locale (e.g. en_US). Its arguments are a list of possible languages. Use func_get_args() to get the argument array. The first known language should be used by your service. Return language that has been set. _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). _domains)){ $file = "domains/$this->_currentLang/$domain.php"; $this->_domains[$domain] = include($file); } $this->_currentDomain = $this->_domains[$domain]; } ... private $_currentDomain; private $_domains = array(); } ?> The above example is a possible translation solution where keys are stored in PHP files which return an associative array of key => translation. method setVar($key,$value) This method matches i18n:name calls. It builds an interpolation context for later translate calls. _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. _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; } ... } ?> method setEncoding($encoding) PHPTAL class calls this method to inform your translation service what encoding is used by the template. translate() method should return strings in that encoding. If you always use the same encoding for templates and translation files (i.e. UTF-8), you can leave this method empty. Working with gettext 1. Creating the translation directory structure 2. Portable Object files 3. Translation Domain 4. Using Translator in PHP 5. Variable interpolation gettext is a standard GNU internationalization / translation system which can be used with PHP and which is supported by PHPTAL. The usage of gettext(TM) is simple but you will have to perform some tests to be sure everything works fine on your system. First, PHP must be compiled with the --with-gettext flag. See PHP documentation for how to do this. You can test your installation using following peace of code: // // test if gettext extension is installed with php // if (!function_exists("gettext")) { echo "gettext is not installed\n"; } else { echo "gettext is supported\n"; } Creating the translation directory structure The PHP gettext(TM) extension requires a specific structure which will contain your translation files. /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 ... The language code is composed of two characters defining the language itself (en, fr, es, ...) and two characters defining the country (US, GB, FR, ES, ...). The directory pattern is: //LC_MESSAGES/ Portable Object files PO files are plain text files that contain your translation. You can safely edit them by hand. po minimalistic example (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" Once edited, each PO file must be indexed using: msgfmt mydomain.po -o mydomain.mo This command won't work if you don't have gettext(TM) tools installed on your system. This will produce a MO file (machine object) indexing your translation for quick access. Then you have to translate this file in other languages. po minimalistic example (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 franc,ais" The translation file must also be indexed: 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(TM)'s performance to split your application translations in more than one file. Using Translator in PHP 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(TM) translator, you can use gettext() too. Variable interpolation The I18N namespace 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: Welcome you currently have unread messages! Because i18n:translate contains a value 'welcome', the template data will be ignored and the message given by gettext(TM) will be used instead. Creating custom expression modifiers PHPTAL comes with some basic expression modifiers (not:, exists:, string:, php:, path:). These modifiers are defined by ZPT specifications but PHPTALES can be extended with your own modifiers to manipulate strings, date, money numbers, objects, whatever... The aim of a modifier is to return some PHP code that will be included in the template PHP source. Modifiers are used at parse time. If you change the behavior of a modifier, you'll have to delete generated PHP files and reparse all templates using it. Please note that modifiers produce code, and mustn't echo data! Any PHP function starting with "phptal_tales_" is usable as a modifier. Modifiers takes two arguments: o $src: the source string after the "modifier:" keyword o $nothrow: a boolean which determines whether exceptions may be thrown or not by phptal_path() resolution. This boolean must be propagated whenever you call another phptal_tales_* modifier from within your own modifier. For example, in the following TAL template, 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. An expression like: Will use 2 modifiers: o some-modifier: with "my/path/value" as $src argument and $nothrow set to true because an alternative exists o 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. Remember, path: is the implicit modifier used when no other modifier is specified. Modifiers can use other modifiers to generate simpler PHP code. The example below shows this. // // 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).')'; } Appendix A. Note for system administrators PHPTAL functions by generating PHP files from the template's logic, this means that it needs a directory to store those generated files so they can be parsed by the PHP interpreter. By default PHPTAL will use the system's temp directory (via PHP's sys_get_temp_dir() function if available) or will try to guess where it should be, /tmp on Unix like systems and c:\windows\temp on Microsoft ones, to store the compiled templates. The default destination can be changed to your liking by calling setPhpCodeDestination() method with the appropriate path. Be it the system's temp directory or a custom one, it needs to have its permissions setup as to allow the PHP running process (the Apache user if using mod_php or the cgi/fastcgi user otherwise) to create and update files in that directory. PHPTAL creates one file for each different template file and one file for each tag if using phptal:cache. It doesn't create separate files for macros (which are simply compiled as PHP functions inside the compiled template file). These files are automatically cleaned up once in a while, more specifically, each time a template is compiled there is random probability, controlled by setCachePurgeFrequency() method, which will just delete files older than set by setCacheLifetime() method. Alternatively you can also schedule the deletion of old/unused files by running this from an Unix-like shell (e.g. in a cron job): find /tmp/ -name tpl_\* \( -atime +1 -o -mtime +14 \) -delete Appendix B. Useful links o ZPT Zope Page Template front page, o TAL the Template Attribute Language page, o METAL is the Macro Expansion of TAL, o TALES the TAL Expression Syntax. Appendix C. Greetings Big thanks goes to: o ZPT team, who made these useful specifications, o The PHPTAL community for their support, help and reports, o Jean-Michel Hiver, who 'forced' me to look at them, o Olivier Parisy, the first enthusiastic PHPTAL user and bug finder,