Open Power Template 2.0

Copyright © Invenzzia Group 2008-2009
Available under the terms of license: GNU Free Documentation License 1.2
Generated: 14.09.2010

Table of Contents

Table of Contents
1. Preface
2. Installation
Next »

1. Preface

The concept to separate presentation (XHTML code) and logic (scripts) layers has a long story. We can mix those two parts, use PHP itself to separate them or to manipulate the output document with DOM. There is also one more solution - template engines. The PHP script is accompanied by an additional file with a template which describes, how to display the data. A template engine is a system that puts the data generated by the script in the templates, thus generating the complete output for the user. The template does not have to be written in PHP, too. We can use a separate language there, because PHP code is too complex and too long for such tasks, even if it was being developed to deal with it. The new language uses very short and intuitive constructs that hide all the algorithmic issues away from the template designer, no matter whether he is familiar with programming or not. Moreover, the language can allow us to do things in the way that is impossible to achieve in PHP because of its structure.

Open Power Template

Open Power Template 2 is a successor of popular template engine for PHP5. We have redesigned it from scratch, because we wanted to continue our ideas and implement new functionality that was impossible to achieve on the old engine. The main source of new features and improvements are the users. Of course, we know that the code rewriting breaks the backward compatibility, but on the other hand we can now fix everything that worked or looked poorly. But in fact, the main project philosophy, goals and solutions are still present.

OPT can be described in two points: performance and the KISS rule (Keep It Small and Simple). We try to keep the simple operations simple and intuitive and to simplify more complex things. We always analyze existing projects and develop solutions to solve encountered problems easily. During the coding, we pay attention on the performance, optimizing the code as much as possible. OPT also means more than one way to work. We think that there is no the best model for all the tasks and we rather offer a set of tools which you can use, depending on your demands.

Open Power Template is a part of "Open Power Libs" project which is a set of supporting libraries for your scripts and frameworks. In order to work, it requires the core OPL files that are included in every OPT release.

Features

Some of the features offered by the new version:

The feature list seems to be quite long, but everything is on the right place. All you have to do is to learn and use a couple of simple rules, and the rest is simple. The library is available under the terms of new BSD license.

Documentation

This document concerns the Open Power Template library only. It also contains the basic information on the OPL core, but more details can be found in its own documentation.

Table of Contents
2. Installation
1. Preface
« Previous
2.1. Standard installation
Next »

2. Installation

OPT can be installed in the same way, as the other OPL libraries, however here we are going to describe this process briefly. You can choose one of two types of install: as a set of files and as PHAR archive (PHP Archive).

Requirements

OPT requires at least PHP 5.2 (we recommend PHP 5.3, because it contains some extra classes that must be emulated on earlier versions). The necessary modules are:

  1. PCRE
  2. SPL
  3. PHAR (only for PHAR version)

All of them are enabled by default since PHP 5.3.

2. Installation
2.1. Standard installation
2. Installation
« Previous
2.2. PHAR installation
Next »

2.1. Standard installation

This chapter describes the installation as a set of PHP files.

Directory structure

In the downloaded archive you can find a directory called /lib with all the necessary OPT and OPL source code. Create in your project directory tree a new directory and copy there the contents of /lib. If you already use other OPL libraries, copy OPT into the existing directory. Do not be afraid to overwrite the files if you are asked to do so. Unless they are modified, there is nothing to worry.

In case of OPT, you must also create two directories for your templates. Let assume that the first one is called /templates and the second one - /templates_c. You can choose any other name, if you want.

Be sure that PHP has write access to /templates_c and read access to /templates.

PHP code and the configuration

In the beginning, you must load the OPL core, configure the path to the libraries and set the autoloader. The following code is used then:

<?php
require('./libs/Opl/Base.php'); // 1
Opl_Loader::setDirectory('./libs/'); // 2
Opl_Loader::register(); // 3
 
$tpl = new Opt_Class; // 4
// your script
  1. We load the OPL core.
  2. We set the library path. It should end with /, but it is not necessary. We recommend to set the absolute path, because OPL does not use include_path by default. In case of PHP 5.2 this method is required, because it also loads the emulation code for some extra classes from PHP 5.3.
  3. We register the autoloader that will automatically locate and load the classes.
  4. We create the main parser object.

All the OPL libraries report the errors as exceptions. For OPL, the base exception class is Opl_Exception and for OPT - Opt_Exception. OPT contains a very convenient error handler that shows some extra information that should help to solve the problem, and adds additional explanations, why you see it and where to look for mistakes.

Once we created the object, the library must be configured. We do it by setting the values of some fields in $tpl object. The most important are:

sourceDir
Path to the /templates/ directory.
compileDir
Path to the /templates_c/ directory.
contentType
Content type. We can use one of predefined values or enter the MIME type manually.
charset
Used output encoding.

Before we definitely start, we must also call Opt_Class::setup(). Do not forget about it, because it plays a very important role:

<?php
require('./libs/Opl/Base.php');
Opl_Loader::setDirectory('./libs/');
spl_autoload_register(array('Opl_Loader', 'autoload'));
 
try
{
    $tpl = new Opt_Class;
    $tpl->sourceDir = './templates/';
    $tpl->compileDir = './templates_c/';
    $tpl->contentType = Opt_Output_Http::XHTML;
    $tpl->charset = 'utf-8';
    $tpl->setup();
 
    $view = new Opt_View('template.tpl');
    $view->hello = 'Hello, world!';
 
    $out = new Opt_Output_Http;
    $out->setContentType();
    $out->render($view);
}
catch(Opt_Exception $exception)
{
    Opt_Error_Handler($exception);
}

Finally, to work with OPT, we need two types of objects:

  1. Views - they represent a template with some script data assigned to it, as well as other rules, such as format definitions or inheritance lists. In the script, they are objects of Opt_View class and one object should match exactly one top template (possibly accompanied with dependent templates).
  2. Outputs - they decide, where to send the processed view results.

Using a view is very easy. In the constructor, we specify the template assigned to the view, and later we simply assign the data to template variables using the syntax $view->variableName = value. There are several other data assignment methods which are described deeper in the API reference. As our view is ready, we need to create an output. We are going to use Opt_Output_Http, a standard OPT class that is responsible for HTTP header management and sending the templates to the browser. Once it is initialized, we call Opt_Output_Http::setContentType() to create the headers for the specified content type. The method supports full content negotiation, when connected with Open Power Classes library. Finally, we call Opt_Output_Http::render() on our view object to send the results to the browser.

Note that different output systems may have some requirements. For example, the HTTP output assumes that all the templates are valid XML files. This means that you cannot open a tag in one template and close it in another. So, to be sure that the script output will be also a valid XML document, we can parse only one template. But do not worry - OPT provides several better solutions to make a template modularization. They are explained in the next chapters.

The templates

We save the templates in ./templates/ directory. To speed up the task, OPT compiles them to the PHP code every time they are changed. The compiled versions are stored in ./templates_c/ whose content is managed by OPT and you do not have to do there anything. You can delete the contents in this folder anytime you want. OPT will recompile all the templates then.

This is an example template in /templates/template.tpl:

<?xml version="1.0" ?>
<opt:root>
<opt:prolog />
<opt:dtd template="xhtml10transitional"/>
<html>
<head>
    <title>My first OPT template</title>
</head>
<body>
    <p>My first OPT template</p>
    <p>Script message: {$hello}</p>
</body>
</html>
</opt:root>

The third and fourth line expand the output XML prolog and the DTD for XHTML 1.0 Transitional, because the prolog we can see in the template is for internal use only. In the line four from the end, we specify an expression in curly brackets. It displays the content of the variable $hello in the specified place. When you run the example, instead of curly brackets we see there "Hello world!".

The details concerning creating templates and using OPT are described later.

See also:

2. Installation
2.2. PHAR installation
2.1. Standard installation
« Previous
3. Template syntax
Next »

2.2. PHAR installation

This chapter describes an experimental feature that is not completed yet. The details might change before releasing the first stable version.

This chapter describes installing OPT as PHAR. It is recommended to read the previous chapter, too, because here we will only explain the differences.

PHARs

PHARs (PHP Archives) are special files similar to JAR in Java. In other words, they group several files as one bigger archive. PHP supports PHAR since PHP 5.2.0 after installing the necessary module, and since PHP 5.3.0, PHAR extension is enabled by default.

OPL as PHAR

Because PHARs can do the initial configuration on their own, the startup code is a bit simpler here. All we have to do is to put somewhere the downloaded archives and to include them:

<?php
require('./opl.phar');
require('./opt.phar');
 
try
{
    $tpl = new Opt_Class;
    $tpl->sourceDir = './templates/';
    $tpl->compileDir = './templates_c/';
    $tpl->contentType = Opt_Output_Http::XHTML;
    $tpl->charset = 'utf-8';
    $tpl->setup();
 
    $view = new Opt_View('template.tpl');
    $view->hello = 'Hello, world!';
 
    $out = new Opt_Output_Http;
    $out->setContentType();
    $out->render($view);
}
catch(Opt_Exception $exception)
{
    Opl_Error_Handler($exception);
}

Even with PHARs, there may be a need to keep additional directory structure for OPL. For example, if an add-on is not available as PHAR, you must put it in the filesystem and inform OPL, where it can find it.

<?php
require('./opl.phar');
require('./opt.phar');
Opl_Loader::setDirectory('./libs/');

The details can be found in OPL documentation.

See also:

Table of Contents
3. Template syntax
2.2. PHAR installation
« Previous
3.1. Compiler modes
Next »

3. Template syntax

In this chapter, we are going to describe the syntax used in OPT templates. It is based on XML language, and if you are familiar with it, you should have no problems to understand. Otherwise, you should consider reading something about XML.

The execution of the template takes some time, so OPT tries to optimize the process. When you run a template for the first time, OPT compiles it into pure PHP code, which can be executed much faster. Moreover, the result is saved on the hard disk and later, OPT simply loads the precompiled version. This implicates some issues that you must know about. First of all, OPT is not a Document Object Model. The script cannot modify every tag it likes and whenever it likes unless it was programmed in the template itself. However, a huge majority of programmers do not need such functionality, so this is not a real limitation for them. Otherwise, you should think over whether the template engines are the right choice.

OPT philosophy

Before we start, let us tell abot the syntax philosophy.

Configurable compatibility
The XML parser used by OPT is very flexible and allows to set a wide variety of compatibility levels with XML standard.
KISS
The simple things should be simple to achieve.
DRY
There is no need to reinvent the wheel every time we write a new template.
Declarative style
Declarative style means that we only specify, what we want to see, not how to do this. Unlike many other template engines, OPT contains built-in declarative instructions. Such style is very convenient for people who know little about programming, but not only. Also the programmers should enjoy the simplicity and clearness of OPT templates.
No programming in templates
The templates full of loops, conditions and logical operations are very hard to maintain and understand. You should avoid it, if possible.

How much PHP is in OPT syntax?

Like many other template engines, OPT compiles the templates into the native PHP code. This means that usually the syntax is based on that language and includes some sort of support for the default PHP types, etc. The programmers can even put PHP code snippets in the templates. It has some disadvantages, like:

  1. Poor code reusability - sometimes the code sample cannot be reused again in another place even inside the same PHP application!
  2. Problems with refactoring - as the applications grow, we may need to change some solutions. The templates must be rewritten as well.
  3. Problems with portability - what about backends? They often share the same layout. Do we need to write the templates every time we start something new?

In OPT we decided to divide the syntax into two parts:

  1. Abstract, platform- and type-independent part.
  2. PHP-based part.

In the first case, OPT automatically converts the code to the valid PHP parts according to the information provided by the script and the context. If you change the format, you simply modify your script and recompile templates. In the second case, OPT compiles everything as it is. It is you who must make sure that everything is correct, and who eventually will pay for it.

The access to the PHP syntax part can be controlled in the compiler configuration. You can disable some minor issues, or simply turn it off to ensure that nobody tries to manipulate the internals of the objects passed to the templates. The security issues are discussed later.

Who is this chapter for?

We recommend the PHP programmers to read this chapter, too, because they can find here a lot of practical notices and advices. OPT syntax contains several programming instructions and ports to PHP language elements, and this document may be read by people who are not familiar with programming. Because of this, we marked the chapters:

  1. For everyone - recommended to read.
  2. Simple programming construct that is recommended.
  3. Programming construct that probably will not be necessary. You

The second type is marked with:

This chapter contains useful programming information that are simple to understand. In case of questions, contact your programmer.

The third type is marked with:

This chapter contains information about programming constructs that are not recommended to use. If you are only a template author, probably you will not have to use them. In case of questions, contact your programmer.

Moreover, we are going to use additional information frames:

Some information for programmers

Important information

This frame informs about common errors and mistakes.

We will also use the following terms:

Template
A file with the source template code that tells where to put the data and how to display them.
Compiled template
The template compiled into the PHP code.
Template result, output
The code that is produced by putting the data into a template and is ready to be sent to the browser.
3. Template syntax
3.1. Compiler modes
3. Template syntax
« Previous
3.2. CDATA sections
Next »

3.1. Compiler modes

The template syntax is quite flexible and depends on various compiler settings and modes. We set them so that they fully suit our needs.

XML mode
This is the default work mode. The compiler processes the input file as a valid XML document with prolog. The attribute syntax is strictly checked, as well as tag closing order, including the static ones, that are a part of the output document.
HTML mode
The compiler allows to be less restrictive, when it comes to some syntax elements. Although this mode is still too strict in some areas, when it comes to HTML compatibility (for example, the tags must be closed in the correct order), but in fact, this allows to produce nice and clean HTML documents.
Quirks mode
OPT recognizes and processes only its own tags. The rest is parsed as a static text, including ordinary XML/HTML tags. The mode quite resembles the OPT 1.x with XML compatibility mode enabled. Quirks mode does not use so much resources during the compilation, as the two modes above.

For generating website output code, we recommend to use the first or the second mode. Thanks to the (X)HTML code analysis, the instructions may cooperate with ordinary tags much better than in other template engines, PHP or the quirks mode.

The full list of issues concerning XML can be found below:

  1. Document prolog - in the XML mode it is required in all templates, however it it is not sent to the output system. To display an XML prolog in the template result, we must use opt:prolog instruction.
  2. DTD - currently OPT does nothing with DTD, but it can be changed in the future versions. In the XML mode, it is recommended to use opt:dtd instruction with some predefined DOCTYPE templates.
  3. Root node - in the XML mode, the document may contain only one root node. For sub-templates that generate only a small part of the output, opt:root instruction is designed.
  4. CDATA sections - they are sent to the output, but its content is not parsed by OPT. The exact behavior can be controlled by opt:literal instruction.
  5. Comments - by default they are ignored by the parser, but there is a possibility to send them to the output.
  6. White spaces - the programmer may decide whether to cut out the unnecessary white spaces.
  7. Unicode - the XML standard allows to use Unicode characters in the tag names. By default, this is disabled in out due to performance issues, but it can be turned on.
  8. Namespaces - it the namespace is registered in OPT, the elements assigned to it are processed by the parser. The rest is sent to the output.

Below, we present two simple templates of a tiny website written in the XML mode:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<opt:root>
<opt:prolog />
<opt:dtd template="xhtml10transitional" />
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" lang="pl">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>{$title}</title>
</head>
<body>
    <div id="logo">
        <h1>A small website</h1>
    </div>
    <div id="navigation">
        <ul>
            <li opt:section="navigation"><a parse:href="$navigation.url">{$navigation.tytul}</a></li>
        </ul>
    </div>
 
    <div id="menu">
        <h1>Menu</h1>
        <ul>
            <li opt:section="menu">
                <opt:attribute name="class" if="$menu.important">important</opt:attribute>
                <a parse:href="$menu.url">{$menu.tytul}</a>
            </li>
        </ul>
    </div>
    <div id="content">
        <opt:section name="content">
            <opt:include from="content"/>
        </opt:section>
    </div>
    <div id="footer">
        <p>Copyright ABC 2007</p>
    </div>
</body>
</html>
</opt:root>

In XML mode, only one template can be sent do the browser directly and this is why the example below contains the entire HTML code of a website. However, we still can use smaller templates for website parts. This is done with opt:include and opt:section provides a loop, so we can load more sub-templates. They may contain forms, lists, galleries etc. and they will appear inside <div id="content"> tag.

The XML prolog that we can see in the template, will not be sent to the browser. It means that we must use opt:prolog in order to provide there a valid XML document. It sets the correct XML version and the encoding, taking the necessary information from the library configuration.

Because our template is an XML document, we cannot put the data everywhere we want to. In the tag content, we must use curly brackets and to make the tag attribute value dynamic, we must change its namespace into parse. Otherwise, the value will be treated as a static text. In order to generate an optional attribute that appears only from time to time, we use opt:attribute tag with the additional condition in opt:if attribute that defines, when to show this attribute.

This is a sample sub-template:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<opt:root>
    <h1>Title</h1>
    <p>This is a page content.</p>
</opt:root>

We must put there the XML prolog, too, and add a root node. OPT helps us here with opt:root. With this instruction, you can also set some template-specific settings. If such style does not suit you, all you have to do is to change some configuration directives to make the compiler less restrictive.

<h1>Title</h1>
<p>This is a page content.</p>

The template above can also be accepted by the compiler.

For programmers

In the script-side, there are only two real modes: XML and quirks. The HTML mode is in fact a set of additional settings that can be turned on or off, depending on our needs. To mark that we mean both XML and HTML modes, we will use the "XML/HTML" name in the next chapters of this documentation.

In the quirks mode, the output document can be generated from separate templates parsed one by another. In XML/HTML, we can parse only one template directly, but it can include sub-templates. This assures the compiler that the sum of valid XML templates will also give a valid output with the tags enclosed in the correct order. The output is buffered, and in case of error, the default error handler deletes the current content and displays the message instead. The errors are handled as exceptions, so it is not hard to change the default behavior.

3. Template syntax
3.2. CDATA sections
3.1. Compiler modes
« Previous
3.3. XML Prolog and DTD
Next »

3.2. CDATA sections

A CDATA section is a special part of the XML document that is visible by the parser, but it is not processed. It allows to keep XML tags as a static text that will not be parsed. By default, OPT parses the CDATA section , like in XML - the content is displayed with the surrounding delimiters, but not parsed. However, sometimes we do not want that:

<script type="text/javascript">
<![CDATA[
    document.write('Text ]]>{$info}<![CDATA[ - here is some info from the script.');
]]>
</script>

Of course, OPT will destroy such code, because the JavaScript will be full of opening and closing CDATA tags. The problem can be solved with the opt:literal instruction. Now, the CDATA sections are still not parsed, but at least their tags are not rewritten to the output:

<script type="text/javascript">
<opt:literal><![CDATA[
    document.write('Text ]]>{$info}<![CDATA[ - here is some info from the script.');
]]></opt:literal>
</script>

By default, opt:literal produces CDATA tags around its content, so the final result is:

<script type="text/javascript">
<![CDATA[
    document.write('Text SOME SCRIPT INFO - here is some info from the script.');
]]>
</script>

opt:literal can also produce dynamic comment content or be transparent.

JavaScript and CSS content enclosed with CDATA section can cause problems with some older browsers so your code may not work properly. It is recommended to use mode="comment_cdata" attribute on opt:literal instruction to solve this problem. More details can be found in chapter about opt:literal and on Mozilla Developer Center website.

See also:

3. Template syntax
3.3. XML Prolog and DTD
3.2. CDATA sections
« Previous
3.4. Entities
Next »

3.3. XML Prolog and DTD

To keep the compatibility with the XML standard, the OPT compiler must provide support for XML prolog and DTD. The exact behavior is controlled with prologRequired directive. The available values are true or false.

If prologRequired is set to true, the XML prolog must be set in every template, but it is not sent to the output system. In order to create a prolog or DTD for the browser, we need opt:prolog and opt:dtd instructions:

<?xml version="1.0" standalone="yes" ?>
<opt:root>
<opt:prolog standalone="no"/>
<opt:dtd template="xhtml-strict"/>
<html>
<head>
</head>
 
<!-- the template -->
</html>
</opt:root> 

If we need some special DTD, we can write it inside opt:dtd tag and enclose in CDATA sections:

<![CDATA[<!DOCTYPE hi [
    <!ELEMENT hi (#PCDATA)>
]>]]>

OPT sets XML version to "1.0", and the default encoding is taken from the configuration.

If prologRequired is set to false, OPT does not require prologs in every template. Moreover, when it finds one, it checks its syntax, but also sends it to the output system. In the quirks mode the prolog is treated as an ordinary text.

3. Template syntax
3.4. Entities
3.3. XML Prolog and DTD
« Previous
3.5. Expressions
Next »

3.4. Entities

Because OPT contains an XML parser, the entities used in the templates are parsed on the server-side. Only the five special XML characters are converted back to the entities before sending the document to the server: amp, quot, lt, gt, apos. This is usually a good thing. Especially, if you are using UTF-8, you do not have to worry about how the entities are handled, because the browser accepts both the entity strings and the direct character codes.

OPT supports the Unicode entities like &#184; and &#0xB8. The special HTML entities like &nbsp; or &Acute; are parsed only if the configuration option htmlEntities is set to true. If the parser encounters an unknown entity, it generates an exception. You can register your own entities in the parser. The default entities added by OPT are &lb; for { and &rb; for }.

If you need to display an entity in the output document, you may use the function entity().

See also:

3. Template syntax
3.5. Expressions
3.4. Entities
« Previous
3.5.1. Variables
Next »

3.5. Expressions

So far, we have discussed the static template elements, that are rewritten to the output. Now we are going to describe something that adds more dynamics to the templates - the expressions. The expression role is to produce some value, especially to display it somewhere. An expression can be put in the static text or as a tag attribute value. The following exaple shows the valid and invalid ways to do so:

<!-- good -->
<p>Expression as a part of static text: {$variable}</para>
 
<!-- bad -->
<p {$variable}>The expression must not be placed in that way.</p>
 
<!-- bad -->
<p class="{$variable}">The expression must not be placed in that way.</p>
 
<!-- good -->
<p parse:class="$variable">Expression as an attribute value.</p>
 
<!-- good -->
<opt:if test="$variable">Expression as an OPT instruction attribute value.</opt:if>

The details concerning OPT instructions will be explained later.

If you are familiar with PHP expression system, you should have no problems with understanding the OPT one. We did not reinvent the wheel, but simply used the existing conventions and patterns in order to make it more readable. Most of the rules and techniques that applied in PHP, are also available in OPT and all you have to do is to pay attention on the operator symbols which may differ.

Open Power Template provides a built-in expression lexer that parses the expressions and reports the errors. If the expression is invalid, the compiler will generate an exception during the compilation which looks like this: Unexpected token OPCODE_XXX (xxx) in expression (expression). It informs that the compiler came across the specified token (we see the type: OPCODE_XXX and the exact value), but it must not be used in that place. Moreover, it shows the full expression so that you could locate it in the template.

3.5. Expressions
3.5.1. Variables
3.5. Expressions
« Previous
3.5.2. Values
Next »

3.5.1. Variables

Variables are items that are used to storing values. Each variable has its own unique name that allows to identify it. The value of a variable can be any type of data: strings, numbers, floating point numbers, logical values, or even compound PHP data types, such as arrays and objects. The variable name begins with an underscore or a letter, and later the digits from 0 to 9 are also allowed. Variable names are case-sensitive. In OPT, there are several types of variables.

Simple variables

Simple variables are intended to be created by the script that executes the template. Their name is followed with a dollar sign. The script can assign any data to this variables, and the template can place it somewhere, so this type is used to communicate between the logic and the presentation layer. Below, we can see a sample use:

<p>Hello my friend, do you need {$item}?</p>

If the script assigns the text sunglasses to $item, the parser returns the following result:

<p>Hello my friend, do you need sunglasses?</p>

In the next part of the documentation, the term variables means usually simple variables.

By default, the variables are assigned to the specified template only. So, if the script assigns some value to $foo in template1.tpl, this value will not be seen in template2.tpl. In this case we say that the variables are local. The configuration allows to make the variables global by default.

Template variables

The template variables are created and managed by the template itself. The difference is that the name begins with @, not $. It was introduced to avoid naming collisions, and technically, they are handled differently in the OPT core. Note that template variables are always global: if template1.tpl sets the variable @foo, it is also visible in every template that is parsed later.

Language variables

This type of variable provides a support to the translation interface in OPT. It looks like this: $group@identifier and it returns a text that is assigned to the specified identifier in the specified group. The translation interface should assign different text to the same identifier, depending on the language set by the script.

<p>{$form@field_name}: <input type="text" name="name" /></p>

Of course, nothing wrong will happen if you find some other use for this syntax. Note that you may also create a translation interface port with backtick strings.

Containers

You are not limited to store only one value in a variable. If a variable can handle more values, we call it simply container. Each container element of the container has its own unique index, which may be either alphanumeric or numeric: foo, 7 etc. The index allows to access the value from a container: $container.element. Containers are very convenient, both for the programmer and for you. Let's assume we want to display some information about a person. Using normal variables, the code looks like this:

<p>Name: {$person_name}</p>
<p>Surname: {$person_surname}</p>
<p>Age: {$person_age}</p>

However, each of these variables must be assigned manually, whereas such data are usually retrieved in the script as arrays or something like that. It's much easier to assign entire group of values, but this leads us to the following template:

<p>Name: {$person.name}</p>
<p>Surname: {$person.surname}</p>
<p>Age: {$person.age}</p>

The containers are also useful for template designers, because some functions can operate directly on whole container. For example, we can display the number of items in the container:

<p>{count($person)} parameters describe this guy:</p>
<p>Name: {$person.name}</p>
<p>Surname: {$person.surname}</p>
<p>Age: {$person.age}</p>

The next example illustrates, how to create some kind of financial report:

{@formattedProfits are money($profits)}
 
<p>Corporation customer profits: {@formattedProfits.corporation}</p>
<p>Individual customer profits: {@formattedProfits.individual}</p>
<p>Training profits: {@formattedProfits.training}</p>
<p>Total profit: {money(sum($profits))}</p>
{@formattedProfits are money($profits)}
Here we say that we want to keep all the values in $profits in the money notation, for example $35.45. The new container must be saved to the new variable: $formattedProfits in order not to overwrite the original container.
{money(sum($profits))}
We request to sum all the values in $profits (that is why we have not overwritten that container!) and display the result in the money notation.

As you can see, many functions, like money() can operate both on single values and the entire containers.

Unlike many other template engines, containers do not represent any particular PHP type. Although they are arrays by default, they may be fully functionable objects as well. The script gives the compiler hints on the container types and OPT generates a suitable PHP template that fulfills the demands.

Arrays

Arrays are one of the PHP compound types that has been ported to OPT. Arrays are much like containers - they are also variables that store more than one value. The syntax is a bit different and uses square brackets to enclose the index name: $array[index]. Moreover, the index name can be loaded from other variable: $array[$index]. Note that arrays also cooperate with many functions:

{@formattedProfits are money($profits)}
 
<p>Corporation customer profits: {@formattedProfits[corporation]}</p>
<p>Individual customer profits: {@formattedProfits[individual]}</p>
<p>Training profits: {@formattedProfits[training]}</p>
<p>Total profit: {money(sum($profits))}</p>

We do not recommend using the array syntax unless it is really necessary. Containers allow to create more generic templates that can be reused with other PHP applications, even if they use different internal types to pass the data to the parser.

Special variables

OPT reserves three variable names for its own purposes:

$sys or $opt
This is a special container that provides various information on the parser and instructions.
$global
The container with global variables that are visible in all the parsed templates.
$this
The container with local variables assigned to the particular template.

The basic information of the $sys special variable is:

$sys.version
OPT version
$sys.const.name
The value of specified PHP constant.

Many instructions share their own information using $sys. For example, the section status information are available under $sys.section.foo.

For programmers

Contrary to PHP, OPT does not report nonexistent variables as mistakes. However, if you wish to be informed of them, you can simply change the error reporting level in the configuration (errorReporting directive) to E_ALL | E_NOTICE.

3.5. Expressions
3.5.2. Values
3.5.1. Variables
« Previous
3.5.3. Operators
Next »

3.5.2. Values

In the expressions we are allowed to use constant values, too. Open Power Template supports the following types:

Let's take a deeper look at the strings. They are enclosed in single quotes only, because double quotes are a part of XML syntax. In order to put a quote into a string, we follow it with a backslash: \. To display a backslash, we put two backslashes into the string.

'this is a text'
'this is a text: \' - with a quote'
'this is a text: \\ - with a backslash'

In some cases, it is allowed to write a string without quotes. However, it must fulfill two conditions. Firstly, it has to be a single word, and more precisely - an identifier. It must begin with an underscore or a letter, later we can also use numbers. Secondly, it must appear at the position, where strings are allowed in the expression:

word
word1 neq word2
eq eq eq

In all of these examples the condition 1 is fulfilled. Let's check the second one. In the first example, we have a single word at the beginning of the expression. No string operators are allowed here, and moreover, there is no operator called word, so it must be a string. In the second case, word1 and word2 are also treated as strings, because they are connected with the operator neq, and it must not have another operator as a neighbor. The most interesting is the last example. eq is an operator, but at the position 1 and 3, operators are not allowed. Here, this word will be a string. The second position is different. The two values must be connected with an operator, so the second eq will be processed in this way. If we replaced it with word, the template would not compile, because there would be no such operator.

The only keyword that breaks the rule described above is is. Currently, it cannot be used in the string context due to the parsing issues.

In OPT, you must not put the data directly in the strings, like in PHP: "foo $variable bar". Instead, you have to use the string concatenation operator ~

'foo '~$variable~' bar'

OPT supports also three special values (written in lower case):

It should be noted that they are treated as numbers by the parser.

3.5. Expressions
3.5.3. Operators
3.5.2. Values
« Previous
3.5.4. Functions
Next »

3.5.3. Operators

Smaller expressions can create bigger structures thanks to operators. An operator takes the values of the expressions on both left and right side and produces some other result. An example is $a + $b that returns a sum of $a and $b. If we use it in templates, we would print a sum of these variables:

<p>{$a + $b}</p>

There is also a small set of single argument operators that operate on only one value.

In OPT, some operators have two forms: symbolic and text. They work in the same way. In case of text operators, the compiler can recognize them from the context. If we write eq in a place, where operators are not allowed, it will be treated as a string.

Operator list:

Symbolic Text Example Description
== eq $a == $b true, if $a is equal $b
!= neq $a != $b true, if $a is not equal $b
=== eqt $a === $b true, if $a is equal $b and both of them are of the same type
!== neqt $a !== $b true, if $a is not equal $b or both of them are of different types
gt $a > $b true, if $a is greater than $b
lt $a < $b true, if $a is lower than $b
gte, ge $a >= $b true, if $a is greater or equal $b
lte, le $a <= $b true, if $a is lower or equal $b
and $a and $b true, if $a and $b are true
or $a or $b true, if $a or $b or both of them are true
xor $a xor $b true, if $a or $b is true, but not both of them at the same time
! not ! $a true, if $a is false
+ add $a + $b sum of $a and $b
- sub $a - $b difference of $a and $b
* mul $a * $b product of $a and $b
/ div $a / $b quotient of $a and $b
% mod $a % $b remainder of $a divided by $b
++ $a++, ++$a returns $a, and then increases by 1, or firstly increases by 1, and then returns.
-- $a--, --$a returns $a, and then decreases by 1, or firstly decreases by 1, and then returns.
~ $a ~ $b concatenates two values as strings
= is, are @a is $b assigns the value of the right-side expression to the variable on the left

A very important thing is the operator precedence. It defines, which operators are processed in the first place, if they are used one by another, like: $a + $b * $c. Multiplication is more important, so the parser will start from $b * $c and then the value of $a will be added to the result. The operator precedence is the same, as in PHP and it is illustrated below:

  1. ++, --
  2. !
  3. mul, div, mod
  4. add, sub, ~
  5. lt, lte, gt, gte
  6. eq, neq, eqt, neqt
  7. and, or, xor

To change the precendence manually, we use brackets: ($a + $b) * $c.

3.5. Expressions
3.5.4. Functions
3.5.3. Operators
« Previous
3.5.5. Backticks
Next »

3.5.4. Functions

Another important syntax element are functions. A function produces some result and to achieve this, it may take one or more arguments. A simple example is upper(). If we pass a text to it, the function will return the same text, but with all the lower case letters changed into upper case. The result can be used then in other places, for example as an argument of other function. Note that operators are in fact kinds of functions, because they also take arguments and produce some result.

The function call syntax is very similar, like in many other programming languages and in mathematics. First, we write the function name, and in the brackets we specify the arguments separated with a colon. Some correct examples can be found below:

a_function()
 
a_function($argument)
 
a_function(5)
 
a_function($argument1, $argument2)
 
a_function($a + $b, $c + $d)
 
a_function($argument1, other_function($argument1))
 
$a is a_function($argument)
  1. A function that takes no arguments.
  2. A function with one argument, whose value is taken from $argument variable.
  3. A function with one argument, whose value is constant.
  4. A function with two arguments.
  5. A function with two arguments that are smaller expressions.
  6. A function with two arguments, where the second one is a result of another function.
  7. A function result that is saved in a variable.

OPT provides a rich set of default functions, and the new ones can be registered by the programmer. They are described later.

3.5. Expressions
3.5.5. Backticks
3.5.4. Functions
« Previous
3.5.6. Objects
Next »

3.5.5. Backticks

OPT supports a special type of strings written in the backticks: `. Their behavior can be programmed by the programmer. In the example above, the backticks are used as a template access to the Access Control List system:

<opt:if test="`/user/control/security`">
    <p>User IP: {$user.ip}</p>
</opt:if>

Currently, OPT does not support a concatenation of the backtick strings, but this feature is planned to appear in the future releases.

For programmers

Backtick string handler is an ordinary function or object method that takes one argument - the backtick string content. It may be registered in the template using a configuration directive backticks:

function myBacktickHandler($string)
{
    return strtoupper($string);
} // end myBacktickHandler();
 
$tpl->backticks = 'myBacktickHandler';

Note that using the backticks without a handler registered causes an exception.

See also:

3.5. Expressions
3.5.6. Objects
3.5.5. Backticks
« Previous
3.5.7. HTML escaping
Next »

3.5.6. Objects

This page describes a feature strictly related to PHP language. We do not recommend to use it in your projects unless absolutely necessary, as it reduces the portability and links the templates with a concrete script implementation.

Open Power Template expression syntax can optionally support PHP objects directly from the template side.

Supported OOP features

Enabling or disabling object support

This feature is strictly related to the PHP language which may lead to the problems with portability, refactoring and security. This is why it may be disabled in some scripts or even not supported. In order to enable the OOP on the template side, set the following configuration options to true:

$tpl = new Opt_Class;
// ...
$tpl->basicOOP = true;
$tpl->advancedOOP = true;
 
// ...
$tpl->setup();

Moreover, if you are going to create new objects or accessing the static class members, you have to register the appropriate classes in the template engine:

$tpl->register(Opt_Class::PHP_CLASS, 'templateClassName', 'realPHPClassName');

OPT does not support PHP namespaces on the template side. However, you can specify the class namespace in the register() method.

Accessing object members

OPT provides only one object access operator: ::. Depending on the context, it may refer either to the static or normal members:

$object::field - accessing object field
$object::method() - accessing object method
className::field - accessing static class field
className::method() - accessing static class method

The complex calls are also possible: className::method()::field::submethod().

Creating new objects

If the advancedOOP option is enabled, OPT allows you to create new objects of registered classes:

$object is new className
$object is new className('constructor arguments')

With this option, you may also clone existing objects:

$objectA is clone $objectB

Why should you not use objects in templates?

You could have used to use objects with pure PHP or other template engine, but there you had no other choice! The reasons why you should not use them in templates are:

The OPT techniques to replace objects

3.5. Expressions
3.5.7. HTML escaping
3.5.6. Objects
« Previous
3.6. Function reference
Next »

3.5.7. HTML escaping

The variable values placed in the HTML code may break our output structure or add strange tags to the result. Here is an example:

<p parse:style="$foo">Text</p>

If for some reason the value of $foo was <div>bar</div>, the result would be:

<p style="<div>bar</div>">Text</p>

However, in OPT it is not. The parser provides advanced escaping control that changes the dangerous characters into HTML entities so that they would not break the output code.

Attribute-level control

OPT assumes that on the attribute level, all the expressions placed as attribute values, must be escaped. Going back to our example, the result will be:

<p style="&lt;div&gt;bar&lt;/div&gt;">Text</p>

Text-level control

On the level of expressions in curly brackets, the escaping is controlled in three ways:

  1. In the OPT configuration, using the escape directive.
  2. In the current template - in opt:root or opt:extend instructions the attribute escape with the values yes or no.
  3. In the current expression with the modifiers e: and u:

This is an example:

<opt:root escape="no">
    <p>This expression will not be escaped: {$variable}</p>
    <p>This expression will be escaped: {e:$variable}</p>
</opt:root>

The modifier e: at the beginning of the expression turns on the escaping, if it is disabled, and u: disables it.

OPT is smart enough not to escape the same expression twice, if we turned it on both in the configuration and in the expression itself.

3. Template syntax
3.6. Function reference
3.5.7. HTML escaping
« Previous
3.6.1. absolute()
Next »

3.6. Function reference

In this chapter, the available functions are described.

In OPT, all the functions that operate on the specified value, take it always as the first parameter, even if their equivalent in PHP requires it as the last one!

Function data

Unlike PHP, many functions may operate both on a single value and a container of values. In the second case, the function is applied to all of the container elements:

 
<p>A formatted, single value: {money($profits.corporation)}</p>
 
<p>The function used on the container:</p>
 
{@formattedProfits are money($profits)}
 
<p>Corporation customer profits: {@formattedProfits.corporation}</p>
<p>Individual customer profits: {@formattedProfits.individual}</p>
<p>Training profits: {@formattedProfits.training}</p>

The function result is returned and must be saved to another variable in order to be used or passed to another function.

OPT and Unicode

The available functions are not designed for any particular character encoding due to the planned support for Unicode in PHP6. If you want to use UTF-8 with PHP 5.x, you have to be patient or write your own versions of the functions. We will try to release an optional set of UTF-ready functions as a plugin before releasing the first stable OPT version.

See also:

3.6. Function reference
3.6.1. absolute()
3.6. Function reference
« Previous
3.6.2. autoLink()
Next »

3.6.1. absolute()

Referencenumber absolute(number $number)

Returns the absolute value of the specified $number.

This function can operate also on a container of numbers, producing a new container with their absolute values.

3.6.2. autoLink()

Referencestring autoLink(string $text, [, string $class = null [, string $target = '_blank']])
Versionssince 2.0.5

Turns all URL-s into clickable links in the specified $text. The optional arguments are:

$class - the link CSS class.

$target - the link target (default to _blank).

Note that if HTML escaping is enabled, you have to turn it off for the function result with the u: modifier in order to display it properly:

<p>{u:autoLink($text)}</p>

The function performs an automatic HTML escaping before parsing the $text argument against URL-s. It is not possible to parse a text that already contains HTML.

3.6. Function reference
3.6.3. average()
3.6.2. autoLink()
« Previous
3.6.4. capitalize()
Next »

3.6.3. average()

Referencenumber average(container $container)

The function assumes that all the values in the container are numbers and returns their average value.

See also:

3.6. Function reference
3.6.4. capitalize()
3.6.3. average()
« Previous
3.6.5. contains()
Next »

3.6.4. capitalize()

Referencestring capitalize(string $text)

Capitalizes the first letter in the $text.

<!-- result: "Hello world" -->
{capitalize('hello world')}

This function can operate also on a container of strings, capitalizing them all and returning the modified container.

3.6. Function reference
3.6.5. contains()
3.6.4. capitalize()
« Previous
3.6.6. containsKey()
Next »

3.6.5. contains()

Referenceboolean contains(container $container, mixed $item)

Returns true, if the $container contains $item.

A note for PHP programmers: the function can be applied to objects, if they implement ArrayAccess and either Iterator or IteratorAggregate interface.

3.6. Function reference
3.6.6. containsKey()
3.6.5. contains()
« Previous
3.6.7. count()
Next »

3.6.6. containsKey()

Referenceboolean containsKey(container $container, mixed $key)
Versionssince 2.0-RC2

Returns true, if the container $container contains the specified key.

A note for PHP programmers: the function can be applied to objects, if they implement ArrayAccess interface.

3.6. Function reference
3.6.7. count()
3.6.6. containsKey()
« Previous
3.6.8. countChars()
Next »

3.6.7. count()

Referenceinteger count(container $container)

Returns the number of elements in the $container.

3.6. Function reference
3.6.8. countChars()
3.6.7. count()
« Previous
3.6.9. countSubstring()
Next »

3.6.8. countChars()

Referenceinteger countChars(string $text)

Returns the number of characters in the string $text.

3.6. Function reference
3.6.9. countSubstring()
3.6.8. countChars()
« Previous
3.6.10. countWords()
Next »

3.6.9. countSubstring()

Referenceint countSubstring(mixed $haystack, string $needle)
Versionssince 2.0.5

Counts the occurences of $needle in $haystack. The haystack can be either a string or a container where the function counts the occurences in all the container elements.

3.6. Function reference
3.6.10. countWords()
3.6.9. countSubstring()
« Previous
3.6.11. cycle()
Next »

3.6.10. countWords()

Referencemixed countWords(string $text [, $format [,$charlist ]])

Depending on the $format value (default is 0), the function:

$charlist may contain the list of characters that are considered as a word.

The simplest use:

<h1>{$article.title}</h1>
 
{$article.body}
 
<p>Words: {countWords($article.body)}</p>
3.6. Function reference
3.6.11. cycle()
3.6.10. countWords()
« Previous
3.6.12. date()
Next »

3.6.11. cycle()

Referencemixed cycle(array $array | ... )
Versionssince 2.0.1

Returns the next value in the specified cycle. If the internal counter reaches the end of items, it returns to the beginning. The list of values to display in cycle can be provided as a list of arguments or as an array.

<opt:section name="items">
<tr parse:class="cycle('brighter', 'darker')">
    <td>{$item.name}</td>
    <td>{$item.value}</td>
</tr>
</opt:section>
3.6. Function reference
3.6.12. date()
3.6.11. cycle()
« Previous
3.6.13. entity()
Next »

3.6.12. date()

Referencestring date(string $format [, integer $timestamp ])

Returns the current date in the specified $format. If $timestamp is specified, returns the date pointed by that timestamp. The detailed format description can be found in PHP manual.

See also:

3.6. Function reference
3.6.13. entity()
3.6.12. date()
« Previous
3.6.14. firstof()
Next »

3.6.13. entity()

Referencestring entity(string)

Allows to display an entity in the output document.

Because OPT contains an XML parser, all the entities are parsed on the server-side. Usually, this is not a bad thing, because the entities have been introduced mostly for the user convenience and the web browser is able to handle both the entities and the original characters. However, in certain situations it may be necessary to send the entity in the browser. We cannot prevent the parser from processing it, so we may use this function to encode it:

<p>An example of &amp;Acute; entity: {u:entity('Acute')}</p>

The result would be:

<p>An example of &amp;Acute; entity: &Acute;</p>

In order to work, this function must not be escaped. The best way to achieve this is to prepend the expression with u: modifier.

The function recognizes any valid XML entity name (excluding Unicode symbols), for example Acute, #184 and #xB8. If the function is not able to parse the entity name, it generates an exception which is captured by the script.

See also:

3.6. Function reference
3.6.14. firstof()
3.6.13. entity()
« Previous
3.6.15. indent()
Next »

3.6.14. firstof()

Referencemixed firstof(...)

Returns the first argument that is not empty:

{firstof($foo, $bar, $joe)}

This may be used to set default values for the variables, if they are not defined by the script:

<p parse:class="firstof($class, 'defaultClass')">...</p>
3.6. Function reference
3.6.15. indent()
3.6.14. firstof()
« Previous
3.6.16. isImage()
Next »

3.6.15. indent()

Referencestring indent(string $text, integer $num [, string $with ])

Indents every new line in $text with $num characters $with (if this argument is not set, spaces are used by default).

<pre>
{strip('
This is a text
that needs proper
indentation', 4)}
</pre>

The result:

<pre>
    This is a text
    that needs proper
    indentation
</pre>

This function can operate also on a container of strings, making indents to them all and returning the modified container.

3.6. Function reference
3.6.16. isImage()
3.6.15. indent()
« Previous
3.6.17. isUrl()
Next »

3.6.16. isImage()

Referenceboolean isImage(string $text)

Returns true, if the $text is a valid URL/path to the image file. The function checks it by the file extension. Currently recognized ones are:

  1. JPG
  2. PNG
  3. BMP
  4. GIF
  5. SVG

Sample use:

<!-- displays the IMG tag, if the `$text` contains a path to the image -->
<img parse:src="$text" opt:if="isImage($text)" />
3.6. Function reference
3.6.17. isUrl()
3.6.16. isImage()
« Previous
3.6.18. lower()
Next »

3.6.17. isUrl()

Referenceboolean isUrl(string $text)

Returns true, if the $text is a valid URL. The code below shows, how to use this function to make the user name an URL to his website in case he defined its address in the profile.

<a parse:href="$user.www" opt:on="isUrl($user.www)">{$user.nickname}</p>

The function does not accept malformed URL-s or URL parts, for example with removed protocol that are usually accepted by the current browsers. In other words, the string www.example.com is not accepted as a valid URL, but http://www.example.com - is.

3.6. Function reference
3.6.18. lower()
3.6.17. isUrl()
« Previous
3.6.19. money()
Next »

3.6.18. lower()

Referencestring lower(string $text)

Makes all characters lowercase in $text:

<!-- result: "foo" -->
{lower('Foo')}

This function can operate also on a container of strings, operating on them all and returning the modified container.

3.6. Function reference
3.6.19. money()
3.6.18. lower()
« Previous
3.6.20. nl2br()
Next »

3.6.19. money()

Referencestring money(float $amount, string $format)

Formats the specified number $amount in order to be a valid money amount string. The $format defines the format and the detailed description of available codes can be found in the PHP manual.

This function can operate also on a container of strings, operating on them all and returning the modified container.

The behavior of this function depends on the current script locale.

This function is not available the systems without strfmon capabilities. This definition includes Microsoft Windows operating system family.

3.6. Function reference
3.6.20. nl2br()
3.6.19. money()
« Previous
3.6.21. number()
Next »

3.6.20. nl2br()

Referencestring nl2br(string $text)

Replaces the newline characters in $text into <br /> tags.

This function can operate also on a container of strings, capitalizing them all and returning the modified container.

3.6. Function reference
3.6.21. number()
3.6.20. nl2br()
« Previous
3.6.22. pad()
Next »

3.6.21. number()

Referencestring number(number $num [, integer $decimals [, string $decimalSeparator [, string $thousandSeparator]]])

Formats the number $num in order to look nice in the text. The optional attributes specify the used format:

  1. $decimals - the number of decimals to display
  2. $decimalSeparator - the separator of the integer and decimal part.
  3. $thousandSeparator - the thousand separator

Sample use:

<!-- print: "1,234.56" -->
{number(1234.567, 2, '.', ',')}

If the formatting arguments are not defined, the function reads the format from OPT configuration.

This function can operate also on a container of strings, operating on them all and returning the modified container.

3.6. Function reference
3.6.22. pad()
3.6.21. number()
« Previous
3.6.23. pluralize()
Next »

3.6.22. pad()

Referencemixed pad(mixed $value, int $length [, string $padString, [, string $paddingType]])
Versionssince 2.0.5

Pads a string $value to a certain length with another string. If the input string length is greater than or equal to $length, no padding takes place. Otherwise, the string is padded with $padSymbol symbols which are spaces by default. $paddingType specifies the padding type:

right - pads on the right (default)

left - pads on the left.

both - pads on the both sides.

$padString can be truncated if the number of padded characters is not evenly divided by the $padSymbol length.

This function can operate also on a container of strings, padding all of them and returning the modified container.

3.6. Function reference
3.6.23. pluralize()
3.6.22. pad()
« Previous
3.6.24. position()
Next »

3.6.23. pluralize()

Referencestring pluralize(int $number, string $singularForm [, string $pluralForm1, string $pluralForm2 ... ])
Versionssince 2.0.5

Attempts to pluralize the singular form $singularForm for the given number $number. If the plural forms are not specified, the function asks the installed translation interface for the plural form. Otherwise it matches one of the specified plural forms according to the rules specified in the $pluralForms OPT configuration option. By default, the rules for English language are installed.

Sample use:

{@userCount is 0}
<!-- display: "We have 0 users on our website." -->
<p>We have {$userCount} {pluralize($userCount, 'user', 'users')} on our website.</p>
 
{@userCount is 1}
<!-- display: "We have 1 user on our website." -->
<p>We have {$userCount} {pluralize($userCount, 'user', 'users')} on our website.</p>
 
{@userCount is 6}
<!-- display: "We have 6 users on our website." -->
<p>We have {$userCount} {pluralize($userCount, 'user', 'users')} on our website.</p>

Plural forms for other languages

Different languages may handle plural forms differently, i.e. by using different cases. The default behaviour can be easily reprogrammed on the script side through the $pluralForms configuration option. The option contains an associative list of PHP expressions matching different cases and the grammar forms to be used. The matching number must be encoded as %d. The final element is always taken as the final alternative and is assumed to be true.

Below, we can see an example for Polish language, where for numbers 0 and greater than 4 we have to change the case to genitive:

$tpl->pluralForms = array(
    '%d == 0 || %d > 4' => 2,   // The genitive form will be the third on the argument list
    '%d == 1' => 0, // Singular form, the first 
    '%d' => 1   // In all other cases, use second form
);

The same sample template, but for Polish laguage:

{@userCount is 0}
<!-- display: "0 użytkowników." -->
<p>{$userCount} {pluralize($userCount, 'użytkownik', 'użytkownicy', 'użytkowników')}.</p>
 
{@userCount is 1}
<!-- display: "1 użytkownik." -->
<p>{$userCount} {pluralize($userCount, 'użytkownik', 'użytkownicy', 'użytkowników')}.</p>
 
{@userCount is 3}
<!-- display: "3 użytkownicy." -->
<p>{$userCount} {pluralize($userCount, 'użytkownik', 'użytkownicy', 'użytkowników')}.</p>
 
{@userCount is 6}
<!-- display: "6 użytkowników." -->
<p>{$userCount} {pluralize($userCount, 'użytkownik', 'użytkownicy', 'użytkowników')}.</p>

If the option refers to an undefined form, an exception is thrown.

On PHP 5.3 the $pluralForms may be a closure which returns the number of argument to be used for the given number.

Translation interface

On multilingual websites, we do not know the number of available grammar forms for each language that might be implemented. Fortunately, if the plural forms are not specified, pluralize() asks the translation interface for the plural form and leaves to the programmer implementing the proper inflection algorithm depending on the currently selected language.

The function expects the translation interface to implement the pluralize() method that takes two arguments: the number and the singular form. The $pluralForms configuration option is ignored then. Note that the method is not required by Opl_Translation_Interface to be implemented. In this case, the function throws an exception.

See also:

3.6. Function reference
3.6.24. position()
3.6.23. pluralize()
« Previous
3.6.25. range()
Next »

3.6.24. position()

Referenceint position(string $haystack, string $needle [, int $offset]])
Versionssince 2.0.5

Finds position of the first occurence of $needle in $haystack string. If the optional $offset is provided, the function searches from the specified offset. If the $needle is not found, the function returns NULL.

Sample use:

<a parse:href="$url">Click here</a>
<opt:if test="position($url, 'debug') !== null">
    <p>Warning: the link above switches on the debug mode.</p>
</opt:if>
3.6. Function reference
3.6.25. range()
3.6.24. position()
« Previous
3.6.26. regexReplace()
Next »

3.6.25. range()

Referencestring range(integer $start [, integer $end])

Returns a string $start - $end or if both of the arguments are the same number - returns only this number. If $end is not specified, the function takes the current year as its value. Sample uses:

<!-- returns "50 - 60" -->
{range(50, 60)}
 
<!-- returns "50" -->
{range(50,50)}
 
<!-- displays "Copyright 2007-2009 Foo" -->
<p>Copyright {range(2007)} Foo</p>
3.6. Function reference
3.6.26. regexReplace()
3.6.25. range()
« Previous
3.6.27. replace()
Next »

3.6.26. regexReplace()

Referencestring regexReplace(string $text, string $replacedPattern, string $replacement)

Replaces all the occurrences of $replacedPattern in $text with $replacement, using Perl-compatible regular expressions.

See also:

3.6. Function reference
3.6.27. replace()
3.6.26. regexReplace()
« Previous
3.6.28. scalar()
Next »

3.6.27. replace()

Referencestring replace(string $text, string $replacedText, string $replacement)

Replaces all the occurrences of $replacedText in $text with $replacement:

{@text is 'Hello, world!'}
<!-- Display: "Hello, universe!" -->
<p>{replace(@text, 'world', 'universe')}
3.6. Function reference
3.6.28. scalar()
3.6.27. replace()
« Previous
3.6.29. spacify()
Next »

3.6.28. scalar()

Referenceboolean scalar(mixed $value)
Versionssince 2.0-RC2

Returns true, if the specified value is a scalar, that is:

False is returned for containers and PHP resources.

3.6. Function reference
3.6.29. spacify()
3.6.28. scalar()
« Previous
3.6.30. stddev()
Next »

3.6.29. spacify()

Referencestring spacify(string $text [, string $delimiter])

Puts $delimiter every two characters in the $text. By default, $delimiter is set to one space:

{spacify('This is a text')}
{spacify('This is a text', '-')}

The result:

T h i s   i s   a   t e x t
T-h-i-s- -i-s- -a- -t-e-x-t

This function can operate also on a container of strings, spacifying them all and returning the modified container.

3.6. Function reference
3.6.30. stddev()
3.6.29. spacify()
« Previous
3.6.31. strip()
Next »

3.6.30. stddev()

Referencenumber stddev(container $container)

The function assumes that all the values in the container are numbers and returns their standard deviation.

See also:

3.6. Function reference
3.6.31. strip()
3.6.30. stddev()
« Previous
3.6.32. stripTags()
Next »

3.6.31. strip()

Referencestring strip(string $text)

Reduces the groups of white characters in $text into a single space:

{strip('This     is  a     text')}

The result:

This is a text

This function can operate also on a container of strings, stripping them all and returning the modified container.

3.6. Function reference
3.6.32. stripTags()
3.6.31. strip()
« Previous
3.6.33. sum()
Next »

3.6.32. stripTags()

Referencestring stripTags(string $text [, string $allowedTags ])

Strips the XML/HTML tags from $text. In the optional argument, $allowedTags, we may specify the allowed tags that must not be stripped:

<p>User interests: {stripTags($user.interests)}</p>
<p>User signature: {stripTags($user.signature, '&lt;a&gt;&lt;br&gt;')}</p>

Stripping HTML should be a part of the presentation layer only in certain cases. If you are going to display the same value in many places, consider moving this task to your script in order not to make a mistake.

This function can operate also on a container of strings, spacifying them all and returning the modified container.

Remember that the allowed tag names must be written using entities, if they are specified explicitly in the template.

3.6. Function reference
3.6.33. sum()
3.6.32. stripTags()
« Previous
3.6.34. truncate()
Next »

3.6.33. sum()

Referencenumber sum(container $container)

The function assumes that all the values in the container are numbers and returns their total sum.

See also:

3.6. Function reference
3.6.34. truncate()
3.6.33. sum()
« Previous
3.6.35. upper()
Next »

3.6.34. truncate()

Referencestring truncate(string $text, integer $length [, string $ending [, boolean $breakWords ]])

Truncates the text $text to the specified $length. Optionally, you might define the $ending that is added to the $text, if it has been truncated. The last argument allows to control if the function might break words (true, default value) or not (false). Below you can see a sample results for the text This is a very long text that needs to be truncated:

{@text is 'This is a very long text that needs to be truncated'}
 
<!-- result: "This is a very lo" -->
{truncate(@text, 17)}
 
<!-- result: "This is a very lo..." -->
{truncate(@text, 17, '...')}
 
<!-- result: "This is a very long text that needs to be truncated" -->
{truncate(@text, 500, '...')}
 
<!-- result: "This is a very..." -->
{truncate(@text, 17, '...', false)}

This function can operate also on a container of strings, truncating them all and returning the modified container.

3.6. Function reference
3.6.35. upper()
3.6.34. truncate()
« Previous
3.6.36. wordWrap()
Next »

3.6.35. upper()

Referencestring upper(string $text)

Makes all characters uppercase in $text:

<!-- result: "FOO" -->
{upper('Foo')}

This function can operate also on a container of strings, operating on them all and returning the modified container.

3.6. Function reference
3.6.36. wordWrap()
3.6.35. upper()
« Previous
3.7. Instructions
Next »

3.6.36. wordWrap()

Referencestring wordWrap(string $text, integer $length [, string $break [, boolean $cut ]])

This function works similarly to truncate(), but instead of truncating the text, it tries to break it with $break (<br /> tag by default). If $cut is set to true, the string is always wrapped to the specified width, so if it contains a too long word, it is broken apart.

In the $break argument, you might use the formatting codes: \r, \n, \t and \\ indicating the following characters: carriage return, new line, tabulation, backslash.

Remember that the tag name in $break must be written using entities, if it is specified explicitly in the template.

3. Template syntax
3.7. Instructions
3.6.36. wordWrap()
« Previous
3.7.1. opt:attribute
Next »

3.7. Instructions

An instruction is any XML tag in one of the registered namespaces that:

Instructions are one of the main OPT syntax elements. They allow to create lists or display something conditionally. Let's take a closer look at them, using one of the instructions, opt:section. It is used to make lists. Below we can find a template that contains it:

<div id="menu">
    <h3>Menu</h3>
    <ol>
        <opt:section name="menu">
        <li><a parse:href="$menu.address">{$menu.title}</a></li>
        </opt:section>  
    </ol>   
</div>

The code between <opt:section> and </opt:section> is treated as a look of the single list element. The section tells the parser, how to create a list from it, but it is not visible in the output code itself. There is only a result of its work, for example:

<div id="menu">
    <h3>Menu</h3>
    <ol>
        <li><a href="page1.php">Page 1</a></li>
        <li><a href="page2.php">Page 2</a></li>
        <li><a href="page3.php">Page 3</a></li>
        <li><a href="page4.php">Page 4</a></li>
    </ol>
</div>

Some of the instructions have very sophisticated form and consist of many tags. The other ones can be used in certain places only.

Usually, the instructions are written in the declarative style. It means that you only tell, what you want to see, not how to implement it or how it should work. The goal is to eliminate the imperative programming with lots of loops and conditions from the templates, because it only makes the code ugly and hard to modify. The programming constructs are still available, but we recommend not to use it, and let the compiler work for you.

The instruction tags may take arguments as XML attributes. Each of them has strictly defined type that you have to follow. The following types are possible:

The first three types are expressions - the value you type is sent to the expression parser, so we can load their values from variables or functions. If the default type does not suit us, we can change it using another namespace:

<opt:instruction foo="here is a text by default"/>

This sample instruction takes a string as the foo attribute. It is not a hard string, so we can load it from the variable. However, to do this, we must change the namespace to parse in order to mark that we want a full expression here:

<opt:instruction parse:foo="$load_me_from_variable"/>

This namespace does not allow the assignment operator.

A similar relationship works also in the opposite direction. There are many instructions that take a full expression as an argument, but if we want to specify a string there, by default we have to deal with lots of quotes.

<opt:instruction2 foo="'I must have been enclosed in both double and single quotes. One of them come from XML, the second one - from OPT expressions'"/>

We can avoid it by using str namespace:

<opt:instruction2 str:foo="Now the parser knows that we have a string here, so we don't need single quotes."/>

The namespace does not have to be changed, if the string is a single word. As we remember from the chapter on expressions, OPT can recognize the string from the context. We can't change the namespace, if we want to concatenate a string with some variable. Otherwise, the variable call would be treated as a part of the text.

<opt:instruction2 bar="'add something here: '~$variable"/>

The last four types cannot be changed. OPT needs them to compile the template properly and this is why their values cannot be loaded from variables or generated by expressions.

Notation

The instruction arguments are provided in this manual in the form of a table. If we find an asterisk in the "Name" field, it means that the instruction accepts any number of attributes of the specified type.

3.7. Instructions
3.7.1. opt:attribute
3.7. Instructions
« Previous
3.7.2. opt:block
Next »

3.7.1. opt:attribute

opt:attribute defines a new, dynamic attribute in the parent tag. It does not have a content, but takes two attributes that identify the name and the value of the new attribute:

Name Type Required? Description
name Expression Yes New attribute name
value Expression Yes New attribute value
ns Expression No Attribute namespace

Below, you can find an example that allows to define a new attribute for <div> whose name is read from the variable:

<div>
    <opt:attribute name="$attributeName" value="$attributeValue"/>
    Content
</div>

The instruction cooperates with OPT instruction attributes. If we wish to read the attribute list from a section, we can use the following solution:

<div>
    <opt:attribute name="$attributes.name" value="$attributes.value" opt:section="attributes"/>
    Content
</div>

opt:attribute cannot add attributes to other OPT instructions, except for opt:tag.

If the attribute already exists in the tag or has been defined with the previous instance of opt:attribute, the instruction throws an exception.

Conditional attributes

opt:attribute can also cooperate with opt:if attribute to add an attribute to the parent tag conditionally.

<div>
    <opt:attribute name="class" value="highlight" opt:if="$highlightDiv"/>
    Content
</div>

Handling namespaces

The optional attribute ns can be used to set the attribute namespace. The value may be loaded from a variable or specified manually, for example:

<foo>
    <opt:attribute str:ns="xml" name="$attrName" value="$attrValue" />
</foo>
 
<foo>
    <opt:attribute ns="$ns" str:name="foo" value="$attrValue" />
</foo>

The ns attribute accepts empty values. The namespace part is not generated then.

Conditional attribute values

Since OPT 2.0.2 it is possible to create elegant conditional value selector:

<opt:attribute str:name="foo">
    <opt:value test="$condition1">Value 1</opt:value>
    <opt:value test="$condition2">Value 2</opt:value>
    <opt:value test="$condition3">Value 3</opt:value>
    <opt:value>Default value</opt:value>
</opt:attribute>

OPT will check, which condition is passed and select the appropriate value for the attribute. If we do not specify the test attribute in opt:value, it becomes the default attribute value. The default attribute value can be also defined with value attribute in the main tag:

<opt:attribute str:name="foo" str:value="Default value">
    <opt:value test="$condition1">Value 1</opt:value>
    <opt:value test="$condition2">Value 2</opt:value>
    <opt:value test="$condition3">Value 3</opt:value>
</opt:attribute>

There must not be either two opt:value tags without the test attribute or a opt:value without test attribute and `value attribute in the instruction at the same time.

As you can see, the value is defined as a tag value, not as an attribute. Contrary to other instruction, OPT forces here some limitations. opt:value must not contain any other tag, including the instruction. The following construct is forbidden:

<opt:value test="$condition">Text <foo> ... </foo> text</opt:value>

Note that you can still use ordinary OPT expressions:

<opt:value test="$condition">Text {$expression} text</opt:value>

The value list can be loaded from a snippet with opt:use attribute.

If there is no default value and the instruction cannot select any other value (none of the conditions is true), the entire attribute is not displayed.

See also:

3.7. Instructions
3.7.2. opt:block
3.7.1. opt:attribute
« Previous
3.7.3. opt:capture
Next »

3.7.2. opt:block

opt:block creates a port for custom block objects in the template. To get to know more about blocks, see this chapter.

Name Type Required? Description
from Expression Yes Where to load the block object from
* Expression No Any other tags are converted into block attributes

The block port is a runtime instruction and the script may freely assign different block objects to the port:

<opt:block from="$block">The content controlled by the block.</opt:block>

See also:

3.7. Instructions
3.7.3. opt:capture
3.7.2. opt:block
« Previous
3.7.4. opt:component
Next »

3.7.3. opt:capture

opt:capture captures the output of some template part and allows to paste it in other places.

Name Type Required? Description
as ID Yes The unique name of the captured output

The access to the captured parts is possible with $sys.capture.name:

<opt:capture as="options">
    <a href="page/edit">Edit</a> | <a href="page/remove">Remove</a>
</opt:capture>
 
<h1>Some data</h1>
{$data1.text}
 
{u:$sys.capture.options}
 
<h1>Some other data</h1>
{$data2.text}
 
{u:$sys.capture.options}

The code captured by one view is also visible in other views executed by OPT.

Contrary to opt:snippet and opt:insert, this instruction captures the output code that would normally be sent to the browser. If you change one of the variables used in the opt:capture block between two placements $opt.capture.myBlock, the change will not be visible:

{@foo is 1}
<opt:capture as="foo">{@foo}</opt:capture>
{$sys.capture.foo}
{@foo is 2}
{$sys.capture.foo}

Remember that $sys.capture.blockName may be escaped when placed in the HTML code. If you are going to capture a lot of HTML, it is safe to display them with the u: modifier {u:$sys.capture.alotofHTML} just to be sure that it will not be escaped.

The captured content can be also displayed with opt:insert. It allows to select the content dynamically:

<opt:capture as="foo">
Some content here...
</opt:capture>
 
{@captured is 'foo'}
<opt:insert captured="@captured">
The default content.
</opt:insert>
3.7. Instructions
3.7.4. opt:component
3.7.3. opt:capture
« Previous
3.7.5. opt:dtd
Next »

3.7.4. opt:component

opt:component creates a port for custom component objects in the template. To get to know more about components, see this chapter.

Name Type Required? Description
from Expression Yes Where to load the component object from
datasource Expression No The datasource for the component
template Identifier No The snippet name with the component content
* Expression No Any other tags are converted into component attributes

The port is a runtime instruction and the components may be loaded from variables etc. In the example below, we can see, how to create a dynamic form generated by the script:

<form method="post" parse:action="$action">
<opt:section name="form">
    <opt:component from="$form.component" datasource="$formData" template="genericFormLayout" />
</opt:section>
</form>

Component port content

Parameters

The component parameter values can be read using $system.component.parameterName special variable call:

<opt:component from="$component">
    <p>Title: {$system.component.title}</p>
</opt:component>

The parameter values can be set either with the optional attributes of the opt:component tag or with the opt:set tag:

<opt:set str:name="parameterName" str:value="parameterValue" />

Both of the attributes require the OPT expressions. To provide a constant string value, we may use the str: namespace.

Displaying the component

The place where the component should be displayed is marked by the opt:display tag. The optional attribute values (OPT expressions) will be passed to the component display() method:

<opt:component from="$component">
    <p>Title: {$system.component.title}</p>
    <opt:display />
</opt:component>

Event handlers

The port may handle various events generated by the component with the opt:onEvent tag. It takes one required attribute: name with the event name:

<opt:component from="$component">
    <p>Title: {$system.component.title}</p>
    <opt:display />
    <opt:onEvent name="error">
        <p class="error">The field has been filled incorrectly: {$error}</p>
    </opt:onEvent>
</opt:component>

HTML tags with attributes managed by the components

The components may manage the attributes of certain HTML tags. This is primarily used to change the CSS class of the incorrectly filled component transparently. The tags are marked by the opt:component-attributes namespace:

<opt:component from="$component">
<div opt:component-attributes="default">
    <p>Title: {$system.component.title}</p>
    <opt:display />
    <opt:onEvent name="error">
        <p class="error">The field has been filled incorrectly: {$error}</p>
    </opt:onEvent>
</div>
</opt:component>

The opt:component-attributes attribute has been introduced in OPT 2.0.2. In the previous versions, the only way to manage the attributes was moving the tag to the special com namespace:

<opt:component from="$component">
<com:div>
    <p>Title: {$system.component.title}</p>
    <opt:display />
    <opt:onEvent name="error">
        <p class="error">The field has been filled incorrectly: {$error}</p>
    </opt:onEvent>
</com:div>
</opt:component>

The com namespace is still available for backward compatibility, but we do not recommend to use it in the new projects.

Integration with snippets

Usually, we would like to pack the port content into a snippet in order to use the same form layout across all the forms in our project. We may insert the snippet either using the ordinary opt:use attribute or with the template attribute that supports component-specific features.

Take a look at the following example: we have some statically deployed components. We want to use the shared layout, but we need to set the extra component parameters, so we use the template attribute. It does not remove the opt:set tags from the default content:

<opt:snippet name="formLayout">
<div opt:component-attributes="default">
    <p>{$system.component.title}</p>
    <p><opt:display /></p>
</div>
</opt:snippet>
 
<opt:someComponent template="formLayout">
    <opt:set str:name="title" str:value="Your age" />
</opt:someComponent>

The code above is equivalent to:

<opt:someComponent>
<opt:set str:name="title" str:value="Your age" />
<div opt:component-attributes="default">
    <p>{$system.component.title}</p>
    <p><opt:display /></p>
</div>
</opt:someComponent>

See also:

3.7. Instructions
3.7.5. opt:dtd
3.7.4. opt:component
« Previous
3.7.6. opt:dynamic
Next »

3.7.5. opt:dtd

Although OPT neither parses nor checks the DTD of the document, it is not said that such feature will not appear in the future. This is why we should use opt:dtd to generate the output DTD for our templates. It also supports some predefined DTD templates for the most popular XML applications:

Name Type Required? Description
template Identifier No The name of the DTD template

The accepted values of template are:

If the template attribute is not specified, opt:dtd expects its content to contain the custom DTD that will be sent to the browser. It should be enclosed in the CDATA section in order to fulfill the XML semantics (CDATA tags will be ignored, like in opt:literal).

<?xml version="1.0" ?>
<opt:prolog />
<opt:dtd template="xhtml10transitional" />
 
<html xmlns="http://www.w3.org/1999/xhtml" lang="en_US" xml:lang="en_US">
<head>
    <title>Page title</title>
</head>
<body>
    ....
</body>
</html>

Alternative use:

<?xml version="1.0" ?>
<opt:prolog />
<opt:dtd><![CDATA[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
]]></opt:dtd>
 
<html xmlns="http://www.w3.org/1999/xhtml" lang="en_US" xml:lang="en_US">
<head>
    <title>Page title</title>
</head>
<body>
    ....
</body>
</html>

There is no requirement to place opt:dtd at the beginning of the template. Even if it is deeply nested in other XML tags, the document type definition will always appear before the document content.

There cannot be two document type definitions in the template. The last opt:dtd call overwrites the previous DTD-s set by this and/or other instructions.

See also:

3.7. Instructions
3.7.6. opt:dynamic
3.7.5. opt:dtd
« Previous
3.7.7. opt:extend
Next »

3.7.6. opt:dynamic

The content of this tag must remain dynamic even if the template output is cached. The compiler captures the PHP code generated by the content and allows to include it in the cached file. Note that OPT does not provide any caching system and this feature must be implemented in the external cache engine in order to be supported.

If there is no caching system set in the view, the instruction has no effect on the template.

See also:

3.7. Instructions
3.7.7. opt:extend
3.7.6. opt:dynamic
« Previous
3.7.8. opt:for
Next »

3.7.7. opt:extend

opt:extend allows to use the template inheritance in your templates. It must be the root tag of the template, where we define the content. Any other location causes an exception. The instruction may take several attributes:

Name Type Required? Description
file Hard string Yes The template to be extended with the current one
escaping Option No Escaping control.
dynamic Option No Do we allow dynamic inheritance for this template?
* Hard string No Alternative templates to be extended.

The content of opt:extend may contain opt:snippet instructions only. Any other tags are ignored. The example below shows, how to use opt:extend and template inheritance:

homepage.tpl:
<?xml version="1.0" ?>
<opt:extend file="layout.tpl">
    <opt:snippet name="header">
        <h1>Our website</h1>
        <p>Home page</p>
    </opt:snippet>
    <opt:snippet name="content">
        <p>Welcome to our website!</p>  
    </opt:snippet>
</opt:extend>
layout.tpl:
<?xml version="1.0" ?>
<html>
<head>
    <title>{$title}</title>
</head>
<body>
<div id="header">
    <opt:insert snippet="header"/>
</div>
<div id="content">
    <opt:insert snippet="content"/>
</div>
<div id="footer">
    <p>&copy; Someone</p>
</div>
</body>
</html>

We can parse it with the following script:

<?php
// OPT initialization here
$view = new Opt_View('homepage.tpl');
$view->title = 'Home page - Our Website');
 
$output->render($view);
?>

The result:

<?xml version="1.0" ?>
<html>
<head>
    <title>Home page - Our Website</title>
</head>
<body>
<div id="header">
    <h1>Our website</h1>
    <p>Home page</p>
</div>
<div id="content">
    <p>Welcome to our website!</p>  
</div>
<div id="footer">
    <p>&copy; Someone</p>
</div>
</body>
</html>

Our script template, homepage.tpl extends the layout.tpl that defines the basic HTML structure for the output and specifies the places, where to insert the header and the content. homepage.tpl fills these places with the proper content. Notice that the PHP script sees only homepage.tpl and does not have to know about layout.tpl. To get to know more about template inheritance in OPT, please read this chapter.

OPT allows multiple inheritance, too.

Escaping control

Using the escaping attribute you may control the HTML escaping in the current template expressions. If it is not specified, OPT uses the default OPT settings. The value of this attribute is not a subject of inheritance:

template_a.tpl:
<opt:extend file="template_b.tpl" escaping="yes">
    <opt:snippet name="foo">
        <p>Enabled escaping: {$htmlContent}</p>
        <opt:parent />
    </opt:snippet>
</opt:extend>
template_b.tpl:
<opt:extend file="template_c.tpl" escaping="no">
    <opt:snippet name="foo">
        <p>Disabled escaping: {$htmlContent}</p>
        <opt:parent />
    </opt:snippet>
</opt:extend>
template_c.tpl:
<opt:root escaping="yes">
    <opt:insert snippet="foo">
        <p>Enabled escaping: {$htmlContent}</p>
    </opt:snippet>
</opt:extend>

If the $htmlContent value is A string with <strong>HTML</strong>, these templates will give us the following result:

<p>Enabled escaping: A string with &lt;strong&gt;HTML&lt;/strong&gt;</p>
<p>Disabled escaping: A string with <strong>HTML</strong></p>
<p>Enabled escaping: A string with &lt;strong&gt;HTML&lt;/strong&gt;</p>

See this chapter to get to know more about HTML escaping.

Inheritance brances

The template does not have to extend a single file. We are allowed to define multiple inheritance branches - alternative groups of files to be extended. Suppose we are developing a website with two versions of the layout: full and simplified. The full layout displays the logos, menus etc. around the content. However, if someone wants to print our website, he probably does not want such stuff. In order not to write all the content templates twice for both of the layouts, we might define the simple branch, where the templates extend the simple layout file.

<opt:extend file="standard_layout.tpl" simple="simple_layout.tpl">
    <opt:snippet name="content">
        <h1>Message</h1>
        <p>{$message}</p>
    </opt:snippet>
</opt:extend>

Now OPT may decide to use the standard layout (the default choice) or to follow the templates in simple branch. In this solution, the script still does not have to remember the name of simple_layout.tpl file.

For programmers

For each template, you may define any number of branches. To choose one, use the Opt_View::setBranch() method from your view object. The NULL value means that we want to use the default files. OPT uses the following rules to choose the template to be extended:

Dynamic template name

OPT processes the template inheritance during the template compilation, and this is the main reason why we are not able to read the template name from a variable, like <opt:extend file="'template_'~$tplId~.'.tpl'">. However, the runtime dynamic inheritance is still possible. In the opt:extend tag we add the dynamic attribute set to "yes". Now the script can select the inherited template file name using Opt_View::inherit() method. If the script does not provide any template, the default one is used:

<opt:extend file="default_template.tpl" dynamic="yes">
    ....
</opt:extend>

For programmers

From the script side, the inheritance chain is compiled as one, big file saved under the name of the top template. Such compilation requires significantly more memory and you must be sure that OPT will get enough resources to complete its tasks. If the script does not allocate too many objects or releases them earlier, the server with 16 MB should not cause problems. The estimated amount of memory required for the compilation process can be found in the debug console.

See also:

3.7. Instructions
3.7.8. opt:for
3.7.7. opt:extend
« Previous
3.7.9. opt:foreach
Next »

3.7.8. opt:for

This chapter contains information about programming constructs that are not recommended to use. If you are only a template author, probably you will not have to use them. In case of questions, contact your programmer.

opt:for is a loop that repeats its content until the specified expressions are true. The syntax looks familiar for anyone who programmed in PHP or C language. We define three expressions:

  1. The initial expression executed before entering the loop.
  2. The ending condition tested to check if the loop must be finished.
  3. The iteration expression executed after each iteration.
Name Type Required? Description
begin Assignment expr. Yes The initial expression.
while Assignment expr. Yes The ending condition
iterate Assignment expr. Yes The iteration expression
separator Expression No The separator that will be put between every two list elements. More about separators.

Below, you can find a sample code that displays numbers from 1 to 10:

<ol>
<opt:for begin="@i is 0" while="@i lt 10" iterate="@i++">
    <li>{@i + 1}</li>
</opt:for>
</ol>

The result:

<ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
</ol>

In the example, we create the @i variable that becomes our iteration counter. The initial value is set to 0. In the while attribute we specify that the loop must continue until the value of @i is less than 10, and every iteration we increment it by 1 (iterate attribute). It must be noted that the same effect can be achieved with much simpler construct, opt:repeat.

opt:for cooperates with separators:

<p><opt:for begin="@i is 1" while="@i lte 6" iterate="@i++" str:separator=" / ">{@i}</opt:for></p>

The result:

1 / 2 / 3 / 4 / 5 / 6

More separator examples can be found in the chapter explaining separators.

See also:

3.7. Instructions
3.7.9. opt:foreach
3.7.8. opt:for
« Previous
3.7.10. opt:grid
Next »

3.7.9. opt:foreach

This chapter contains information about programming constructs that are not recommended to use. If you are only a template author, probably you will not have to use them. In case of questions, contact your programmer.

opt:foreach iterates over the elements of container, PHP array or object. The index and the value of current element are saved to the variables pointed by the programmer, so that he could use them. The elements are returned in the order of the internal PHP data representation. Even if they are enumerated in the ascending order, but have been put in a more random way, the actual results may be different than expected.

Name Type Required? Description
array Expression Yes A container with the elements. Note that if the container will be a scalar value, we will get an PHP error.
value ID Yes The name of the variable to save the element values to.
index ID No The name of the variable to save the element indices to.
separator Expression No The separator that will be put between every two list elements. More about separators.

A sample code:

<p>The options:</p>
<ol>
    <opt:foreach array="$optionList" index="name" value="value">
    <li><strong>{@name}:</strong>{@value}</li>
    </opt:foreach>
</ol>

The result:

<p>The options:</p>
<ol>
    <li><strong>Option 1:</strong> Option value 1</li>
    <li><strong>Option 2:</strong> Option value 2</li>
    <li><strong>Option 3:</strong> Option value 3</li>
</ol>

In the example, opt:foreach was executed for a 3-element array. For each of them, the content of the loop was executed, and their index and values were available through @name and @value variables.

In OPT this loop has one extra feature: opt:foreachelse placed directly in opt:foreach. It may contain the alternative content to be displayed if the specified container is empty. Below, you can find a modified example:

<p>The options:</p>
<ol>
    <opt:foreach array="$optionList" index="name" value="value">
    <li><strong>{@name}:</strong>{@value}</li>
    <opt:foreachelse>
    <li>We are sorry, there are no options.</li>
    </opt:foreachelse>
    </opt:foreach>
</ol>

It does not matter where you exactly place opt:foreachelse - before the main content or after it.

The result for an empty container will look like this:

<p>The options:</p>
<ol>
    <li>We are sorry, there are no options.</li>
</ol>

opt:foreach should not be used too often. Open Power Template provides much more powerful instructions, sections. They are more portable and easier to use in the templates. Moreover, they hide the implementation from the user which is very important, when two sections are connected with an one-to-many relationship.

opt:for cooperates with separators:

<p><opt:foreach array="$things" value="name" str:separator=", ">{@name}</opt:foreach></p>

A sample result:

hammer, saw, screwdriver, vice

More separator examples can be found in the chapter explaining separators.

See also:

3.7. Instructions
3.7.10. opt:grid
3.7.9. opt:foreach
« Previous
3.7.11. opt:if
Next »

3.7.10. opt:grid

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

opt:grid is one of section instructions used to display the lists in tables of the specified column number. If the list size is not a multiplication of the column number, the last row is automatically filled with empty items. The instruction breaks the lists, when this is necessary. Despite standard attributes, it requires one extra attribute:

Name Type Required? Description
cols Expression Yes Column number

A sample use:

<table>
<opt:grid name="gallery" cols="3">
    <tr>
        <opt:item>
            <td><img parse:src="$gallery.picture"/></td>
        </opt:item>
        <opt:emptyItem>
            <td>&nbsp;</td>
        </opt:emptyItem> 
    </tr>
</opt:grid>
</table>

Directly in opt:grid we define a layout of a single row. In the place for cells, we place two tags:

  1. opt:item - defines a cell layout of the list element.
  2. opt:emptyItem - defines a layout of empty cells that are used to fill the last row, if the list does not contain enough elements.

Empty list support

We may define the optional content to be displayed if the list is empty with the opt:gridelse tag:

<table>
<opt:grid name="gallery" cols="3">
    <tr>
        <opt:item>
            <td><img parse:src="$gallery.picture"/></td>
        </opt:item>
        <opt:emptyItem>
            <td>&nbsp;</td>
        </opt:emptyItem> 
    </tr>
    <opt:gridelse>
        <tr><td>We are sorry, but the gallery is empty.</td></tr>
    </opt:gridelse>
</opt:grid>
</table>

Note that you must not use opt:gridelse, if our selector works together with opt:show. In this case, opt:showelse is available:

<opt:show name="gallery" cols="3">
<p>My pictures:</p> 
<table>
<opt:grid>
    <tr>
        <opt:item>
            <td><img parse:src="$gallery.picture"/></td>
        </opt:item>
        <opt:emptyItem>
            <td>&nbsp;</td>
        </opt:emptyItem> 
    </tr>
</opt:grid>
</table>
<opt:showelse>
    <p>We are sorry, but the gallery is empty.</p>
</opt:showelse>
</opt:show>

See also:

3.7. Instructions
3.7.11. opt:if
3.7.10. opt:grid
« Previous
3.7.12. opt:include
Next »

3.7.11. opt:if

This chapter contains useful programming information that are simple to understand. In case of questions, contact your programmer.

opt:if is a very useful instruction. It allows to display some code, only if the specified condition is passed. There are several possible uses: for example, we may test if the script set a variable for us before entering a code that shows its value. Note that you do not have to test the initial conditions of many OPT instructions (like sections), because they do it for their own.

The instruction takes one attribute:

Name Type Required? Description
test Expression Yes A condition that must be true to display the tag content.

Suppose we have a community website where the users can publish notes on various topics. They optionally may specify their website address, and moreover - the note might be promoted. These two issues must be taken into account while designing a template:

<div class="note">
    <h1>{$note.title}</h1>
    <p>Date: {$note.date}</p>
    <opt:if test="$note.www">
        <p><a parse:href="$note.www">Website</a></p>
    </opt:if>
    <p>{$note.body}
</div>
 
<opt:if test="$note.rank == 3">
    <p>This note has been promoted.</p>
</opt:if>

In the first place, we can see, how to use opt:if to test if a variable is set. If the script does not provide the $note.www variable, the paragraph with an URL is not even visible in the browser. Without the condition, the template would always display an URL, even if it was empty and redirected the user to a vacuum. At the end of the template, we want to show a suitable message, if the note is promoted. We compare the $note.rank to 3 and if they are equal, the user sees an extra text. More about writing OPT expressions and conditions can be found in this chapter.

opt:if may perform alternative operations, if the condition is not true. They are defined with opt:elseif and opt:else placed directly in opt:show. The first one represents an alternative condition to check. This tag may be used several times in one instruction. OPT will test them one after another till one is passed. The content in opt:else is displayed, if all the conditions for the instruction fail. It may be used only once:

<!-- the exact condition content is not important here -->
 
<opt:if test="condition1">
 
    This text will show, if the condition1 is passed.
 
    <opt:elseif test="condition2">
        This text will show, if the condition1 fails and condition2 is passed.
    </opt:elseif>
    <opt:elseif test="condition3">
        This text will show, if the condition1 and condition2 fail, but condition3 is passed.
    </opt:elseif>
    <opt:else>
        This text will show, if neither of the conditions is passed.
    </opt:else>
</opt:if>

Note that placing opt:elseif and opt:else in any tag other than opt:if causes an error:

<opt:if test="condition">
    <div>
        <opt:else>  <!-- WRONG! We have put opt:else in DIV! --> </opt:else>
    </div>
</opt:if>

Now we might get back to our example and modify it to display a message about the promotion for the rank 3 and something else for the rest:

<opt:if test="$note.rank == 3">
    <p>This note has been promoted.</p>
    <opt:else>
        <p>This is an ordinary note.</p>
    </opt:else>
</opt:if>
3.7. Instructions
3.7.12. opt:include
3.7.11. opt:if
« Previous
3.7.13. opt:insert
Next »

3.7.12. opt:include

opt:include executes an external view and displays it in the specified place of the current template. It has three different cases of use.

Including the statically constructed view

In this case, the currently executed template constructs a new OPT view, using the specified attributes:

Name Type Required? Description
file string Yes The template to be included
default string No The default template, if the template defined in file does not exist.
branch string No The inheritance branch used for the included template.
import option No If set to yes, the new view imports all the template variables from the current view.
* expression No The view arguments that will be visible as variables there.

This case is useful, if we need to include the specified file:

<div>
    <opt:include file="left_menu.tpl" import="yes"/>
</div>

As the most important attributes are normal strings, we may change their namespace to parse in order to load their values from a variable:

<div>
    <opt:include parse:file="$leftMenuTemplate" import="yes" />
</div>

Including a script-defined view

Here, the view is already created by the script and all we want to do is to execute it:

Name Type Required? Description
view expression Yes The view to be displayed
default string No The default template, if the view template does not exist.
branch string No The inheritance branch used for the included template.
import option No If set to yes, the new view imports all the template variables from the current view.
* expression No The view arguments that will be visible as variables there.
<div>
    <opt:include view="$leftMenuView" />
</div>

Here, we do not have to worry about the data for the $leftMenuView. As this view is created by the script, we may assume that the script has already provided the necessary data for it.

Integrating with sections

opt:include can also integrate with the sections. In this case, the following set of attributes is used:

Name Type Required? Description
from hard string Yes The existing and currently active section name
default string No The default template, if the view template does not exist.
branch string No The inheritance branch used for the included template.
import option No If set to yes, the new view imports all the template variables from the current view.
* expression No The view arguments that will be visible as variables there.

An example:

<opt:section name="modules">
    <div class="module">
        <h1>{$modules.name}</h1>
        <opt:include from="modules" />
    </div>
</opt:section>

This template allows to load the views from the section and execute them automatically. We assume that the view object is stored under $modules.view variable. The optional attributes give us the possibility to do extra configuration of the loaded views.

The default content

If the template we try to execute does not exist, we might deal with it in two ways. The first one is to select an alternative template with default attribute:

<opt:include parse:file="$template" default="default_template.tpl"/>

Alternatively, we may define the default content directly in opt:include:

<opt:include parse:file="$template">
    <p>We are sorry, but the template {$template} does not exist.</p>
</opt:include>
3.7. Instructions
3.7.13. opt:insert
3.7.12. opt:include
« Previous
3.7.14. opt:literal
Next »

3.7.13. opt:insert

opt:insert allows to place the template snippets in the specified place. It is a part of the template inheritance feature.

Name Type Required? Description
snippet hard string Yes The name of the snippet
ignoredefault option No If set to yes, the default value of opt:insert is not treated as the snippet parent.

An example:

<opt:snippet name="foo">
    <p>I am your snippet.</p>
</opt:snippet>
 
<div>
    <opt:insert snippet="foo" />
</div>

Contrary to opt:capture, the inserted code remains fully functional. In the following example we change the value of the variable displayed in the snippet. We see that the value also changes in the output, between two inserts of the same code.

<opt:snippet name="foo">
    <p>Value: {@hoo}</p>
</opt:snippet>
 
<div>
    {@hoo is 1}
    <opt:insert snippet="foo" />
 
    {@hoo is 2}
    <opt:insert snippet="foo" />
</div>

The result:

<div>
    <p>Value: 1</p>
    <p>Value: 2</p>
</div>

The default content

opt:insert allows to define the default content in case the snippet does not exist:

<div>
    <opt:insert snippet="foo">
        <p>Oops, one snippet is missing.</p>
    </opt:insert>
</div>

By default, if the snippet contains the opt:parent tag and does not overload any other snippet, the default content of opt:insert will be treated as the parent:

<opt:snippet name="foo">
    <p>This is a snippet code.</p>
    <opt:parent />
</opt:snippet>
 
<div>
    <opt:insert snippet="foo">
        <p>This is a default code.</p>
    </opt:insert>
</div>

The result:

<div>
    <p>This is a snippet code.</p>
    <p>This is a default code.</p>
</div>

We can turn this off by setting the attribute ignoredefault to yes in opt:insert:

<opt:snippet name="foo">
    <p>This is a snippet code.</p>
    <opt:parent />
</opt:snippet>
 
<div>
    <opt:insert snippet="foo" ignoredefault="yes">
        <p>This is a default code.</p>
    </opt:insert>
</div>

The result:

<div>
    <p>This is a snippet code.</p>
</div>

Now, the default content will never appear, if there is any snippet named foo.

Inserting the content from opt:capture

opt:insert may also display the content captured by opt:capture. Contrary to the $system variable, the captured part can be chosen dynamically. The instruction takes only one attribute then:

Name Type Required? Description
captured Expression Yes The name of the captured part

If the tag contains some content, it is treated as a default content and displayed, if the specified captured part does not exist:

<opt:capture as="foo">
Some content here...
</opt:capture>
 
{@captured is 'foo'}
<opt:insert captured="@captured">
The default content.
</opt:insert>

See also:

3.7. Instructions
3.7.14. opt:literal
3.7.13. opt:insert
« Previous
3.7.15. opt:prolog
Next »

3.7.14. opt:literal

opt:literal instruction controls the parsing of XML CDATA sections with OPT. Normally, OPT compiles them according to their semantics: it moves them to the output, but without parsing their content. CDATA tags are moved, too. For template designer, such behavior is not always acceptable. Having the JavaScript code and some data do be put into it, we would like to escape part of that code not to be parsed by OPT, but at the same time, it will be decorated with a huge amount of <![CDATA[ and ]]> and the browser will not parse such result.

<script type="text/javascript">
<![CDATA[
    document.write('Hello my friend, do you need {$object} or ]]>{$object}<![CDATA[?');
]]>
</script>

In the example above, the output JS code is simply destroyed. Here it's a place for opt:literal. We can enclose our JS code inside it, and then the template CDATA sections will not be rewritten to the output. Instead, opt:literal will generate its own CDATA beginning and end that encloses the complete code:

<script type="text/javascript">
<opt:literal>
<![CDATA[
    document.write('Hello my friend, do you need {$object} or ]]>{$object}<![CDATA[?');
]]>
</opt:literal>
</script>

Result:

<script type="text/javascript">
<![CDATA[
    document.write('Hello my friend, do you need {$object} or sunglasses?');
]]>
</script>

However, opt:literal offers you more decorators. It can be used to generate dynamic HTML comments or simply to hide the existence of CDATA sections from the browser. The behavior may be chosen with the following attribute:

Name Type Required? Default Description
type ID No cdata What to display?

Allowed values are:

See also:

3.7. Instructions
3.7.15. opt:prolog
3.7.14. opt:literal
« Previous
3.7.16. opt:put
Next »

3.7.15. opt:prolog

opt:prolog generates the output XML prolog, if the configuration option prologRequired is enabled. In this case the standard XML prologs put in the template are not sent to the browser and this instruction is the only solution then. It may take up to three attributes:

Name Type Required? Description
version Expression No The XML version. Default value is "1.0"
encoding Expression No The encoding, taken by default from the OPT configuration.
standalone Expression No The "standalone" attribute in the prolog. The default value is "no".

The instruction has one advantage over a typical prolog - we may read the values from the variables.

<?xml version="1.0" encoding="UTF-8" ?>   <!-- with "prologRequired" enabled, this line won't appear in the browser. -->
<opt:prolog standalone="yes"/>    <!-- the prolog for the browser will be generated here. -->

There is no requirement to place opt:prolog at the beginning of the template. Even if it is deeply nested in other XML tags, the prolog will always appear before the document content.

See also:

3.7. Instructions
3.7.16. opt:put
3.7.15. opt:prolog
« Previous
3.7.17. opt:repeat
Next »

3.7.16. opt:put

Puts an OPT expression in the specified place. It has the similar meaning to the standard curly bracket syntax.

Name Type Required? Description
value Expr. with assign. Yes The expression to be evaluated.

The following lines perform exactly the same task:

Text {$variable} Text
Text <opt:put value="$variable" /> Text

Because opt:put is an instruction, it can be combined with other instructions. An example:

<opt:put value="$section.item" opt:section="section" separator=" | " />

It can generate a sequence like this:

Foo | Bar | Joe
3.7. Instructions
3.7.17. opt:repeat
3.7.16. opt:put
« Previous
3.7.18. opt:root
Next »

3.7.17. opt:repeat

opt:repeat is a simple loop that repeats its content the specified number of times. It takes two attributes:

Name Type Required? Description
times Expression Yes The number of iterations
separator Expression No The separator layout. More about separators can be found here.

A simple example:

<ol>
    <opt:repeat times="10">
    <li>{$sys.repeat + 1}</li>
    </opt:repeat>
</ol>

And its result:

<ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
</ol>

The number of current iteration is stored under $sys.repeat. Note that the iterations are enumerated from 0. This is why we are adding 1 to each index in order to display human-readable numbers that start from 1.

opt:repeat supports separators that work exactly, like in other loops. The examples of use can be found in the chapter explaining separators.

See also:

3.7. Instructions
3.7.18. opt:root
3.7.17. opt:repeat
« Previous
3.7.19. opt:section
Next »

3.7.18. opt:root

opt:root provides a tag that may become a neutral root tag in the template. The XML standard permits only one main tag per file, but sometimes smaller templates represent a part of the code that does not follow this rule:

<?xml version="1.0" ?>
<opt:root>
    <div>
        ...
    </div>
    <div>
        ...
    </div>
</opt:root>

Without opt:root, we would have to remove one of the <div> tags in order to make the template standard compliant.

opt:root may also take optional attributes that apply to the current template.

Name Type Required? Description
include Hard string No The template file to be included before compilation.
dynamic Option No Do we allow dynamic inclusion for this template?
escaping Option No Per-template escaping settings.

Escaping control

Using the escaping attribute you may control the HTML escaping in the current template expressions. If it is not specified, OPT uses the default OPT settings.

<opt:root escaping="yes">
 
    <p>The HTML in the variable values will be escaped in this template: {$variable}</p>
 
</opt:root>

Including external templates

Suppose we have two templates with HTML form code: form1.tpl and form2.tpl. We want to define a global layout for a form control in a snippet. It must be common for all the templates. If we achieve the modularity with opt:include, using the template inheritance is not very convenient:

<!-- definitions.tpl -->
<opt:root>
    <opt:snippet name="control">
        <!-- the control layout -->
    </opt:snippet>
 
    <opt:insert snippet="content" />
</opt:root>
 
<!-- form1.tpl -->
<opt:extend file="definitions.tpl">
    <opt:snippet name="content">
        <!-- the form content that uses the "control" snippet. -->  
    </opt:snippet>
</opt:extend>

In this case, we have to declare the form in the snippet, too. It must be loaded in definitions.tpl to be visible in the browser. Of course if we have other tasks for defitions.tpl, this is not so bad solution, but OPT provides another mechanism for such situations. opt:root can include an external template before the compilation just to load the snippets:

<!-- definitions.tpl -->
<opt:root>
    <opt:snippet name="control">
        <!-- the control layout -->
    </opt:snippet>
</opt:root>
 
<!-- form1.tpl -->
<opt:root include="definitions.tpl">
    <!-- the form content that uses the "control" snippet. -->  
</opt:root>

Now we do not have to pack the form content in a fake snippet, because we do not use the template inheritance.

Note that the include attribute is processed during the template compilation and it is not possible to load its value from a variable. In the included template, all the content, except opt:snippet tags, is ignored, because it is hard to say, where it should appear. (Before? After the main template content? If so, then why?) OPT prevents us against the infinite recursive inclusions (A includes B, which includes A). The compilation is canceled and the library shows the error message.

However, note that you may set the attribute dynamic to yes, which has the same effect as in opt:extend - it allows to set the template to be included by the script using the Opt_View::inherit() method.

Note on the performance and script resources. We do not recommend to create compound inclusion chains (A includes B which includes C, which includes D etc.). The include attribute is the only part of the compiler that is processed recursively. In case of advanced dependencies between the templates, the compilation may cause PHP stack overflow (Nesting level too deep message).

See also:

3.7. Instructions
3.7.19. opt:section
3.7.18. opt:root
« Previous
3.7.20. opt:selector
Next »

3.7.19. opt:section

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

opt:section is the basic section instruction used to display flat, linear lists and supports all the section basic features. Inside the opt:section we define the layout of a single list element:

<ul>
    <opt:section name="categories">
    <li>{$categories.name}, {$categories.productCount}</li>
    </opt:section>
</ul>

Empty lists

We may define the optional content to be displayed if the list is empty with the opt:sectionelse tag:

<ul>
    <opt:section name="categories">
        <li>{$categories.namae}, {$categories.productCount}</li>
        <opt:sectionelse>
        <li>We are sorry, there are no categories in the system.</li>
        </opt:sectionelse>
    </opt:section>
</ul>

opt:sectionelse must be located directly within opt:section, but it is up to you, whether you put it before or after the content.

opt:sectionelse is disabled, if the section is used together with opt:show. In this case, the alternative message can be declared with opt:showelse:

<opt:show name="categories">
<ul>
    <opt:section>
        <li>{$categories.name}, {$categories.productCount}</li>
    </opt:section>
</ul>
<opt:showelse>
    <p>We are sorry, there are no categories in the system.</p>
</opt:showelse>
</opt:show>

See also:

3.7. Instructions
3.7.20. opt:selector
3.7.19. opt:section
« Previous
3.7.21. opt:separator
Next »

3.7.20. opt:selector

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

opt:selector works much like opt:section. The difference is that here we may define several types of element layout. OPT will choose one for each element, depending on one of its fields (by default, it is called "item"). Below, we can find an example of displaying the links to the result pages. Each list element represents one page, however we do not want them do look exactly the same. With opt:selector we define different looks for various types of pages (standard, active, dotted part to hide too many pages etc.):

<opt:selector name="pages">
    <opt:page>[ <a parse:href="$page.address">{$page.number}</a> ]</opt:page>
    <opt:active>[ <a parse:href="$page.address" style="active">{$page.number}</a> ]</opt:active>
    <opt:dots>...</opt:dots>
</opt:selector>

With opt:selector we defined different layout for various types of pages:

  1. Default page link
  2. Current page
  3. The dots to shorten the list, if there are too many pages (for example, [ 1 ] ... [ 5 ] [ 6 ] [ 7 ] ... [ 15 ]).

Now, the pagination system must simply generate a list of pages with a field item to identify the requested type.

Note that the layout types are defined as separate tags in one of namespaces recognized by OPT. The rest of tags located directly in opt:selector will be ignored. OPT tries to match the list elements to the tag names.

Normally, OPT looks for the element type name in the variable $sectionName.item. This can be changed with an extra attribute, type:

Name Type Required? Description
test ID No The name of element variable with the type name
<opt:selector name="pages" test="pageType">
    <opt:page>[ <a parse:href="$page.address">{$page.number}</a> ]</opt:page>
    <opt:active>[ <a parse:href="$page.address" style="active">{$page.number}</a> ]</opt:active>
    <opt:dots>...</opt:dots>
</opt:selector>

Now the types are taken from $pages.pageType instead of $pages.item.

Empty lists

You may define the alternative content to be displayed, if the list is empty, using the tag opt:selectorelse:

<opt:selector name="pages" test="pageType">
    <opt:page>[ <a parse:href="$page.address">{$page.number}</a> ]</opt:page>
    <opt:active>[ <a parse:href="$page.address" style="active">{$page.number}</a> ]</opt:active>
    <opt:dots>...</opt:dots>
    <opt:selectorelse>Sorry, the result set is empty.</opt:selectorelse>
</opt:selector>

Note that you must not use opt:selectorelse, if our selector works together with opt:show. In this case, opt:showelse is available:

<opt:show name="pages" test="pageType">
    <p><opt:selector>
        <opt:page>[ <a parse:href="$page.address">{$page.number}</a> ]</opt:page>
        <opt:active>[ <a parse:href="$page.address" style="active">{$page.number}</a> ]</opt:active>
        <opt:dots>...</opt:dots>
    </opt:selector></p>
    <opt:showelse>
    <p>Sorry, the result set is empty.</p>
    </opt:showelse>
</opt:show>

opt:selector and snippets

opt:selector supports OPT snippets. Let's get back to our pagination example. It is obvious that on huge website we are not going to declare the layouts in each of our 100 templates that display some result sets. Instead of that, the generic layout can be moved to an external snippet and loaded with opt:use attribute:

<opt:snippet name="defaultPagination">
    <opt:page>[ <a parse:href="$defaultPagination.address">{$defaultPagination.number}</a> ]</opt:page>
    <opt:active>[ <a parse:href="$defaultPagination.address" style="active">{$defaultPagination.number}</a> ]</opt:active>
    <opt:dots>...</opt:dots>
</opt:snippet>
 
<opt:selector name="pages1" opt:use="defaultPagination" />
 
<opt:selector name="pages2" opt:use="defaultPagination" />

See also:

3.7. Instructions
3.7.21. opt:separator
3.7.20. opt:selector
« Previous
3.7.22. opt:show
Next »

3.7.21. opt:separator

opt:separator instruction can be used only within loop instructions. It allows to define a content that is displayed between every two iterations of the loop. It takes no attributes:

<opt:repeat times="5">
    <opt:separator> / </opt:separator>
    {$opt.repeat}
</opt:repeat>

The code above produces the following result:

1 / 2 / 3 / 4 / 5

As we can see, the separator content apears neither before the first element nor after the last one.

Attribute version

The separators can be defined with an extra loop instruction attribute, separator that takes any expression as its value. By switching to the str namespace, we may define a static separator, or stay in the default one to read it from variable:

<opt:repeat times="5" str:separator=" / ">
    {$opt.repeat}
</opt:repeat>

This code will produce the same code, as the first example. Below, we can see, how to load the separator from a variable:

<opt:repeat times="5" separator="$separatorDef">
    {$opt.repeat}
</opt:repeat>
 
<opt:repeat times="5">
    <opt:separator>{$separatorDef}</opt:separator>
    {$opt.repeat}
</opt:repeat>

Note that the opt:separator tag allows to create more sophisticated separators constructed from other instructions. In the last example, we see a dynamic separator that grows during the execution:

<opt:repeat times="5">
    <opt:separator><opt:repeat times="$opt.repeat">-</opt:repeat></opt:separator>
    {$opt.repeat}
</opt:repeat>

The result:

1-2--3---4----5

See also:

3.7. Instructions
3.7.22. opt:show
3.7.21. opt:separator
« Previous
3.7.23. opt:snippet
Next »

3.7.22. opt:show

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

opt:show allows to define the section neighborhood layout. It has an important property - if the section does not get any elements, the neighborhood is not visible in the browser, too. In the example, we hide the <ol> tags, when there are no elements to display:

<opt:show name="list">
<ol>
    <opt:section>
    <li>{$list.name}</li>
    </opt:section>
</ol>
</opt:show>

When using opt:show, you have to remember about two issues:

  1. All the section attributes are defined in opt:show, and the section tag (opt:section, opt:tree, etc.) remains empty in order to be connected with opt:show. The section tags with attributes are possible within opt:show, but they will become independent sections.
  2. opt:show does not start section. We can neither access the section element variables etc. within this tag nor make it parent for any other nested section.

An example to the first issue:

<opt:show name="section1">
    <opt:section name="section2">
 
    </opt:section>
 
    <opt:section>
 
    </opt:section>
</opt:show>

The first opt:section contains some attributes, so it is not connected to opt:show. Even if it is empty, the content of opt:show is displayed and vice versa. However, the second one has no attributes and it will be cooperating with our instruction. Moreover, section1 will not become a parent of section2, because in fact, it is not started yet. The relationship would be established, if the code looked like that:

<opt:show name="section1">
    <opt:section>
        <opt:section name="section2">
 
        </opt:section>
    </opt:section>
</opt:show>

Empty list messages

We may define the optional content to be displayed if the list is empty. We use opt:showelse tag then:

<opt:show name="list">
<ol>
    <opt:section>
    <li>{$list.name}</li>
    </opt:section>
</ol>
<opt:showelse>
    <p>We are sorry, the list is empty.</p>
</opt:showelse>
</opt:show>

When using opt:showelse, the section tags must not contain their internal tags that perform the same thing (like opt:sectionelse).

See also:

3.7. Instructions
3.7.23. opt:snippet
3.7.22. opt:show
« Previous
3.7.24. opt:tag
Next »

3.7.23. opt:snippet

opt:snippet saves its content under the specified name. It can be inserted in many other places with opt:insert instruction or opt:use attribute. Contrary to opt:capture, the saved code remains dynamic. If we change the value of an variable used in the snippet between two insertions, it will be visible in the output. The instruction is a part of template inheritance system, however it may be also used for many other purposes.

attributes:

Name Type Required? Description
name ID Yes The snippet unique name.

Below, you can see a template that illustrates the snippet properties:

<opt:snippet name="snippet">
 
    <p>The value of <em>foo</em>: {@foo}</p>
 
</opt:snippet>
 
<!-- Shows "The value of foo: 5" -->
{@foo is 5}
<opt:insert snippet="snippet" />
 
<!-- Shows "The value of foo: 6" -->
{@foo is 6}
<opt:insert snippet="snippet" />

opt:snippet is processed during the compilation and neither declared nor included snippet names may be loaded from variables. The template may use the snippets defined in other template, however - we must assure that it will be loaded by the compiler, either using template inheritance or opt:root include attribute. Note that opt:include instruction does not guarantee that the snippets from the included template will be loaded.

Although the snippet name must be unique, it is possible to create several snippets named identically:

<opt:snippet name="foo">
    SNIPPET 1 
</opt:snippet>
 
<opt:snippet name="foo">
    SNIPPET 2
</opt:snippet>
 
<opt:snippet name="foo">
    SNIPPET 3
</opt:snippet>
 
<opt:insert snippet="foo" /> <!-- what will be the result? -->

The first look at the code suggests that the new snippets overwrite the old ones and we will see "SNIPPET 3". Unfortunately, they don't because of the way the template inheritance is processed in the compiler. The output will be "SNIPPET 1", the first snippet that uses the name foo. However, the later definitions are not lost. Snippet 2 becomes a parent of snippet 1, and snippet 3 - of snippet 2. Each snippet may display the content of its parent using the opt:parent tag. (Do you see here some similarities to the method overloading in the object oriented programming?)

<opt:snippet name="foo">
    The beginning of snippet 1
    <opt:parent />
    The end of snippet 1
</opt:snippet>
 
<opt:snippet name="foo">
    The beginning of snippet 2
    <opt:parent />
    The end of snippet 2
</opt:snippet>
 
<opt:snippet name="foo">
    The beginning of snippet 3
    <opt:parent />
    The end of snippet 3
</opt:snippet>
 
<opt:insert snippet="foo" />

Such code produces the following result:

    The beginning of snippet 1
        The beginning of snippet 2
            The beginning of snippet 3
            The end of snippet 3
        The end of snippet 2
    The end of snippet 1

As we see, opt:parent is ignored, if the snippet does not have any parent. However, note that opt:insert instruction used to paste snippets in the new place, may have an alternative content in case the snippet is not defined. By default, that content is also treated as a parent of the pasted snippet! However, this can be disabled with opt:insert ingoredefault attribute.

See also:

3.7. Instructions
3.7.24. opt:tag
3.7.23. opt:snippet
« Previous
3.7.25. opt:tree
Next »

3.7.24. opt:tag

opt:tag allows to create a tag with a dynamically generated name:

Name Type Required? Description
name Expression Yes The tag name
single Option No If set to "no" (the default value), OPT shortens the tag to the single form: <tag /> only if the same form is used in the template. "yes" shortens the tag also if its content is built from white characters.
ns Expression No Tag namespace

With opt:attribute you may add the name and single attributes to the new tag, so that they could not be parsed by OPT:

<opt:tag name="$tagName" id="$id">
    <opt:attribute str:name="name" str:value="foo"/>
 
    Some content
 
</opt:tag>

A sample result:

<abc name="foo" id="bar">
    Some content
</abc>

If the attribute ns is not set, the namespace may be defined by the name attribute. The ns attribute allows to do this dynamically - if the value is empty, the separating colon is not printed.

opt:tag cannot create dynamically the OPT instruction tags, because it is processed during the execution, not compilation of the template. If we try to give the tag the name opt:section, it will simply appear in the output.

See also:

3.7. Instructions
3.7.25. opt:tree
3.7.24. opt:tag
« Previous
3.8. OPT attributes
Next »

3.7.25. opt:tree

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

opt:tree is one of the section instructions designed to display hierarchical data (trees) with any level of nesting. The instruction does not support one of section attributes, order - because of the used algorithm the result displayed in the descending order would be strange.

Inside the opt:tree tag we place two new ones: opt:list and opt:node. The first tag tells, how to open and close the new nesting level, whereas opt:node defines the layout of a single element within a tree. In both of them, we need to put also another tag: opt:content to mark, where to display the nested content:

<opt:tree name="tree">
    <opt:list><ul><opt:content/></ul></opt:list>
    <opt:node><li>{$tree.name} <opt:content /></li></opt:node>  
</opt:tree>

Sample result:

<ul>
    <li>Element 1</li>
    <li>Element 2 <ul>
        <li>Element 2.1 <ul>
            <li>Element 2.1.1</li>
        </ul></li>
        <li>Element 2.2</li>
    </ul></li>
    <li>Element 3</li>
</ul>

In opt:list, the opt:content tag specifies, where to show the elements of the currently opened level, and in opt:node, where the nested list could appear. Note that we do not have to enclose opt:tree itself within <ul> tags, because they are automatically added, when the instruction starts to generate the initial nesting level.

The opt:content tag must not be located within any other tag that generates PHP code during the compilation. Especially, it must not be placed within other instruction tags.

A sample of invalid opt:content location:

<opt:tree name="tree">
    <opt:list><ul><opt:content/></ul></opt:list>
    <opt:node><li>{$tree.name} <opt:repeat times="3"><opt:content /></opt:repeat></li></opt:node>  
</opt:tree>

Empty list support

You may define the alternative content to be displayed, if the list is empty, using the tag opt:treeelse:

<opt:tree name="tree">
    <opt:list><ul><opt:content/></ul></opt:list>
    <opt:node><li>{$tree.name} <opt:content /></li></opt:node>
    <opt:treeelse><p>We are sorry, but there is nothing to show.</p></opt:treeelse>
</opt:tree>

With opt:show instruction, opt:treeelse must not be used:

<opt:show name="tree">
    <p>Tree:</p>
 
    <opt:tree>
        <opt:list><ul><opt:content/></ul></opt:list>
        <opt:node><li>{$tree.name} <opt:content /></li></opt:node>
    </opt:tree>
 
    <opt:showelse>
        <p>We are sorry, but there is nothing to show.</p>
    </opt:showelse>
</opt:show>

For programmers

opt:tree requires each element to have a variable $sectionName.depth that defines the depth of the element. Its value can be easily counted recursively, if the tree is stored in a traditional format. If the tree is stored in the database and we are using the nested sets algorithm (also known as modified preorder tree traversal), such value is automatically counted during the retrieving the tree from the database. A sample list from the first example would look like this:

name=Element 1, depth=1
name=Element 2, depth=1
name=Element 2.1, depth=2
name=Element 2.1.1, depth=3
name=Element 2.2, depth=2
name=Element 3, depth=1

See also:

3. Template syntax
3.8. OPT attributes
3.7.25. opt:tree
« Previous
3.8.1. opt:attributes-build
Next »

3.8. OPT attributes

We have already learned that the instructions may parse particular XML tags in our templates to perform some tasks. The same applies to tag attributes. The attributes that are parsed by the instructions can be recognized by their namespaces. By default, they are members of opt namespace, but custom instructions may register them in different ones. The attributes are also very powerful and are able to manipulate the tags they are assigned to. They can be used both with ordinary and instruction tags.

See also:

3.8. OPT attributes
3.8.1. opt:attributes-build
3.8. OPT attributes
« Previous
3.8.2. opt:attributes-ignore
Next »

3.8.1. opt:attributes-build

Versionssince 2.0.1

Builds the tag attributes from a container:

<div opt:attributes-build="$attributeList">
    ...
</div>

The container element index is taken as an attribute name, and the element value - as the value. The attribute can be combined with opt:attributes-ignore to provide a list of ignored container elements:

<div opt:attributes-build="$attributeList" opt:attributes-ignore="$ignoreList">
    ...
</div>

See also:

3.8. OPT attributes
3.8.2. opt:attributes-ignore
3.8.1. opt:attributes-build
« Previous
3.8.3. opt:capture
Next »

3.8.2. opt:attributes-ignore

Versionssince 2.0.1

The attribute is used to specify the list of ignored attributes for opt:attributes-build.

<div opt:attributes-build="$attributeList" opt:attributes-ignore="$ignoreList">
    ...
</div>

The ignored attribute list can be provided either as a container of names or a string in the format:

name1, name2, name3

The following script generating the data for the code above will produce the following result:

$view->attributeList = array(
    'attr1' => 'value1',
    'attr2' => 'value2',
    'attr3' => 'value3'
);
$view->ignoreList = array(
    'attr2'
);

Result:

<div attr1="value1" attr3="value3">
    ....
</div>

See also:

3.8. OPT attributes
3.8.3. opt:capture
3.8.2. opt:attributes-ignore
« Previous
3.8.4. opt:content
Next »

3.8.3. opt:capture

The attribute does exactly the same, as opt:capture instruction. Its value must be a valid identifier that allows to access the captured output:

<div>
    <a href="$url" opt:capture="url">{$urlName}</a>
    <p>{$sys.capture.url}</p>
</div>

Note that the tag with opt:capture attribute also belongs to the captured output.

See also:

3.8. OPT attributes
3.8.4. opt:content
3.8.3. opt:capture
« Previous
3.8.5. opt:dynamic
Next »

3.8.4. opt:content

The attribute takes any expression as its value. If the result is non-empty or the specified variable exists, it will be displayed in the place of default tag content:

<h1 opt:content="$article.title">Default title</h1>

If the variable $article.title is not defined, we see the text Default title.

Escaping issues

The attribute value may use the escaping modifiers e: and u:. The escaped content is used only to display the expression value. The emptiness is checked on the original value.

See also:

3.8. OPT attributes
3.8.5. opt:dynamic
3.8.4. opt:content
« Previous
3.8.6. opt:if
Next »

3.8.5. opt:dynamic

Versionssince 2.0.1

This is a variant of opt:dynamic instruction that uses attributes instead of tags to represent itself. The dynamic content is enabled, if the attribute value is set to yes:

<div opt:dynamic="yes">
    <p>Some dynamic content: {$variable}</p>
</div>

See also:

3.8. OPT attributes
3.8.6. opt:if
3.8.5. opt:dynamic
« Previous
3.8.7. opt:on
Next »

3.8.6. opt:if

This attribute allows to display the tag conditionally:

<p opt:if="$showParagraph == true">This paragraph is displayed only if the condition is true.</p>

The code above is equivalent of:

<opt:if test="$showParagraph == true">
<p>This paragraph is displayed only if the condition is true.</p>
</opt:if>

Within opt:if attribute the opt:elseif and opt:else tags do not work.

See also:

3.8. OPT attributes
3.8.7. opt:on
3.8.6. opt:if
« Previous
3.8.8. opt:section
Next »

3.8.7. opt:on

The attribute allows to display a tag conditionally, but with keeping the content.

<p><a parse:href="$user.www" opt:on="isAddress($user.www)">{$user.nick}</a></p>

If the $user.www is a valid URL, we will see:

<p><a href="http://www.example.com/">Nickname</a></p>

And if not:

<p>Nickname</p>

Note that such code has no effect when using with instruction tags.

See also:

3.8. OPT attributes
3.8.8. opt:section
3.8.7. opt:on
« Previous
3.8.9. opt:selector
Next »

3.8.8. opt:section

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

This attribute applied to any tag changes it into a section with the specified name. It allows to reduce the following code:

<ol>
    <li opt:section="list">{$list.variable}</li>
</ol>

Instead:

<ol>
    <opt:section name="list">
    <li>{$list.variable}</li>
    </opt:section>
</ol>

The tag with opt:section attribute is also a member of section content and the element variables can be used in its attributes:

<ol>
    <li parse:class="$list.css" opt:section="list">{$list.variable}</li>
</ol>

The disadvantage of opt:section attribute is that we cannot set additional section options, except separators:

<p><opt:put value="$section.name" opt:section="section" str:separator=" / "/></p>

Sample result:

<p>Value 1 / Value 2 / Value 3 / Value 4</p>

opt:show integration

The opt:section attribute can be used together with opt:show. Because the attribute must have any value, it is assumed that in this case, it must contain the same section name, as it is defined in opt:show. With opt:show we are also allowed to define extra section attributes:

<opt:show name="list" order="desc">
<ol>
    <li opt:section="list">{$list.variable}</li>
</ol>
</opt:show>

Empty lists

Within the tag with opt:section, you must not use the opt:sectionelse attribute. The only solution for empty lists is opt:show and opt:showelse.

See also:

3.8. OPT attributes
3.8.9. opt:selector
3.8.8. opt:section
« Previous
3.8.10. opt:single
Next »

3.8.9. opt:selector

This chapter describes one of section instructions. We recommend to read Sections first to get to know more about them.

This attribute applied to any tag changes it into a selector with the specified name. It allows to reduce the following code:

<ol>
    <li opt:selector="list">
        <opt:foo>Layout 1: {$list.variable}</opt:foo>
        <opt:bar>Layout 2: {$list.variable}</opt:bar>
        <opt:joe>Layout 3: {$list.variable}</opt:joe>
    </li>
</ol>

Instead:

<ol>
    <opt:selector name="list">
        <opt:foo><li>Layout 1: {$list.variable}</li></opt:foo>
        <opt:bar><li>Layout 2: {$list.variable}</li></opt:bar>
        <opt:joe><li>Layout 3: {$list.variable}</li></opt:joe>
    </opt:selector>
</ol>

The attribute has similar features, as opt:section.

See also:

3.8. OPT attributes
3.8.10. opt:single
3.8.9. opt:selector
« Previous
3.8.11. opt:use
Next »

3.8.10. opt:single

The attribute creates a single tag, causing its content to be lost. Its purpose is to close properly the HTML tags that are naturally single, like <img> in case we had to use some OPT instructions to perform some operations on them. Below, you can find an example:

<img src="image.png">
    <opt:attribute name="$imgExtraAttrName" value="$imgExtraValue" />
</img>

OPT will produce the following output for it:

<img src="image.png" someAttribute="someValue">
</img>

With opt:single we may force to create a single tag here and omit the content.

<img src="image.png" opt:single="yes">
    <opt:attribute name="$imgExtraAttrName" value="$imgExtraValue" />
</img>

The result:

<img src="image.png" someAttribute="someValue" />

See also:

3.8. OPT attributes
3.8.11. opt:use
3.8.10. opt:single
« Previous
3.9. Topics
Next »

3.8.11. opt:use

This attribute works similarly to opt:insert tag - it pastes the content of the snippet as the tag content. The original content is kept, if the snippet does not exist.

<opt:snippet name="newBetterParagraph">
    This is a content of the better paragraph!
</opt:snippet>
 
<p opt:use="newBetterParagraph">This is the default content</p>

Similarly to opt:insert, the original content may still appear, if the snippet contains opt:parent. This feature can be used to pack the existing content in the new tags. In the example below, we pack the text in the paragraph in an URL:

<opt:snippet name="url">
    <a parse:href="$url"><opt:parent /></a>
</opt:snippet>
 
<p opt:use="url">The text to be changed into URL.</p>

Section integration

opt:use allows to define the generic section content. When pasted to the section tags, the snippet is automatically connected to the new section:

<opt:snippet name="element">
    <li>{$element.name}</li>
</opt:snippet>
 
<p>Categories:</p>
<ol>
    <opt:section name="categories" opt:use="element" />
</ol>
 
<p>Products:</p>
<ol>
    <opt:section name="products" opt:use="element"></opt:section>
</ol>

When the element snippet is pasted to the categories sections, the variables $element.foo become $categories.foo etc. If we later paste the same snippet in the products section content, the same variables become $products.foo. This applies to all kinds of sections.

See also:

3. Template syntax
3.9. Topics
3.8.11. opt:use
« Previous
3.9.1. Sections
Next »

3.9. Topics

This chapter describes various topics concerning OPT syntax.

3.9. Topics
3.9.1. Sections
3.9. Topics
« Previous
3.9.2. Blocks
Next »

3.9.1. Sections

By sections we understand a group of instructions providing a comprehensive support for displaying various types of lists. They are strongly automated, intuitive and allow to achieve a lot without much effort. In many other template engines, similar solutions often depend on the data format and moreover, the template designer must connect the nested lists manually. In this place we must make it clear: if you do not use sections in OPT, you do not use this library properly. In this chapter we would like to provide a complete explanation of this topic then. The chapter contains the information common for all of the section types. The details and differences are explained in the particular section type reference.

Section types

There is a group of instructions that may be called sections. They share the common section API, but are used to display different types of lists. In OPT 2, we have the following section instructions:

  1. opt:section - the basic sections to display flat lists.
  2. opt:selector - they display flat lists, but allow to define more than one list element layouts that are chosen dynamically depending on the element settings.
  3. opt:tree - sections to display hierarchical lists (tree data).
  4. opt:grid - displays the data in a grid M x N elements.

However, because they share the basic API, they can cooperate one with another.

Section structure

On the template side, the section consists of the opening and closing tag. Between them, we define the layout of the single list element. The details of this process depend on the section type. For example, to display a hierarchical list, the opt:tree instruction requires more information than opt:section needs to display a flat list. Below, you can find an example of a simple section:

<opt:section name="section">
 
    <p>{$section.var1}, {$section.var2}</p>
 
</opt:section>

opt:section displays the elements linearly, one after another. The code above informs that the elements must be displayed as HTML paragraphs. Each section has its own name that should be unique (there are some exceptions from this rule, however this will be explained later). As we see, using the section name, we can access the list element variables: $section.var, $section.var2. The template above will work for each list whose elements contain two variables: var1 and var2.

The section name must not be read from template variables: <opt:section name="$block">.

The sections must take the list data somehow, but we haven't even defined any datasource variable. It is not a mistake. We can always define the location with the optional datasource attribute, but OPT goes further. Our section has already its own unique name: section, so it would be nice if it read the data from the variable $section. In other words, if the datasource is not specified, the section uses its own name to find the data. The two sections below do exactly the same thing:

<opt:section name="section">
    ....
</opt:section>
 
<opt:section name="section" datasource="$section">
 
</opt:section>

Let's take a look at the element variables: $section.var1 and $section.var2. OPT gives us here a couple of freedom. In practice, the section name does not have to be the first element of such container. We are allowed to follow it with any number of identifiers and the compiler will simply ignore them: $foo.bar.joe.section.var1. Once the section name is found in such call, the next identifier points to the element variable, and later, the identifiers works exactly like in typical containers. If $section.var1 be a container with three elements: foo, goo and hoo, we would access them like that:

<opt:section name="section">
    <p>{$section.var1.foo}</p>
    <p>{$section.var1.goo}</p>
    <p>{$section.var1.hoo}</p>
</opt:section>

Of course, if the variable $foo.bar.joe does not contain any of active section names, it is parsed as a normal variable or container.

The section elements do not have to be containers, but also simple scalar values:

<opt:section name="section">
    <p>Here, the elements are simple, scalar values: {$section}</p>
</opt:section>

Data formats

Of course, if the section operates on some real data, they must have a format. Usually, the template engines require you to deal with it on the template side, which is not convenient and decreases the portability. As you have probably noticed, we have said nothing about the formats in the already presented examples. This is one of the unique section features - they are fully format-independent. It is only the programmer and the PHP script issue and the template designer does not have to know, what the data really are. Notice that such code is much easier to read and portable. We can simply copy huge parts of one template, paste them in totally different place and they will simply adapt to the new location.

The format independence is one of the most important section features. As a template designer, you only mark, how should some elements containing some kind of data look like. The exact implementation is chosen during the compilation according to the information provided by the PHP script.

Note that sections have nothing to do with the actual iteration process over the data structure. They do not put any constraints on the data structure type, element indexation or order resolving which are treated as an internal issue of the used data format. In the templates, you should never make assumptions about the sections that rely on such technical details.

Nested sections

The sections can be nested one in another. Suppose we want to display a list of categories, and each of them must contain some sample products. The code below solves this problem:

<opt:section name="categories">
    <p>Category: {$categories.name}</p>
    <p>Featured products:</p>
    <ul>
 
    <opt:section name="products">
        <li>{$products.name}</li>
    </opt:section>
 
    </ul>
</opt:section>

OPT assumes that if we nest one section in another, their data are connected with an one-to-many relationship. The compiler automatically establishes a connection between the categories and products using the data format rules.

If the default behavior does not satisfy us, OPT provides several management tools. The most important is the parent attribute which allows to choose the parent section for the one-to-many relationship. The asterisk value * means the section is not related to any upper-level ones. Below, we can find a more complex version of the last example. Now we have some discounts for each category, but we do not want to display them above the product list. Instead, the discounts should be repeated for each product, because we want to create a links that allow to check the new price etc. So, the discount section must be located in products, but connected with a relationship to categories.

<opt:section name="categories">
    <p>Category: {$categories.name}</p>
    <p>Featured products:</p>
    <ul>
 
    <opt:section name="products">
        <li><p>{$products.name}</p>
            <p>Discounts: 
            <opt:section name="discounts" parent="categories" str:separator=", ">
                <a parse:href="'discount.php?did='~$discounts.id~'&amp;prodid='~$products.id">Discount  {$discount.name}</a>
            </opt:section>
            </p>
        </li>
    </opt:section>
    </ul>
</opt:section>

This example also shows that in the nested sections we still have the full access to the upper-level section data.

To sum up, the following codes do exactly the same:

Version 1:
<opt:section name="categories">
    <p>Category: {$categories.name}</p>
    <p>Featured products:</p>
    <ul>
 
    <opt:section name="products">
        <li>{$products.name}</li>
    </opt:section>
 
    </ul>
</opt:section>
Version 2:
<opt:section name="categories">
    <p>Category: {$categories.name}</p>
    <p>Featured products:</p>
    <ul>
 
    <opt:section name="products" parent="categories">
        <li>{$products.name}</li>
    </opt:section>
 
    </ul>
</opt:section>

If you use the datasource attribute, such section must not be connected with a relationship to any other upper-level section. The parent attribute is ignored then. Of course, you can always create such relationship manually, by specifying one of the upper section element variables as a datasource, but we do not guarantee you this will always work correctly. It will decrease the code portability and partially, get you dependent on the data formats.

<opt:section name="upper">
    <opt:section name="lower" datasource="$upper.someVariable">
        ...
    </opt:section>
</opt:section>

We strongly do not recommend using such code.

Section attributes

Below, you can find a list of section tag attributes:

Name Type (values) DEscription
name hard string The section name. It is the only required attribute,
parent hard string The name of the upper-level and active section to create an one-to-many relationship. The asterisk * means "no relationship". By default, the sections are connected to the parent section.
datasource expression Specifies the data source for the current section. If we use this attribute, parent is ignored.
order asc, desc Specifies the element display order. The desc displays the elements in the descending order. Not all of the sections support it. In this case this attribute is ignored.
display expression An optional expression that forces to display the alternative section content message, if evaluated to true.
separator expression The separator that will be put between every two list elements. More about separators.

Alternative section content

If the section contains no elements, we can specify the alternative content to display. In the section content we put the additional tag opt:sectiontypeelse (the exact name depends on the used section type), where we define the message:

<opt:section name="categories">
    <p>Category: {$categories.name}</p>
 
    <opt:sectionelse>
        <p>We are sorry but there are no categories in the system.</p>
    </opt:sectionelse>
</opt:section>

opt:show instruction

OPT provides more complex way to support empty sections. It is the opt:show instruction:

<opt:show name="categories">
    <ul>
    <opt:section>
        <li>{$categories.name}</li>
    </opt:section>
    </ul>
 
    <opt:showelse>
        <p>We are sorry but there are no categories in the system.</p>
    </opt:showelse>
</opt:show>

In the example above, opt:show allows us not to display empty <ul> tags if there are no categories in the system. The detailed information on this instruction can be found on its documentation page. Here we will only give you the most basic rules.

All the section attributes must be put in opt:show and the section tag must not contain them, because they are imported automatically to it. If the section tag contains them, OPT does not bind it with the opt:show. Suppose we have a list of products and if the products are displayed, we want to show also some bookmarks:

<opt:show name="products">
    <p>Bookmarks: <opt:section name="bookmarks" str:separator=", "/>{$bookmarks.name}</opt:section></p>
 
    <ul>
    <opt:section>
        <li>{$products.name}</li>
    </opt:section>
    </ul>
</opt:show>

The first opt:section has attributes, so it is not bound to the opt:show. The second opt:section tag is empty and OPT imports the attributes from opt:show right here.

The opt:show instruction does not start a section. The access to the section elements like $products.name is possible only within opt:section. It is also not possible to connect bookmarks and products with a relationship in the example above.

You must not define part of the attributes in opt:show and then add some extra ones in opt:section or similar.

Conditional sections

Sometimes we may want to show the section under a particular condition. OPT offers two ways to achieve this. The first one is the display attribute. If it is evaluated to true, the section shows the alternative content message even if it actually contains some items. To disable the section completely, we must use opt:if:

<opt:section name="foo" opt:if="$someCondition">
    Section content
</opt:section>

$sys special variable extensions

Using the $sys.section.sectionName special variable, you can get some extra information about the specified section. The available values are:

  1. $sys.section.sectionName.count - returns the number of the elements in the section.
  2. $sys.section.sectionName.size - returns the current section element size.
  3. $sys.section.sectionName.iterator - the current element index (warning: it may vary among various data formats!)
  4. $sys.section.sectionName.first - true if the current element is the first section element.
  5. $sys.section.sectionName.last - true if the current element is the last section element.
  6. $sys.section.sectionName.extreme - true if the current element is the first or the last section element.

Some section types might define also additional values.

Remember that you must avoid attempts to modify any of the special variables above. Some of them are not in fact real variables, whereas others may cause unexpected behavior, when modified.

See also:

3.9. Topics
3.9.2. Blocks
3.9.1. Sections
« Previous
3.9.3. Components
Next »

3.9.2. Blocks

Writing the ordinary instructions is a bit complicated task in OPT, especially if you have to implement a simple task. Here, the blocks come into action. They consist of two parts:

  1. PHP objects that implement Opt_Block_Interface - here the programmer may simply define all the logic he needs.
  2. A template port (opt:block instruction) which uses the specified interface to render the block.

To show, how they work, let's create a simple block class in PHP:

<?php
class columnBlock implements Opt_Block_Interface
{
    private $_view;
    private $_order;
    private $_active;
 
    public function __construct($colId = null, $order = null, $active = null)
    {
        $this->_colId = $colId;
        $this->_order = $order;
        $this->_active = $active;
    } // end __construct();
 
    public function setView(Opt_View $view)
    {
        $this->_view = $view;
    } // end setOptInstance();
 
    public function onOpen(Array $attributes)
    {
        $colId = (isset($attributes['column']) ? $attributes['column'] : $this->_colId;
        $order = (isset($attributes['order']) ? $attributes['order'] : $this->_order;
        $active = (isset($attributes['active']) ? $attributes['active'] : $this->_active;
 
        echo '<a href="?column='.$colId.'&order='.$order.'"'.($active ? ' class="active"' : '').'>';
    } // end onOpen();
 
    public function onClose()
    {
        echo '</a>';
    } // end onClose();
 
    public function onSingle(Array $attributes)
    {
        /* null */
    } // end onSingle();
} // end columnBlock;

Such block class can be used to create clickable column headers in a table to choose the sort order etc.:

$view->columns = array(0 =>
    array('title' => 'Column 1', 'click' => new columnBlock(1, 0, true)),
    array('title' => 'Column 2', 'click' => new columnBlock(2, 0, false)),
    array('title' => 'Column 3', 'click' => new columnBlock(3, 0, false)),
);

Now we can create a dynamic table header which also very clean:

<table>
    <thead>
        <opt:section name="columns">
        <th><opt:block from="$columns.block">{$columns.title}</opt:block></th>
        </opt:section> 
    </thead>
    <tbody>
        ....
    </tbody>
</table>

With the instruction opt:block, we have created a port where we can deploy any block object. Of course the example above is very simplified. In real world applications, it would be good to add more complicated logic to determine the columns automatically or to integrate with other script interfaces.

Statically deployed blocks

In the previous example, we were using both PHP and templates to create a fully functionable block. However, you do not have to create every block object manually in the script. OPT allows you to register block classes as XML tags:

$tpl->register(Opt_Class::OPT_BLOCK, 'opt:column', 'columnBlock');

Now we can use them much like instructions:

<table>
    <thead>
        <th><opt:column column="1">Column 1</opt:column></th>
        <th><opt:column column="2">Column 2</opt:column></th>
        <th><opt:column column="3">Column 3</opt:column></th>
    </thead>
    <tbody>
        ...
    </tbody>
</table>

In this case, OPT creates a block object automatically and destroys it after the job is finished.

Block functionality

The advantage over the instructions is that the blocks are processed at runtime, so your script may dynamically choose the blocks that need to be displayed. However, they have many limitations. The blocks are not able to manipulate the XML tree, they cannot be also used to create loops. However, they may display their content conditionally. The onOpen() method must return true or false then.

OPT provides also more compound objects: components. The basic idea behind them is the same, but they offer much more options that are useful when working with HTML forms.

See also:

3.9. Topics
3.9.3. Components
3.9.2. Blocks
« Previous
3.9.4. Template modularization
Next »

3.9.3. Components

The OPT instructions have one disadvantage that is quite significant in some tasks. They are processed during the compilation, performing some manipulations to the output code. However, sometimes we might need to let the script control, what to show. In the previous chapter we have met blocks, simple PHP objects that can be run transparently in ports, predefined places in a template. Now it is time for components. You can think of them like blocks with extended functionality. In fact the way they work and their design is very similar to them, so if you have not read about blocks yet, please refer to the previous chapter, because here we assume that you are familiar with them.

Component overview

Like in blocks, components are PHP objects that share the same interface that allows them to communicate with the templates. They can be deployed in the templates in the places called component ports. The port may either deploy an object passed and initialized by the script, or create such object on its own.

The primary goal for components is to support form management. We can find here many features useful in building dynamic, easy-to-write and easy-to-use forms:

Component structure

We are going to show the component API features using an example. The sample component is going to display the <input> form field and provide some extra logic, like:

Let's begin with a component port. Like blocks, you can register component classes as XML tags, so we are going to register our component class as opt:myInput. In order to deploy it statically, we do:

<opt:myInput datasource="$fieldData">
    <div opt:component-attributes="main">
        <p>{$sys.component.title} <span opt:if="$sys.component.description">{$sys.component.description}</span></p>
        <opt:display />
 
        <opt:onEvent name="error">
            <p class="error">{$sys.component.errorMessage}</p>
        </opt:onEvent>
    </div>
</opt:myInput>

The component is active within the opt:myInput tags that define its neighborhood. In our case, we specify here the detailed layout of a form field, including the error reporting, field description and layout manipulations.

  1. Notice that the <div> tag is equipped with the opt:component-attributes attribute. Thus, it allows the component to add dynamically extra attributes. In our case, the component is going to set the proper CSS class, depending on the field state (valid, invalid, etc.).
  2. Using the $sys.component we can read various component attributes. Here, we use them to specify the field title, description and ID, assuming that the script provides them in the $fieldData container declared as a data source.
  3. The tag <opt:display> defines a place, where the exact field should appear. We are allowed to add here some extra attributes. They will be passed to the component.
  4. <opt:onEvent> tag captures various component events. In our case, the component generates only one event: error. It tells the template engine that the error message should appear under the field.

Now we are ready to write a component PHP class that makes use of all those features. It must implement the Opt_Component_Interface which requires several methods. Our component is not going to have any advanced logic. It will simply read all the necessary information from the data source, but remember that you have the whole power of PHP here. Your components can be a part of the form validation engine, so that you would not have to initialize them with lots of data.

<?php
class myInputComponent implements Opt_Component_Interface
{
    private $view;
    private $name = '';
    private $valid = true;
    private $params = array();
 
    public function __construct($name = '')
    {
        $this->name = $name;
    } // end __construct();
 
    public function setView(Opt_View $view)
    {
        $this->view = $view;
    } // end setOptInstance();

For user convenience, we allow to specify the name in the class constructor. Later, we have to implement setView() method. It is called by the template to pass the view object to the component. Using it, we can also recognize, when the object is being deployed in the template.

Now we must implement the component parameter support:

    public function setDatasource($data)
    {
        foreach($data as $name => $value)
        {
            $this->set($name, $value);
        }
    } // end setDatasource();
 
    public function set($name, $value)
    {
        switch($name)
        {
            case 'name':
                $this->name = $value;
                break;
            case 'valid':
                $this->valid = $value;
                break;
            default:
                $this->params[$name] = $value;
        }
    } // end set();
 
    public function get($name)
    {
        if($name == 'name')
        {
            return $this->name;
        }
        if(isset($this->params['_'.$name]))
        {
            return $this->params['_'.$name];
        }
        return $this->params[$name];
    } // end get();
 
    public function defined($name)
    {
        return isset($this->params[$name]);
    } // end defined();

The set() and get() methods are used to manage single parameters, whereas setDatasource() is programmed to treat the specified data source as a set of parameters for the components. Of course, you might modify the meaning of the "datasource" term to suit your needs.

    public function manageAttributes($nodeName, $attributeList)
    {
        if($nodeName == 'div#default' && !$this->valid)
        {
            $attributeList['class'] = 'error';
        }
        return $attributeList;
    } // end manageAttributes();

Using the manageAttributes() method, the component can manipulate the attribute lists of all the tags equipped with opt:component-attributes attribute. The value of this attribute is added to the tag name and concatenated with # symbol. Here, we want to add/modify the class attribute in case of error.

The code that displays the component must be placed in the display() methods, where the template can also pass some attributes:

    public function display($attributes = array())
    {
        $attributes['type'] = 'text';
        $attributes['name'] = $this->name;
        $attributes['id'] = $this->name.'_id';
 
        if(!$this->valid)
        {
            $attributes['value'] = htmlspecialchars($_POST[$this->name]);
        }
        elseif(isset($this->params['value']))
        {
            $attributes['value'] = htmlspecialchars($this->params['value']);
        }
 
        echo '<input';
        foreach($attributes as $name=>$value)
        {
            echo ' '.$name.'="'.$value.'"';
        }
        echo '/>';
    } // end display();

In the example implementation, we can see, what the components can do. In our case, if the component flag valid is set to false, the field value is read directly from the POST data, so neither the script nor the template has to deal with it.

The last thing is event processing. In the template port we may define one or more opt:onEvent tags. The port asks the deployed component, whether the specified event occurred and if the component answer is positive, the code associated to the event is displayed. In addition, the component may perform some extra actions, such as initializing new template variables. Our component is going to support only one event: error. It will be fired, if the flag valid is set to false. The component will pass the error message to the template then.

    public function processEvent($event)
    {
        if($event == 'error')
        {
            if(!$this->valid)
            {
                $this->_view->errorMessage = (isset($this->params['errorMessage']) ? $this->params['errorMessage'] : 'No error message specified');
                return true;    // Fire the event!
            }
        }
        return false;
    } // end processEvent();
} // end myInputComponent;

Our component is ready. However, currently it can be deployed only in the default opt:component port. If you want to make this component deployed statically, you have to register the component class in OPT:

$tpl->register(Opt_Class::OPT_COMPONENT, 'opt:myInput', 'myInputComponent');

You do not have to choose the opt namespace, but also use your own. In this case, please remember that the new namespace must be registered, too!

Component port overview

Here we are going to take a deeper look at the component port features.

Ports

We have two types of ports:

<!-- custom port -->
<opt:component from="$variable">
    ...
</opt:component>
 
<!-- static port -->
<opt:someComponent>
    ...
</opt:someComponent>

The first port loads the components from the template variable (or any other expression) specified in the from attribute, so you can pass different components, whenever you want, depending on your needs. On the other hand, the static port automatically creates the component object and deploys it there.

If your component requires extra manual configuration, remember that you have to configure it in the template, when you deploy it statically.

A nice trick about custom ports is that the components can be loaded from the section. This is the easiest way to create a form generator - you create different components that represent the form fields such as inputs, selects, text areas etc. and put their objects in the section:

<opt:section name="dynamicForm">
    <opt:component from="$dynamicForm.component">
        ....
    </opt:component>
</opt:section>

The content of the port represents the component layout. In case of HTML forms, this definition includes some container like <div> or table row, field title and description, as well as the place, where the errors should be displayed. Within the port, you have the full access to the component parameters and other stuff. However, specifying the layout for each component used in the presentation layer can be a frustrating, especially if you need to change something later. Fortunately, the components co-operate with snippets (opt:snippet). You may define the component layout in one place and use it later everywhere:

<opt:snippet name="componentLayout">
    <div opt:component-attributes="default">
        <p><label parse:for="$system.component.id">{$system.component.title}</label></p>
 
        <opt:display />
 
        <opt:onEvent name="error">
            <p>An error occurred: {$errorMessage}</p>
        </opt:onEvent>
    </div>
</opt:snippet>
 
<!-- now we can build a nice HTML form: -->
<form method="post" action="#">
 
<forms:input template="componentLayout" />
<forms:textarea template="componentLayout" />
<forms:combo template="componentLayout" />
 
<input type="submit" value="Send" />
</form>

Note that the components load the snippets with the template attribute, not opt:use. The reason is explained below.

Parameters

The component API provides the support for the component parameters. They can be accessed using the special block $system.component.parameterName, and created either by the script or by the template. In the templates, you can set the parameter to the component with the opt:set tag placed directly in the port tag:

<opt:component from="$component">
    <opt:set str:name="title" str:value="Some title" />
</opt:component>

The custom port tag attributes are also interpreted as component parameters.

Let's get back to the form example from the previous chapter. The reason why we are using the template attribute instead of opt:use in the ports is related to the component parameters. As you should know, opt:use removes the tag body, if it finds the specified snippet. However, in case of components we would not like to remove opt:set tags and template remembers about them:

<!-- this code will work -->
<forms:input str:name="name" template="componentLayout">
    <opt:set str:name="title" str:value="Your name" />
    <opt:set str:name="description" str:value="Please enter your name" />
</forms:input>
 
<!-- this code will not -->
<forms:input str:name="name" opt:use="componentLayout">
    <opt:set str:name="title" str:value="Your name" />
    <opt:set str:name="description" str:value="Please enter your name" />
</forms:input>

Data source

The bigger data sets can be loaded from the data sources. You may specify a data source for the component with the datasource attribute:

<opt:component datasource="$list">
    ...
</opt:component>

The exact meaning of data source depends on the components you are going to use.

Displaying the component

The port represents the overall component layout, but the component itself must be displayed somewhere, too. To mark the place to show a component, use the opt:display tag:

<forms:input>
    <div opt:component-attributes="default">
        <p><label parse:for="$system.component.id">{$system.component.title}</label></p>
 
        <!-- show the INPUT field here -->
        <opt:display />
 
        <opt:onEvent name="error">
            <p>An error occurred: {$errorMessage}</p>
        </opt:onEvent>
    </div>
</forms:input>

Note that opt:display can accept optional attributes that are passed to the component. This can be used to configure the look of the component:

<opt:display str:class="someCSSClass" />

Events

The component may generate also various events which can be captured by the component port. If an event occurs, we can display some extra content. A common use is error handling:

<opt:onEvent name="error">
    <p>An error occurred: {$errorMessage}</p>
</opt:onEvent>

The name specifies the name of the event we want to capture. Note that you can capture events that the component object does not support. In this case it should not fire them, however please note that the exact component implementations may not follow this suggestion.

Attribute management

The components can also manipulate the attributes of various HTML tags. In order to allow such manipulations for the specified tag, you have to add the opt:component-attributes to it and define an identifier. A common use is to configure the CSS class of the field container, like <div>:

<forms:input>
    <div class="defaultCSSClass" opt:component-attributes="default">
        ...
    </div>
</forms:input>

Now the component can easily change the CSS class associated to <div>. Please note that you may set the com namespace to more than one tag within one port.

Open Power Template 2.0 provides also the second system of attribute management. The same effect can be achieved, if we move the <div> tag to the special com namespace, for example com:div. However, in this case we are not able to give the tag an unique identifier, so the manageAttributes() method cannot distinguish two <div>-s within the same component. The opt:component-attributes attribute has been introduced in OPT 2.0.2 and we recommend to use it since then.

Conclusion

Components are a very powerful tool designed especially for the form processing. If you do not need the functionality they offer, please use blocks.

See also:

3.9. Topics
3.9.4. Template modularization
3.9.3. Components
« Previous
3.9.4.1. Template inclusion
Next »

3.9.4. Template modularization

Every bigger website consists of dozens or even hundreds template files. It is obvious that they have to be managed somehow and the common elements must not be duplicated. Open Power Template provides several tools to make your templates more modular. In this chapter we are going to introduce and explain them.

Problem

Let's take a look a simple website with some news, articles and contact data. Each module has its own template, because the news look different than articles, whereas contact data are usually accompanied by a form that allows to send us an e-mail directly from the website. However, no matter what module we are looking at, they are always displayed within the same layout, with a header, menu, place for the main content and the footer. Moreover, as our database grows, we should think of a pagination mechanism - the page list should also look the same on whole website. The same issue concerns the forms - we need a generic form component design used every time we need to construct the form so that all the fors share the same layout and logics. If we need to make a change, it would be nice to make it only in one place and recompile the templates to propagate it rather than modifying about 50 files one by one.

To sum up, we need the following things:

Solution 1 - template concatenation

Many template engines, as well as PHP, allow you to build the output document from smaller pieces of HTML code using the following algorithm:

  1. Display the page header and the beginning of the menus.
  2. Display all the menus.
  3. Display the end of the menus and the beginning of the content.
  4. Display the content.
  5. etc...

This leads us to the following templates:

overall_header.tpl:
<html>
<head>
    ...
</head>
<body>
<div id="header">
    ...
</div>
<div id="menu">
menu.tpl:
    <ul>
        <li><a href="#">Index</a></li>
        ....
    </ul>
content_begin.tpl:
</div>
<div id="content">
index.tpl:
    Some index content here.
overall_footer.tpl:
</div>
<div id="footer">
    ...
</div>
</body>
</html>

Of course, it is possible to build a valid HTML output from them, but note that they are hard to maintain. You have to define in the PHP code that content_begin.tpl must be executed after the menus are rendered and the menus are before the content. What if the webmaster is going to create a layout with a different structure? What if you are going to add new elements to the layout? Are you going to modify both the script and the templates, producing more and more files? This solution is not only ineffective, but does not provide any visible logic. In other words - it is very bad.

Solution 2 - template inclusion

This time, we are going to locate the main layout structure in one file. It will contain some places, where the script could plug-in module-dependent content, menus etc.

<html>
<head>
    ....
</head>
<body>
<div id="header">
    ...
</div>
<div id="menu">
    [ LAUNCH ASSIGNED MENU TEMPLATES HERE ]
</div>
<div id="content">
    [ LAUNCH ASSIGNED CONTENT TEMPLATES HERE ]
</div>
<div id="footer">
    ...
</div>
</body>
</html>

Now the HTML structure is kept in only one place. If you are going to add some extra common elements to it, you have to modify only one file - the template, whereas the script has nothing to do here. Its task is only to define, what templates must be launched in the first and the second placeholder and the template engine must provide some mechanisms to execute them.

Solution 3 - template inheritance

Template inheritance works much like inheritance in the object-oriented programming, but it applies to templates instead of classes. Here, the script does not even know about the common layout file - it only executes the module template which extends the layout template it wants and fills the empty placeholders with some HTML code. Take a look at sample templates:

layout.tpl:
<html>
<head>
    ....
</head>
<body>
<div id="header">
    ...
</div>
<div id="menu">
    ...
</div>
<div id="content">
    [ CONTENT PLACEHOLDER ]
</div>
<div id="footer">
    ...
</div>
</body>
</html>
module.tpl:
[ EXTEND layout.tpl ]
 
[ CONTENT SNIPPET ]
Some content here
[ END OF CONTENT SNIPPET ]

The template engine notices that our module template wants to extend layout.tpl, so it takes the content defined by module.tpl and puts it in the specified place in the layout template, producing a valid output.

Open Power Template-related issues

So far, we have described the basic modularization techniques on the examples written in pseudocode that do not apply to any real template engine. It is time to explain, how they work in OPT and how to use them in this particular library.

If you are going to make use of all the XML features offered by OPT, you have to forget about the solution 1 (template concatenation). In the XML mode, the standard HTTP output prevents you from rendering more than one template directly. For example, the following PHP code will cause an exception:

$output = new Opt_Output_Http;
$output->render('template1.tpl');
$output->render('template2.tpl'); // exception! This output has already rendered a template!

The reason is very simple. In this mode, the templates must be valid XML files with the tags enclosed in the correct order. In other words, you must not open <body> in template1.tpl and close it in template2.tpl. Moreover, the output should also be a valid document, and valid XML documents have only one root tag. This leads us to simple conclusion. If the opened tags must be closed within the same template and the produced output must have exactly one root tag, there might be only one main template.

It is not possible to execute more than one main template in the XML mode using the HTTP output.

OPT provides a complex and built-in support for template inclusion and inheritance. To include one template within another, you use opt:include instruction. The inheritance is done with a set of the following instructions: opt:extend, opt:root, opt:snippet and opt:insert. Both of them can be used within the same project and it is a recommended way to work with OPT. Note that opt:include works during the execution time, so by connection it with sections, you might create a loop that is able to load any number of templates in the same place, depending on the script needs. On the other hand, the inheritance is processed during the compilation time. Although dynamic inheritance is still allowed (the script may choose the template to be extended), it is harder to display two module templates at the same time in the same placeholder.

opt:include works during the template execution, whereas the template inheritance is processed during the compilation.

The details on the implementation of those two elements can be found in chapters about the inheritance and the inclusion.

Mixing the template inheritance and inclusion

As we mentioned before, OPT allows you to use both inheritance and inclusion at the same time. This is the recommended way to work with this library. opt:include instruction should be used to create the main layout template:

<html>
<head>
    ....
</head>
<body>
<div id="header">
    ...
</div>
<div id="menu">
    <!-- display all the menu views here -->
    <opt:section name="menuViews">
        <opt:include from="menuViews" />
    </opt:section>
</div>
<div id="content">
    <!-- display all the content views here -->
    <opt:section name="contentViews">
        <opt:include from="contentViews" />
    </opt:section>
</div>
<div id="footer">
    ...
</div>
</body>
</html>

The inheritance can be used in at the module template level. It is especially useful when working with HTML forms. It is a common situation that the same form is used all around the website, but sometimes you need to add one or two extra components. Assuming this, we would like to use the same form component style for all our forms with the possibility to change it if needed.

Let's get to work. First, we start with two sample templates that define the component layout:

standardComponentLayout.tpl:
<opt:root>
    <opt:snippet name="componentLayout">
        <div class="form element">
            <opt:display /> <!-- a component element -->
            <p>{$opt.component.title}</p>
        </div>
    </opt:snippet>
</opt:root>
otherComponentLayout.tpl:
<opt:root>
    <opt:snippet name="componentLayout">
        <div class="other classes">
            <span>{$opt.component.title}</span>
            <opt:display /> <!-- a component element -->
        </div>
    </opt:snippet>
</opt:root>

Now, we need the basic form template:

some_form.tpl:
<opt:root include="standardComponentLayout.tpl" dynamic="yes">
    <form method="post" parse:action="$form.action">
    <div class="form">
        <opt:insert snippet="initialExtraComponents" />
 
        <opt:inputField template="componentLayout">
            <opt:set name="name" str:value="field1" />
            <opt:set name="title" str:value="Some field" />
        </opt:inputField>
 
        <opt:inputField template="componentLayout">
            <opt:set name="name" str:value="field2" />
            <opt:set name="title" str:value="Some other field" />
        </opt:inputField>
 
        <opt:insert snippet="finalExtraComponents" />
 
        <opt:insert snippet="actionButtons">
        <div class="buttons">
            <input type="submit" value="Send" />
        </div>
        </opt:insert>
    </div>
    </form>
</opt:root>

Here we assume that the script contains the implementations of the inputField components that we are using. Note that this form defines three placeholders for snippets: initialExtraComponents, finalExtraComponents and actionButtons. If the module displays only the basic form, these placeholders will remain empty, and in case of the last one - the default content will be used. The result is that the user sees only the basic form.

If we need to reuse the same form in other place, but with extra components, we just extend the basic template:

some_form_modified.tpl:
<opt:extend file="some_form.tpl">
    <opt:snippet name="finalExtraComponents">
 
        <opt:inputField template="componentLayout">
            <opt:set name="name" str:value="field3" />
            <opt:set name="title" str:value="Yet another field" />
        </opt:inputField>
 
    </opt:snippet>
</opt:extend>

If the module decides to use this particular template, the user will see a form with three components:

  1. Some field
  2. Some other field
  3. Yet another field (added by the some_form_modified.tpl template)

Note that using the Opt_View::inherit() API method, the script may also set the different form component layout.

Conclusion

The template modularization is a very important issue and OPT provides several advanced tools that should satisfy all your needs. The details concerning their features, possibilities and limitations can be found in the next pages of this documentation.

See also:

3.9.4. Template modularization
3.9.4.1. Template inclusion
3.9.4. Template modularization
« Previous
3.9.4.2. Template inheritance
Next »

3.9.4.1. Template inclusion

The template inclusion is a way to organize and modularize your templates in OPT. It uses opt:include instruction to include one template within another. What is important, the new template is loaded during the execution stage, so the file name may be loaded from a variable or even section, creating a powerful tool.

Note that OPT uses the idea of views on the script side. A view is a template with the data associated to it and is implemented as an PHP object. opt:include follows this concept and also operates on views.

Basic use of opt:include

Using opt:include is very simple. To include a left menu content in the following example, we must only specify the file name.

<div id="menu">
    <opt:include file="left_menu.tpl" />
</div>

However, this code is not fully completed. As we mentioned earlier, opt:include operates on views. In this particular case, OPT creates a new view for our template, but does not assign any data to it. In other words, your script has no way to assign the values to local variables in left_menu.tpl and they remain empty. There are two ways to solve this problem.

  1. Pass the variable values as extra parameters.
  2. Use the import attribute.
<!-- pass the variables manually -->
<div id="menu">
    <opt:include file="left_menu.tpl" variable1="$someValue" variable2="calculateMe()" />
</div>
 
<!-- import all the variables from the current view -->
<div id="menu">
    <opt:include file="left_menu.tpl" import="yes" />
</div>

Loading the templates dynamically

In most cases, we would like to add more dynamics to our templates. We wish to choose the template to be included depending on our needs. The following code allows us to do this.

<div id="menu">
    <opt:include parse:file="$leftMenuTemplate" import="yes" />
</div>

We had to change the namespace of the file attribute to parse to notify OPT that the value we want to specify is not a string, but whole expression with variables etc. However, in this case we also create a new view from scratch and the rules for passing variable to it introduced in the previous part are still correct.

Loading views

So far, our views have been created on the template side in the background. However, sometimes we would like to prepare them on the script side and just execute them from another template. This is also possible with opt:include:

<div id="menu">
    <opt:include view="$leftMenuView" />
</div>

Here, the script has a full control over the variables passed to the left menu view, as it created it. But if you need, you may still pass some extra variables with the attributes or using import (the imported variables are added to the view and do not overwrite them).

OT offers you a fast way to load the views from the section:

<div id="menu">
    <opt:section name="menuViewContainer">
        <opt:include from="menuViewContainer" />
    </opt:section>
</div>

opt:include assumes that the view object can be found at $menuViewContainer.view section variable.

Organizing your templates with opt:include

We are going to show you the ideas of a complete and ready-to-use solution for working with opt:include. We assume that the modules of our website create their own view objects. The script provides them a special API that allows them to assign the particular view to the specified placeholder. It is very easy to write - just a two-dimensional array and a function/method:

<?php
class placeholderManager
{
    private $_views = array();
 
    public function definePlaceholder($placeholder)
    {
        if(!isset($this->_views[$placeholder]))
        {
            $this->_views[$placeholder] = array();
        }
    } // end definePlaceholder();
 
    public function addView($placeholder, Opt_View $view)
    {
        if(!isset($this->_views[$placeholder]))
        {
            // error!
        }
        // The standard format of OPT sections.
        $this->_views[$placeholder][] = array('view' => $view);
    } // end addView();
 
    public function register(Opt_View $mainView)
    {
        foreach($this->_views as $name => $viewList)
        {
            $mainView->assign($name.'Views', $viewList);
        }
    } // end register();
} // end placeholderManager;

Now we are going to prepare the main template:

<html>
<head>
    ...
</head>
<body>
<div id="header">
    ...
</div>
<div id="menu">
    <!-- display all the menu views here -->
    <opt:section name="menuViews">
        <opt:include from="menuViews" />
    </opt:section>
</div>
<div id="content">
    <!-- display all the content views here -->
    <opt:section name="contentViews">
        <opt:include from="contentViews" />
    </opt:section>
</div>
<div id="footer">
    ...
</div>
</body>
</html>

Of course we can define as many placeholders, as we like. Moreover, the placeholders defined in the template do not have to be defined in the script. In this case, the sections will simply show nothing and the placeholder - remain empty. If you like to provide the support for placeholders also in the module templates, consider registering the placeholder data as global variables.

Now we have a complete solution. The modules create a view object, when executed, associate data and register it in our placeholder manager. The script passes them to the templates and OPT displays them in the right place.

See also:

3.9.4. Template modularization
3.9.4.2. Template inheritance
3.9.4.1. Template inclusion
« Previous
3.9.5. Quirks mode
Next »

3.9.4.2. Template inheritance

Template inheritance is a relatively new concept of modularizing the templates. It offers you another way to compose the output document from several template files and is very similar to the concepts that can be found in the object-oriented programming. In this chapter we are going to describe the implementation of template inheritance in Open Power Template.

General concepts

Below, you can find a picture that illustrates how the inheritance really works.

Template inheritance in OPT

The picture shows the situation with two templates. The first one, called base template, defines the structure of the output code and leaves spare placeholders. The placeholders may be filled with the default content. If we used the base template directly, we would see no absolutely no difference, when compared to the case without the placeholders. However, the things change if we run the template inheritance. Here, another template (called main template), extends the base template and defines the snippets with a new content for the placeholders. After the compilation, we get a complete output. The extended placeholders are filled with the new content from main template, whereas the rest of the placeholders and the output code structure is taken from the base template.

If you already know object-oriented programming, you should see many familiar issues here. The snippets can be treated as methods, and the template as a normal class.

Template inheritance in OPT

Template inheritance can be processed at the two different stages: the compilation and the execution. Both of them have their advantages and disadvantages. OPT features the first model. The compiler produces only one file with the inherited templates already concatenated. The benefits of this model are:

The disadvantage is that the name of the extended template must be known before the compilation and cannot be changed later. In other words, we cannot load the name from a variable or generate dynamically. Fortunately, OPT is free of these drawbacks and in spite of processing the inheritance during the compilation, the overall process can be still dynamic and your script may choose the template to be inherited at any moment.

The template inheritance chain may be as long as you want. You may create a template that extends a template that extends another template. Moreover, we do not need to extend a physical template file, because OPT supports also extending snippets.

Template inheritance in practice

So far, we have been introduced the theory of OPT inheritance model and we are now ready to see, how to use it in real templates. The inheritance uses the two basic instructions for copying a part of the source template into another place: opt:snippet and opt:insert. First, let's build the base template base.tpl:

<opt:root>
<html>
<head>
    <title>My website - {$title}</title>
</head>
<body>
    <div id="header">
        <opt:insert snippet="header">
        <h1>My website</h1>
        </opt:insert>
    </div>
    <div id="content">
        <opt:insert snippet="content">
        <p>Sorry, no content defined.</p>
        </opt:insert>
    </div>
    <div id="footer">
        <p>Copyright You {range(2009)}</p>
    </div>
</body>
</html>
</opt:root>

This template defines two placeholders: header and content, so we can overwrite the standard header and content. The news module may fill them with custom content in the template news.tpl:

<opt:extend file="base.tpl">
    <opt:snippet name="content">
        <opt:insert snippet="hello_text" />
        <h1>News</h1>
        <div class="news" parse:id="'e'~$news.id" opt:section="news">
            <h1>{$news.title}</h1>
            <p>{$news.body}</p>
        </div>
    </opt:snippet>
</opt:extend>

Once we compile and execute this template, we should get something like this:

<html>
<head>
    <title>My website - News</title>
</head>
<body>
    <div id="header">
        <h1>My website</h1>
    </div>
    <div id="content">
        <h1>News</h1>
        <div class="news" id="e2">
            <h1>New articles</h1>
            <p>Today, the new articles have been published. We encourage you
            to read them, because they are really brilliant!</p>
        </div>
        <div class="news" id="e1">
            <h1>Hello world!</h1>
            <p>Welcome to my new website!</p>
        </div>
    </div>
    <div id="footer">
        <p>Copyright You 2009</p>
    </div>
</body>
</html>

However, please notice that news.tpl defines another placeholder in the snippet, hello_text. Let's think, how we could possibly use it. So far, we can create a template for the news module, but now we want to publish them also on the index page. Suppose that you wish to display some form of invitation with the website users above the news list, but you do not want it to appear, if the user visited the news module directly. To achieve this, we may extend news.tpl with index.tpl and define the content for hello_text placeholder. Moreover, we will modify the header a bit:

<opt:extend file="news.tpl">
    <opt:snippet name="hello_text">
        <p>Welcome to my website!</p>
    </opt:snippet>
 
    <opt:snippet name="header">
        <h1>My website</h1>
        <p>Hi everyone!</p>
    </opt:snippet>
</opt:extend>

Now, if the user reads the news through the index page, he or she should see the following content:

<html>
<head>
    <title>My website - News</title>
</head>
<body>
    <div id="header">
        <h1>My website</h1>
        <p>Hi everyone!</p>
    </div>
    <div id="content">
        <p>Welcome to my website!</p>
        <h1>News</h1>
        <div class="news" id="e2">
            <h1>New articles</h1>
            <p>Today, the new articles have been published. We encourage you
            to read them, because they are really brilliant!</p>
        </div>
        <div class="news" id="e1">
            <h1>Hello world!</h1>
            <p>Welcome to my new website!</p>
        </div>
    </div>
    <div id="footer">
        <p>Copyright You 2009</p>
    </div>
</body>
</html>

Remember that you can modify any template in the inheritance chain at any time. The templates that depend on it will notice the change automatically and recompile.

Overwriting the snippets

Suppose that the author of news.tpl template forgot to create a placeholder hello_text in it, he went off on a long holiday and left us alone. We would rather not touch his code, but still want to display the introductory text. Don't worry, there is still a way to do this. Our index.tpl may overwrite the snippet content from the news.tpl file, but its content is not lost. Using opt:parent tag, we can still access it:

<opt:extend file="news.tpl">
    <opt:snippet name="content">
        <p>Welcome to my website!</p>
        <opt:parent />
    </opt:snippet>
 
    <opt:snippet name="header">
        <h1>My website</h1>
        <p>Hi everyone!</p>
    </opt:snippet>
</opt:extend>

The effect will be exactly the same. What if the content snippet is not overwritten? There are two possible behaviors. If the placeholder does not define a default content, opt:parent does nothing. Otherwise, OPT inserts the default placeholder content on opt:parent. Sometimes, this is not a behavior we would expect, so we can disable it in the placeholder:

<opt:insert snippet="content" ignoredefault="yes">
<p>The default content is not accessible with opt:parent anymore.</p>
</opt:insert>

Dynamic inheritance

As we mentioned in the introduction, OPT provides a dynamic inheritance and the script may freely choose at any moment of time, what our template must extend. There are two mechanisms: branches and custom selection.

Branches

Branches are simply alternative inheritance chains that templates may define. The templates still decide, what template files they use, but the script may choose now from more than one offered case. Let's get back to our example with news.tpl. We can display it on the main website, but our users ask for a printable, low-resource version. Branches are the perfect tool to achieve this effect. We need another base template; this time it will be optimized for printer devices, print.tpl:

<opt:root>
<html>
<head>
    <title>My website - {$title}</title>
</head>
<body>
    <h1>My website - {$title}</h1>
 
    <opt:insert snippet="content">
    <p>Sorry, no content defined.</p>
    </opt:insert>
</body>
</html>
</opt:root>

Then, we teach news.tpl, how to use it:

<opt:extend file="base.tpl" print="print.tpl">
    <opt:snippet name="content">
        <opt:insert snippet="hello_text" />
        <h1>News</h1>
        <div class="news" parse:id="'e '~$news.id" opt:section="news">
            <h1>{$news.title}</h1>
            <p>{$news.body}</p>
        </div>
    </opt:snippet>
</opt:extend>

We have defined the second inheritance branch called print. If the script decides to use it, our news will appear in the more printer-friendly environment. In the complex inheritance chains, there may occur a situation where the script requests a branch, but the template does not define a file for it. In this case, OPT follows the file pointed in the file attribute.

Custom selection

In custom selection, we let the script to choose the template we inherit for us. The template side does not require much effort:

<opt:extend file="base.tpl" dynamic="yes">
    <opt:snippet name="content">
        <opt:insert snippet="hello_text" />
        <h1>News</h1>
        <div class="news" parse:id="'e'~$news.id" opt:section="news">
            <h1>{$news.title}</h1>
            <p>{$news.body}</p>
        </div>
    </opt:snippet>
</opt:extend>

By adding the dynamic attribute, we allow the script to manipulate the extended template for us. However, if the script does not use this feature, OPT will follow to the default file pointed by file attribute.

Extending snippets

Sometimes we do not want to extend a particular template, but rather leave this as an optional choice to the programmer. In this case, we do not need to define a separate template with the default structure, because opt:extend may also extend already existing snippets. Below, you can find a sample:

<opt:extend file="base_form" dynamic="yes">
    <!-- the form definition (using components) -->
    <opt:snippet name="form_body">
        <opt:input str:name="first_name" template="form_item_layout">
            <opt:set str:name="title" str:value="First name" />
        </opt:input>
        <opt:input str:name="last_name" template="form_item_layout">
            <opt:set str:name="title" str:value="Last name" />
        </opt:input>
    </opt:form>
 
    <!-- the default structure for displaying the form -->
    <opt:snippet name="base_form">
        <form method="post" action="somewhere.php">
            <opt:insert snippet="form_body" />        
            <div class="buttons"><input type="submit" value="Send" /></div>
        </form>
    </opt:snippet>
</opt:extend>

Such file is very flexible. If we do not want to use the template inheritance, it behaves like an ordinary file and uses the snippet base_form as a base template without the need to move it to a separate file. However, the script may choose a custom base template thanks to the presence of dynamic attribute.

Inheritance and HTML escaping

Like opt:root, opt:extend also allows to specify the default HTML escaping policy for the template. In this case, the snippets defined in this template keep these setting in other templates. The example is shown in the opt:extend reference.

opt:root and inclusion

Sometimes we also want opt:root instruction in the base template to load some snippets. They may define the default layouts for the form fields or the pagination and it would be nice to load them without using the template inheritance, which could be a bit tricky and inconvenient. In this case we may employ the include attribute:

<opt:root include="form_snippets.tpl">
<html>
<head>
    <title>My website - {$title}</title>
</head>
<body>
    <div id="header">
        <opt:insert snippet="header">
        <h1>My website</h1>
        </opt:insert>
    </div>
    <div id="content">
        <opt:insert snippet="content">
        <p>Sorry, no content defined.</p>
        </opt:insert>
    </div>
    <div id="footer">
        <p>Copyright You {range(2009)}</p>
    </div>
</body>
</html>
</opt:root>

If a certain snippet is defined both in the inheriting template and included by include attribute from the base template, the snippets from the inheriting template always overwrite the second one.

Limitations and resource usage

The template inheritance is not a good choice, when we want to display the content of more than one module in the page content, as it does not have real loops. Of course, this could be achieved by dynamic inheritance and lengthening the inheritance chain, but we must be also aware of the resource usage. During the compilation, OPT must load all these template structures to the memory and process it. Very long chains may require huge amounts of memory and time to compile to store all the snippets and put them in the placeholders. If it reaches the PHP resource limits, the script will obviously crash before finishing its job.

Conclusion

OPT provides a powerful template inheritance system. It can be used alone, or combined with the modularization techniques described in the previous chapter featuring opt:include instruction. We especially recommend it to deal with HTML forms, because it offers a really advanced and flexible way to manage them.

See also:

3.9. Topics
3.9.5. Quirks mode
3.9.4.2. Template inheritance
« Previous
4. Programmer's Guide
Next »

3.9.5. Quirks mode

Quirks mode is a special template parsing mode for the documents that do not have the XML document structure. It resembles the XML compatibility mode from Open Power Template 1.x. In this mode, everything that is not an OPT instruction or expression is treated as a plain text.

Activation

The quirks mode can be enabled globally by setting the mode configuration option:

$tpl->mode = Opt_Class::QUIRKS_MODE;

It can be also enabled for a single view:

$view = new Opt_View('template.tpl');
$view->setMode(Opt_Class::QUIRKS_MODE);

Usage

In quirks mode, only the OPT instructions and expressions are parsed and the compiler does not check anything else except entities. Below you can see a sample template:

<html>
<title>Foo</title>
<p {$class}>This is a text
<ol>
<opt:section name="foo">
<li>{$foo.item}
</opt:section>
</ol>
</HTML>

The ordinary HTML tags are not validated, so you do not have to close them properly. On the other hand, the OPT tags still must be enclosed in the valid order. Moreover, the expressions in curly brackets may be used as attribute sources for the tags. Moreover, the output systems allow to concatenate the output document from the smaller pieces.

Limitations

The quirks mode brings some limitations:

  1. No XML/HTML error control.
  2. OPT attributes cannot be used with HTML tags.
  3. You cannot use any features that are supposed to co-operate with the ordinary (non-OPT) tags in the document.

When to use the quirks mode?

The quirks mode has been designed for processing the non-XML documents without the need to use a separate template engine for them. This includes for example SQL, CSV or plain text files. It is not recommended to use it with HTML and (especially) XHTML, as it may lead to serious problems in the future.

Table of Contents
4. Programmer's Guide
3.9.5. Quirks mode
« Previous
4.1. Initialization
Next »

4. Programmer's Guide

The Programmer's Guide for OPT shows, how to use Open Power Template 2 library from the script-side. You can read these chapters from start to finish to get to know dozens of practical and helpful issues. Before we start, let us introduce the main concepts of OPT Application Programming Interface.

The structure of OPT

Open Power Template 2 is a part of Open Power Libs 2 family (currently its only quite mature member). In order to provide a friendly cooperation between the libraries, they share a common core that provides the basic interfaces and features, such as autoloading, plugin system and error handling. When we talk about Open Power Template 2 API source code, we always mean the source code together with the OPL core.

It does not mean that you have to visit the Invenzzia website again and download another package. The core is already included in the installation package of OPT and all you have to do is to copy the files to your project directory structure.

In this guide, we will sometimes refer to the OPL core, explaining OPT-specific issues and the basic usage, however for the detailed information, you should also read the OPL User Manual.

How does OPT work?

Open Power Template 2 is a template engine that provides its own templating language instead of using plain PHP. Because of the performance reasons, the XML templates are not parsed and executed directly by OPT. Instead, the library uses a small trick. If it notices that a certain template is used for the first time or it has been modified, it compiles it into plain and simple PHP script and saves the result on the hard drive. Then, the library simply includes the compiled file and lets the PHP parser do the rest.

The compilation requires a significant amount of work to be done, so the library consists of two, partially independent parts:

  1. The front-end - manages the script data and executes PHP templates.
  2. The compiler - compiles the XML templates to the PHP code.

The front-end is quite small, and moreover - OPL core automatically loads most of the code on demand, so that your script is not overloaded by the template compiler or exception handler if it is not used.

System requirements

As it was said in the installation chapter, Open Power Template 2 was written for PHP 5.3. However, because this version has not been released yet, the OPL core provides a compatibility layer for PHP 5.2 that adds the missing classes and interfaces. It is automatically loaded, if the system requirements are not satisfied. Of course, the compatibility layer generates a small slowdown, but there is nothing to worry about. The extra classes and interfaces are used by the template compiler, not the front-end.

OPT versus other template engines

The standalone template engines available on the Internet, like Smarty, usually have a simple Application Programming Interface that consists of one general-purpose class. It is very simple in use, especially in smaller projects or for people that are not familiar with the object-oriented programming, but causes many problems if we are going to work with frameworks and bigger applications. On the other side, we have the template engines that are parts of popular frameworks, with nice, objective design. The OPT API resembles the framework solutions, providing several specialized classes that perform certain tasks and must be combined together in order to execute a template. If you are familiar with the first concept, this may be a bit confusing to you at the beginning, but once you master it, you will see that it is much more intuitive and powerful.

4. Programmer's Guide
4.1. Initialization
4. Programmer's Guide
« Previous
4.1.1. List of configuration options
Next »

4.1. Initialization

The first thing we have to do is to initialize and configure our script in order to use OPT.

Autoloader

We start with setting up the autoloader which must be included manually. You can find it in Opl/Base.php file - it is a part of the OPL core:

<?php
require('../vendor/opl/Opl/Base.php');
Opl_Loader::setDirectory('../vendor/opl/');
Opl_Loader::register();

OPT does not force you to use include_path. With Opl_Loader::setDirectory() method, we specify the path to the OPL library directories. Finally, we register the autoloader in PHP. The OPL core provides a very flexible mechanism of loading the files that allows you to choose different locations for each library and even a file:

<?php
require('../vendor/Opl/Base.php');
Opl_Loader::mapLibrary('Opl', '../vendor/Opl/');
Opl_Loader::mapLibrary('Opt', '../anotherDirectory/Opt/');
Opl_Loader::register();

While this feature is rather useless in most of the script, as we usually want to keep similar pieces of the code in the same place, we might find it necessary if we wish to use PHAR-s.

Once we have registered the autoloader, we might also configure the options that affect all the OPL libraries:

Opl_Registry::setState('opl_debug_console', true);
Opl_Registry::setState('opl_extended_errors', true);

The first line enables the debug console. In this mode, each HTTP request will generate an extra pop-up window providing some information about OPT, like the executed templates, their execution time, the current configuration or estimated memory usage during the template compilation. The second option is very useful in the development environment, because it causes the default exception handler to show all the possible information about the exception and system state that helps to find the problem.

Do not forget to disable those two options on the production server!

Creating the main OPT class

Once our script knows, how to load OPL code, we are going to initialize Open Power Template. It is also a quite easy task. All we have to do is to create an object of Opt_Class class and set some configuration directives. They are available as public class variables:

$tpl = new Opt_Class;
$tpl->sourceDir = './templates/';
$tpl->compileDir = './templates_c/';

The complete list of configuration options can be found in List of configuration options. Note that you can also load the configuration from an array or INI file with loadConfig() method:

$tpl = new Opt_Class;
 
// Loading from INI file.
$tpl->loadConfig('./opt.ini');
 
// Loading from array
$cfg = array(
   'sourceDir' => './templates/',
   'compileDir' => './templates_c/'
);
$tpl->loadConfig($cfg);

The external configuration source does not have to set all the directives. Feel free to mix both of the configuration sources in one script.

Registering additional stuff

Although OPT comes bundled with a wide variety of different template instructions and functions, you will probably want to write custom system-specific ones. This is the right place to do it. You can either use plugins (see Plugins) or register the new stuff manually using Opt_Class::register() method. The details, how to register different items can be found in Extending OPT.

Finalizing the initialization

To finish the initialization, we call:

$tpl->setup();

This method is quite necessary, because it must prepare some data in order to make the rest of the script fully functional. Never forget to call it!

4.1. Initialization
4.1.1. List of configuration options
4.1. Initialization
« Previous
4.2. Working with views
Next »

4.1.1. List of configuration options

Below, you can find a list of the configuration options available in OPT.

Paths and directories

sourceDir
The path to the directory with the XML templates. It must be readable by the script.
The associative list of paths and their identifiers that contain XML templates. They must be readable by the script.
compileDir
The path to the directory with the compiled PHP templates. The script must have a write access to it.
pluginDir
The path to the directory that contains OPT plugins.
The list of paths to the directories with OPT plugins.
pluginDataDir
The path to the directory, where the plugin engine may store some internal information to speed up the plugin loading. You can set it to the same directory as compileDir.

Front-end options

compileMode
Opt_Class::CM_DEFAULT - the templates are recompiled only if they have been modified. Recommended for the development environment.
Opt_Class::CM_PERFORMANCE - the templates are never recompiled. Recommended for the production environment with the assumption that we will not be modifying the templates directly on the server.
Opt_Class::CM_REBUILD - the templates are recompiled every request. Recommended for the OPT instruction developers.
charset
The default charset used by Opt_Output_Http::setContentType(). Default value: utf-8
contentType
The default content type used by Opt_Output_Http::setContentType(). Default value: Opt_Output_Http::XHTML.
gzipCompression
True to enable the output compression in Opt_Output_Http output system. You need to have the zlib extension installed in order to use this feature. Please note that it is recommended to enable the compression directly in the HTTP server, not in the script. Default value: true
headerBuffering
True to buffer the HTTP headers by Opt_Output_Http and send them just before the HTML code. This allows to overwrite the previously set header. Default value: false
contentNegotiation
True to enable content negotiation for Opt_Output_Http::setContentType(). You need to have the Open Power Classes library installed in order to use this feature.
errorReporting
The default error reporting during the template execution. After the template is executed, OPT restores the previous script settings. Default value: E_ALL^E_NOTICE
stdStream
The default identifier of the sourceDir path used, if the identifier is not specified explicitly. Default value: file.
debugConsole
True to enable the debug console for OPT. Remember to disable this option in your production environment! Default value: false
allowRelativePaths
Controls whether the relative paths are available as template names. This is disabled by default.
Available since OPT 2.0.1.

Compiler options

mode
Opt_Class::CHOOSE_MODE - the compilation mode is chosen by the output system.
Opt_Class::XML_MODE - the templates are compiled in the XML mode.
Opt_Class::QUIRKS_MODE - the templates are compiled in the quirks mode.
unicodeNames
Whether to allow the Unicode characters in the XML tags. Default to false due to the performance issues.
htmlAttributes
Whether to allow the shortened form of attributes, for example <tag attribute> as a synonym to <tag attribute="attribute">. Default to false.
printComments
Whether to print the template comments in the output. Default to false.
prologRequired
Whether the XML prolog is required. The default setting, true means that the output prolog must be generated with opt:prolog instruction. Otherwise, the prologs are checked for the valid syntax and rewritten to the output.
stripWhitespaces
Whether to strip the unnecessary white spaces from the template. Default to true.
singleRootNode
Whether a single root node is obligatory. Default to true.
basicOOP
Whether the use of OOP in OPT expressions is allowed. Default to true
advancedOOP
Whether the templates can create objects and clone them. Default to true.
backticks
A callback to the backtick handler.
NULL, if the backticks are not supported.
strictCallbacks
If set to true (the default setting), OPT strictly checks the callbacks during the compilation to ensure that the requested functions really exist.
escape
Whether to escape the displayed expression values by default. Default to true.
variableAccess
Opt_Class::ACCESS_LOCAL - the template variables are local. The access to global variables is possible via $global. This is the default setting.
Opt_Class::ACCESS_GLOBAL - the template variables are global by default. The access to local variables is possible via $this.
defaultFormat
The default data format used by OPT.

Misc. options

compileId
Allows to add an extra identifier to the compiled template name.
moneyFormat
The default money format used by the money() function.
numberDecimals
The default setting for the first argument of number() function.
numberDecPoint
The default setting for the second argument of number() function.
numberThousandSep
The default setting for the third argument of number() function.
pluralForms
The plural form configuration used by pluralize() function.
4. Programmer's Guide
4.2. Working with views
4.1.1. List of configuration options
« Previous
4.3. Working with output systems
Next »

4.2. Working with views

In Open Power Template, the script works on views. A view can be seen as a template with some script data and settings associated to it. Your website may create several views during the HTTP request processing. For example, you will create a separate view for a template that defines the overall website layout, and another view for the currently loaded content.

Creating views

To create a view, you simply create a new object of Opt_View class:

$view = new Opt_View('some_template.tpl');

In the constructor, you can provide the template name. OPT will look for this template in the directory specified by the sourceDir directive. If you do not want or do not know the template name, you can always set it later with setTemplate() method:

$view = new Opt_View;
$view->setTemplate('some_template.tpl');

Template paths

In the Opt_View constructor we specify the template file name which is stored in the directory pointed by the sourceDir option. The template name may contain some subdirectories, if they are present in the sourceDir directory:

$tpl->sourceDir = './templates/';
 
// Gives './templates/template.tpl'
$view = new Opt_View('template.tpl');
 
// Gives './templates/subdirectory/template.tpl'
$view = new Opt_View('subdirectory/template.tpl');

Note that you must not follow the template name with a slash, because it is automatically added to the sourceDir paths.

OPT supports PHP streams, allowing to register multiple template paths within a sourceDir option. Each path must be given an unique name and the default one should be called file. We select the requested path in the view constructor by following the template name with the path name and a colon:

$tpl->sourceDir = array(
    'file' => './templates/',
    'database' => 'db://database/'  // a sample database stream handler
);
 
// File from the disk
$view = new Opt_View('file.tpl');
 
// Another file from the disk
$view = new Opt_View('file:file.tpl');
 
// File from the database
$view = new Opt_View('database:template.tpl');

The detailed requirements for template names depend on the selected PHP stream. For more information about streams, please visit the corresponding chapter in the PHP manual.

In Open Power Template 2.1 template path handling will be programmable via inflectors. They will allow to handle more complex path resolving rules that are very common in a modular software (i.e. CMS-es).

Assigning the data

In order to show the results of the script with a template engine, we need to assign them to at least one view. They automatically become template variables. Consider the following example. We have a template:

<h1>{$page.title}</h1>
 
{$page.content}

Assigning the data to the template variables is very easy:

$view = new Opt_View('my_template.tpl');
$view->page = array(
    'title' => 'Some title',
    'content' => 'Some content'
);

The Opt_View class provides several different methods to assign the data to a view. They allow for example to assign a variable by reference or to extract an associative array as a list of variables:

$bigText = 'This is a very long and big text.';
 
$view->assignRef('content', $bigText);
$view->assignGroup(array(
    'foo' => 'abc',
    'bar' => 'def',
    'joe' => 'ghi'
));

By default, the template variables are local. This means that our view does not see the variables of other views and vice versa. Moreover, different views can use the same variable name to represent different data:

$view1 = new Opt_View('template1.tpl');
$view2 = new Opt_View('template2.tpl');
 
$view1->variable = 'Foo';
$view2->variable = 'Bar';

However, sometimes this is not exactly what we want. Perhaps you would like to create a container of global settings that must be accessible in all the view templates. Fortunately, the global variables are supported, too:

<p>Local variable: {$variable}</p>
<p>Global variable: {$global.variable}</p>

On the script-side, we can create global template variables, using the static methods from Opt_View:

$view = new Opt_View('template.tpl');
$view->variable = 'Foo';
Opt_View::assignGlobal('variable', 'Bar');

Basically speaking, the names of the methods that affect global variables are ended with the word Global and all the local variable methods have their equivalents for global variables.

Managing the view data

The Opt_View class provides also some methods to manage the data that have been already assigned to the view. We can always check, whether the variable was defined:

if($view->defined('variable'))
{
    echo 'The variable has been defined!';
}
if(isset($view->variable))
{
    echo 'The variable has been defined!';
}

It is also possible to remove an existing variable with remove() method or read its current value with read().

Creating views for the website

Now it's time for the practice. As we previously said, your website will be probably composed of more than one view. It is very important to organize the views in a clear way in order not to get lost. Below, we provide the description of the recommended template structure for a website:

  1. In the file layout.tpl we will keep the structure of our HTML code, with HEAD section, page header, footer, menus and a place for the content.
  2. Different modules of the website will have one or more templates that fill the place for the content with the code they want to show. For example, the news module will display the list of news, and the contact page - a contact form.
  3. The module views will be assigned as the template variables to the layout views and included with opt:include instruction.

This is the layout.tpl file:

<?xml version="1.0" ?>
<opt:root>
<opt:prolog />
<opt:dtd template="xhtml10transitional" />
<html>
<head>
    <title>{$title}</title>
    <!-- some meta tags, CSS styles etc. -->
</head>
<body>
<div id="header">
    <h1>{$global.website.name}</h1>
</div>
<div id="content">
    <opt:include view="$module">
        <p>We are sorry, but we are unable to load the template.</p>
    </opt:include>
</div>
<div id="footer">
    <p>&copy; You {range('2009')}</p>
</div>
</body>
</html>
</opt:root>

And this is one of our modules (news.tpl):

<?xml version="1.0" ?>
<opt:root>
    <h1>News</h1>
 
    <opt:section name="news">
    <div class="news" parse:id="'e '~$news.id">
        <h2>{$news.title}</h2>
 
        <p>{$news.body}</p>
    </div>
    </opt:section>
</opt:root>

OPT template syntax and XML standard compliance is very flexible. In this example, we assume that you are working with the default, and the most restrictive set of rules. They force you to keep only one root tag in the template and to add an XML prolog to them. Please note that OPT does not send those prologs to the browser. Instead, it uses opt:prolog instruction to generate it.

The following piece of code creates the suitable views and initializes them with the data:

<?php
// Initialize OPT here
 
$moduleView = new Opt_View('news.tpl');
// Let's assume that the news are loaded from the ORM.
$moduleView->news = ORM::selectNews();
 
$layoutView = new Opt_View('layout.tpl');
$layoutView->title = 'Index';
 
// Assign the module view to the layout
$layoutView->module = $moduleView;
 
// Add some global data for the templates.
Opt_View::assignGlobal('website', array(
    'title' => 'My Website',
    'address' => 'http://www.example.com/'
));

Conclusion

This chapter showed, how OPT represents the templates on the script-side and how to manage the views. The careful users have probably noticed that simple creation of a view object does not mean that the template associated with it will be executed and sent to the user browser. In order to perform this task, we need another type of objects, called output systems. Their idea is explained in the next chapter.

4. Programmer's Guide
4.3. Working with output systems
4.2. Working with views
« Previous
4.4. Error handling
Next »

4.3. Working with output systems

The OPT views introduced in the previous chapter are not able to execute themselves automatically. Our application must be able to determine, when to execute them and what to do with the results. In many template engines, this is a part of the main class and the destination place is chosen with different methods or their arguments and it has nothing to do with object-oriented programming. On the other hand, Open Power Template operates a term output system.

Output system is a class that is able to execute the views and decides, where to send the generated HTML code.

The OPT package provides two default output systems: HTTP and Return.

Rendering the views

Let's back to the example from the previous chapter. We have created two view objects: $layoutView and $moduleView assigned to the first one. In order to render them and show something to the user, we have to create an output system object:

$output = new Opt_Output_Http;
$output->render($layoutView);

With this simple code, we have executed all the templates and the results have been sent to the browser. Now other people can visit our website. Note that we do not render $moduleView. This is not a mistake. The module view is automatically executed by the opt:include instruction used in layout.tpl file and processed with exactly the same output system. It does not mean that $moduleView suddenly starts to see the local variables of $layoutView unless explicitly specified in opt:include instruction.

You must render the top-level view only.

Working with standard output systems

Now we are going to show the features of the standard output systems provided by PHP.

Opt_Output_Http

This output sends the generated result to the user browser. It provides several extra features:

  1. Additional XML validity control
  2. HTTP Header management
  3. Content-negotiation

Let's take a look at the first point. Every bigger website is composed of more than one XML templates. They have to be nested one in another, because you cannot open an XML tag in one template and close it in another file, as it would break the XML rules. This leads us to the conclusion that our script has to have only one root template that defines the basic output HTML code structure. The HTTP output system must assure that we do not try to execute more than one top-level view, because this would automatically mean that we try to create an invalid XML output from valid templates. Check out the following code: you will notice that it will generate an exception:

$output->render($view);
$output->render($view);

The conclusion is simple. The Opt_Output_Http::render() method can be called only once, for only one view object.

Opt_Output_Http class helps us also with managing HTTP headers. In most cases, you will need to set the Content-type header together with the encoding information:

$output = new Opt_Output_Http;
$output->setContentType(Opt_Output_Http::XHTML, 'utf-8');
$output->render($view);

OPT provides a few predefined constants that define the most popular content-types, but you can also specify any other content type manually: setContentType('text/plain'). The predefined content types are:

  1. Opt_Output_Http::XHTML - application/xhtml+xml or text/html, depending on the user browser.
  2. Opt_Output_Http::HTML - text/html.
  3. Opt_Output_Http::FORCED_XHTML - force application/xhtml+xml always, if the browser supports it.
  4. Opt_Output_Http::WML - text/vnd.wap.wml
  5. Opt_Output_Http::XML - application/xml
  6. Opt_Output_Http::TXT - text/plain

By default, OPT checks the XHTML document content type accessibility only. The full content negotiation is available, when Open Power Template cooperates with Open Power Classes package. Unfortunately, it is at the early development stage and you need to wait for a while before it will be accessible.

Opt_Output_Return

This output system returns the generated code as a result of render() method. You can save it to a variable and use somehow in your script:

$output = new Opt_Output_Return;
 
$content = $output->render($view);

This output system does not have any limitations on the number of render() calls. The same output system instance can execute many OPT view objects.

4. Programmer's Guide
4.4. Error handling
4.3. Working with output systems
« Previous
4.5. Working with sections
Next »

4.4. Error handling

Another aspect of Open Power Template 2 that needs to be described is the error handling. The library provides a significant number of tools that help you discovering and eliminating problems. In this chapter, we will describe all of them.

OPT errors

OPT reports the errors as PHP exceptions. Each error type has its own exception class and all of them share the same base exception class, Opt_Exception, derivated from Opl_Exception. Moreover, the certain groups of exception have also their own base classes in the middle. The modularization helps the programmer to choose, which exceptions must be handled in the specified piece of code. To see the list of available exceptions, please read the Error messages appendix.

Basically speaking, capturing a thrown exception is nothing more but using the try ... catch construct:

try
{
    // The script that uses OPT goes here.
}
catch(Opt_Exception $exception)
{
    // The exception handling code goes here.
}

Default exception handler

A common practice is to let the programmer to write the exception handler that will handle information about the problem somehow. The application usually provides an exception handling system, for example some sort of log files etc. but the displayed messages are often very laconic and hard to understand, especially for the new library users. On the other hand, Open Power Template provides a standard error handler that aims to display as much information about the problem, as possible. It respects the debugging settings and does not display any potentially dangerous pieces of information in the production mode, whereas in the development mode its diagnosis may be very helpful.

OPT error handler at work

In the picture, we can see that OPT error handler displayed the exception message and the place where the exception occurred. Moreover, it noticed that the exception concerns template compilation and sections and provided some extra data:

  1. The name of the template that caused the problem.
  2. The current stack list that allows us to notice that we do not have a section named "foo" on the stack.
  3. The information explaining, why the exception probably occurred and where to look for the solution.

To use the exception handler, you must create an object of Opt_ErrorHandler class and call the display() method:

$handler = new Opt_ErrorHandler;
$handler->display($exception);

To respect the tradition and the OPT 1.x legacy, OPT 2 provides also a function Opt_ErrorHandler() that does exactly the same task (in fact, it is only a wrapper for the code above).

If the exception is thrown during the template execution or compilation, the compiler state is reseted, so that it could be usable again, and the exception handler removes the already printed output HTML from the buffer.

Extending the exception handler

You can easily extend the default exception handler with extra functionality by overriding the default Opt_ErrorHandler class. The OPL core provides the necessary framework to process the context exception information and all we have to do is to specify, what to display when a certain exception occurs.

The context information is stored in the protected variable $_context. It is automatically initialized by Opt_ErrorHandler, so if you do not want to loose this information, you have to fill it in the class constructor:

<?php
class My_ErrorHandler extends Opt_ErrorHandler
{
    public function __construct()
    {
        $this->_context['ExceptionName'] => array('The configuration');
    } // end __construct();
} // end My_ErrorHandler;

As you see, the $_context field is an associative array and the keys are exception class names. As a value, we provide another associative array with a list of informer that can display various data.

Informer is a special method in the OPL error handling utility that is able to display something in the exception report.

Below, you can find a sample configuration of the OPT exception shown on the screen above.

$this->_context['Opt_SectionNotExists_Exception'] = array(
    'TemplateInfo' => array(),
    'StackInfo' => array(1 => 'Actual section stack'),
    'ErrorInfo' => array(1 => 'The nested sections can be connected together with a relationship. By default,
        OPT tries to establish such relationship automatically, but you can modify the default behavior with
        the "parent" attribute. This exception occurs, because the template tries use the "parent" attribute
        to create a relationship to a section that does not exist. Check whether the specified section names
        are correct.')
    );

This exception is handled by three informers and two of them accept arguments. The arguments for an informer are provided as a list of values indexed from 1.

The list of informers provided by OPT and OPL can be found below:

  1. ErrorInfo - used to display some extra information about the exception.
  2. StackInfo - prints a stack that has been assigned to the particular exception. Our task is to give it a name.
  3. BasicConfiguration - prints the basic library configuration.
  4. Backtrace - prints the debug backtrace.
  5. TemplateInfo - shows the name of the currently compiled template.
  6. BugtrackerInfo - informs that the exception is caused by a bug in the library and it should be reported to the developers.

Writing your own informer is very easy. All you have to do is to create a protected method _printInformerName() with at least one argument (the first one must be always the exception we are displaying). We can display the text, using the basic echo subroutine:

    protected function _printMyInformer(Opt_Exception $exception, $argument)
    {
        echo 'I am the informer and I have an argument: '.$argument;
    } // end _printMyInformer();

You can now use it in your exception context definition:

$this->_context['Opt_Some_Exception'] = array(
    'MyInformer' => array(1 => 'Some argument')
);

PHP Bug #40479 problem

PHP Bug #40479 is a bug found in Zend Engine 2 memory management code in 2007. It seems to appear only in certain, complex scripts and causes the PHP parser to make a segmentation fault. It is very hard to track and so far, nobody has managed to create a simple test case that could give a clue, why it exactly occurs. Unfortunately, one of the OPT code parts is affected by the problem. If the data format parser tries to raise Opt_APIHookNotDefined_Exception, the script goes down. This is only a minor issue and unless you are writing your own data format, you should never encounter it, however we decided that in this particular case OPT will display the error message with a simple die() subroutine in order not to lead to a potential damage of your data.

Conclusion

Open Power Template 2 reports the errors with the exception system. You can handle them on your own or use the standard error handler provided by OPT. It is very helpful in the problem diagnosis, as it tries to show extra potentially useful information and tips, where to look for the solution.

4. Programmer's Guide
4.5. Working with sections
4.4. Error handling
« Previous
4.6. Data formats
Next »

4.5. Working with sections

Sections can be considered as smart loops. On the template side, they allow to avoid dealing with the implementation issues, such as iterating through the data structure, checking the existence of elements etc. More about section features can be found in the Sections chapter and here we are going to describe, how to use them on the script side.

Basic overview

Suppose we want to display a list of comments on our blog. We must specify, how a single comment should look like and where to display particular item variables:

<h2>Comments</h2>
<opt:show name="comments">
    <p>There are {$commentNum} comments.</p>
    <opt:section>
        <div class="comment">
            <p class="author">Written by {$comments.author} on {$comments.date}</p>
 
            {$comments.body}
        </div>
    </opt:section>
 
    <!-- the alternative content, if there are no comments -->
    <opt:showelse>Nobody has written a comment yet! Be first!</opt:showelse>
</opt:show>

To populate a section, we must create a template variable with the same name as the section:

$view = new Opt_View('comments.tpl');
 
$view->commentNum = 3;
$view->comments = array(0 =>
    array('author' => 'John', 'date' => 'Mar 13 2009', 'body' => 'Nice article!'),
    array('author' => 'Adam', 'date' => 'Mar 15 2009', 'body' => 'Thanks a lot, it was very helpful'),
    array('author' => 'Susan', 'date' => 'Mar 17 2009', 'body' => 'One small suggestion... '),
);

As you see, the variable comments is an array of arrays. Each sub-array represents the data of a single comment. If the main array is empty or we do not set the variable, the user will see Nobody has written a comment yet! Be first!

By default, the section items must be enumerated from 0 and the indices must not contain any holes, like 3, 4, 6. Using a database row index is a bad choice.

Relationships

Two nested sections can be connected with a one-to-many relationship. It means that their data somehow depend one by another:

<h1>Categories</h1>
 
<p>Below, you can find a list of the categories and the products associated with them.</p>
 
<opt:section name="categories">
    <h2>{$categories.name}</h2>
    <p>{$categories.description}</p>
    <opt:show name="products">
    <p>Products:</p>
    <ul>
        <li opt:section="products">{$products.name}</li>
    </ul>
    <opt:showelse>There are no products in this category.</opt:showelse>
    </opt:show>
</opt:section>

OPT automatically connects two nested sections with a relationship and we must provide proper data for them in our script. By default, it is done by creating two variables, one for each section and populating it separately:

$view = new Opt_View('categories.tpl');
$view->categories = array(0 =>
    array('name' => 'Fruit', 'description' => 'Our best fruit!'),
    array('name' => 'Vegetables', 'description' => 'Vegetables from the best farms.')
);
 
$view->products = array(0 =>
    // Products in "Fruit"
    array(0 =>
        array('name' => 'Apples'),
        array('name' => 'Pears'),
        array('name' => 'Bananas'),
    ),
    // Products in "Vegetables"
    array(0 =>
        array('name' => 'Tomatos'),
        array('name' => 'Onions'),
        array('name' => 'Carrots'),
    )
);

In products, we provide a separate list of products for each category listed in categories. The items are matched by the array indices.

Using opt:tree

OPT provides a special section instruction to render hierarchical data structures (trees), opt:tree:

<opt:tree name="tree">
    <opt:list><ul><opt:content /></ul></opt:list>
    <opt:node><li>{$tree.title} <opt:content /></li></opt:node>  
</opt:tree>

They are only a different type of sections and we can apply the same rules to them, as to the other types. However, opt:tree has one special requirement in order to work. We must provide somehow the data that describe, what element is nested in whom. We may achieve it by adding a special variable to the tree item, depth:

$view = new Opt_View('tree.tpl');
 
$view->tree = array(0 =>
    array('title' => 'Main category 1', 'depth' => 0),
    array('title' => 'Main category 2', 'depth' => 0),
    array('title' => 'Subcategory 2.1', 'depth' => 1),
    array('title' => 'Main category 3', 'depth' => 0),
    array('title' => 'Subcategory 3.1', 'depth' => 1),
    array('title' => 'Item 3.1.1', 'depth' => 2),
    array('title' => 'Item 3.1.2', 'depth' => 2),
    array('title' => 'Item 3.1.3', 'depth' => 2),
    array('title' => 'Subcategory 3.2', 'depth' => 1),
    array('title' => 'Subcategory 3.3', 'depth' => 1),
    array('title' => 'Item 3.3.1', 'depth' => 2),
    array('title' => 'Main category 4', 'depth' => 0),
    array('title' => 'Subcategory 4.1', 'depth' => 1),
    array('title' => 'Item 4.1.1', 'depth' => 2)
);

Now we get a tree with the items nested properly.

The tree checks the validity of the depth element items. Firstly, it determines the initial depth, taken from the first element. In the case above, the initial depth is 0. You may choose the other initial depths, such as 1 or 533, too. In the next elements, the depth must be greater or equal to the initial depth. If it becomes lower for some reason, the template would throw an exception.

Using opt:selector

opt:selector is a combination of ordinary sections and the switch statement. It allows to define several possible ways to render an item and automatically chooses between them. Let's take a look at the menu. We have there some entitled URL-s, but we want also to show horizontal lines between the groups of items, and moreover, one of the items must be marked as the currently viewed. opt:selector is the best choice for this task:

<div id="menu">
    <ul>
    <opt:selector name="menu">
        <opt:active><li class="active"><a parse:href="$menu.address">{$menu.title}</a></li></opt:active>
        <opt:horiz><li class="horiz">&nbsp;</li></opt:horiz>
        <opt:default><li><a parse:href="$menu.address">{$menu.title}</a></li></opt:default>
    </opt:selector> 
    </ul>
</div>

In order to populate a selector, we must provide an extra variable to each item that indicates the requested way to display it. It must match the name of one of OPT tags within the selector (without the namespace):

$view = new Opt_View('menu.tpl');
$view->menu = array(0 =>
    array('item' => 'default', 'title' => 'Index', 'address' => '#'),
    array('item' => 'active', 'title' => 'Login', 'address' => '#'),
    array('item' => 'default', 'title' => 'Register', 'address' => '#'),
    array('item' => 'horiz'),
    array('item' => 'default', 'title' => 'Articles', 'address' => '#'),
    array('item' => 'default', 'title' => 'Contact', 'address' => '#'),
);

Note that $menu.item does not have to be named item. You can control, what variable to take the item type from, using test attribute on the opt:selector attribute.

Are arrays the only way to populate a section?

No at all. In this chapter we have shown the default way of populating sections that uses ordinary arrays, but the sections are much more flexible. In the next chapters, you will meet the concept of data formats that allow the sections to support anything you want.

Conclusion

Sections are very flexible OPT constructs and we recommend to use them rather than opt:foreach or opt:for.

See also:

4. Programmer's Guide
4.6. Data formats
4.5. Working with sections
« Previous
4.6.1. Available data formats
Next »

4.6. Data formats

OPT introduces an unique feature called "data formats". Finally, you do not have to worry about the data structures while implementing a template. The idea is simple. In the template syntax we have a thing called generic containers: $variable, $container.item etc. We know, what they are supposed to do, but it is not said, what they are - arrays, objects or maybe something more. Before you start the template compilation, your script may choose the exact format for them. In other words, the script can tell that the container $foo is accessible like an object, and the code $foo.bar will be compiled as $this->_data['foo']->bar. The other container, $goo, may become an array, and then we will get $this->_data['goo']['hoo'] for $goo.hoo. This gives us some benefits. Let's consider some real world scenarios.

  1. We have some people responsible for the templates in our team. They wish to start writing templates for the article module, but the project designer and the programmers have not worked on this module yet and some of the solutions are a big unknown. With other template engines and PHP itself, the template designers may risk and write those files, implementing some default structures, but if the programmers decide to change something, the templates must be corrected as well. Such problem does not occur in OPT. The template designers may use generic containers and let the programmers to choose the exact format in the script. They can even create the templates while the project designers are still working on the code structure project.

  2. The template designers finished their job and completed the templates for the article module. However, the programmers work on another part of the website, and the customer wants to inspect the module layout and navigation. One of the programmers may write a demo version that fills the template with sample data put into the simple arrays. Once the module is written, the arrays may be replaced with the database calls, but still - the template source files remain the same, they just need to be recompiled.

  3. The programmers are doing the code refactoring and they have noticed an inconsequence in the data generated by the script. In some places, it makes a database call, retrieves the data, puts it to the template engine, but part of the templates does not use them. The decide to retrieve the data on demand, so they write a class that implements the Opt_Generator_Interface and bind it to the template containers. The templates themselves still remain untouched. All they have to do is to recompile them, and OPT will do the rest.

Using data formats

In order to demonstrate, how to use formats, we need a simple template:

<h1>{$article.title}</h1>
 
<p>{$article.body}</p>
 
<opt:if test="$user.read">
    <p class="userinfo">{$user.name}, you have already read this article</p>
<opt:else>
    <p class="userinfo">{$user.name}, you have not read this article yet. <a parse:href="'rate.php?uid='~$user.id">Rate it!</a></p>
</opt:else>
</opt:if>

This template has two generic containers: $article and $user. At this stage, we don't know, what they really are and in the script we can choose the exact format, depending on our needs:

class Article
{
    public $title;
    public $body;
 
    public function __construct($title, $body)
    {
        $this->title = $title;
        $this->body = $body;
    } // end __construct(); 
} // end Article;
 
$view = new Opt_View('article.tpl');
$view->article = new Article('Some article', 'Lorem ipsum dolor sit amet...');
$view->user = array(
    'id' => 53,
    'name' => 'Johnny',
    'read' => true, // he has already read this article
);
 
// Now, we choose the format
$view->setFormat('article', 'Objective');
 
$output->render($view);

If we do not specify any special format for a variable or container, the compiler assumes that they are PHP arrays. However, in our case the $article is neither an array nor it implements the ArrayAccess interface. Using the Opt_View::setFormat() we inform the compiler that $article is an object by choosing the Objective format for it.

More complex containers

OPT also provides the support for more complicated container calls, like $foo.bar.joe. $foo is an object, while $bar is an object that implements our own Awesome_Access_Interface (let's assume that we already have an OPT format implementation for it). To mark this fact, we may do the following thing:

// Access the 'foo' items as object calls
$view->setFormat('foo', 'Objective');
 
// Access the 'foo.bar' items with our awesome access interface
$view->setFormat('foo.bar', 'AwesomeAccess');

Sections

It can be said that sections are built on OPT data formats. We start from a sample template:

<p>Our customers:</p>
 
<ol>
    <opt:section name="customers">
    <li>{$customers.firstName} {$customers.lastName}</li>
    </opt:section>
</ol>

In case of sections, we can find two areas where the data formats may work:

  1. Section itself - how to iterate over the elements and how to access them. The format affects the <opt:section> and </opt:section> tags.
  2. Section elements - how to access the element variables, like $customers.firstName and $customers.lastName.

OPT provides the control over both of the areas. If the section iterates over an object that implements the Iterator interface, but the elements are arrays:

class Customers_Rowset implements IteratorAggregate
{
    private $_customers = array(0 =>
        array('firstName' => 'John', 'lastName' => 'Sawyer'),
        array('firstName' => 'Adam', 'lastName' => 'Brown'),
        array('firstName' => 'Jennifer', 'lastName' => 'Wells')
    );
 
    public function getIterator()
    {
        return ArrayIterator($this->_customers);
    } // end getIterator();
} // end Customers_Rowset;
 
$view->customers = new Customers_Rowset;

OPT formats implement the decorator pattern and this feature is used in sections. We specify the format for the section and decorate it with the section element format:

$view->setFormat('customers', 'Objective/Generic');

If both sections and their elements are of the same format, we can simply write Objective instead of Objective/Objective.

Nested sections are supported by specifying the format for each of them separately:

<opt:section name="categories">
<div class="category">
    <h3>{$categories.title}</h3>
 
    <ol>
    <opt:section name="products">
        <li>{$products.name}</li>
    </opt:section>
    </ol>
</opt:section>

The PHP code:

$view->setFormat('categories', 'Objective/SingleArray');
$view->setFormat('products', 'Objective');

The top section data are always taken from the variable buffer. Nested section data are treaded as one of the parent section elements, so the data for products will be retrieved using SingleArray format.

Note that sections do not put any assumptions on the element indexes, data structure type or element order resolving. It is a data format issue and depends on the chosen one. For example, for Array and SingleArray the section elements require element indexes to start with 0 and grow continuously. On the other hand, Objective accepts any kind of element indexes. The details can be found in the data format descriptions. Your templates should never assume that a particular index type is available. For any special uses, consider writing your own data format, which is descriped later.

Hiding the fact that a variable or section is actually global

Normally, we use the $global container to access the global variables. However, sometimes we would like to hide this fact, especially when it comes to sections. Since OPT 2.0.1, we can use Opt_View::setFormatGlobal() here:

// Sets a format for '$global.foo' in all the templates
Opt_View::setFormatGlobal('foo', 'Format');
 
// Sets a format for '$foo' in all the templates
Opt_View::setFormatGlobal('foo', 'Format', false);

Supported feature issue

Not all of the formats can be used everywhere. Each of them implements a particular set of features. For example, RuntimeGenerator works only with sections and cannot be used for section elements or generic containers.

4.6. Data formats
4.6.1. Available data formats
4.6. Data formats
« Previous
4.6.2. Formats and expressions
Next »

4.6.1. Available data formats

There are five default data formats available in OPT, and three features they may implement:

  1. Ordinary variables
  2. Sections
  3. Section elements

In this chapter, we will describe them and show, how they should be used.

Available formats

Array
The default format, implements variables, sections and section elements.
SingleArray
Very similar to Generic. The only difference is how the child section data are handled.
Objective
Implements variables, sections and section elements. Assumes that the data are objects that may implement one of the standard interfaces, like Iterator in case of sections.
StaticGenerator
The format that generates the data on the fly when they are really needed. It implements only sections.
RuntimeGenerator
Similar to StaticGenerator, but if no generator is provided, it assumes that the script assigned some static data for the sections.
SplDatastructure
Section format that iterates through SPL doubly linked list from PHP 5.3. It is available since OPT 2.0.5.

Array

This is the default data format for OPT. It evaluates all the containers as ordinary PHP arrays:

{$container.item} is equivalent to {$container['item']}

The section data are also parsed as arrays. The data format requires a separate template variable to store the data of nested sections in the following style:

<opt:section name="s1">
    {$s1.item}
    <opt:section name="s2">
        {$s2.item}
    </opt:section>
</opt:section>

The PHP code to populate the section with this data format:

$view->s1 = array(0 =>
    array('item' => 'Foo'),
    array('item' => 'Bar'),
    array('item' => 'Joe'),
);
$view->s2 = array(0 =>
    // The data for "s2" items that are in the relationship with "Foo".
    array(0 =>
        array('item' => 'A'),
        array('item' => 'B'),
    ),
    // The data for "s2" items that are in the relationship with "Bar".
    array(0 =>
        array('item' => 'C'),
        array('item' => 'D'),
    ),
    // The data for "s2" items that are in the relationship with "Joe".
    array(0 =>
        array('item' => 'E'),
        array('item' => 'F'),
    )
);

And so on for the deeper relationships.

This data format requires the section element indexes to start from 0 and be continuous, i.e. 0, 1, 2, 3, etc. Any holes in the index enumeration are not supported.

SingleArray

This data format is an extension to the ordinary Array format. It modifies the section behavior, so that the nested sections are taken from the appropriate items of the upper-level sections. To populate the sections presented above, we would use the following code:

$view->s1 = array(0 =>
    array('item' => 'Foo', 's2' =>
        array(0 =>
            array('item' => 'A'),
            array('item' => 'B'),
        ),
    ),
    array('item' => 'Bar', 's2' =>
        array(0 =>
            array('item' => 'C'),
            array('item' => 'D'),
        ),
    ),
    array('item' => 'Joe', 's2' =>
        array(0 =>
            array('item' => 'E'),
            array('item' => 'F'),
        ),
    ),
);
$view->setFormat('s1', 'SingleArray');  // This is not necessary, as the top-level sections are not affected.
$view->setFormat('s2', 'SingleArray');

In other words, the data for the nested sections must be stored under the upper-level section item variable whose name corresponds to the nested section name. In the example the nested section is called s2, so the item with its data in s1 must also be called s2.

The other aspects of Array format are not changed and work in the same way, including the index enumeration issue.

Objective

This data format evaluates the containers as the object calls:

{$container.item} is equivalent to {$container::item}

It can be also applied to sections, allowing to iterate through objects that implement both Countable and Traversable interfaces (i.e. Iterator or IteratorAggregate). The nested section data are taken from the appropriate upper-level section item field, like in SingleArray. The data format also supports the section item variables that are evaluated as object field calls.

This data format does not put any special requirements on section element indexes.

StaticGenerator

This data format can be used with sections only and allows to lazy-load their data when they are really needed. The script must provide an object of Opt_Generator_Interface to the section variable instead of the list of elements.

The data format must be decorated in order to specify, what type of data the requested function is going to generate.

class myGenerator implements Opt_Generator_Interface
{
    public function generate($what)
    {
        return array(0 =>
            array('item' => 'Item 1'),
            array('item' => 'Item 2'),
            array('item' => 'Item 3'),
        );
    } // end generate();
} // end myGenerator;
 
$view = new Opt_View('view.tpl');
$view->section = new myGenerator();
$view->setFormat('section', 'RuntimeGenerator/Array');

The format is especially useful, if our web application supports many different themes. Some sections may be not used in some themes and with the generators, the web application does not waste the time to return the unnecessary data for them.

RuntimeGenerator

This data format is an extension to the StaticGenerator. It allows to provide the generator optionally. If we do not specify the generator, the sections will look for the ordinary data.

SplDatastructure

PHP 5.3.0 introduced a group of SPL classes implementing various generic data structures. This data format allows section iteration over SplDoublyLinkedList objects, making use of their extra features such as iteration mode. SplDatastructure may be decorated with another format to specify the type of each collection element. By default, they instantiate into arrays or scalar values:

$list = new SplDoublyLinkedList;
$list->push('foo');
$list->push('bar');
$list->push('joe');
 
$view->collection = $list;
// List elements can be arrays or scalar values.
$view->setFormat('collection', 'SplDatastructure');

The format handles the section order attribute in the following manner:

order="asc" - iterates in the default data structure order. I.e. for a queue, it displays elements from the first to the last one, but for a stack - starting from the top.

order="desc" - iterates in the data structure order opposite to the default. For a queue - from the last to the first one, and for a stack - starting from the bottom.

If the data is not a valid SplDoublyLinkedList object or the specified list is empty, OPT skips the section and optionally jumps into the alternative block.

SplDatastructure format is available since Open Power Template 2.0.5.

SplDatastructure does not support variable access. It means that (according to the presented example) you cannot write for example {$collection} in the template and access some methods directly.

4.6. Data formats
4.6.2. Formats and expressions
4.6.1. Available data formats
« Previous
4.7. Template inheritance
Next »

4.6.2. Formats and expressions

A lot of OPT functions can operate on whole containers, executing the base function on all of their elements. For example, we can capitalize a single string: capitalize('string') or a container of strings: capitalize($containerOfStrings).

Formats and functions

In case of the following code:

{@container = someFunction($otherContainer)}

The result container should always be an array - the $otherContainer elements are repacked to the generic format, so that the compiler could generate a proper code for them.

Formats and assignment operators

In the following code:

{@bar is $foo.bar}

Nothing is repacked. The compiler detects that we want to take a sub-container into another variable and will automatically set the correct format for the new variable.

Formats and other expressions

In the complicated expressions, like $foo.bar + $foo.joe the variables behave according to their formats. However, some results may have no sense:

{$joe is $foo~$bar}

If $foo and $bar are containers, the final result may be strange or even produce PHP warnings or fatal errors. If you are worried about the template security, you should take a look at the configuration directives that allow to control the syntax features.

4. Programmer's Guide
4.7. Template inheritance
4.6.2. Formats and expressions
« Previous
4.8. Custom escaping functions
Next »

4.7. Template inheritance

Template inheritance is another way of modularizing your templates - composing the output document from several template files. It is very similar to the concepts that can be found in the object-oriented programming. In this chapter we are going to describe the implementation of template inheritance in Open Power Template.

Short introduction to snippets

One of the advantages of the new templating language is the fact that it may allow the manipulations impossible in pure PHP. One of such manipulations is described right here. It is the fundamental feature of the template inheritance, and moreover, it is widely used in many other places. As you should remember, the template processing in OPT consists of two phases:

The first phase is performed only when the template has been modified. Because of the performance reasons, in any other cases, OPT just executes the previously compiled template. So far, we have met the features that are active during the execution: the expressions, sections, etc. - all of them process the data from the script and generate the output HTML code. On the other hand, snippets work during the compilation. They work much like macros - they capture a piece of the template code and allow to paste it in several other places. To see, how they work, consider the following template:

<?xml version="1.0" ?>
<opt:root>
    <opt:snippet name="foo">
        <p>The value of the variable is {@variable}</p>
    </opt:snippet>
 
    {@variable is 5}
    <opt:insert snippet="foo" />
 
    {@variable is 10}
    <opt:insert snippet="foo" />
</opt:root>

The result produced by the template is:

<p>The value of the variable is 5</p>
<p>The value of the variable is 10</p>

The code captured by the snippet still remains dynamic. Inserted in different places, it could produce different results, depending on the actual variable values etc. Furthermore, it is even more smart. In some cases, it is able to self-modify to provide a better integration with a particular context. It is especially visible with the sections:

<?xml version="1.0" ?>
<opt:root>
    <opt:snippet name="user">
    <div>
        <p>Nick: {$user.nick}</p>
        <p>Age: {$user.age}</p>
    </div>
    </opt:snippet>
 
    <opt:section name="members" opt:use="user" />
    <opt:section name="admins" opt:use="user" />
</opt:root>

Let's unroll the actual code to see, what goes to the execution stage:

<?xml version="1.0" ?>
<opt:root>
    <opt:section name="members">
    <div>
        <p>Nick: {$members.nick}</p>
        <p>Age: {$members.age}</p>
    </div>
    </opt:section>
    <opt:section name="admins">
    <div>
        <p>Nick: {$admins.nick}</p>
        <p>Age: {$admins.age}</p>
    </div>
    </opt:section>
</opt:root>

The variable $user (the same, as the snippet name) has been automatically replaced with the section names, which simplifies the reuse of the section content. For example, if we have a pagination system built with opt:selector, we do not have to copy and paste the pagination layout into every template that uses it. We could simply define a snippet in an extra template, include this template and load the snippet content to the opt:selector instruction, like in the example above.

The snippets require different inclusion techniques than opt:include. They will be explained soon.

General overview

Template inheritance resembles the ordinary inheritance from the object-oriented programming. The classes are represented by templates, and the methods - by snippets. We may extend one template with another and overwrite the snippets it defines or add some new ones. The only difference is the base template which specifies, where the snippets should be placed and displayed. Below, we can see an image that illustrates the process:

Template inheritance in OPT

The base template may define the base XHTML code structure, with the <html>, <body> tags etc. Moreover, it leaves some spare placeholders (with opt:insert). Then, we extend the template with another one which provides the snippets for the placeholders. As a result, OPT generates the complete output document, where the snippets are inserted into placeholders, overwriting the default content.

A single base template can be extended by many different templates that could contain the code for different website modules: news, articles, etc. Furthermore, if the module A is based on B, it may extend the templates of the module B and overwrite snippets defined by them. OPT automatically manages the compilation issues.

The template inheritance system in OPT has the following properties:

A sample code

Let's create some sample templates that use the inheritance:

The base template (base.tpl):

<?xml version="1.0" ?>
<opt:root>
<opt:prolog />
<opt:dtd template="xhtml10transitional" />
<html>
<head>
    <title>Hello!</title>
</head>
<body>
    <h1>Title</h1>
    <opt:insert snippet="content">
        <p>Some default content here.</p>
    </opt:insert>
</body>
</html>
</opt:root>

The extending template (extending.tpl):

<?xml version="1.0" ?>
<opt:extend file="base.tpl">
    <opt:snippet name="content">
        <p>Hi universe!</p>
    </opt:snippet>
</opt:extend>

The PHP code that runs the template:

$view = new Opt_View('extending.tpl');
 
$output = new Opt_Output_Http();
$output->render($view);

As we run the code, we should get the following result:

<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <title>Hello!</title>
</head>
<body>
    <h1>Title</h1>
    <p>Hi universe!</p>
</body>
</html>

In the PHP code, we call only the template on the top of the inheritance chain, or in other words - the template that we do not want to extend anymore. From the script-side, we do not have to know about the existence of the base template. The whole inheritance chain is executed in the content of a single view, so all the templates share the same data and see their template variables. The template inheritance increases the performance, compared to the same code achieved with an ordinary opt:include instruction. We have less objects and the template engine does not have to perform so many disk operations to detect whether the files need to be recompiled.

Try to modify the base template. Although we do not mention it in the script, OPT knows that it exists and the change is visible automatically, because the library checks also for the modifications of the dependent templates.

In both opt:root and opt:extend you may set the escaping attribute which defines the default escaping policy for a single template. The snippets always remember the policy of their "mother" template and restore it even after the insertion.

Dynamic inheritance with branches

Let's take a look at the examples above once more. We can say about them that they form a chain, because OPT loads them one after another. However, in some templates, the chain may split into several different chains called branches. Using the hints from the script, the compiler decides which chain to follow. This leads us to the dynamic template inheritance where the script decides, what templates to extend. Let's try to create a simple message template:

<?xml version="1.0" ?>
<opt:extend file="overall.tpl" simple="simple.tpl">
    <opt:snippet name="content">
        <h1>Message</h1>
        <p>{$message}</p>
        <p class="bar"><a parse:href="$link">OK</a></p>
    </opt:snippet>
</opt:extend>

In the opt:extend tag, we have two branches:

  1. file - it redirects us to the overall.tpl template. In our example, it may contain a full version of the website layout.
  2. simple - redirects to simple.tpl. It may contain a simplified version of the website layout.

By default, OPT follows the file branch, and to select the simple branch, we have to inform OPT in the PHP code about this:

$view = new Opt_View('message.tpl');
$view->setBranch('simple');

Your PHP code may switch the branch at any time and you do not have to pay attention to the template compilation.

In more complex chains, OPT always tries to follow the selected branch. If it is not defined in a particular template, OPT follows the default file branch as an exception.

True dynamic inheritance

Branches, yet interesting, do not offer a true dynamic template inheritance, as the script cannot select the template names to extend directly. Fortunately, such system is also present in OPT 2. The template part is very simple - we just need to inform that OPT may expect a dynamically selected file in the template:

<?xml version="1.0" ?>
<opt:extend file="default.tpl" dynamic="yes">
    <!-- ... -->
</opt:extend>

In the script, we need to use the inherit() method:

$view->inherit('extending.tpl', 'extended.tpl');

If we want to extend the main view template, we may use the shortened form:

$view->inherit('extended.tpl');

The complex chains are also possible:

$view = new Opt-View('template1.tpl');
$view->inherit('template2.tpl');
$view->inherit('template2.tpl', 'template3.tpl');
$view->inherit('template3.tpl', 'template4.tpl');

Although you do not have to pay attention to the template compilation, please be aware that all the chains your script is able to produce do need to be compiled which is a quite demanding process and may slow down your website, if it will be taking place too often.

Template inheritance versus template inclusion

The techniques work on different template processing stages. Template inheritance takes place during the compilation and thus it produces a faster and more compact final code. On the other hand, opt:include can be configured on-the-fly directly by the template and use the template variables.

Let's consider a situation, where the script can execute more than one action at once. The actions want to display their own templates. With the template inheritance, it is impossible to create a true loop that would display the action templates in the same snippet. You would have to use the dynamic inheritance and complex control flows. Furthermore, the actions would need to share the same variable scope, as the inheritance works inside a single view. The same limitations do not occur in opt:include. The actions may simply create their own views and pack them into a section:

<opt:section name="contentViews">
    <opt:include from="contentViews" />
</opt:section>

Note that you can combine the template inheritance and inclusion in one project, depending on the current needs of the particular templates.

Conclusion

Open Power Template provides an advanced template inheritance system. This chapter does not cover all of its features, but rather gives you an idea, how it works and what can be done with it.

See also:

4. Programmer's Guide
4.8. Custom escaping functions
4.7. Template inheritance
« Previous
4.9. Internationalization
Next »

4.8. Custom escaping functions

Currently OPT sanitizes the script data placed in the templates with simple htmlspecialchars() function. However, we are aware of the fact it is not perfect. This part of the library will be extended in the future, but currently OPT provides the ability to use a custom function here. In order to to that, just register a new OPT function called escape:

$tpl->register(Opt_Class::PHP_FUNCTION, 'escape', 'myFilter');

The registered function must take exactly one argument - the text to be sanitized.

In case of this function, it is not allowed to use argument order manipulators like #2,1,3#.

See also:

4. Programmer's Guide
4.9. Internationalization
4.8. Custom escaping functions
« Previous
4.10. Components and forms
Next »

4.9. Internationalization

OPT provides several features that make the internationalization easy.

Translation interface

Open Power Libs core contains the interface Opl_Translation_Interface that allows you to write a class that serves text with the specified identifier and assigned to a specified group. OPT template syntax provides a port for this interface using the language variables $group@identifier:

<p>{$news@title}: {$news.title}</p>
<p>{$news@author}: {$news.author}</p>

In order to make it work, you have to register an object of a class that implements that interface. A sample implementation can be found below:

<?php
 
class translationInterface implements Opl_Translation_Interface
{
    private $_original = array(
        'foo' => array('bar' => 'Value 1', 'joe' => 'Value 2'),
        'goo' => array('bar' => 'Modificable value: %s'),
    );
 
    private $_modified = array();
 
    public function _($group, $id)
    {
        if(isset($this->_modified[$group][$id]))
        {
            return $this->_modified[$group][$id];
        }
        if(isset($this->_original[$group][$id]))
        {
            return $this->_original[$group][$id];
        }
        return '';
    } // end _();
    public function assign($group, $id)
    {
        $args = func_get_args();
        unset($args[0]);
        unset($args[1]);
        if(isset($this->_original[$group][$id]))
        {
            if(!isset($this->_modified[$group]))
            {
                $this->_modified[$group] = array();
            }
            $this->_modified[$group][$id] = vsprintf($this->_original[$group][$id], $args);
        }
    } // end assign();
} // end translationInterface;
 
$tpl->setTranslationInterface(new translationInterface());
 
// You can create the views and parse the templates now

There are two ways of registering the translation interface in OPT. The first one is to use the Opt_Class::setTranslationInterface() method:

$tpl->setTranslationInterface($tf);

The second one is to register the object directly in Opl_Registry object, so that the other libraries can also get it. However, this must be done before you call Opt_Class::setup().

Opl_Registry::set('opl_translate', new translationInterface());
 
...
 
// Now the translation interface is imported into OPT from the global OPL registry
$tpl->setup();

Another interesting feature of the translation interface is that you can assign values to the text from the template side using the assign() function:

{assign($foo@bar, $variable)}
 
<!-- now the $foo@bar should contain some text with a value from the script -->
{$foo@bar}

It is up to you, how you implement this feature in your script. In our case, we used PHP vsprintf() function so that we can use formatting codes like %s or %d to indicate the places in the text, where to put some values.

The translation interface has one disadvantage - you cannot specify the default interface messages directly in the template, so you must be sure they are defined in some external file and load them somehow.

See also:

4. Programmer's Guide
4.10. Components and forms
4.9. Internationalization
« Previous
4.11. Caching
Next »

4.10. Components and forms

The components have been designed to help you building HTML forms. They provide a robust interface both on the script and template side to deal with this task. The basic ideas concerning components can be found in Syntax / Components chapter and the guide to writing your own component classes - in Extending / Components. Here we are going to show some practical techniques.

Component overview

The components offer you the following features:

We assume that you have already read the documents mentioned above and have the basic knowledge, how they really work and how they look like.

A basic form

Below, we present a sample HTML form that asks the user for its name, surname and the age. We assume that we have the following components available:

  1. inputComponent - form:input
  2. textareaComponent - form:textarea
  3. selectComponent - form:select
<?xml version="1.0" ?>
<opt:root>
    <form method="post" action="script.php">
        <form:input name="name">
            <div opt:component-attributes="default">
                <label parse:for="$system.component.name">Name:</label>
                <opt:display />
 
                <opt:onEvent name="error">
                    <p class="error">Error: {$system.component.error}</p>
                </opt:onEvent>
            </div>
        </form:input>
 
        <form:input name="surname">
            <div opt:component-attributes="default">
                <label parse:for="$system.component.name">Surname:</label>
                <opt:display />
 
                <opt:onEvent name="error">
                    <p class="error">Error: {$system.component.error}</p>
                </opt:onEvent>
            </div>
        </form:input>
 
        <form:select name="age" datasource="$availableAges">
            <div opt:component-attributes="default">
                <label parse:for="$system.component.name">Your age:</label>
                <opt:display />
 
                <opt:onEvent name="error">
                    <p class="error">Error: {$system.component.error}</p>
                </opt:onEvent>
            </div>
        </form:select>
    </form>
</opt:root>

We may run it with the following code:

// The configuration
$tpl = new Opt_Class;
 
// ...
 
$tpl->register(Opt_Class::OPT_NAMESPACE, 'form');
$tpl->register(Opt_Class::OPT_COMPONENT, 'form:input', 'inputComponent');
$tpl->register(Opt_Class::OPT_COMPONENT, 'form:textarea', 'textareaComponent');
$tpl->register(Opt_Class::OPT_COMPONENT, 'form:select', 'selectComponent');
$tpl->setup();
 
// The script
 
$view = new Opt_View('my_form.tpl');
$view->availableAges = array(0 =>
    'Under 10',
    '10 - 18',
    '19 - 25',
    '25 - 35',
    '35 - 50',
    '50 - 65',
    'Above 65'
);
 
// The composition of the output document, executing etc. here

As you can see, the component classes can be registered in OPT and receive their own XML tags. As we have chosen the form namespace for them, we must register the namespace, too, so that OPT knows that it must parse it. The rest depends on the component source code - they may be smart enough to import most of the necessary settings, including the validation result, from the form processing library, using the name provided as the name attribute.

However, we see that the template is quite long and these are just three form fields! Fortunately, thanks to snippets, we may write only one, universal field structure and use it across all our forms. Let's create the snippets.tpl file:

<?xml version="1.0" ?>
<opt:root>
    <opt:snippet name="formField">
        <div opt:component-attributes="default">
            <label parse:for="$system.component.name">{$system.component.title}: </label>
            <opt:display />
            <opt:onEvent name="error">
                <p class="error">Error: {$system.component.error}</p>
            </opt:onEvent>
        </div>
    </opt:snippet>
</opt:root>

Then, we insert the snippet to the component ports:

<?xml version="1.0" ?>
<opt:root include="snippets.tpl">
    <form method="post" action="script.php">
        <form:input name="name" template="formField">
            <opt:set str:name="title" str:value="Name" />
        </form:input>
 
        <form:input name="surname" template="formField">
            <opt:set str:name="title" str:value="Surname" />
        </form:input>
 
        <form:select name="age" datasource="$availableAges" template="formField">
            <opt:set str:name="title" str:value="Your age" />
        </form:select>
    </form>
</opt:root>

The code in the snippet is automatically merged with the ports. If we wish to modify the overall look of the form fields, we just modify the snippets.tpl file. Please note that we have not modified any line of the PHP code. With Open Power Template, the script does not have to deal with the view issue, like in many PHP frameworks. The template engine gives you all the necessary tools to build even very complex forms.

Dynamic forms

The components do not have to be statically deployed all the time. As the component logic is a PHP object, our form processor may generate such objects for each field in the form and put them into a section:

<?xml version="1.0" ?>
<opt:root include="snippets.tpl">
    <form method="post" action="script.php">
        <opt:section name="fields">
            <opt:component from="$fields.component" template="formField" />
        </opt:section>
    </form>
</opt:root>

The PHP script manages the components chosen to represent the form elements, but the template still has the control over the field layout thanks to the snippet. More advanced solution may allow to assign the fields to various containers, so that we could have different sections for each of the container and different layouts:

<?xml version="1.0" ?>
<opt:root include="snippets.tpl">
    <form method="post" action="script.php">
        <opt:section name="container1">
            <opt:component from="$container1.component" template="formField_TypeA" />
        </opt:section>
        <opt:section name="container2">
            <opt:component from="$container2.component" template="formField_TypeB" />
        </opt:section>
    </form>
</opt:root>

If we have a field that needs a custom treatment, we may still define it manually:

<?xml version="1.0" ?>
<opt:root include="snippets.tpl">
    <form method="post" action="script.php">
        <opt:section name="container1">
            <opt:component from="$container1.component" template="formField_TypeA" />
        </opt:section>
 
        <form:textarea name="content">
            <div class="content" opt:component-attributes="default">
                <div class="wysiwyg">
                    <!-- some WYSIWYG buttons here -->
                </div>
                <opt:display />
 
                <opt:onEvent name="error">
                    <p class="error">Error: {$system.component.error}</p>
                </opt:onEvent>
                <opt:onEvent name="anotherEvent">
                    <!-- some code here -->
                </opt:onEvent>
            </div>
        </form:textarea>
 
        <opt:section name="container2">
            <opt:component from="$container2.component" template="formField_TypeB" />
        </opt:section>
    </form>
</opt:root>

Complex form technical issues

In the last example, one of the components used in the form was statically deployed. The static deployment means that the component object is created on the template-side. Our components should be prepared for that. Usually, we would also export the whole form object or its data to the template, and such component could find and load them automatically in setView() method:

class inputComponent implements Opt_Component_Interface
{
    private $_initialized = false;
    private $_name = '';
 
    public function __construct($name = '')
    {
        if(is_string($name))
        {
            // Created explicitely by the user or by the template engine
            $this->_initialized = false;
            $this->_name = $name;
        }
        elseif(is_array($name))
        {
            // Passing an array is a signal that the component has been created
            // By the form processor factory:
            $this->initialize($name);
        }
 
    } // end __construct();
 
    public function setView(Opt_View $view)
    {
        if(!$this->_initialized && !$view->defined('form'))
        {
            throw new Exception('The component is not initialized!'); // sure, why not exceptions?
        }
 
        // Feed the component with the data obtained from the
        // form processor obtained from the view object just
        // before the deployment. This is the last chance.
        if(!$this->_initialized)
        {
            $form = $view->get('form');
            $this->initialize($form->getFieldData($this->_name));
        }
    } // end setView();
 
    /**
     * This method is not included in the Opt_Component_Interface.
     * Our form processor could use it to feed the component with the
     * necessary data.
     */
    public function initialize(Array $array)
    {
        // some code here...
    } // end initialize();
} // end inputComponent;

There is also another technique available, but it involves creating new instructions. Even more advanced form processors could have their own abstraction layer created over the <form> tag, for example <opt:form> that automatically integrates with the PHP form object created by the script and generates the necessary attributes. In this situation, the <opt:form> instruction processor is also allowed to modify the default compiler behavior of the static component deployment.

The component processor registers several possible conversions for the component deployment code. Instead of creating a new component object, the static deployment tag: <form:input> etc. can refer to the form processor factory object. The template code is not affected, but becomes even more flexible.

So, as we have the instruction processor (see Extending / Instructions to get to know, how to write them), we may add some extra code to it that will call the form processor factory method instead of creating a new object:

class Opt_Instruction_Form extends Opt_Compiler_Processor
{
 
    public function processNode(Opt_Xml_Node $node)
    {
        // the rest of the instruction processing code goes here
        // ...
 
        // replace the standard deployment with our code:
        $this->_compiler->setConversion('##component', '$_form->componentFactory(\'%CLASS%\', \'%TAG%\', %ATTRIBUTES%)');
        $node->set('postprocess', true);
        $this->_process($node);
    } // end processNode(); 
 
    public function postprocessNode(Opt_Xml_Node $node)
    {
        // Do not forget to remove the conversion outside the opt:form tag!
        $this->_compiler->unsetConversion('##component');
    } // end postprocessNode();
} // end Opt_Instruction_Form;

As the compiler generates the PHP code, our conversion pattern contains the new PHP code that should get us the component. Of course, the componentFactory() method must be implemented in the form processor class. The code uses some placeholders defined by the component processor:

  1. %CLASS% - the component class name.
  2. %TAG% - the component tag name.
  3. %ATTRIBUTES% - the PHP code of the associative array that contains custom component port attributes.

They can help the factory method to identify, what component object should be returned.

The conversion can be also applied for a single component class only. Moreover, the same trick works for blocks, too.

Conclusion

As you can see, the components give us new opportunities of the form layout management. The code is very simple, portable and scalable: it can handle both the simplest and the most complex form structures without bigger problems. Compare it to the various solutions found in popular PHP frameworks, where we were in trouble unless we followed the path determined by the developers. Furthermore, the modularization techniques use the basic OPT features, such as snippets (note: template inheritance uses them, too! Think about combining the forms and the template inheritance!). This is another advantage over pure PHP-based solutions.

See also:

4. Programmer's Guide
4.11. Caching
4.10. Components and forms
« Previous
5. Extending OPT
Next »

4.11. Caching

Although Open Power Template does not have its own caching system, it provides an API to write and use external caching systems. To see, how to implement a caching system in OPT, please read Extending OPT: Caching systems. This chapter covers the topic of using it.

General overview

The caching systems are represented in OPT by objects implementing Opt_Caching_Interface interface. They may be enabled on two levels:

  1. Globally - the new views import the global caching system.
  2. Per-view - the caching system is enabled only in a single view. It overwrites the global setting.

Global caching settings

In order to enable a caching system globally, we need a caching system object and the object of Opt_Class:

$cache = new cachingSystem;
$tpl = new Opt_Class;
// ...
 
$tpl->setCache($cache);

To disable a caching system, the setCache() method must be called without an argument:

$tpl->setCache();

The newly created views will import the caching system into their scope.

Local caching settings

The view objects also provide setCache() method:

$cache = new cachingSystem;
$view = new Opt_View('template.tpl');
// ...
 
$view->setCache($cache);

It overwrites the global setting and is applied only for the view template.

Caching system configuration

Open Power Template interface provides only the necessary methods to communicate with the caching system. The certain settings depend on the used caching system implementation and have nothing to do with OPT. Refer to your caching system manual in order to learn, how to configure it properly.

Where to get the caching systems from?

You might wonder why OPT does not have its own caching system. The answer is simple: this is not a template engine task, even if it makes use of it. Many frameworks already provide their own advanced and robust implementations of caching libraries. Following the Ockham rule, we do not see the point in reinventing the wheel. The Opt_Caching_Interface is simple enough to make the integration very easy and you might check out the existing framework ports as they may offer ready implementations.

See also:

Table of Contents
5. Extending OPT
4.11. Caching
« Previous
5.1. Introduction to plugins
Next »

5. Extending OPT

This chapter shows, how to extend OPT with new features.

See also:

5. Extending OPT
5.1. Introduction to plugins
5. Extending OPT
« Previous
5.2. New functions
Next »

5.1. Introduction to plugins

Open Power Template provides a plugin architecture that allows you to install new extensions easily. To initialize the plugins, you need two extra directories pointed by the directives:

For example:

$tpl->sourceDir = './templates/';
$tpl->compileDir = './templates_c/';
$tpl->pluginDataDir = './templates_c/';
$tpl->pluginDir = './plugins/';
$tpl->setup();

The setup() method will load the plugins automatically. Alternatively, you may load them manually earlier with Opt_Class::loadPlugins().

Writing plugins

The plugins are normal PHP files. They have an access to the contents of your Opt_Class object, so they can request registering various items in OPT. A single plugin can add several new items to your library. The only types of content that require a strict structure are instructions and data formats. Writing plugins for them is described later. Below, you can find a sample plugin file:

<?php
 
function myFunction()
{
    return 'Hi universe!';
} // end myFunction();
 
$this->register(Opt_Class::PHP_FUNCTION, 'myFunction', 'myFunction');

The file name does not matter in this case. Simple, isn't it? Now you can save the file in your plugin directory.

5. Extending OPT
5.2. New functions
5.1. Introduction to plugins
« Previous
5.3. New blocks
Next »

5.2. New functions

Functions are the easiest to create and register in OPT, as they have exactly the same form in the templates:

function myFunction($argument1, $argument2)
{
  // do something here
} // end myFunction();
 
// ...
$tpl->register(Opt_Class::PHP_FUNCTION, 'myFunction', 'myFunction');

Practical tricks

The functions can either return the value or echo it. At the first sight, it gives the same effect in both of cases:

<p>{myFunction()}</p>

However, if we use the function in a more complex expression:

{@variable is myFunction()}

We will see the difference. In the first case, the function result is saved in the variable, in the second - printed, and the variable contains nothing.

Your functions should always return the value instead of echoing it, unless you know, what you are doing.

Writing aggregate functions

Open Power Template supports aggregate functions that can operate either on a single value or a container. You can easily write your own aggregate functions, too, using a simple interface provided by Opt_Function:

function myAggregateFunction($item, $argument)
{
    if(Opt_Function::isContainer($item))
    {
        return Opt_Function::processContainer('myFunction', array($item, $argument));
    }
 
    // Do something here with a single value
    return $item.' '.$argument;
} // end myAggregateFunction();

If we notice that the first argument is a valid container (an OPT convention says that the value that we are going to operate on, should be provided as the first argument), we return the value from processContainer() static method. As the first argument, we must provide the name of the function that will modify a single value (usually it will be the same function, but this does not have to be the rule), and as the second one - the list of arguments. Please note that the first item of this array must be the container!

Registering functions

During the function registration, we may perform several tricks. As you have probably noticed, we have specified the function name twice. The first name shows, how the function will be named in the template. The second one is the real PHP function name and it may be any valid PHP code. In fact, you are allowed to register static methods as OPT functions, namespace elements and many other things:

$tpl->register(Opt_Class::PHP_FUNCTION, 'myFunction', 'myClass::myStaticMethod');

Another interesting feature is the ability to modify the argument order during the template compilation, so that you do not have to create a PHP wrapper function. The order rules are specified before the real PHP function name and are enclosed within #:

$tpl->register(Opt_Class::PHP_FUNCTION, 'regexReplace', '#3,1,2#preg_replace');

It could be read like this:

We may also specify some optional values:

$tpl->register(Opt_Class::PHP_FUNCTION, 'foo', '#3,1,2:null#foo');

In this case, the third argument in templates is optional, but in PHP it is required. We must specify the default value (here, null) for the compiler then by adding :null to the position number.

5. Extending OPT
5.3. New blocks
5.2. New functions
« Previous
5.4. New components
Next »

5.3. New blocks

See: Syntax / Topics / Blocks

5. Extending OPT
5.4. New components
5.3. New blocks
« Previous
5.5. New output systems
Next »

5.4. New components

See: Syntax / Topics / Components and Guide / Components

5. Extending OPT
5.5. New output systems
5.4. New components
« Previous
5.6. New caching systems
Next »

5.5. New output systems

Output systems decide, what to do with the output document produced from a view. By default, OPT provides two output systems:

  1. HTTP - sends the document to the browser.
  2. Return - returns the document code back to the script.

Output systems are very easy to write. They are represented as classes implementing Opt_Output_Interface. The interface provides two methods:

Below, you can find an implementation of Opt_Output_Return used in OPT:

class Opt_Output_Return implements Opt_Output_Interface
{
    /**
     * Returns the output name.
     *
     * @return String
     */
    public function getName()
    {
        return 'Return';
    } // end getName();
 
    /**
     * Executes the specified view and return the results back
     * to the script.
     *
     * @param Opt_View $view The rendered view
     * @return String
     */
    public function render(Opt_View $view)
    {
        ob_start();
        $view->_parse($this);
        return ob_get_clean();
    } // end render();
} // end Opt_Output_Return;

The getName() method simply returns the output system name. The render() method takes the view object as an argument. In order to parse the view, we call the internal _parse() method, passing a reference to the output system itself. This method takes also an optional second argument: $exception = true which controls the error handling. If the argument is set to false, the missing template is not reported as an exception. In order to capture the result, we use ob_start() and ob_get_clean() PHP functions.

Tips and tricks

Obtaining the Opt_Class object

The Opt_Class object can be obtained from the Open Power Libs registry:

$tpl = Opl_Registry::get('opt');

This entry is always initialized, if the main object is created.

Template modes

The templates in OPT can work in three (technically two) modes:

  1. XML mode
  2. HTML mode
  3. Quirks mode

If your output system needs to know the mode of the executed view, you can obtain it directly from the view object:

public function render(Opt_View $view)
{
    $mode = $view->getMode();
    // ...
} // end render();

The mode is represented by Opt_Class constants XML_MODE and QUIRKS_MODE.

Some output systems may find this information necessary. For example, sending two different XML templates to the browser would lead to produce a response with invalid XML document. If your output systems is vulnerable to this problem, it should throw the Opt_OutputOverloaded_Exception after the second attempt to parse an XML view. See the Output/Http.php implementation for details.

Caching engines

The output system should not deal with caching. To see, how to implement a caching engine for OPT, see Guide: Caching.

See also:

5. Extending OPT
5.6. New caching systems
5.5. New output systems
« Previous
5.7. New data formats
Next »

5.6. New caching systems

OPT does not come bundled with any native caching system, although one is being developed as a part of Open Power Classes project. This article shows, how to connect the template engine to an external caching system, provided (for example) by your framework. All you need is to implement the Opt_Caching_Interface in one of your classes. The interface consists of two methods:

public function templateCacheStart(Opt_View $view);
public function templateCacheStop(Opt_View $view);

The first method must perform the following operations:

  1. Check, if the cached content needs to be refreshed.
  2. If the cache must be refreshed, the method is expected to start capturing the content and return false.
  3. Otherwise, it must read and display the content with echo and return true.

The second method is executed at the end of cache rebuilding. Its purpose is to finalize the capturing and save the new content.

A sample implementation could look like this:

class myCache implements Opt_Caching_Interface
{
    private $_refresh = false;
 
    public function refresh()
    {
        $this->_refresh = true;
    } // end refresh();
 
    public function templateCacheStart(Opt_View $view)
    {
        if(!file_exists('./'.$view->getTemplate().'.txt') || $this->_refresh)
        {
            $tpl = Opl_Registry::get('opt');
            $tpl->setBufferState('cache',true);
 
            ob_start();
            return false;
        }
        echo file_get_contents('./cache.txt');
        return true;
    } // end templateCacheStart();
 
    public function templateCacheStop(Opt_View $view)
    {
        file_put_contents('./'.$view->getTemplate().'.txt', ob_get_clean());
 
        $tpl = Opl_Registry::get('opt');
        $tpl->setBufferState('cache',false);
    } // end templateCacheStop();
} // end myCache;

Such caching object can be registered now in the Opt_View object to enable caching features.

If you are working with Zend Framework, please take a look at the OPL for Zend Framework port which contains a plugin that allows to use the Zend_Cache component with OPT.

If you are wondering, why our caching system needs the main OPT object and why we are calling setBufferState(), please read the chapter below.

Capturing <opt:dynamic>

The opt:dynamic instruction informs that the specified part of template must remain dynamic even after caching. In order to use this feature, the caching system must support it. Here, we are going to describe, how to capture the dynamic content of the template.

When using opt:dynamic, the caching system must combine the cache file from the static parts of the template and the PHP code snippets that must remain dynamic. This means that the output cache file must be a PHP script, instead of a file with static text. The static content is collected by Opt_View object that processes the template, and PHP snippets can be found in the compilation directory. Once we use opt:dynamic, the template compiler produces two output files - the new one, with .dyn extension, contains the serialized array of the PHP code for our caching system.

The concatenation must be done in templateCacheStop() and below, you can find a sample implementation:

public function templateCacheStop(Opt_View $view)
{
    if($view->hasDynamicContent())
    {
        $staticParts = $view->getOutputBuffers();
        $dynamicParts = unserialize(file_get_contents($view->_convert($view->getTemplate())));
 
        $output = '';
        $cnt = sizeof($staticParts);
        for($i = 0; $i < $cnt; $i++)
        {
            $output .= $staticParts[$i];
            $output .= '<'.'?php '.$dynamicParts[$i].' ?'.'>';
        }
        $output .= ob_get_flush();
        // save the file...
    }
    else
    {
        // static cache here...
    }
} // end templateCacheStop();

In the source code, we can see that we need to close the last output buffer manually with ob_get_flush(). The output buffer must be flushed, too. Otherwise, it won't appear on the script when the cache is regenerated.

opt:dynamic requires us to use the advisory output buffer management provided by the Opt_Class to detect, whether the caching output buffering has actually been used. This means that templateCacheStart() must call: $tpl->setBufferState('cache',true); and templateCacheStop() - $tpl->setBufferState('cache',false).

See also:

5. Extending OPT
5.7. New data formats
5.6. New caching systems
« Previous
5.7.1. Variables
Next »

5.7. New data formats

Many OPT instructions and language structures do not have their true equivalent in PHP language which is used as a base language for the compiled templates. Data formats are the way to provide the compiler an implementation of these instructions and a flexibility for the programmer. On the template side, it is no more necessary to know the implementation issues - we just use the generic instructions and select the proper data format in the script, depending on its current needs. In this chapter we are going to show, how to write custom data formats for Open Power Template 2.

Data format - a closer look

From the compiler point of view, a data format is a class extending Opt_Compiler_Format containing various PHP code snippets. If the compiler or an instruction wants to generate a piece of PHP code, it sends to the data format a request with the snippet name and the format class should return it. Sometimes the data formats needs extra information from the caller. It is provided with extra arguments assigned similarly to the template variables in the views. Finally, the programmer may decorate one data format with another. This causes the certain snippets to use another snippets from the decorated format during the PHP code generation, thus extending the functionality and creating many different possible combinations.

Using data formats in the compiler

To use a data format in the compiler code, we have to obtain the data format object first. This can be done with Opt_Compiler_Class::getFormat() method. As arguments, we specify the identifier of a resource (the same as in Opt_View::setFormat()). Moreover, we may decide, whether to create a new object every time we call the method or restore an existing one, if possible:

$format = $this->compiler->getFormat('variable', true);

Then, we can request a code snippet with get() method:

$format->assign('item', 'variablename');
echo $format->get('variable:main');

As we can see, the data are assigned to the format with the assign() method. In this case, we requested the format to create a PHP call to the template variable $variablename, which is later printed. The PHP snippets belong to different groups providing a certain functionality. Before we start using the data format, it is recommended to check, if the returned object actually supports it:

if($format->supports('variable'))
{
    // our code goes here
}
else
{
    // we cannot process $variablename, because the data format does not support "variable" group
    throw new Opt_FormatNotSupported_Exception('variablename', 'variable');
}

Finally, we may request the data format to perform a certain action not connected with PHP code generation and check the value of the data format property:

// perform some action
$format->action('variable:someaction');
 
if($format->property('variable:someproperty'))
{
    // then do something extra...
}

Implementing our data format

The data format is created by extending Opt_Compiler_Format class and overwriting the abstract method _build() used to prepare a snippet. Let's take a look at a simple data format:

class Opt_Custom_Format extends Opt_Compiler_Format
{
    protected $_supports = array('variable');
    protected $_properties = array('variable:assign' => true);
 
    protected function _build($name)
    {
        switch($name)
        {
            // let the variable to become a function
            case 'variable:main':
                return 'myVariableGenerator(\''.$this->_getVar('item').'\')';
            case 'variable:assign':
                return 'myVariableModifier(\''.$this->_getVar('item').'\', '.$this->_getVar('value').')';
 
            // some other snippets go here...
        }
    } // end _build();
} // end Opt_Custom_Format;

The $_supports protected field contains a list of functionalities supported by a certain data format. The $_properties list contains a list of properties and their values in the data format. The construction of _build() method is quite simple. We just get the snippet name and decide, what piece of code to generate. We may obtain the format variables with _getVar() method.

Format variables are something completely different and independent from the view and template variables! Do not confuse these two ideas.

To perform actions, we simply overwrite the action() method, for example:

public function action($name)
{
    if($name == 'section:forceItemVariables')
    {
        $this->_sectionItemVariables = true;
    }
} // end action();

Conclusion

The basics of writing data formats are easy, but you have probably noticed that we are already on the half of the way. We must learn about different snippets used in OPT and what the compiler expects there to be. It will be introduced in the next chapter. In the next chapters we are going to use the following terms:

piece of expression
a part of PHP expression. It must not contain curly brackets, semicolons etc. and must be deployable via copy+paste in any valid PHP expression.
piece of code
any part of PHP code. Curly brackets, semicolons, PHP control structures are allowed.
5.7. New data formats
5.7.1. Variables
5.7. New data formats
« Previous
5.7.2. Container items
Next »

5.7.1. Variables

In this chapter, we are going to learn, how to change the behavior of variables with OPT data formats.

Configuration

The variable access snippets are located in the variable group, so you must add it to $_supports field in your data format. Furthermore, you need to set up the property variable:assign. It is a boolean value that controls whether your data format supports assignments of a new value to the variables. Another property is variable:useReference. Its value should be true if the data format permits reading the variables via references. For example, the data format shown in the example below that implements the template variables as functions, should use false here, as it is not possible in PHP to access the returned function values via references.

Reading the values

The snippet that returns the PHP code to read a variable value is called variable:main. It should return a piece of expression that reads the value. The variable name is provided in the item format variable.

    case 'variable:main':
        return 'myVariableReader(\''.$this->_getVar('item').'\')';

OPT supports both local and global template variables. The data formats may return different PHP codes for those two types of variables. The requested type is provided in the access format variable which takes two possible values:

  1. Opt_Class::ACCESS_LOCAL - accessing a local variable
  2. Opt_Class::ACCESS_GLOBAL - accessing a global variable

The alternative PHP code:

    case 'variable:main':
        if($this->_getVar('item') == Opt_Class::ACCESS_LOCAL)
        {
            return 'myVariableReader(\''.$this->_getVar('item').'\')';
        }
        else
        {
            return 'myGlobalVariableReader(\''.$this->_getVar('item').'\')';
        }

Modifying the variable values

It is also possible to control the variable assignments via the variable:assign snippet. The new value is provided in the value format variable that must be placed somewhere in the returned code.

The value format variable actually contains neither PHP nor OPT expression that generates the specified value and there is no way to identify, what exactly the programmer is going to assign. The exact value is an internal token used by the expression engine to identify subexpressions.

A sample code:

    case 'variable:assign':
        return 'myVariableModifier(\''.$this->_getVar('item').'\', '.$this->_getVar('value').')';
5.7. New data formats
5.7.2. Container items
5.7.1. Variables
« Previous
5.7.3. Sections
Next »

5.7.2. Container items

The data formats allow you to control the access to the container subitems, for example $container.subitem. They are programmed similarly to ordinary variables.

The snippets described above are also used in sections: $sectionName.sectionItem.

Configuration

The variable access snippets are located in the item group, so you must add it to $_supports field in your data format. Furthermore, you need to set up the property variable:assign. It is a boolean value that controls whether your data format supports assignments of a new value to the variables. Another property is item:useReference that should be set to true if the item value can be accessed via reference, similarly to variable:useReference.

Reading the values

Similarly to variables, reading the value is performed with the item:main snippet. It should return a piece of expression that reads the value. The item name is provided in the item format variable. OPT concatenates the pieces coming from all the items creating a container call, so the snippet must form a valid PHP even then:

    case 'item:main':
        return '->readItem(\''.$this->_getVar('item').'\')';

Note that the snippet begins with an object call ->. Let's take a look at a sample container call: $container.item. The first part, $container is processed with variable:main snippet, giving us something like that $this->_data['container'] in the default data format. Then, .item is processed with our data format and item:main snippet that gives ->readItem('item'). The final code is $this->_data['container']->readItem('item').

It is up to programmer to validate the variable before choosing the data format.

Modifying the variable values

It is performed with item:assign snippet similarly to variable:item:

    case 'item:assign':
        return '->saveItem(\''.$this->_getVar('item').'\', '.$this->_getVar('value').')';
5.7. New data formats
5.7.3. Sections
5.7.2. Container items
« Previous
5.7.4. Format plugins
Next »

5.7.3. Sections

The data formats can be used to affect the section behavior. Sections are quite complex elements, when it comes to the implementation and there are many issues that you have to pay attention to.

Configuration

The snippets that affect section behavior are a part of section group that must be added to your $_supports list. The following properties are required to be set:

  1. section:useReference - true, if the section variables can be obtained via reference (similarly to variable:useReference and item:useReference)
  2. section:anyRequests - specify there any extra requests from the section manager that are required by the data format to create a relationship with the parent section. The request is saved to the requestedData format variable. The recognized values are:
    • ancestorNames - returns the names of all the section ancestors.
    • ancestorNumbers - returns the numbers (nesting levels) of all the section ancestors.
  3. section:itemAssign - true, if the assignment to the current list item (i.e. $sectionName) is allowed.
  4. section:variableAssign - true, if the assignment to the current list item variable (i.e. $sectionName.variable) is allowed.

The list of all the snippets used by sections is:

  1. section:init
  2. section:endLoop
  3. section:isNotEmpty
  4. section:started
  5. section:finished
  6. section:done
  7. section:loopBefore
  8. section:startAscLoop
  9. section:startDescLoop
  10. section:item
  11. section:variable
  12. section:reset
  13. section:next
  14. section:valid
  15. section:populate
  16. section:count
  17. section:size
  18. section:iterator
  19. section:isFirst
  20. section:isLast
  21. section:isExtreme

Conventions

The section instructions require the data format to use the following variable names in PHP code snippets:

  1. $_sectSECTIONNAME_vals - the list of section items.
  2. $_sectSECTIONNAME_v - the currently rendered item.
  3. $_sectSECTIONNAME_cnt - the number of items in the list.

The code snippets returned by your data formats are obliged to use the variable names mentioned above.

Usually, the default data formats use also $_sectSECTIONNAME_i or $_sectSECTIONNESTING_i as an iterator variables, but this is not required.

If the $_sectSECTIONNAME_v variable is required, OPT executes the section:forceItemVariables action to notify the data format about it.

Basic iteration

The most important snippets are section:init, section:isNotEmpty, section:startAscLoop, section:startDescLoop and section:endLoop.

First, the section must obtain the list data that are going to be rendered. This is done in section:init. Basically, if the section has no parent, the data should be located somewhere in the $this->_data array that stores the local variables unless the datasource attribute is provided. However, in other case we must ask the parent section for the necessary data. Below, you can find the complete implementation from SingleArray data format:

// Get the section data
$section = $this->_getVar('section');
 
// If the section has a parent, we must obtain also the parent section data and ask
// its data format to generate the link to the data.
if(!is_null($section['parent']))
{
    $parent = Opt_Instruction_BaseSection::getSection($section['parent']);
    $parent['format']->assign('item', $section['name']);
 
    // Access via reference, if possible
    if($parent['format']->property('section:useReference'))
    {
        return '$_sect'.$section['name'].'_vals = &'.$parent['format']->get('section:variable').'; ';
    }
    return '$_sect'.$section['name'].'_vals = '.$parent['format']->get('section:variable').'; ';
}
// The "datasource" attribute is set
elseif(!is_null($section['datasource']))
{
    return '$_sect'.$section['name'].'_vals = '.$section['datasource'].'; ';
}
// Otherwise, we simply get the data from a template variable
else
{
    $this->assign('item', $section['name']);
    return '$_sect'.$section['name'].'_vals = &'.$this->get('variable:main').'; ';
}

Note that sections do not have to read their data from the parent section variable. For example, the default Array data format uses separate template variables for storing nested sections and uses the iteration variables to create relationships. It makes use of the section:anyRequests property, asking for the list of parent section nesting levels, so that it could generate the correct calls:

$section = $this->_getVar('section');
 
if(!is_null($section['datasource']))
{
    return '$_sect'.$section['name'].'_vals = '.$section['datasource'].'; ';
}
 
$this->assign('item', $section['name']);
$code = '$_sect'.$section['name'].'_vals = &'.$this->get('variable:main');
 
$ancestors = $this->_getVar('requestedData');
foreach($ancestors as $i)
{
    $code .= '[$_sect'.$i.'_i]';
}
 
return $code.';';

Once we have obtained the data, we have to check whether the list actually contains any item. This is done with a conditional instruction and the data format must provide the condition in section:isNotEmpty snippet:

$section = $this->_getVar('section');
return 'is_array($_sect'.$section['name'].'_vals) && ($_sect'.$section['name'].'_cnt = sizeof($_sect'.$section['name'].'_vals)) > 0';

The condition must also save the total number of items to the $_sectSECTIONNAME_cnt variable, as it is shown in the code snippet above.

Optionally, the data format may define some extra code in the following snippets:

  1. section:started - executed just after the condition that checks if the section contains elements (opt:show tag)
  2. section:loopBefore - executed just before entering the section loop.
  3. section:finished - opposite of section:started. It is executed just before finishing the section condition block (</opt:show>).
  4. section:done - executed after the condition block.

If the data format has nothing to add to these snippets, it should return empty strings then.

Note that the condition block is always added to the section, even if it does not use opt:show tag.

Finally, the data format provides a basic loop header that iterates through all the list elements. There are expected two versions of it: section:startAscLoop for the ascending order and section:startDescLoop for the descending order. It is up to you what loop to use. For example, both Array and SingleArray data formats use ordinary for, whereas Objective provides foreach. The snippet must contain an opening curly bracket:

return 'for($_sect'.$section['nesting'].'_i = 0; $_sect'.$section['nesting'].'_i < $_sect'.$section['name'].'_cnt; $_sect'.$section['nesting'].'_i++){ ';

This is the absolute minimum that must be implemented for section, however it is not the end.

Section record structure

The code snippets shown above make use of the $section array obtained from the section format variable. It contains all the information about the processed section:

  1. name - the section name
  2. parent - the name of the parent section or null
  3. order - asc for ascending order and desc for descending
  4. datasource - the compiled expression provided in the datasource attribute or null
  5. display - the display conditional expression provided in the display attribute or null
  6. separator - the value of the separator attribute or null
  7. show - the opt:show node or null
  8. node - the main section node
  9. attr - the attribute node, if it is an attributed section or null
  10. format - the data format that is responsible for implementing the specified section

The array for the current section can be always obtained from the format variable list:

$section = $this->_getVar('section');

If we know the name of a different section, we may obtain its array, too:

$section = Opt_Instruction_BaseSection::getSection('someOtherSection');

Accessing list elements

Another element that data formats are responsible for is reading the current list item variables:

<opt:section name="foo">
    This: {$foo}
    And this: {$foo.bar}
</opt:section>

The $foo access is processed with section:item snippet and should return the whole item, for example:

$section = $this->_getVar('section');
return '$_sect'.$section['name'].'_v';

The $foo.bar access is processed with section:variable snippet. Here, the following strategy is recommended:

  1. The data format generates the code for $foo part and redirects .bar to the item:item snippet unless it has some specific needs.
  2. If the data format decorates another format, the .bar should be redirected to the decorator.

The example code:

$section = $this->_getVar('section');
if($this->isDecorating())
{
    return '$_sect'.$section['name'].'_v'.$this->_decorated->get('item:item');
}
return '$_sect'.$section['name'].'_v->'.$this->_getVar('item');

The decorator pattern allows using different data formats for sections and for section item data (for example, the list itself is an object, but the list items are arrays).

In the code snippets above, we assumed that the current section item is always stored in $_sect'.$section['name'].'_v variable. It it not always true. For example, in the data formats where sections are based on for loop, it would be nice to have a direct access: $_sect'.$section['name'].'_vals[$_sect'.$section['nesting'].'_i]. Here we face a significant problem. While opt:section does not care about it, some other section do. To avoid it, OPT notifies the data format, if it expects the section item to be stored in $_sect'.$section['name'].'_v. We can capture the notification using the action() method:

public function action($name)
{
    if($name == 'section:forceItemVariables')
    {
        $this->_sectionItemVariables = true;
    }
} // end action();

Then, we may use this state variable to generate different PHP code:

$section = $this->_getVar('section');
if($this->_sectionItemVariables)
{
    if($this->isDecorating())
    {
        return '$_sect'.$section['name'].'_v'.$this->_decorated->get('item:item');
    }
    $section = $this->_getVar('section');
    return '$_sect'.$section['name'].'_v[\''.$this->_getVar('item').'\']';
}
if($this->isDecorating())
{
    return '$_sect'.$section['name'].'_vals[$_sect'.$section['nesting'].'_i]'.$this->_decorated->get('item:item');
}
return '$_sect'.$section['name'].'_vals[$_sect'.$section['nesting'].'_i][\''.$this->_getVar('item').'\']';

Similarly to variable:assign and item:assign, the sections may provide section:itemAssign and section:variableAssign to process assigning new values to the section variables.

Iteration snippets

opt:section, the simplest section instruction uses the section:startAscLoop and section:startDescLoop to generate the iteration code. The other section types have more complex implementation and they require a lower-level iteration snippets to be available. The set of iteration snippets is quite similar to the methods provided by the PHP Iterator interface:

  1. section:reset - PHP code that resets the iterator to the first element.
  2. section:next - PHP code that moves to the next section item.
  3. section:valid - PHP expression that tests if the current item actually exists.

For the descending section order, the snippets above should actually iterate from the last to the first list item!

Usually, when using the iteration snippets, the section code must make use of the $_sect'.$section['name'].'_v mentioned earlier. OPT may request to populate this variable with the current item data, using the section:populate snippet. The code below comes from the Objective format implementation:

$section = $this->_getVar('section');
if($section['order'] == 'asc')
{
    return '$_sect'.$section['name'].'_v = $_sect'.$section['name'].'_vals->current(); $_sect'.$section['name'].'_i = $_sect'.$section['name'].'_vals->key();';
}
else
{
    return '$_sect'.$section['name'].'_v = current($_sect'.$section['name'].'_vals); $_sect'.$section['name'].'_i = key($_sect'.$section['name'].'_vals);';
}

The iterator variable should be returned by the section:iterator snippet, for example:

$section = $this->_getVar('section');
return '$_sect'.$section['name'].'_i';

Special section variable

OPT sections provide some extra information through the $system special variable. The data format should generate the proper PHP codes for all the calls concerning sections. Firstly, we may request to count the number of list items and the variables in the item (implementations from the Array format):

case 'section:count':
    $section = $this->_getVar('section');
    return '$_sect'.$section['name'].'_cnt';
case 'section:size':
    $section = $this->_getVar('section');
    if($this->_sectionItemVariables)
    {
        return 'sizeof($_sect'.$section['name'].'_v)';
    }
    return 'sizeof($_sect'.$section['name'].'_vals[$_sect'.$section['nesting'].'_i])';

Then, we have three expressions that should test whether the current item is:

  1. The first item on the list (section:isFirst)
  2. The last item on the list (section:isLast)
  3. The first OR the last item on the list (section:isExtreme)

Conclusion

Although implementing a data format for sections seems to be quite complex contrary to the variables and container items, but gives you fantastic opportunities impossible to achieve with ordinary PHP interfaces. Individual users may not find it useful, but framework developers or the programmers who wish to create an advanced code base for their projects should be very interested in creating specialized sections that allow to provide the direct access to various data hidden behind abstract and simple sections.

5.7. New data formats
5.7.4. Format plugins
5.7.3. Sections
« Previous
5.8. New instructions
Next »

5.7.4. Format plugins

Data formats can be packed as plugins, however, they need a more sophisticated format. Open Power Template requires the format plugins to:

A sample instruction plugin can be found below:

<?php
// format.Plugin.php
 
class Opt_Format_Plugin extends Opt_Compiler_Format
{
    // ...
} // end Opt_Format_Plugin;

Note that you can also register the data format manually:

$tpl->register(Opt_Class::OPT_FORMAT, 'Foo');   // Registers "Foo" from "Opt_Format_Foo" class
$tpl->register(Opt_Class::OPT_FORMAT, 'Foo', 'Some_Class'); // Registers "Foo" from "Some_Class" class

See also:

5. Extending OPT
5.8. New instructions
5.7.4. Format plugins
« Previous
5.8.1. Processor overview
Next »

5.8. New instructions

Instructions are undoubtedly the most demanding, but also the most powerful way to extend Open Power Template. Most of the library functionality is stored in instructions. In this chapter, we will take a closer look at their architecture and ways to create new ones. However, please note that this requires a bit of knowledge abot the template compiler and how it works.

How the instructions are handled?

The instructions are parsed with the instruction processors. From the technical point of view, it is hard to say, what the instruction really is. In fact, a single processor can handle multiple instructions, a single instruction can be handled by multiple processors, a single instruction may consist of several XML tags and a single XML tag in the opt: namespace can be supported by different instructions. In this chapter, we are going to talk about the instruction processors and how you will split the provided features into instructions - it is up to you.

The instruction parsing is very easy. The processor registers the XML tags and attributes it recognizes (they must lie in one of the namespaces registered in OPT, for example opt:) and since then, the compiler redirects all the occurrences of these items to this processor. Now we must generate a suitable PHP code for them.

What the instruction processors could do?

Note that the tags and attributes in the special namespaces do not appear in the output. The only signs of their occurrence are the results they produce:

5.8. New instructions
5.8.1. Processor overview
5.8. New instructions
« Previous
5.8.2. Generating PHP code
Next »

5.8.1. Processor overview

The code snippet below shows the general structure of an instruction processor:

<?php
 
class Opt_Instruction_MyInstruction extends Opt_Compiler_Processor
{
    protected $_name = 'processorname';
 
    public function configure()
    {
        $this->_addInstructions(array('opt:myInstruction1', 'opt:myInstruction2'));
        $this->_addAttributes(array('opt:myAttribute1', 'opt:myAttribute2'));
    } // end configure();
 
    public function reset()
    {
        // some code ...
    } // end reset();
 
    public function processNode(Opt_Xml_Node $node)
    {
        // some code...
    } // end processNode();
 
    public function processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
    {
        // some code...
    } // end processNode();
 
    public function processSystemVar($system)
    {
        // some code...
    } // end processSystemVar();
} // end Opt_Instruction_MyInstruction;

The most important method of the processor is configure(). Using _addInstructions() and _addAttributes() we define, what XML tags and attributes should be redirected to this processor. The reset() method is called once the compilation is finished. We may use it to clear the processor state before the next compilation.

The rest of method processes everything possible. The processor operates on a template XML tree represented by various types of nodes and inserts the PHP code snippets to them. They will appear in the output compiled template after the linking. Basically, we may handle all the instructions or attributes in one method: processNode() or processAttribute(). However, we may also use the default implementations which redirect the execution to different protected methods, using the tag/attribute name. For example, opt:example tag will be redirected to _processExample(). It is up to you what way you are going to choose.

The instruction processor class name does not have to begin with Opt_Instruction since OPT 2.0.1.

Processing the node children

If the specified tag name is assigned to one of the processor, Open Power Template compiler does not visit the children of such tag by default. The processor must decide what to do with them. It may process them with DOM-like API or redirect them to the processing manually:

public function processNode(Opt_Xml_Node $node)
{
    // Visit also the children
    $this->_process($node);
} // end processNode();

This method is recommended, if the specified node may contain the HTML code again and we would like to make it visible in the output. We may consider a complex instruction:

<opt:instruction>
    <opt:tag1>HTML goes here</opt:tag1>
    <opt:tag2>HTML goes here</opt:tag2>
</opt:instruction>

We do not permit the HTML directly in opt:instruction, so we do not send this node to the processing, but rather use the DOM-like API to manipulate the data:

public function processNode(Opt_Xml_Node $node)
{
    // do not perform recursive search
    $tags1 = $node->getElementsByTagNameNS('opt', 'tag1', false);
    $tags2 = $node->getElementsByTagNameNS('opt', 'tag2', false);
 
    if(sizeof($tags1) != 1)
    {
        throw new Opt_InstructionTooManyItems_Exception('opt:tag1', 'One');
    }
    if(sizeof($tags2) != 1)
    {
        throw new Opt_InstructionTooManyItems_Exception('opt:tag2', 'One');
    }
    // Send the contents of the subnodes to further processing
    $this->_process($tags1[0]);
    $this->_process($tags2[0]);
} // end processNode();

Postprocessing

The processNode() method is executed before entering the child nodes. Sometimes, we may also want to perform some operations after visiting the children. The postprocessing is activated on demand for a particular node:

public function processNode(Opt_Xml_Node $node)
{
    $node->set('postprocess', true);
    $this->_process($node);
} // end processNode();
 
public function postprocessNode(Opt_Xml_Node $node)
{
    // do some extra stuff here...
} // end processNode();

Working with attributes

Working with instruction attributes is very similar to working with nodes. However, we get both the reference to the attribute and the node the attribute is assigned to. In this case, we do not have to send the children of the node to the processing manually. The postprocessing is activated for a particular attribute:

public function processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
{
    doSomeStuff($attr->getValue());
    $attr->set('postprocess', true);
} // end processNode();
 
public function postprocessAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
{
    doSomeStuff($attr->getValue());
} // end processNode();
5.8. New instructions
5.8.2. Generating PHP code
5.8.1. Processor overview
« Previous
5.8.3. Parsing attributes
Next »

5.8.2. Generating PHP code

OPT instructions do not generate the PHP code linearly. Instead, the code snippets are appended or prepended to different buffers associated with each node in the XML tree. There are many buffers available for a single node, inserted into different parts of the tag.

Available code buffers

Code buffers in XML tags

The locations of the code buffers around the XML tags are shown below:

<tag>...</tag> node:

Location of the code buffers around the XML tag

<tag /> node:

Location of the code buffers around the single XML tag

The TAG_AFTER and TAG_BEFORE buffers wrap the whole node and are present in both tag types. We may paste there the PHP code snippets that affect the whole tag, for example pack it in a conditional block or a loop. It is also possible to wrap either opening or closing tag. The content buffers are available only, if the tag is not empty, that is - it contains any children or the TAG_CONTENT buffer is set. This buffer has a priority over the XML content when rendering.

Note that the single tags use different buffer set and it is important to know, what tag we are trying to add the snippets to.

It is possible to replace the output tag name with TAG_NAME buffer. However, unlike to other buffers, it may store only one snippet at once.

The code buffers around the attribute list are also available:

Location of the code buffers around XML attributes

Code buffers around the attributes

The TAG_ATTRIBUTES_AFTER and TAG_ATTRIBUTES_BEFORE wrap the whole attribute list which may be used to control, whether to display them or not. The TAG_BEGINNING_ATTRIBUTES and TAG_ENDING_ATTRIBUTES should be used to generate new, dynamic attributes either before or after the XML-defined ones.

A single attribute uses the following buffers:

  1. ATTRIBUTE_BEGIN, ATTRIBUTE_END - executed before and after the attribute.
  2. ATTRIBUTE_NAME - attribute name replacement (maximum one snippet allowed).
  3. ATTRIBUTE_VALUE - attribute value replacement.

Code buffers around other nodes

Opt_Xml_Cdata, Opt_Xml_Text, Opt_Xml_Expression and Opt_Xml_Root are wrapped with TAG_BEFORE and TAG_AFTER buffers only.

Adding a snippet to the buffer

Each node is equipped with two methods that allow to add new PHP code snippets to the buffers:

public function processNode(Opt_Xml_Node $node)
{
    $node->addAfter(Opt_Xml_Buffer::TAG_BEFORE, ' if(condition) { ');
    $node->addBefore(Opt_Xml_Buffer::TAG_AFTER, ' } ');
 
    $this->_process($node);
} // end processNode();

addAfter() appends the snippet to the end of the buffer, whereas addBefore() prepends it to the beginning of the buffer.

To enclose a node in curly brackets, you should use both of the functions, as it is show in the sample code above. addBefore(Opt_Xml_Buffer::TAG_BEFORE) and addAfter(Opt_Xml_Buffer::TAG_AFTER) wrap the existing snippets in the curly brackets, too, whereas the opposite setting causes the newly inserted block to be wrapped in the existing snippets.

It is possible to fill the buffers in any of the nodes composing the XML tree at any moment of instruction processing.

Other buffer operations

Sometimes, it is necessary to copy the existing buffer from one node to another. OPT provides copyBuffer() method here:

public function processNode(Opt_Xml_Node $node)
{
    // Let $subnode be any other node.
 
    $node->copyBuffer(Opt_Xml_Buffer::TAG_CONTENT, $subnode, Opt_Xml_Buffer::TAG_SINGLE_AFTER);
 
    $this->_process($node);
} // end processNode();

In the example above, we copy the content of TAG_SINGLE_AFTER buffer in $subnode to the TAG_CONTENT buffer in $node. The current buffer size can be obtained with bufferSize(bufferID) method, and clearing the buffer - with clear() (without an argument - clears all the buffers in the node).

5.8. New instructions
5.8.3. Parsing attributes
5.8.2. Generating PHP code
« Previous
5.8.4. XML manipulations
Next »

5.8.3. Parsing attributes

Usually, the instruction tags take some attributes to configure themselves. OPT supports parsing the attribute list with a convenient method, _extractAttributes(). The sample use can be found here:

$params = array(
    'attr1' => array(0 => self::REQUIRED, self::EXPRESSION),
    'attr2' => array(0 => self::REQUIRED, self::ID),
    'attr3' => array(0 => self::OPTIONAL, self::ID, null),
    'attr4' => array(0 => self::OPTIONAL, self::EXPRESSION, null)
);
 
$this->_extractAttributes($node, $params);

The $params array contains a configuration. Each attribute is described by some flags:

  1. REQUIRED, if the attribute is required; otherwise - OPTIONAL
  2. The attribute type:
    • HARD_STRING - any string
    • NUMBER - any number (decimal or hexadecimal)
    • ID - a valid identifier
    • ID_EMP - a valid identifier or empty value
    • BOOL - yes or no
    • EXPRESSION - an OPT expression
    • ASSIGN_EXPR - an OPT expression with assignments allowed
    • STRING - an OPT expression with string value by default
  3. Default value for optional attributes.

The configuration is passed by reference and the method replaces it with the extracted attribute values.

Attribute types

The attribute types divide into two groups. The first one are ordinary values - they are used by the compiler and the end user cannot read their values from a template variable or an expression. Moreover, the end user cannot change the expected type. This group includes HARD_STRING, NUMBER, ID, ID_EMP and BOOL. Another group are the OPT expressions, where the user may specify a template variable, function call etc. There are three such types: EXPRESSION, ASSIGN_EXPR and STRING. Note that the user may switch between these three types by changing the attribute namespace, so even if you have specified STRING, you must be prepared to work with an expression, too.

The instruction processor does not get an expression result in this place, but the expression compiled to PHP code that should be pasted to one of the code snippets and inserted to code buffers. There is no way to predict the expression value during the compilation. Sample use:

public function processNode(Opt_Xml_Node $node)
{
    $params = array(
        'test' => array(0 => self::REQUIRED, self::EXPRESSION)
    );
    $this->_extractAttributes($node, $params);
 
    $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, ' if('.$params['test'].'){ ');
    $node->addAfter(Opt_Xml_Buffer::TAG_AFTER, ' } ');
    $this->_process($node);
} // end processNode();

Variable number of attributes

To handle a variable number of attributes, we have to add a special attribute item to the configuration that will specify the expected type for all the "unknown" attributes:

$params = array(
    'attr1' => array(0 => self::REQUIRED, self::EXPRESSION),
    '__UNKNOWN__' => array(0 => self::OPTIONAL, self::EXPRESSION, null)
);
 
$unknown = $this->_extractAttributes($node, $params);

The extra attributes are returned as a separate array, so that the instruction processor could distinguish them from the predefined attributes.

Specific cases

If we are programming the instruction attribute, we cannot use the _extractAttributes() method to parse the attribute value. Let's consider the following code:

<div opt:myinstruction="$variable">
    ...
</div>

We want to parse the attribute value as an expression and to do this, we need to call the compileExpression() compiler function manually:

public function processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
{
    $expression = $this->_compiler->compileExpression($attr->getValue(), false, Opt_Compiler_Class::ESCAPE_ON);
 
    $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, ' if('.$expression.'){ ');
    $node->addAfter(Opt_Xml_Buffer::TAG_AFTER, ' } ');
} // end processAttribute();

The method takes three arguments:

  1. The expression to compile
  2. Whether we allow to use the assignment operator (true) or not (false)
  3. The escaping policy settings: Opt_Compiler_Class::ESCAPE_ON, Opt_Compiler_Class::ESCAPE_OFF or Opt_Compiler_Class::ESCAPE_BOTH to make the user choose.

Unless we are going to parse the value as an expression, we have to process the value on our own.

5.8. New instructions
5.8.4. XML manipulations
5.8.3. Parsing attributes
« Previous
5.8.5. $system special variable
Next »

5.8.4. XML manipulations

The instruction processors operate on an XML tree. Its organization is quite similar to the Document Object Model and if you have worked with them, you should feel familiar with OPT. However, there are some significant differences and OPT-specific solutions implemented. This chapter shows some aspects of working with OPT-XML trees.

Node types

The XML tree may contain the nodes of the following types:

  1. Opt_Xml_Element - represents the XML tags. May contain subnodes.
  2. Opt_Xml_Text - contrary to DOM, this class is just a container for the text nodes: Opt_Xml_Cdata and Opt_Xml_Expression.
  3. Opt_Xml_Cdata - the character data node, contains the text values.
  4. Opt_Xml_Expression - represents the OPT expressions in curly brackets mixed with the static text.
  5. Opt_Xml_Root - the root node of the tree.
  6. Opt_Xml_Comment - represents XML comments.
  7. Opt_Xml_Attribute - represents an XML attribute. Unlike other node types, these nodes are managed separately by Opt_Xml_Element.
  8. Opt_Xml_Dtd - represents a DTD. Unlike other node types, these nodes are managed separately by Opt_Xml_Root.
  9. Opt_Xml_Prolog - represents an XML prolog. Unlike other node types, these nodes are managed separately by Opt_Xml_Root.

Retrieving the nodes

The children of a node can be retrieved with a simple foreach:

foreach($node as $subnode)
{
    // do something with $subnode here
}

The Opt_Xml_Scannable class that is a base for Opt_Xml_Element, Opt_Xml_Root and Opt_Xml_Text, provides also several methods to retrieve certain nodes of a specified type:

$list = $node->getElementsByTagName('foo', true);
$list = $node->getElementsByTagNameNS('opt', 'foo', true);
$list = $node->getElementsExt('opt', 'foo');

The first method searches for the foo node with an empty namespace. The second argument informs, that this must be a recursive search: we visit all the descendants of the node, not only the direct children. The second method is a generalization of getElementsByTagName() - it allows also searching for a certain namespace. You may use an asterisk to ignore a certain argument:

// search for all the descendants in "opt" namespace
$list = $node->getElementsByTagNameNS('opt', '*', true);
 
// the same, as getElementsByTagName()
$list = $node->getElementsByTagNameNS('*', 'foo', true);

The last method is OPT-specific. It visits all the descendants, but once it finds a matching node, it ignores its descendants. Take a look at the example:

<opt:foo>
    <div>
        <opt:bar>
 
            <opt:foo>
                <div>
                    <opt:bar>Hello world!</opt:bar>
                </div>
            </opt:foo>
 
        </opt:bar>
    </div>
</opt:foo>

And the PHP code:

$barNodes = $node->getElementsByTagNameNS('opt', 'bar');

The code returns only the first occurrence of opt:bar and does not reach the deeper node. This is a very important feature of this method, especially in the instruction context. Suppose that your instruction consists of several tags and moreover it may be nested one in another. The outer instruction must not find and operate on the inner instruction tags and it can be achieved with getElementsExt().

If you need yet another way of visiting the nodes, take a look at the following PHP code template that you might extend to suit your needs:

$queue = new SplQueue;
$queue->enqueue($node);
while($queue->count() > 0)
{
    $item = $queue->dequeue();
 
    // do something here
 
    foreach($item as $subitem)
    {
        $queue->enqueue($subitem);
    }
}

Remember, never use the true recursion with XML trees!

Node comparison

To compare the two nodes, use the === operator:

if($node1 === $node2)
{
    // some action here...
}

The == operator causes the object properties to be compared, which slows down the compiler and produces recursive calls.

Inserting and appending the nodes

The instructions may insert or replace the nodes within the XML tree. The node types that are allowed to contain children, extend the Opt_Xml_Scannable class that provides the common node management methods. You may use the following methods to insert new nodes into a tree and they are used similarly to their DOM equivalents:

The new node is provided always as the first argument. The second one identifies the reference node which may be specified either with an order number or the node object.

Removing the nodes

To remove the children from a node, you may use the following methods:

Note that the physical removal of a node takes place only once you remove the last reference to it.

The PHP garbage collector does not detect reference cycles in PHP 5.2 and earlier. As a result, the node objects that contain some children, are not deleted even if you remove all the external references to them. In order to prepare a node to be deleted, you must call the dispose() method that prepares the node and its contents to be collected:

$node->dispose();
unset($node);

Sorting the nodes

As the PHP code snippets are inserted into the XML node code buffers, sometimes it is necessary to guarantee the correct node order in the children list. OPT provides a special method that sorts the nodes on the children list, using the user-specified criteria:

$node->sort(array(
    'opt:foo',
    'opt:bar',
    'opt:joe',
    '*'
));

The array passed as an argument specifies the element names in the requested order. The array must contain the * element which identifies all the other nodes that may appear on the children list.

5.8. New instructions
5.8.5. $system special variable
5.8.4. XML manipulations
« Previous
5.8.6. Using the data formats
Next »

5.8.5. $system special variable

The instruction processors are allowed to extend the $system special variable. OPT redirects the $system.PROCESSORNAME calls to the specified processor. The processor name can be found in the $_name protected class field:

class Opt_Instruction_MyInstruction extends Opt_Compiler_Processor
{
    protected $_name = 'PROCESSORNAME';
 
    // ...
 
} // end Opt_Instruction_MyInstruction;

The method that handles the special variable is called processSystemVar() and takes an array as the only argument. The array is simply the special variable call (i.e. $system.PROCESSORNAME.something) exploded with a dot:

public function processSystemVar($system)
{
    // Determine the action using the third element of the call.
    switch($system[2])
    {
        case 'foo':
            return doSomething($system[3]);
        case 'bar':
            return doSomethingElse($system[3]);
    }
    return '';
}

The method must return a valid PHP expression code that the special variable will be replaced with.

The expression does not have to provide the write-access, as saving the values to the $system special variable is disabled.

5.8. New instructions
5.8.6. Using the data formats
5.8.5. $system special variable
« Previous
5.8.7. Node and compiler variables
Next »

5.8.6. Using the data formats

Your instructions may also use data formats. It is a good practice to use your own functionality name in your code.

The details concerning the data formats in the instructions can be found here.

See also:

5.8. New instructions
5.8.7. Node and compiler variables
5.8.6. Using the data formats
« Previous
5.8.8. Tips and tricks
Next »

5.8.7. Node and compiler variables

It is possible to store some values in any XML node. This feature is known as node variables and can be used for many purposes:

  1. Saving information for other instruction processors.
  2. Saving information for other part of the same instruction processors.
  3. Informing the compiler, how to process the node.

The nodes are equipped with the methods get() and set():

$node->set('name', 'value');
 
echo $node->get('name');

Accessing to the nonexistent variable causes the get() method to return NULL.

The same methods are available in the Opt_Compiler_Class object.

Available node variables

The following node state variables are recognized by the template compiler:

cdata
Type: Boolean
Only in Opt_Xml_Cdata nodes.
If this variable is set to true, the content of the node is enclosed within <![CDATA[ and ]]>.
commented
Type: Boolean
If this variable is set to true, the entire node and its contents are enclosed within <!-- and -->. The compiler correctly handles nested nodes with this flag enabled and does not produce nested XML comments.
extend
Type: String
Only in Opt_Xml_Root node.
The information for the compiler to extend the current template with the filename stored in this variable. Used to create the template inheritance.
hidden
Type: boolean
If the node is hidden, it is skipped in the last compilation stage, linking the output file. Its contents do not appear in the final result. Initially, all the nodes are hidden; they are automatically converted to visible nodes during the processing, but you may do your own manipulations here.
noEntitize
Type: boolean
Only in Opt_Xml_Cdata nodes.
Causes the XML entities not to be parsed by the compiler.
postprocess
Type: boolean
Informs the instruction processor handler, that the instruction processor must return to this node in the postprocess stage, after processing the node children. This flag can be applied only to the node that has been passed as an argument to the processNode() or processAttribute() method.
single
Type: boolean
Only in Opt_Xml_Element nodes.
Informs that this is a single XML element: <single />. The node is rendered as single if it has no children and this variable is set to true.
snippet
Type: String
Only in Opt_Xml_Root node.
The information for the compiler to extend the current template with the contents of the specified snippet. Used to create the template inheritance.

Furthermore, the following conventions are used:

call:*
The variable names beginning with call: are reserved for the instruction processors that want to store some state for a different instruction processor that could deal with the specified node.
priv:*
The variable names beginning with priv: are reserved for the instruction processor variables and should not be accessed or modified by other instruction processors and the compiler.

Compiler variables

The following variables are available in the template compiler.

branch
The template inheritance branch.
currentTemplate
Contains the currently parsed template name, including the templates loaded by the template inheritance.
escape
The current escaping state (enabled or disabled).
mode
The compilation mode (XML or quirks)
template
Contains the currently parsed top-level template name.
5.8. New instructions
5.8.8. Tips and tricks
5.8.7. Node and compiler variables
« Previous
5.8.9. Instruction plugins
Next »

5.8.8. Tips and tricks

This chapter describes various tips and tricks related to writing the instruction processors.

Conversions

By default, various parts of the compiler work on the original data extracted from the template. However, sometimes it is useful to replace it transparently to something else. The compiler provides the conversion system to manage and perform various conversions on the fly, using the set of simple rules.

An example of a place where the conversions take place are the sections and snippets:

<opt:snippet name="foo">
<p>{$foo.name}</p>
</opt:snippet>
 
<opt:section name="user" opt:use="foo"></opt:section>

Here, the snippet variable, $foo.name is transparently converted into $user.name as the section processor begins to process the inserted content.

The methods to manage conversions are available in the compiler object (Opt_Compiler_Class):

  1. setConversion($pattern, $replacement) - creates a new conversion rule.
  2. unsetConversion($pattern) - removes the conversion rule.
  3. convert($item) - attempts to convert the passed item using the current rules. If none of the rules is present, it returns the original name.

Some conversions recognized by the compiler:

##simplevar_VARIABLENAME

Allows to convert the variable name $variable to some other name. This feature is used in the example above. The section processor reads the snippet name and registers the conversion:

$this->_compiler->setConversion('##simplevar_foo', 'user');

Once the section content has been processed, the conversion is removed.

##var_VARIABLENAME
Allows to convert the template variables @variable to something else. The general rule is very similar to the previous case.

The instruction processors may also create their own conversion points. The available points are described in the chapters concerning certain instructions.

Useful compiler methods

The template compiler provides a number of public methods that let us perform various checks and operations. For example, to locate a different instruction processor, you may use isProcessor() and processor() methods. Read the appropriate API documentation chapter to get to know more about them.

Modifying the template prolog and DTD

The XML prolog and DTD are not kept directly in the XML node. They are managed by the root node (see: Opt_Xml_Root). To create a new prolog or DTD, you must create an object of Opt_Xml_Prolog or Opt_Xml_Dtd and add it to the root node:

$prolog = new Opt_Xml_Prolog;
 
// ...
 
$root->setProlog($prolog);

Removing the CDATA sections

Sometimes we must remove all the CDATA sections from a particular part of the XML document. As we know from the chapter about node variables, it is controlled by the cdata node variable. Fortunately, we do not have to locate the Opt_Xml_Cdata nodes manually, as one of the processors provides a method for this:

$this->_compiler->processor('literal')->disableCDATA($node);

It removes the CDATA sections from the specified node content. If the second, optional argument is set to true, the method also sets the noEntitize node variable to true.

5.8. New instructions
5.8.9. Instruction plugins
5.8.8. Tips and tricks
« Previous
6. Migration
Next »

5.8.9. Instruction plugins

Instructions can be packed as plugins, however, they need a more sophisticated format. Open Power Template requires the instruction plugins to:

A sample instruction plugin can be found below:

<?php
// instruction.Plugin.php
 
class Opt_Instruction_Plugin extends Opt_Compiler_Processor
{
    // ...
} // end Opt_Instruction_Plugin;

Note that you can also register the instruction manually:

$tpl->register(Opt_Class::OPT_INSTRUCTION, 'Foo');  // Registers "Opt_Instruction_Foo"
$tpl->register(Opt_Class::OPT_INSTRUCTION, 'Foo', 'Some_Class'); // Registers "Foo" in the "Some_Class" class

In this case, the class name does not have to begin with Opt_Instruction. The second line shows, how to register such classes in OPT.

See also:

Table of Contents
6. Migration
5.8.9. Instruction plugins
« Previous
6.1. OPT 1.x
Next »

6. Migration

This chapter is intended to help the users of other template engines to get started with OPT 2.x.

6. Migration
6.1. OPT 1.x
6. Migration
« Previous
6.2. PHP
Next »

6.1. OPT 1.x

Open Power Template 2 is a successor of Open Power Template 1 library, however it was written entirely from scratch and the backward compatibility actually does not exist. In this article, the users of the previous version may find useful information and tricks that will help them migrating to the new releases.

Programming interface

The library API has been simplified and many names have been changed to reflect their actual functions better. The code supports many new PHP5 features, such as autoloaders, SPL data structures and PHAR archives. The internal class data have been hermetized and it is not possible to modify them directly from the user space.

The basic issues:

  1. OPT relies now on the common Open Power Libs core that provides the basic functionality such as configuration management, debugging, plug-ins and error handling.
  2. The new class naming convention has been introduced: Opx_Class_Name.
  3. The Opt_Class class is not an all-in-one harvester anymore. Most of its functions have been moved to separate classes.
  4. The templates are represented in the script by views. They are simply objects of the Opt_View class and consist of the template and the data assigned to it.
  5. The template parsing and managing the output has been moved to output objects. They are objects of classes that implement the Opt_Output_Interface. The programmer may write new outputs easily by implementing the specified interface in his classes.
  6. There is no built-in caching mechanism in OPT 2, as this is not a template engine task. Instead, the cache port has been introduced where the programmer may plug-in any caching library using a simple interface.
  7. The plugin architecture and new item registration have been changed.
  8. The errors are still reported by exceptions, but now each error message has its own exception class. Moreover, they are grouped using the class inheritance which allows to capture only particular types of errors.

Template syntax

OPT 1 could not process XML documents, whereas OPT 2 does and makes a real use of this fact. The following compiler modes are available in the new version:

  1. XML mode - in this mode, the templates must be valid XML documents. OPT recognizes and processes XML/HTML tags.
  2. HTML mode - by disabling some XML standard features, you may activate less restrictive HTML mode. Here, OPT still recognizes and processes XML and HTML tags. See Configuration directives.
  3. Quirks mode - it is similar to XML compatibility mode from OPT 1.x. The compiler recognizes only OPT tags and the rest of the document is treated as a static text.

The mode does not affect the OPT tags which must be always valid XML tags. The curly brackets are used to put the OPT expressions in the static text.

<p>
<opt:if test="$variable">
    {$variable}
</opt:if>
</p>

The XML/HTML mode produces several extra implications. It is not allowed to write <tag {$variable}> or <tag attribute="{$variable}">. In order to add a dynamic attribute or tag, you have to use opt:attribute and opt:tag instructions, and to put a dynamic attribute value, you have to switch its namespace into parse::

<p parse:class="$cssClass"> ... </p>

The compiler does not allow to enclose the tags in the incorrect order (even in HTML) and this is the reason why the output must not be a concatenation of several templates executed one after another. In order to create modular templates, you have to use opt:include or template inheritance.

Expressions

The expression language is very similar, but there are some differences in the object access syntax. The naming rules are changed, for example blocks from OPT 1.x are simply called variables now.

The important change is the support for the HTML escaping of the expression results which allows to make automatic XSS filtering on the template side. In OPT 1, the following code would accept every possible value as the variables, even containing new HTML attributes or breaking the code:

<p {$value}>{$text}</p>

The result could be terrible:

<p style="aaa" javascript="aggressive code"><span>a variable that adds some HTML tags</span></p>

OPT introduces a smart three-level escaping control (script, per-template and per-expression control). The escaping rules are:

  1. Every expression put as an XML attribute value should be escaped.
  2. Every expression put as instruction attribute value will be escaped, if the instruction needs it.
  3. Every expression put in the static text will be escaped, if the current escaping policy requires it.

An example of turning on the escaping for a particular variable using the e: modifier:

<p>This variable will be escaped: {e:$variable}</p>

By default, htmlspecialchars() function is used to do the escaping, but the programmer may register his own escaping handler.

Remember that the escaping is not a magic solution for all your problems. While developing your script, you have to remember that the used features will never give you a full protection against all the possible attacks. First of all, they must be used properly and your code should not leave various potentially dangerous options enabled.

Sections

At a first sight, the sections did not change, and the basic functionality remains the same. However, the section behavior has been strictly defined in the new version, and the instructions themselves became more modular. This allows the programmers to create their own section instructions. By default, OPT provides four types of sections:

  1. opt:section - an ordinary section.
  2. opt:tree - displaying the tree. Similar to {tree} instruction from OPT 1.1, but the syntax is completely different.
  3. opt:selector - a combination of section and switch statement.
  4. opt:grid - displaying the items in columns.

They share the same API and may cooperate one with another. The programmer has much more control over creating relationships between the sections thanks to the parent attribute. For example:

<opt:section name="section1">
    <opt:section name="section2">
        <opt:section name="section3" parent="section1">
            ...
        </opt:section>
    </opt:section>
</opt:section>

The internal architecture of sections is now completely hidden to the template designer and configured on the script side. The same applies to the dynamic sections from OPT 1.1 that can be achieved now simply by choosing the StaticGenerator or RuntimeGenerator data format.

Other instructions

OPT 2.0 brings several new instructions and changes the semantics and/or names of the old ones.

  1. {bind} - renamed to opt:snippet and improved to make template inheritance possible.
  2. {insert} - renamed to opt:insert and improved to make template inheritance possible.
  3. {bindEvent} - not implemented. The same effects can be achieved with opt:snippet now.
  4. {bindGroup} - not implemented. The same effects can be achieved with opt:snippet now.
  5. {pagesystem} - not implemented. The same effects can be achieved with more general opt:selector.
  6. {var} - not implemented. Use the assignment operator.
  7. {default} - not implemented. The same effects can be achieved in other ways.
  8. {place} - not implemented. It did not really work correctly and you can achieve much better effects with template inheritance and opt:root.
  9. {php} - not implemented.

Components

The components are still present, but they have been redesigned from scratch to make use of the new features of OPT 2.0. It is much easier to design the component neighborhood and the components are more functional. The programmer may still decide, whether to work with them only on the template side or not. Below, you can find a code of a sample OPT 2.0 component:

<opt:myInput datasource="$fieldData">
    <com:div>
        <p>{$sys.component.title} <span opt:if="$sys.component.description">{$sys.component.description}</span></p>
        <opt:display />
 
        <opt:onEvent name="error">
            <p class="error">{$sys.component.errorMessage}</p>
        </opt:onEvent>
    </com:div>
</opt:myInput>

As you see, the component tag now defines the whole neighborhood and the place to display the component itself is marked with opt:display tag. Moreover, the component can control the HTML attributes of the surrounding tags, if they are moved to the com namespace.

Moreover, a much simpler alternative to components has been introduced: blocks.

Functions

The function set has been widely extended in OPT 2.0. Some of the functions have been ported to the last release of OPT 1.1 before closing that branch: 1.1.5. The significant improvement over OPT 1.x is that many functions became aggregates - they can modify either a single value or a list of values:

{@capitalizedNames is capitalize($names)}
<opt:section name="list" datasource="@capitalizedNames">
    <p>{$list}</p>
</opt:section>

Template modularization

Open Power Template 1.1 offered a limited number of modularization features. There was the include instruction and concatenating the output of the templates. Furthermore, due to the lack of views, the code was longer and harder to maintain.

The situation has changed dramatically in OPT 2.0. Concatenation is not possible anymore in the XML mode, but on the other hand, you get a more advanced opt:include and the dynamic template inheritance. opt:include has been extended to support views and integrated with sections. For example, different actions may create their own views, insert them into a list and the section would render them easily:

$tpl = new Opt_Class;
// some configuration here.
 
$contentContainer = array();
 
// Let's create some views...
$view = new Opt_View('template1.tpl');
$view->customVar = 'some value';
 
$contentContainer[] = array('view' => $view);
 
$view = new Opt_View('template2.tpl');
$view->anotherVar = 'some value';
 
$contentContainer[] = array('view' => $view);
 
// Let's create the main view:
 
$mainView = new Opt_View('main.tpl');
$mainView->content = $contentContainer;
$mainView->title = 'Some title';
 
// Rendering the views:
$output = new Opt_Output_Http;
$output->render($mainView);

And the template code:

<html>
<head>
    <title>{$title}</title>
</head>
<body>
<opt:section name="content">
    <opt:include from="content"><p>Sorry, the specified template has not been found.</p></opt:include>
</opt:section>
</body>
</html>

One of the view advantages is the fact that each view has its own variable scope and you do not have to worry about the naming collisions between different modules. See template inclusion to get to know more.

Another concept, completely new in the OPT project, is the template inheritance, quite similar to the inheritance in the object-oriented programming. Instead of classes, we extend templates and the "methods" are represented by snippets. Below, you can find a sample base template extended by the module-specific content:

<?xml version="1.0" ?>
<opt:root>
<!-- base template -->
<html>
<head>
    <title>{$title}</title>
</head>
<body>
<div id="header">
    <opt:insert snippet="header">
        <h1>My website</h1> 
    </opt:insert>
</div>
<div id="content">
    <opt:insert snippet="content">
        <p>Default content</p> 
    </opt:insert>
</div>
</body>
</html>
</opt:root>

And the extending template:

<?xml version="1.0" ?>
<opt:extend file="base.tpl">
    <opt:snippet name="header">
        <h1>My website</h1>
        <h2>My awesome module</h2>
    </opt:snippet>
    <opt:snippet name="content">
        <p>The module-specific content.</p>
    </opt:snippet>
</opt:extend>

Conclusion

Open Power Template 2 brings lots of new features and significant improvements to the predecessor. If you enjoyed OPT 1.x, you will surely enjoy the new version, too.

6. Migration
6.2. PHP
6.1. OPT 1.x
« Previous
6.3. Smarty™
Next »

6.2. PHP

Many template engines, mostly in the popular frameworks, use PHP as a template language. This article shows, how to switch from PHP-based templates to OPT, what are the differences and why you should do that.

In the presentation layer written in pure PHP, the most characteristic issues are:

  1. Many functions and classes that generate HTML code snippets and return them as their results. They are usually called helpers, because they help writing clean templates.
  2. PHP control structures, such as if or foreach are very common. Every relationship between them must be programmed manually by the template designer.
  3. Complex output flow between nested templates. It is common to parse one template, store its result in a variable and display it later in the other template.
  4. Sometimes the presentation layer reads the data directly from the script structures, or even generates them.

Imperative vs declarative programming

PHP is an imperative language. It means that you specify all the operations it must perform step-by-step using functions and control structures. The control you have is very good, if you are going to optimize something or write a complex algorithm, but it has one important disadvantage. When you look at such code for the first time, you see that it does something and how it is done, but without extra explanations it is often hard to say, what this "it" is. Take a look at the example. You can say get up, find the window, if it is too far away from you, move there. Then push it up, catch the handle and turn it right or simply close that window, please. The first sentence is too long, too complex and someone may not guess, what we want from him to be done. There is one more problem: what if you have to turn the handle to the left or if the window has a completely different closing mechanism?

OPT encourages you to concentrate on the final effect, not the ways to implement it. You should build your template from ready-to-use blocks that implement a small and commonly used algorithm and that can co-operate one with another. However, in order to use them properly, you should forget for a while about functions, PHP loops, and finally - about reinventing the wheel with them. Let's assume we want to display a list of books with their authors. Using PHP, we would write something like this:

<?php if(is_array($this->books)): ?>
<?php foreach($this->books as $book): ?>
<div class="book">
    <h2><?php echo htmlspecialchars($book['title']); ?></h2>
    <p>Authors:</p>
    <ul>
        <?php foreach($book['authors'] as $author): ?>
        <li><?php echo $author['name'].' '.$author['surname']; ?></li>
        <?php endforeach; ?>
    </ul>
</div>
<?php endforeach; ?>
<?php endif; ?>

There some issues in this code that OPT considers wrong:

  1. Too many code for elementary tasks, such as displaying the title.
  2. The code is format-dependent. The $this->books must be an array, and the authors must be saved as the book element authors.
  3. We have to tell PHP that the nested loop is connected with the top one.
  4. Take a deeper look, in the nested we forgot about checking if we can iterate through $book['authors'].
  5. Put more and more PHP and you will notice that the HTML below will be invisible around all those <?php and ?>.

Now the OPT way:

<div class="book" opt:section="books">
    <h2>{$books.title}</h2>
    <p>Authors:</p>
    <ul>
    <opt:section name="authors">
        <li>{$authors.name} {$authors.surname}</li>
    </opt:section>
    </ul>
</div>

The OPT template is much easier to write and maintain. The sections are much more smarter than PHP foreach - they know do not throw warnings if there are no data to display, they know, that authors and books are connected with one-to-many relationship, and finally, you do not have to know, whether the list of books is an array or an object or how the relationship is really implemented on the script side. The same code can be used again without any modifications in other template or in different script. Moreover, OPT knows that the variables must be escaped.

The sections are the most commonly used tools in OPT and they provide even more features for you. Suppose you want to display the authors in the descending order. In PHP, it would depend on the data format - you must know the proper functions, store the output in temporary variables, etc. whereas in OPT all you have to do is to tell that you want to change the order:

<ul>
<opt:section name="authors" order="desc">
    <li>{$authors.name} {$authors.surname}</li>
</opt:section>
</ul>

We know that declarative programming is different from the solutions you have already got used to. However, once you learn more about the declarative instructions in OPT, you will notice that things were never so simple. When we are going to create a new website, we usually begin with the presentation layer which is completed within 30 minutes and usually does not require any further modifications after the script is written. The templates for the administration panel can be used in different projects without changes, even if they are powered by different frameworks. It saves our time and allows us to focus on the project.

Error handling

OPT warns you on much more dangerous problems than PHP and reports them as exceptions. This gives you the possibility to provide a custom error handler that suits your needs and is not a security violation for your website. The common problems that OPT informs about are:

  1. The tags closed in the incorrect order.
  2. Missing attribute value delimiters.
  3. Missing XML prolog.
  4. Calling the resources that are not allowed to be used in templates.
  5. Expression syntax errors, like missing parentheses in (($a + $b) * $c. OPT provides a complex expression parser that captures even such complex mistakes, as method incrementation: ++$object::method()::field::method().
  6. Trying to build an invalid XML output from valid XML templates.

Typical issues solved by OPT

Here we would like to show you, what problems can be solved using declarative programming in OPT:

  1. List processing - sections.
  2. Data separation on lists - opt:separator.
  3. Displaying hierarchical data (trees) - sections (opt:tree)
  4. Displaying the data in columns - sections (opt:grid)
  5. Displaying the pagination links - sections (opt:selector)
  6. Dynamic forms - components
  7. Modular templates - opt:include and the template inheritance. See: Template modularization.
  8. Code reusing - opt:snippet
  9. HTML escaping and XSS filtering - HTML escaping
  10. Data format independence - data formats
  11. Simple runtime tasks - blocks
  12. Internationalization - I18n in OPT

OPT from the script-side

The advantage of PHP-based view layer is usually a good programmer API, especially when we talk about framework. OPT comes with a framework-style API out-of-the-box. You do not have to write massive wrappers that handle the configuration and attempt to put the template engine into a framework structure. OPT's objective architecture provides the following terms:

Below, you can find a sample code that makes use of these features to create a flexible structure of the view layer:

<html>
<head>
    <title>{$title}</title>
</head>
<body>
<opt:section name="content">
    <opt:include from="content"><p>Sorry, the specified template has not been found.</p></opt:include>
</opt:section>
</body>
</html>

And now the PHP code:

$tpl = new Opt_Class;
// some configuration here.
 
$contentContainer = array();
 
// Let's create some views...
$view = new Opt_View('template1.tpl');
$view->customVar = 'some value';
 
$contentContainer[] = array('view' => $view);
 
$view = new Opt_View('template2.tpl');
$view->anotherVar = 'some value';
 
$contentContainer[] = array('view' => $view);
 
// Let's create the main view:
 
$mainView = new Opt_View('main.tpl');
$mainView->content = $contentContainer;
$mainView->title = 'Some title';
 
// Rendering the views:
$output = new Opt_Output_Http;
$output->render($mainView);

Simple, isn't it?

Conclusion

As you can see, a good template engine does not have to introduce new limitations, but may also remove some limitations of PHP. In the past, PHP actually was intended to be a template language, but it is a history now. The language is equipped with richer and richer OOP, the new objective extensions are introduced and the programmers find it as a better and better tool to write massive frameworks and enterprise solutions. At the same time, nothing is done to make writing the templates actually easier.

We know that many programmers do not like template engines because they introduce in fact even more limitations than PHP, as they usually offer just a subset of PHP enclosed in curly brackets. However, this is not the Open Power Template way. And to enjoy OPT, you must not think about it as yet another imperative language for templates...

6. Migration
6.3. Smarty™
6.2. PHP
« Previous
7. API Reference
Next »

6.3. Smarty™

Smarty™ is the most popular template engine for PHP. This chapter covers the migration issues from Smarty 2.6.x to OPT 2.0 and provides a help for the programmers that would like to switch from it.

Basic issues and ideas

The most important difference between the template engines is the syntax. Smarty packs its instructions and commands into curly brackets. The rest of the document is treated as static text and there is no possibility to manipulate its structure. Open Power Template treats the templates as XML documents. The instructions are represented by tags in the opt: namespace and furthermore, the parser understands the HTML structure. The curly brackets are still present, but their usage is limited to put the variables and expressions into a static text:

<p>This is a text {$variable}</p>

The basic idea behind both of the template engines is the same. The template is firstly compiled into the PHP code, and then the template engine simply executes it as a normal script.

When it comes to the project structure, Smarty features imperative programming on the template side, using similar control flow instructions, like in PHP. We use loops and conditional instructions to achieve the required effect. There is also a limited number of eye-candy functions like {mailto} or {html_radios}. Open Power Template favors declarative programming. Although the ordinary programming instructions are still present, they should not be used unless necessary. The template engine offers a set of portable high-level instructions such as opt:section or opt:component to solve the common problems appearing in the templates. The key is to concentrate on the final effect we want to achieve rather than the implementation. Well-written OPT templates are very portable, clean and actually free from the implementation-specific details.

OPT requires the template to be a correct XML document. Especially, the tags must be closed in the proper order.

Syntax elements: instructions and expressions

In Smarty, we work with template functions and expressions containing variables. The template provided a limited number of built-in advanced functions, and the operations on the variable values were possible with modifiers:

{* displaying a variable *}
{$variable}
 
{* a built-in function *}
{if $something}
    Hi universe!
{/if}
 
{* a modifier *}
{$variable|spacify:" "}

Open Power Template structure is completely different. The variables and operators form expressions, like $a + $b. The expression syntax is very similar to the one from the PHP and other programming languages. The expressions may contain functions, also taken from the ordinary programming languages:

<!-- displaying a variable value -->
<p>{$variable}</p>
<!-- a more complex expression -->
<p>{$a + $b}</p>
<!-- a function as a part of the expression -->
<p>{spacify($variable, ' ')}</p>

The functions operate on the argument values and produce a result, similarly to PHP. To create conditions, loops and the rest of this piece of stuff, Open Power Template uses the concept of instructions. An instruction may consist of one or more XML tags or attributes, for example:

<opt:if test="$variable">
<p>A conditionally displayed text</p>
<opt:else><p>Alternative text.</p></opt:else>
</opt:if>
 
<p opt:if="$variable">A conditionally displayed tag.</p>

As the template is an XML document, we cannot use curly brackets directly in the tag, like <tag {$variable}>. Instead, OPT provides several possible techniques, depending on our needs:

<p parse:class="$dynamicallySelectedClass">...</p>
 
<p><opt:attribute name="$attrName" value="$attrValue" />Some text...</p>

In the first case, we simply want to use a dynamic value of an attribute. In this case, we simply change the tag namespace to parse:. In the second one, we want to create a dynamic attribute, where we do not know the name during the compilation. The opt:attribute instruction helps us then.

Expressions

The expression language uses the syntax typical to the ordinary programming languages. Below, you can find a short list of the supported features:

  1. Template variables: $variable.
  2. Local template variables: @variable - they are created and managed by the template only to avoid potential naming collisions with the script data.
  3. Containers: $variable.item
  4. Language variables: $group@text_id - a part of the internationalization system.
  5. Mathematical operators: +, -, *, /
  6. Logical operators: and, or, xor, not
  7. Assignment operator: =, is: $a is 5
  8. The strings are written using single quotes only. Double quotes are not allowed!
  9. Special backtick strings, programmable by the user.
  10. PHP structures syntax: arrays and objects.
  11. Functions: functionName(arguments)

Contrary to Smarty and PHP, template variables do not have actually to be variables. Open Power Template provides an abstraction layer called data formats. Data formats decide, what the particular syntax elements are. It makes the code more portable and frees it from the implementation details. For example, it is not recommended to specify directly the data structure-specific syntax elements:

<!-- not recommended! -->
<p>{$user['id']}</p>
<p>{$anotherUser::name}</p>

Instead, we could use containers and select the appropriate data format on the script side:

<p>{$user.id}</p>
<p>{$anotherUser.name}</p>

The PHP code:

$view->setFormat('user', 'Array');
$view->setFormat('user', 'Objective');

The effect is the same, but now the code is more refactorization-friendly.

Loops

In the template engines, loops are usually used to produce various lists. Smarty offers the programmer two loops: {foreach}, similar to the same control structure in PHP, and {section}. Although Smarty sections had a nice number of features, both of the loops are rather low-level control structures which usually requires to write more code with a higher level of complexity. Let's take a look at a nested loop in Smarty using {foreach}:

{foreach from=$categories key=categoryId item=category}
<div id="c{$categoryId}">
    <h1>{$category.name}</h1>
    <ol>
    {foreach from=$category.products key=productId item=product}
        <li>{$product.name}</li>    
    {/foreach}
    </ol>
</div>
{/foreach}

The same effect using sections is horrible:

{section name=i loop=$categories}
<div id="c{$categories[i].id}">
    <h1>{$categories[i].name}</h1>
    <ol>
    {section name=j loop=$products[i]}
        <li>{$products[i][j].name}</li> 
    {/section}
    </ol>
</div>
{/section}

Open Power Template provides four types of loops:

  1. opt:for
  2. opt:foreach
  3. opt:repeat
  4. sections

However, in most cases you would only need the last one. OPT sections have almost nothing to do with Smarty's. They provide, abstract, high-level interface to display lists on the template side, hiding all the implementation details from the template designer. Let's take at the same example in OPT:

<div parse:id="'c'~$categories.id" opt:section="categories">
    <h1>{$categories.name}</h1>
    <ol>
        <li opt:section="products">{$products.name}</li>
    </ol>
</div>

Here, we used the attribute form, but sections can be also expressed with an <opt:section> tag. Note that we do not have to tell explicitly that products are connected with a relationship with categories. OPT always assumes that the nested section is related to its parent, and if the default behavior does not suit us, we may change it with the parent attribute.

Another advantage of sections is the fact that the template code is completely independent from the real section nature. In the Smarty example, both of the code snippets accepted different data formats:

// For the example with foreach
$tpl->assign('categories', array(0 =>
    array('name' => 'Category 1', 'products' => array(0 =>
        array('name' => 'Product 1'),
        array('name' => 'Product 2'),
        array('name' => 'Product 3'),
    ))
));
 
// For the example with section
$tpl->assign('categories', array(0 =>
    array('name' => 'Category 1')
));
$tpl->assign('products', array(0 =>
    array(0 =>
        array('name' => 'Product 1'),
        array('name' => 'Product 2'),
        array('name' => 'Product 3'),
    )
));

OPT sections use the data formats, mentioned earlier, to deal with such details. We do not have to know them during writing the templates, we just write the PHP script and select the appropriate data format:

// Version 1
$view->setFormat('categories', 'SingleArray');
$view->setFormat('products', 'SingleArray');
$view->categories = array(0 =>
    array('name' => 'Category 1', 'products' => array(0 =>
        array('name' => 'Product 1'),
        array('name' => 'Product 2'),
        array('name' => 'Product 3'),
    ))
);
 
// Version 2
$view->setFormat('categories', 'Array');
$view->setFormat('products', 'Array');
$view->categories = array(0 =>
    array('name' => 'Category 1')
);
$view->products = array(0 =>
    array(0 =>
        array('name' => 'Product 1'),
        array('name' => 'Product 2'),
        array('name' => 'Product 3'),
    )
));

HTML forms

Smarty actually does not provide any support for HTML forms, except five custom functions to produce lists of checkboxes or <select> options. The entire form processing code must be written from scratch. The situation in Open Power Template 2 is completely different. The template engine provides a feature called components which provides the necessary abstraction layer to render and manage the form layouts.

A component consists of two parts:

The division is quite similar to the MVC pattern. Component objects provide the form field logic, whereas the ports decide, how to display them. Furthermore, they are quite independent, as they are connected one to each other during the execution. This means that it is very easy to produce a dynamic HTML form, generated entirely by the script, still retaining the control over the layout in the templates.

To simplify the construction of small or specific ports, OPT supports two types of component ports:

Below, we can find a sample statically deployed port:

<form:input name="name">
    <div opt:component-attributes="default">
        <label parse:for="$system.component.name">Name:</label>
        <opt:display />
 
        <opt:onEvent name="error">
            <p class="error">Error: {$system.component.error}</p>
        </opt:onEvent>
    </div>
</form:input>

The form:input tag can be assigned to the component class that produces a text input field which will be used to create the component object. The components can have various parameters (in the example above, we have one - name with the field identifier). The other features include:

The dynamically deployed ports must load an existing object from a template variable:

<opt:component from="$someField">
    <div opt:component-attributes="default">
        <label parse:for="$system.component.id">{$system.component.title}:</label>
        <opt:display />
 
        <opt:onEvent name="error">
            <p class="error">Error: {$system.component.error}</p>
        </opt:onEvent>
    </div>
</opt:component>

Such port can handle any field.

OPT does not provide ready-to-use components. The programmer must write them on his/her own.

Template modularization

Usually, the application output is constructed of smaller templates. Smarty provides the {include} function:

<div class="content">
    {include file='content.tpl' title=$someTitle otherStuff=$foo}
</div>

Furthermore, as the templates are treated as plain text files, it is possible to concatenate the output on the script-side:

{* header.tpl *}
<html>
<head>
...
</head>
<body>
 
{* content.tpl *}
    <div>
        ...
    </div>
 
{* footer.tpl *}
</body>
</html>

This is not possible in Open Power Template unless you work with the quirks mode. Because of the XML nature of the language, the opened HTML tag must be closed in the same template. The template language provides the opt:include instruction and a feature called template inheritance.

opt:include works similarly to Smarty, except that it can operate on OPT views:

<opt:include str:file="some_template.tpl" localVar="$ourVariable"/>
 
<opt:include view="$viewObject" />

Smarty templates work within the same variable scope. In Open Power Template, the views have their own private scopes and do not see each other's variables. When working with opt:include, we can assign the local view variables with the custom attributes or use the import attribute to import the variables from the current view:

<opt:include str:file="some_template.tpl" import="yes" />

opt:include can be also integrated with sections:

<div id="content">
    <opt:section name="content">
        <opt:include from="content" />
    </opt:section>
</div>

The template inheritance treats the templates similarly to classes in the object-oriented programming. The template contents are grouped into snippets, and a template can extend another template, providing new or overwriting different snippets. The base template provides a structure, where the snippets are rendered. More about the template inheritance and modularization in general can be found here.

API

Smarty API consists of one class-for-everything and the template compiler. OPT uses a more objective approach that resembles the ideas from the popular PHP frameworks. The base class, Opt_Class is used to keep the global configuration, whereas the script operates on views. A view is an object of Opt_View class which consists of the script data assigned to a specified template. To render a view, we need also an output system which decides, where to send the view output. A sample initialization can be found here:

// Configure the library
$tpl = new Opt_Class;
$tpl->sourceDir = './templates/';
$tpl->compileDir = './templates_c/';
$tpl->setup();
 
$view = new Opt_View('some_template.tpl');
$view->templateVariable = 'foo';
$view->anotherVariable = 'bar';
 
$output = new Opt_Output_Http;
$output->render($view);

Furthermore, Open Power Template is not a standalone library, but a part of the Open Power Libs project and requires the OPL core in order to work. The core itself is quite small and provides such features, as:

It is included in the OPT package, so you do not have to download anything extra.

The errors are handled using the PHP5 exceptions. The library provides an advanced, default error handler which provides a rich context help for many exceptions that help identifying the problem and solving it.

Below, you can find an API feature comparison for OPT and Smarty:

Item name Smarty Open Power Template
PHP version PHP4, PHP5 PHP5
API design Class-for-everything Smaller, specialized classes
Views No Yes
Separate variable scopes for templates No Yes
Global configuration Yes Yes
Output Two hard-coded methods Output systems
Template variable management Yes Yes
Error handling With trigger_error() With PHP5 exceptions
Plugin architecture Yes Yes
Caching Yes Interface only
Resources Yes No

OPT does not provide any caching system, as it has been decided that this is not a task for template engines. Instead, it provides the Opt_Caching_Interface which can be used to connect any external caching engine to the library. The resource functionality has been dropped in the early development stage, because it can be implemented independently with PHP streams and the support from the template engine is not necessary.

Extending OPT

Similarly to Smarty, OPT provides a plugin architecture that can be used to extend the template engine with new features. The new features can be registered with Opt_Class::register() method or packed as plugins.

The features that the template engine can be extended with:

Note that many of them actually do not need the plugin architecture thanks to the object-oriented library design. Sometimes we just need to create an object of a specified class and assign it somewhere. The special plugin structure is required to instructions and the data formats only, the other plugins are just plain PHP scripts executed within the Opt_Class context.

Conclusion

There are many significant differences between Smarty and Open Power Template 2. The libraries feature different goals and approaches to the topic. Writing templates in OPT is a bit different from the ordinary programming known from PHP or many other template engines due to lots of declarative features and the concept of Write, what you want to get, not - how it is supposed to work.

Table of Contents
7. API Reference
6.3. Smarty™
« Previous
7.1. Opt_Class class
Next »

7. API Reference

In this chapter we would like to present the Open Power Template API reference. The methods available for the end-user can be found in the Opt_Class, Opt_View, Opt_Output_Http and Opt_Output_Return classes. The rest describes the compiler interface that allows to extend OPT with the new features.

How to use the code?

Open Power Template is a part of Open Power Libs and it requires the core OPL package in order to work. It is provided together with the library. Once you copied the library to the project directory structure, you will find OPT in /path/to/libs/Opt and OPL in /path/to/libs/Opl. Firstly, you have to load the /Opl/Base.php file manually and to initialize the autoloader. Then, you create the object of Opt_Class and configure it.

<?php
// Load the basic code
require('./lib/Opl/Base.php');
 
// Set the OPL files path
Opl_Base::setDirectory('./lib/');
 
// Initialize the autoloader
spl_autoload_register(array('Opl_Loader', 'autoload'));
 
// Create the main OPT object
$tpl = new Opt_Class;
 
?>

Alternatively, you may use the PHAR archives that do some of the basic configuration on their own:

<?php
require('./libs/opl.phar');
require('./libs/opt.phar');
 
$tpl = new Opt_Class;
?>

The detailed information can be found in the OPL core manual.

Conventions

In Open Power Template, there are some conventions concerning the code and naming style:

  1. All the protected and private class elements have names that begin with an underscore: _name().
  2. The methods that should return a particular data, return NULL, if it has not been found or the task has not been completed. Note that this is a bit different, contrary to the PHP standard library where false values are preferred. However, note that sometimes false is a valid value, and in our case, it is always interpreted correctly.
  3. The exceptions are used to report errors.

Code reference issues

In the manual, we make use of several status and type names. Below, you can find their descriptions.

Status

abstract
An abstract method, you have to extend it in the child class.
final
The method cannot be extended.
extendable
The method should be extended, but it is not necessary.
public
Public element
protected
Protected element
private
Private element
static
Static element

Types

string
Any valid string
int
An integer
array
A PHP array
bool
A logical value (true or false)
mixed
More than one valid type. The documentation specified the available types in the description.
void
This pseudo-type informs that the method does not return a value.

Moreover, the class/interface names are also used as types.

7. API Reference
7.1. Opt_Class class
7. API Reference
« Previous
7.1.1. getBufferState()
Next »

7.1. Opt_Class class

ConstructClass

This is the main class of Open Power Template. It provides the configuration, plugin support and initialization issues. You need only one object of this class in your script. Below, a sample initialization is shown:

$tpl = new Opt_Class;
// Load part of the configuration from the external INI file
$tpl->loadConfig('./someconfig.ini');
 
// Configure the paths
$tpl->sourceDir = './templates/';
$tpl->compileDir = './templates_c/';
 
// Register new add-ons
$tpl->register(Opt_Class::OPT_COMPONENT, 'opt:myComponent', 'My_Component_Class');
 
// Perform the initialization
$tpl->setup();

Now we can create Opt_View objects and parse the templates.

Do not forget to call Opt_Class::setup() method.

7.1. Opt_Class class
7.1.1. getBufferState()
7.1. Opt_Class class
« Previous
7.1.2. getCache()
Next »

7.1.1. getBufferState()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean getBufferState( string $name )
Argument list
$name - string
The buffer name
Returned valueThe state of the output buffer.
Versionssince 2.0.1

Open Power Template supports advisory notifications on the output buffer state. They can help deciding, whether to open a new output buffer or using an existing one. Advisory output buffer states are a kind of semaphores. getBufferState() returns true, if the value of the buffer semaphore is greater than zero and false otherwise.

ob_start();
$tpl->setBufferState('buffer', true);
 
// ...
 
if(!$tpl->getBufferState('buffer'))
{
    ob_start();
    $tpl->setBufferState('buffer', true);
}
 
// ...
 
ob_end_flush();
$tpl->setBufferState('buffer', false);

Advisory output buffer states are mostly used in OPT caching systems.

See also:

7.1. Opt_Class class
7.1.2. getCache()
7.1.1. getBufferState()
« Previous
7.1.3. getCompiler()
Next »

7.1.2. getCache()

ConstructFinal accessor method
Visibilitypublic
ReferenceOpt_Caching_Interface getCache()
Returned valueThe current caching interface.
Versionssince 2.0-RC1

Returns the current global caching interface or NULL, if it is not specified.

See also:

7.1. Opt_Class class
7.1.3. getCompiler()
7.1.2. getCache()
« Previous
7.1.4. getTranslationInterface()
Next »

7.1.3. getCompiler()

ConstructFinal accessor method
Visibilitypublic
ReferenceOpt_Compiler_Class getCompiler()
Returned valueThe current compiler object.

Returns the compiler object and optionally loads the necessary classes. Unless you develop instructions or reimplement various core features you do not have to use this method.

Note that this method may cause the compiler to be loaded and initialize which may slow down the library.

7.1. Opt_Class class
7.1.4. getTranslationInterface()
7.1.3. getCompiler()
« Previous
7.1.5. loadConfig()
Next »

7.1.4. getTranslationInterface()

ConstructFinal accessor method
Visibilitypublic
ReferenceOpl_Translation_Interface getTranslationInterface()
Returned valueThe current translation interface.
Versionssince 2.0-beta2

Returns the current OPL translation interface object assigned to OPT. The returned value is null, if no object is set.

See also:

7.1. Opt_Class class
7.1.5. loadConfig()
7.1.4. getTranslationInterface()
« Previous
7.1.6. register()
Next »

7.1.5. loadConfig()

ConstructFinal accessor method
Visibilitypublic
Referencebool loadConfig(mixed $config)
Argument list
$config - mixed
The array of the configuration values or path to the INI file with the configuration.
Returned valueTrue on success.
Versionssince 2.0-dev7

Loads the OPT configuration from external INI file or an array, depending on the $config argument type.

This method is inherited from the OPL core and you will find it also in other OPL projects. Remember that the OPL core is allowed to be rewritten in order to suit specific needs and in this case this method should behave in different way.

See also:

7.1. Opt_Class class
7.1.6. register()
7.1.5. loadConfig()
« Previous
7.1.7. setBufferState()
Next »

7.1.6. register()

ConstructFinal accessor method
Visibilitypublic
Referencevoid register(int $type, mixed $name [, string $value])
Argument list
$type - int
The type of the registered item(s).
$name - mixed
The item name or the list of registered items.
$value - string
The extra item value used with some types.
Thrown exceptionsBadMethodCallException

Registers a new add-on in OPT identified by $type. The next arguments depend on the specified type. The available types are:

  1. Opt_Class::OPT_INSTRUCTION - new instruction processor with the specified $name.
  2. Opt_Class::OPT_NAMESPACE - new XML namespace recognized by OPT specified in $name.
  3. Opt_Class::OPT_FORMAT - new data format specified in $name.
  4. Opt_Class::OPT_COMPONENT - new component XML tag specified in $name. $value is the component class name.
  5. Opt_Class::OPT_BLOCK - new block XML tag specified in $name. $value is the block class name.
  6. Opt_Class::PHP_FUNCTION - new PHP function allowed to be used in templates. $name is the function name visible in templates and `$value - the real PHP function name.
  7. Opt_Class::PHP_CLASS - new PHP class allowed to be used in templates. $name is the class name visible in templates and $value - the real PHP class name.

This method supports also mass registering of items by specifying an array as $name. For types from 1 to 3 it looks like this:

$tpl->register(Opt_Class::OPT_INSTRUCTION, array(
    'Instruction1' => 'My_Instruction_1', 'Instruction2' => 'My_Instruction_2', 'Instruction3' => 'My_Instruction_3'
));

For types from 4 to 7 we have to use associative array:

$tpl->register(Opt_Class::OPT_COMPONENT, array(
    'my:select' => 'My_Select_Component',
    'my:text' => 'My_Text_Component',
    'my:radio' => 'My_Radio_Component'
));

This method must be used before Opt_Class::setup() method.

Registering PHP functions and classes

The templates do not support directly PHP namespaces, however the namespace can be specified during the function/class registration:

$tpl->register(Opt_Class::PHP_CLASS, 'templateClass', 'namespace::myClass');

In the same way we are allowed to register static class methods as functions.

For PHP functions, OPT introduces another interesting feature. Because the argument order in PHP library is quite messy and OPT follows strict rules, the compiler is able to change the order in the compile time, if we specify the additional rules. The order rules are specified before the real PHP function name and are enclosed within #:

$tpl->register(Opt_Class::PHP_FUNCTION, 'regexReplace', '#3,1,2#preg_replace');

It could be read like this:

  1. The first argument in templates must be in the third place in the compiled template.
  2. The second argument - in the first one.
  3. The third one - in the second.

We may also specify some optional values:

$tpl->register(Opt_Class::PHP_FUNCTION, 'foo', '#3,1,2:null#foo');

In this case, the third argument in templates is optional, but in PHP it is required. We must specify the default value (null in this case) for the compiler then by adding :null to the position number.

Registering instructions and formats

For instructions and processors we register their class names and the identifiers they will appear in the parser. If we specify the identifier only, OPT will construct a class name for them, accordingly Opt_Instruction_Identifier and Opt_Format_Identifier. Sometimes we may want to give the classes different names, for example due to the autoloading purposes. In this case, we specify the class as the third argument.

$tpl->register(Opt_Class::OPT_FORMAT, 'Foo');   // Registers "Foo" from "Opt_Format_Foo" class
$tpl->register(Opt_Class::OPT_FORMAT, 'Foo', 'Some_Class'); // Registers "Foo" from "Some_Class"

When registering a group of instructions or formats with a single call of register(), you are obliged to provide assotiative arrays, where the keys are identifiers, and the values - the class names. OPT does not expand the identifiers to the class names in this case.

7.1. Opt_Class class
7.1.7. setBufferState()
7.1.6. register()
« Previous
7.1.8. setCache()
Next »

7.1.7. setBufferState()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setBufferState( string $name , boolean $state )
Argument list
$name - string
The buffer name
$state - boolean
The new buffer state
Versionssince 2.0.1

Open Power Template supports advisory notifications on the output buffer state. They can help deciding, whether to open a new output buffer or using an existing one. Advisory output buffer states are a kind of semaphores. setBufferState() increments the value, if $state is true and decrements for false. The minimum semaphore value is 0. If it reaches this value, setBufferState('buffer', false) has no effect.

ob_start();
$tpl->setBufferState('buffer', true);
 
// ...
 
if(!$tpl->getBufferState('buffer'))
{
    ob_start();
    $tpl->setBufferState('buffer', true);
}
 
// ...
 
ob_end_flush();
$tpl->setBufferState('buffer', false);

Advisory output buffer states are mostly used in OPT caching systems.

See also:

7.1. Opt_Class class
7.1.8. setCache()
7.1.7. setBufferState()
« Previous
7.1.9. setTranslationInterface()
Next »

7.1.8. setCache()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setCache([Opt_Caching_Interface $cache = null])
Argument list
$cache - Opt_Caching_Interface
The new caching interface object to be used with the views.
Versionssince 2.0-RC1

Registers a new global caching interface. The interface will be automatically added to all the newly created views, however, may be later replaced manually in the certain views. The method called without arguments removes the existing global caching interface.

See also:

7.1. Opt_Class class
7.1.9. setTranslationInterface()
7.1.8. setCache()
« Previous
7.1.10. setup()
Next »

7.1.9. setTranslationInterface()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean setTranslationInterface(Opl_Translation_Interface $tf)
Argument list
$tf - Opl_Translation_Interface
The new translation interface to be used to OPT.
Returned valueTrue, if the new translation interface has been installed.
Versionssince 2.0-beta2

Registers the OPL translation interface object to be used for $group@identifier template variable syntax. The argument can be also null, in this case the translation interface is disabled and the method returns false.

See also:

7.1. Opt_Class class
7.1.10. setup()
7.1.9. setTranslationInterface()
« Previous
7.2. Opt_View class
Next »

7.1.10. setup()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setup([mixed $config])
Argument list
$config - mixed
The library configuration to be loaded on setup
Versionssince 2.0-dev7

Performs the main initialization of OPT. If the optional argument $config is specified, it is transparently sent to Opt_Class::loadConfig(). Before using this method, we are obligated to configure the library and load the necessary extensions.

$tpl = new Opt_Class;
$tpl->sourceDir = './templates/';
$tpl->compileDir = './templates_c/';
$tpl->setup();
 
$view = new Opt_View('template.tpl');
$view->foo = 'bar';
 
$out = new Opt_Output_Http;
$out->render($view);

You must not render the views before using Opt_Class::setup(). This may cause an unexpected behavior.

See also:

7. API Reference
7.2. Opt_View class
7.1.10. setup()
« Previous
7.2.1. __construct()
Next »

7.2. Opt_View class

ConstructClass
Versionssince 2.0-dev7

This class represents a view which is an OPT template and the data associated to it. The views can be rendered using the output systems or the opt:include instruction in the templates. It must be pointed that the view contains also all the inherited templates.

Using views

To use a view, we must begin with creating a view object. You should use different object for different templates in your script. Once this step is done, we may assign some data from the script. By default, they will be visible only for the view template, however - we are also able to create global template variables.

$view1 = new Opt_View('template_1.tpl');
$view2 = new Opt_View('template_2.tpl');
 
// Assign the data to local template variables
 
$view2->bar = 'bar';
$view2->foo = 'joe';
 
$view1->foo = 'foo';
$view1->includedView = $view2;
 
// Assign the data to global template variables
 
Opt_View::assignGlobal('globalVar', 'foo');
 
// Render the views:
 
$out = new Opt_Output_Http;
$out->render($view1);

In order to display the $view2, the template template_1.tpl from $view1 must contain the following code:

<opt:include view="$includedView" />
7.2. Opt_View class
7.2.1. __construct()
7.2. Opt_View class
« Previous
7.2.2. __get()
Next »

7.2.1. __construct()

ConstructClass constructor
Reference__construct([string $template])

This the view constructor. We may assign the template file to it during the initialization or do it later:

$view1 = new Opt_View('foo.tpl');
 
$view2 = new Opt_View;
$view2->setTemplate('bar.tpl');

See also:

7.2. Opt_View class
7.2.2. __get()
7.2.1. __construct()
« Previous
7.2.3. __isset()
Next »

7.2.2. __get()

ConstructMagic method
Visibilitypublic
Referencemixed &__get(string $name)
Versionssince 2.0-beta2

This magic method provides an alternative way to read the local template variable value:

echo $view->variable;

The value is returned by reference.

It is recommended to use the magic method instead of Opt_View::get() unless we are not going to specify a dynamic variable name.

See also:

7.2. Opt_View class
7.2.3. __isset()
7.2.2. __get()
« Previous
7.2.4. __set()
Next »

7.2.3. __isset()

ConstructMagic method
Visibilitypublic
Referenceboolean __isset(string $name)
Versionssince 2.0-beta2

This magic method provides an alternative way to check if a template local variable $name exists:

if(isset($view->variable))
{
    // some code...
}

It is recommended to use the magic method instead of Opt_View::defined() unless we are not going to specify a dynamic variable name.

See also:

7.2. Opt_View class
7.2.4. __set()
7.2.3. __isset()
« Previous
7.2.5. __unset()
Next »

7.2.4. __set()

ConstructMagic method
Visibilitypublic
Referencemixed __set(string $name, mixed $value)

This magic method provides an alternative way to create template local variables:

$view->variable = 'Some value';

It is recommended to use the magic method instead of Opt_View::assign() unless we are not going to specify a dynamic variable name.

See also:

7.2. Opt_View class
7.2.5. __unset()
7.2.4. __set()
« Previous
7.2.6. assign()
Next »

7.2.5. __unset()

ConstructMagic method
Visibilitypublic
Referencevoid __unset(string $name)
Versionssince 2.0-beta2

This magic method provides an alternative way to remove a template local variable:

unset($view->variable);

It is recommended to use the magic method instead of Opt_View::remove() unless we are not going to specify a dynamic variable name.

See also:

7.2. Opt_View class
7.2.6. assign()
7.2.5. __unset()
« Previous
7.2.7. assignGlobal()
Next »

7.2.6. assign()

ConstructFinal accessor method
Visibilitypublic
Referencevoid Opt_View::assign( string $name, mixed $data )

Assigns the $data to the local template variable $name:

$view->assign('object', 'sunglasses');

The template:

<p>Hello my friend, do you need {$object}?</p>

You may also create a variable value using the magic method Opt_View::__set(): $view->variable = 'foo'. It is the recommended solution.

See also:

7.2. Opt_View class
7.2.7. assignGlobal()
7.2.6. assign()
« Previous
7.2.8. assignGroup()
Next »

7.2.7. assignGlobal()

ConstructStatic method
Visibilitypublic
Referencevoid assignGlobal( string $name, mixed $data )

Assigns the $data to the global template variable $name:

Opt_View::assignGlobal('object', 'sunglasses');

The template:

<p>Hello my friend, do you need {$global.object}?</p>

See also:

7.2. Opt_View class
7.2.8. assignGroup()
7.2.7. assignGlobal()
« Previous
7.2.9. assignGroupGlobal()
Next »

7.2.8. assignGroup()

ConstructFinal accessor method
Visibilitypublic
Referencevoid assignGroup( array $variables )

Uses an associative array $variables to create many local template variables:

$view->assignGlobal( array(
    'foo' => 'foo',
    'bar' => 'bar',
    'joe' => 'joe'
));

The template:

<p>{$foo}</p>
<p>{$bar}</p>
<p>{$joe}</p>

See also:

7.2. Opt_View class
7.2.9. assignGroupGlobal()
7.2.8. assignGroup()
« Previous
7.2.10. assignRef()
Next »

7.2.9. assignGroupGlobal()

ConstructStatic method
Visibilitypublic
Referencevoid assignGroupGlobal( array $variables )

Uses an associative array $variables to create many global template variables:

Opt_View::assignGlobalGroup( array(
    'foo' => 'foo',
    'bar' => 'bar',
    'joe' => 'joe'
));

The template:

<p>{$global.foo}</p>
<p>{$global.bar}</p>
<p>{$global.joe}</p>

See also:

7.2. Opt_View class
7.2.10. assignRef()
7.2.9. assignGroupGlobal()
« Previous
7.2.11. assignRefGlobal()
Next »

7.2.10. assignRef()

ConstructFinal accessor method
Visibilitypublic
Referencevoid assignRef( string $name, mixed &$value )

Registers a new template local variable with the name $name and passes its value by reference. It should be used for larger variables whose copying could be too slow.

See also:

7.2. Opt_View class
7.2.11. assignRefGlobal()
7.2.10. assignRef()
« Previous
7.2.12. clear()
Next »

7.2.11. assignRefGlobal()

ConstructStatic method
Visibilitypublic
Referencevoid assignRefGlobal( string $name, mixed &$value )

Registers a new template global variable with the name $name and passes its value by reference. It should be used for larger variables whose copying could be too slow.

See also:

7.2. Opt_View class
7.2.12. clear()
7.2.11. assignRefGlobal()
« Previous
7.2.13. defined()
Next »

7.2.12. clear()

ConstructFinal accessor method
Visibilitypublic
Referencevoid clear()
Versionssince 2.0.0

The Opt_View class contains some private static buffers, for example for the global template variables or the captured content. This method can be used to clear those buffers, if it is necessary.

7.2. Opt_View class
7.2.13. defined()
7.2.12. clear()
« Previous
7.2.14. definedGlobal()
Next »

7.2.13. defined()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean defined( string $name )

Returns true, if there already exists a local template variable $name assigned to the current view.

if(!$view->defined('foo'))
{
    $view->foo = $someValue;
}

Alternatively, you may use the following syntax:

if(!isset($view->foo))
{
    $view->foo = $someValue;
}

See also:

7.2. Opt_View class
7.2.14. definedGlobal()
7.2.13. defined()
« Previous
7.2.15. get()
Next »

7.2.14. definedGlobal()

ConstructStatic method
Visibilitypublic
Referenceboolean definedGlobal( string $name )

Returns true, if there already exists a global template variable $name:

if(!Opt_View::defined('foo'))
{
    Opt_View::assignGlobal('foo', $someValue);
}

See also:

7.2. Opt_View class
7.2.15. get()
7.2.14. definedGlobal()
« Previous
7.2.16. getBranch()
Next »

7.2.15. get()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean get( string $name )
Argument list
$name - string
The variable name
Returned valueThe template variable value
Versionssince 2.0-beta2

Returns the value of the $name template variable.

$view->variable = 'foo';
 
echo $view->get('variable'); // prints "foo"

You may also read the variable value using the magic method Opt_View::get(): $view->variable. It is the recommended solution.

See also:

7.2. Opt_View class
7.2.16. getBranch()
7.2.15. get()
« Previous
7.2.17. getCache()
Next »

7.2.16. getBranch()

ConstructFinal accessor method
Visibilitypublic
Referencestring getBranch()
Returned valueThe branch name

Returns the current branch which the view object is associated to.

See also:

7.2. Opt_View class
7.2.17. getCache()
7.2.16. getBranch()
« Previous
7.2.18. getGlobal()
Next »

7.2.17. getCache()

ConstructFinal accessor method
Visibilitypublic
ReferenceOpt_Caching_Interface getCache()
Returned valueThe caching interface object
Versionssince 2.0-RC1

Returns the current caching interface associated with the view. If the global caching interface has been set for OPT and the programmer has not used setCache() method in the view, it returns the global caching interface.

See also:

7.2. Opt_View class
7.2.18. getGlobal()
7.2.17. getCache()
« Previous
7.2.19. getOutputBuffers()
Next »

7.2.18. getGlobal()

ConstructStatic method
Visibilitypublic
Referencemixed getGlobal( string $name )
Versionssince 2.0-beta2

Returns the value of the $name global variable.

Opt_View::getGlobal('variable');

See also:

7.2. Opt_View class
7.2.19. getOutputBuffers()
7.2.18. getGlobal()
« Previous
7.2.20. getTemplate()
Next »

7.2.19. getOutputBuffers()

ConstructFinal accessor method
Visibilitypublic
Referencearray getOutputBuffers()
Returned valueThe static parts of the template.
Versionssince 2.0.1

This method is used in the caching systems to support the opt:dynamic tag in the cached views. It returns the list of cached parts of the executed template, except the last one which must be closed manually.

The dynamic part of the cached template is saved by the compiler in the file with the .dyn extension in the compilation directory. It is a serialized array of PHP code snippets which should be mingled with the static data returned by this method. Below, you can see a sample mingling algorithm:

if($view->hasDynamicContent())
{
    $list = $view->getOutputBuffers();
    $dynamic = unserialize(file_get_contents($tpl->compileDir.$view->_convert($view->getTemplate()).'.dyn'));
 
    $output = '';
 
    for($i = 0; $i < sizeof($list); $i++)
    {
        $output .= $list[$i].'<'.'?php '.$dynamic[$i].' ?>';
    }
    $output .= ob_get_flush();
}

In order to get the content of the last buffer, you must use ob_get_flush(), as the returned code must also appear in the browser.

See also:

7.2. Opt_View class
7.2.20. getTemplate()
7.2.19. getOutputBuffers()
« Previous
7.2.21. getTime()
Next »

7.2.20. getTemplate()

ConstructFinal accessor method
Visibilitypublic
Referencestring getTemplate()
Returned valueThe template file name
Versionssince 2.0-Beta2

Returns the template name associated with the view.

See also:

7.2. Opt_View class
7.2.21. getTime()
7.2.20. getTemplate()
« Previous
7.2.22. hasDynamicContent()
Next »

7.2.21. getTime()

ConstructFinal accessor method
Visibilitypublic
Referencefloat getTime()
Returned valueThe template processing time

Returns the view processing time in seconds.

This method should be used only once the view has already been rendered with some output.

7.2. Opt_View class
7.2.22. hasDynamicContent()
7.2.21. getTime()
« Previous
7.2.23. inherit()
Next »

7.2.22. hasDynamicContent()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean hasDynamicContent()
Returned valueTrue, if the cached content still has some dynamic parts.
Versionssince 2.0.1

This method is used in the caching systems to support the opt:dynamic tag in the cached views. It returns true, if the instruction has been used and there is some content that must remain dynamic even in the cache file. The method should be used in conjunction with getOutputBuffers().

See also:

7.2. Opt_View class
7.2.23. inherit()
7.2.22. hasDynamicContent()
« Previous
7.2.24. remove()
Next »

7.2.23. inherit()

ConstructFinal accessor method
Visibilitypublic
Referencevoid inherit( string $sourceFile [, string $destinationFile ] )

Performs a dynamic inheritance on the templates associated to the current view. The method may be called in two ways:

$view->inherit('inherited_by.tpl');

In this case, the template associated to the view is inherited by inherited_by.tpl. Alternatively, we may do the following:

$view->inherit('inheriting_template.tpl', 'inherited_by.tpl');

Now we inherit the inheriting_template.tpl with inherited_by.tpl. This allows to create compound inheritance chains:

$view = new Opt_View('template1.tpl');
$view->inherit('template2.tpl');
$view->inherit('template2.tpl', 'template3.tpl');
$view->inherit('template3.tpl', 'template4.tpl');

In order to make the dynamic inheritance possible, the templates must allow it:

<opt:extend file="default_file.tpl" dynamic="yes">
 
    <!-- some code snippets -->
 
</opt:extend>

Another way to create the dynamic inheritance is to use branches. See the chapter about inheritance to get to know more.

Extending snippets

In OPT, the templates are not limited to extend whole templates. It is possible to extend one of existing snippets created with opt:snippet instruction. In order to get to know more about extending snippets, see a chapter about template inheritance.

7.2. Opt_View class
7.2.24. remove()
7.2.23. inherit()
« Previous
7.2.25. removeGlobal()
Next »

7.2.24. remove()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean remove( string $name )

Removes the existing local view variable $name and returns true in case of success.

Alternatively, you may use the following syntax:

unset($view->variable);

See also:

7.2. Opt_View class
7.2.25. removeGlobal()
7.2.24. remove()
« Previous
7.2.26. setBranch()
Next »

7.2.25. removeGlobal()

ConstructStatic method
Visibilitypublic
Referenceboolean removeGlobal( string $name )

Removes the existing global template variable $name and returns true in case of success.

See also:

7.2. Opt_View class
7.2.26. setBranch()
7.2.25. removeGlobal()
« Previous
7.2.27. setCache()
Next »

7.2.26. setBranch()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setBranch( string $branch )

Assigns the current view to the specified branch which is used in the template inheritance. In order to disable branches, set $branch to null.

See also:

7.2. Opt_View class
7.2.27. setCache()
7.2.26. setBranch()
« Previous
7.2.28. setFormat()
Next »

7.2.27. setCache()

ConstructFinal accessor method
Visibilitypublic
Referencevoid Opt_View([Opt_Caching_Interface $cache = null])
Versionssince 2.0-RC1

Registers a new view caching interface, overwriting the global caching settings. The method called without arguments, removes the existing caching interface from the view.

See also:

7.2. Opt_View class
7.2.28. setFormat()
7.2.27. setCache()
« Previous
7.2.29. setFormatGlobal()
Next »

7.2.28. setFormat()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setFormat( string $variable, string $format )

Sets the format to the local template variable $variable. To get to know more about data formats in OPT, see a chapter about data formats.

See also:

7.2. Opt_View class
7.2.29. setFormatGlobal()
7.2.28. setFormat()
« Previous
7.2.30. setTemplate()
Next »

7.2.29. setFormatGlobal()

ConstructStatic method
Visibilitypublic
Referencevoid setFormatGlobal( string $item, string $format [, boolean $global = true ] )

Sets the format to the global template item $name. To get to know more about data formats in OPT, see a chapter about data formats.

Opt_View::setFormatGlobal() in fact provides a nice, object-independent wrapper to specify the global variable format without the need of having an Opt_View object. The following two lines are equivalent:

$view->setFormat('global.variable', 'Objective');
Opt_View::setFormatGlobal('variable', 'Objective');

If the $global argument is false, the method does not prepend global. string to the registered item. It can be used to specify the data format of the local variable in all the views, instead of one. Note that a particular view can overwrite this format with setFormat().

The $global attribute is available since OPT 2.0.1.

See also:

7.2. Opt_View class
7.2.30. setTemplate()
7.2.29. setFormatGlobal()
« Previous
7.3. Opt_Output_Http class
Next »

7.2.30. setTemplate()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setTemplate( string $template )

Binds a template file $template to the view. Alternatively, you may bind it directly in the view constructor.

See also:

7. API Reference
7.3. Opt_Output_Http class
7.2.30. setTemplate()
« Previous
7.3.1. getHeaders()
Next »

7.3. Opt_Output_Http class

ConstructClass
ImplementsOpt_Output_Interface
Versionssince 2.0-dev7

This output sends the executed views as a HTTP response to the user. In spite of executing views, it provides also the HTTP header management functionality.

Some of the features require Open Power Classes Opc_Visit class in order to work.

The class defines six constants:

  1. Opt_Output_Http::HTML
  2. Opt_Output_Http::XHTML
  3. Opt_Output_Http::FORCED_XHTML
  4. Opt_Output_Http::WML
  5. Opt_Output_Http::XML
  6. Opt_Output_Http::TXT

These are the default content types for Opt_Output_Http::setContentType() method.

If the templates are written in the XML/HTML mode, the render() method can be called only once, for one view, in this output system. The next attempt raises an exception. This prevents the script from generating an invalid script output from the valid XML templates. In order to create modular templates, please read the template modularization.

Usage

Using Opt_Output_Http is easy:

// Initialize the OPT
$tpl = new Opt_Class;
 
// Create the view
$view = new Opt_View('template.tpl');
$view->data = $someData;
 
// Render the view
$out = Opt_Output_Http;
$out->render($view);
7.3. Opt_Output_Http class
7.3.1. getHeaders()
7.3. Opt_Output_Http class
« Previous
7.3.2. render()
Next »

7.3.1. getHeaders()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean getHeaders()
Returned valueAssotiative array of currently set headers
Versionssince 2.0.2

Returns an assotiative array of the headers that have been set so far. The header name serves as the array index, and the value contains the header value.

$headers = $output->getHeaders();
if($headers['Content-type'] == 'text/html;charset=utf-8')
{
    echo 'This is text/html';
}
7.3. Opt_Output_Http class
7.3.2. render()
7.3.1. getHeaders()
« Previous
7.3.3. sendHeaders()
Next »

7.3.2. render()

ConstructFinal accessor method
Visibilitypublic
Referencemixed render(Opt_View $view)
Argument list
$view - Opt_View
The view to render

Renders the $view and sends the results as a HTTP response. If OPT works in XML mode, this method may be executed only once for maximum one view. This prevents from building an invalid XML document from valid XML templates by concatenating them in the unsupported way.

Creating modular templates that can be used in XML mode is described in a chapter about modularization.

7.3. Opt_Output_Http class
7.3.3. sendHeaders()
7.3.2. render()
« Previous
7.3.4. setContentType()
Next »

7.3.3. sendHeaders()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean sendHeaders()

Sends the buffered HTTP headers to the browser and returns true in case of success. The method recognizes correctly the header names HTTP/1.0 and HTTP/1.1 formatting them according to the HTTP protocol semantics.

The programmer does not have to call this method manually.

7.3. Opt_Output_Http class
7.3.4. setContentType()
7.3.3. sendHeaders()
« Previous
7.3.5. setHeader()
Next »

7.3.4. setContentType()

ConstructFinal accessor method
Visibilitypublic
Referencevoid setContentType( [ mixed $contentType [, string $charset ]] )
Argument list
$contentType - mixed
The requested content type defined either in the string form or with the class constants
$charset - string
The content encoding

Generates the Content-type header. If any of the arguments is not specified or its value is null, it is taken from the OPT configuration. The $contentType can be either the name of the MIME type or the Opt_Output_Http class constant that identifies a particular type:

  1. Opt_Output_Http::HTML
  2. Opt_Output_Http::XHTML
  3. Opt_Output_Http::FORCED_XHTML
  4. Opt_Output_Http::WML
  5. Opt_Output_Http::XML
  6. Opt_Output_Http::TXT

If the OPC library is present, the contentNegotiation configuration directive is allowed. In this case the method uses the Opc_Visit class to verify if the browser supports the specified MIME type.

Do not turn on contentNegotiation unless you have Open Power Classes installed!

The difference between Opt_Output_Http::XHTML and Opt_Output_Http::FORCED_XHTML is visible only if the content negotiation is active. Opt_Output_Http::FORCED_XHTML generates the XHTML content type always, if browser supports it, while in Opt_Output_Http::XHTML it must also have a higher priority than text/html.

7.3. Opt_Output_Http class
7.3.5. setHeader()
7.3.4. setContentType()
« Previous
7.4. Opt_Output_Return class
Next »

7.3.5. setHeader()

ConstructFinal accessor method
Visibilitypublic
Referenceboolean setHeader(string $name, string $value)
Argument list
$name - string
Header name
$value - string
Header value

Sends a HTTP header, sanitizing its value by removing the new line characters. If the configuration directive headerBuffering is disabled, the new header is sent instantly, otherwise it is buffered and sent together with the rendered view.

The method returns false, if the headers have been already sent.

7. API Reference
7.4. Opt_Output_Return class
7.3.5. setHeader()
« Previous
7.5. Opt_Compiler_Class class
Next »

7.4. Opt_Output_Return class

ConstructClass
ImplementsOpt_Output_Interface
Versionssince 2.0-dev7

With this output system, the OPT output is returned by the render() method back to the script, so it can be processed in some other way.

$out = new Opt_Output_Return();
$outputData = $out->render($view);

This output system is less restrictive than Opt_Output_Http when it comes to the XML template limitations. Basically, here you can execute the render() method several times, for different views.

7. API Reference
7.5. Opt_Compiler_Class class
7.4. Opt_Output_Return class
« Previous
7.5.1. __construct()
Next »

7.5. Opt_Compiler_Class class

ConstructClass

The main compiler class. A single compiler can compile only one template or inheritance chain at the same time. If you want to compile another template during the compilation, you have to create the new compiler object. Note that the default constructor can copy all the settings from the other compiler objects.

Compiler state variables

During the compilation, the processors have access to the compiler state variables with the Opt_Compiler_Class::get() and Opt_Compiler_Class::set() methods. The following variables are created and managed by the compiler or the standard instructions:

template
string The main template name that is being compiled.
currentTemplate
string The currently compiled template (including the inherited ones).
mode
int The compilation mode (XML or quirks).
escaping
boolean Per-template HTML escaping rule.
7.5. Opt_Compiler_Class class
7.5.1. __construct()
7.5. Opt_Compiler_Class class
« Previous
7.5.2. addDependantTemplate()
Next »

7.5.1. __construct()

ConstructClass constructor
Visibilitypublic
Reference__construct(mixed $parentObject)
Argument list
$parentObject - mixed
The parent object used to import the parser configuration etc.

Creates the new compiler object. The $parentObject may be either Opt_Class instance or another Opt_Compiler_Class. It imports the necessary settings and initializes the compiler.

7.5. Opt_Compiler_Class class
7.5.2. addDependantTemplate()
7.5.1. __construct()
« Previous
7.5.3. block()
Next »

7.5.2. addDependantTemplate()

Referencevoid addDependantTemplate(string $filename)

Adds the template file name to the dependency list used with the template inheritance. The templates on the list must be checked for the modifications each time the template is executed. If the $template already exists on the list, the method throws Opt_InheritanceRecursion_Exception.

See also:

7.5. Opt_Compiler_Class class
7.5.3. block()
7.5.2. addDependantTemplate()
« Previous
7.5.4. cleanCompiler()
Next »

7.5.3. block()

Referencestring block(string $name)

Returns the name of the PHP block class registered as $name tag. If no block is connected to the tag, the Opt_ObjectNotExists_Exception exception is thrown.

7.5. Opt_Compiler_Class class
7.5.4. cleanCompiler()
7.5.3. block()
« Previous
7.5.5. compile()
Next »

7.5.4. cleanCompiler()

Statusstatic
Referencevoid cleanCompiler()

Resets the compiler to the initial state. It is used in the critical situations, when the compilation process has been broken, to unlock the compiler.

7.5. Opt_Compiler_Class class
7.5.5. compile()
7.5.4. cleanCompiler()
« Previous
7.5.6. compileExpression()
Next »

7.5.5. compile()

Referencevoid compile(string $code, string $filename, string $compiledFilename, int $mode)

Compiles the specified template code $code taken from $filename file and saves it under $compiledFilename on the hard disk. The $mode parameter tells, which compiler mode to use:

  1. XML
  2. Quirks

They are identified by suitable constants in Opt_Class.

The method compiles also the whole template inheritance chain, if necessary. The compiled inherited templates are included in the main template code and are not saved separately.

The method cannot be called recursively within the same compiler object. OPT throws an exception then.

7.5. Opt_Compiler_Class class
7.5.6. compileExpression()
7.5.5. compile()
« Previous
7.5.7. component()
Next »

7.5.6. compileExpression()

Referencearray compileExpression(string &$expression [, $allowAssignment = false [, $escaping = self::ESCAPE_ON]])

It compiles given OPT expression $expression to correct PHP expression. Additional parameters:

  1. $allowAssignment - if on, it is allowed to use an assignment operator in expression.
  2. $escaping - if on, the expression is sent to Opt_Compiler_Class::escape().

The returned value is an array consisting of the compiled expression and extra information concerning it:

$array[0]
(string) The compiled expression
$array[1]
(boolean) Whether the assignment operator is used.
$array[2]
(boolean) Whether the expression is actually a single variable
$array[3]
(string) If the escaping is enabled, here we have the unescaped expression
7.5. Opt_Compiler_Class class
7.5.7. component()
7.5.6. compileExpression()
« Previous
7.5.8. convert()
Next »

7.5.7. component()

Referencestring component(string $name)

Returns the name of the PHP component class registered as $name tag. If no component is connected to the tag, the Opt_ObjectNotExists_Exception exception is thrown.

7.5. Opt_Compiler_Class class
7.5.8. convert()
7.5.7. component()
« Previous
7.5.9. createFormat()
Next »

7.5.8. convert()

Referencestring convert(string $string)

The method tries to convert the specified $string using one of the patterns already defined in the compiler. If no conversion pattern is found, it returns the unmodified $string.

In the compiler, convert() is usually used to change the variable names in some parts of the template. For example, if we combine the opt:use attribute with one of the sections, the instruction registers the pattern to convert the snippet variable name, like $snippet.element to the suitable section name: $section.element.

See also:

7.5. Opt_Compiler_Class class
7.5.9. createFormat()
7.5.8. convert()
« Previous
7.5.10. escape()
Next »

7.5.9. createFormat()

ReferenceOpt_Compiler_Format createFormat(string $variable, string $formatDesc)

Creates the new format object from the specified format description $formatDesc. The $variable contains the variable name for debug purposes.

The $formatDesc must be a list of format names separated with slash / marking the decoration. For example, for Foo/Bar the method creates the Bar format object decorated with the Foo format object.

7.5. Opt_Compiler_Class class
7.5.10. escape()
7.5.9. createFormat()
« Previous
7.5.11. get()
Next »

7.5.10. escape()

Referencestring escape(string $expression [, bool $status = null])

Checks, whether to apply HTML escaping in the specified $expression. Returns the new expression. The priority of checking if the escaping is needed.

  1. The $status parameter is different than NULL.
  2. Current template settings
  3. OPT settings
7.5. Opt_Compiler_Class class
7.5.11. get()
7.5.10. escape()
« Previous
7.5.12. getCurrentTemplate()
Next »

7.5.11. get()

Referencemixed get(string $name)

Returns the compiler state variable with the specified $name or NULL, if the variable is not defined.

7.5. Opt_Compiler_Class class
7.5.12. getCurrentTemplate()
7.5.11. get()
« Previous
7.5.13. getFormat()
Next »

7.5.12. getCurrentTemplate()

Statusstatic
Referencestring getCurrentTemplate()

Returns the currently processed template file name.

7.5. Opt_Compiler_Class class
7.5.13. getFormat()
7.5.12. getCurrentTemplate()
« Previous
7.5.14. importDependencies()
Next »

7.5.13. getFormat()

ReferenceOpt_Compiler_Rewriter getFormat(string $id [ bool $restore = true ] )

Returns the data format object for the specified variable identifier. If the programmer has not defined any special format, the default format object is returned. Normally, the method creates a new format object for each call, but if the optional attribute $restore is set to false, it can also return the previously created object for this $id.

7.5. Opt_Compiler_Class class
7.5.14. importDependencies()
7.5.13. getFormat()
« Previous
7.5.15. inherits()
Next »

7.5.14. importDependencies()

Referencevoid importDependencies(Opt_Compiler_Class $compiler)

Imports the template dependency list from another compiler object.

See also:

7.5. Opt_Compiler_Class class
7.5.15. inherits()
7.5.14. importDependencies()
« Previous
7.5.16. isBlock()
Next »

7.5.15. inherits()

Referencestring inherits(string $name)

Returns the template name that the template $name is extending or null, if there is no such rule in the compiler.

7.5. Opt_Compiler_Class class
7.5.16. isBlock()
7.5.15. inherits()
« Previous
7.5.17. isClass()
Next »

7.5.16. isBlock()

Referencebool isBlock(string $name)

Returns true, if there is a block registered under the specified XML tag $name.

7.5. Opt_Compiler_Class class
7.5.17. isClass()
7.5.16. isBlock()
« Previous
7.5.18. isComponent()
Next »

7.5.17. isClass()

Referencestring isClass(string $name)

Checks, whether the $name represents a class name that is allowed to be used in the templates (this means - is it registered or not). It returns its original PHP name or null, if the class is not registered in the parser.

7.5. Opt_Compiler_Class class
7.5.18. isComponent()
7.5.17. isClass()
« Previous
7.5.19. isFunction()
Next »

7.5.18. isComponent()

Referencebool isComponent(string $name)

Returns true, if there is a component registered under the specified XML tag $name.

7.5. Opt_Compiler_Class class
7.5.19. isFunction()
7.5.18. isComponent()
« Previous
7.5.20. isIdentifier()
Next »

7.5.19. isFunction()

Referencestring isFunction(string $name)

Checks, whether the $name represents a function name that is allowed to be used in the templates (this means - is it registered or not). It returns its original PHP name or null, if the class is not registered in the parser.

7.5. Opt_Compiler_Class class
7.5.20. isIdentifier()
7.5.19. isFunction()
« Previous
7.5.21. isInstruction()
Next »

7.5.20. isIdentifier()

Referencebool isIdentifier(string $id)

Returns true, if the specified $id is a valid identifier:

  1. The first character is a letter or underscore.
  2. The next characters are letters, digits or underscore.
7.5. Opt_Compiler_Class class
7.5.21. isInstruction()
7.5.20. isIdentifier()
« Previous
7.5.22. isNamespace()
Next »

7.5.21. isInstruction()

Statuspublic
ReferenceOpt_Compiler_Processor isInstruction(string $name)

Checks if the $name is the name of the XML tag which can be parsed by one of the instruction processors. It returns the processor object or null, if it cannot recognize the tag.

7.5. Opt_Compiler_Class class
7.5.22. isNamespace()
7.5.21. isInstruction()
« Previous
7.5.23. isOptAttribute()
Next »

7.5.22. isNamespace()

Referencebool isNamespace(string $namespace)

Returns true, if the $namespace is parsed by the compiler.

7.5. Opt_Compiler_Class class
7.5.23. isOptAttribute()
7.5.22. isNamespace()
« Previous
7.5.24. isProcessor()
Next »

7.5.23. isOptAttribute()

ReferenceOpt_Compiler_Processor isOptAttribute(string $name)

Checks if the $name is the name of the attribute which can be parsed by one of the instruction processors. It returns the processor object or null, if it cannot recognize the attribute.

7.5. Opt_Compiler_Class class
7.5.24. isProcessor()
7.5.23. isOptAttribute()
« Previous
7.5.25. parseEntities()
Next »

7.5.24. isProcessor()

ReferenceOpt_Compiler_Processor isProcessor(string $name)

Checks, if there is a processor named $name and returns it. If the processor does not exist, the method returns null.

7.5. Opt_Compiler_Class class
7.5.25. parseEntities()
7.5.24. isProcessor()
« Previous
7.5.26. parseShortEntities()
Next »

7.5.25. parseEntities()

Referencestring parseEntities(string $text)

Parses the HTML entities in the $text and returns the modified version.

The method parses OPT entities &lb; and &rb; as well.

See also:

7.5. Opt_Compiler_Class class
7.5.26. parseShortEntities()
7.5.25. parseEntities()
« Previous
7.5.27. processor()
Next »

7.5.26. parseShortEntities()

Referencestring parseShortEntities(string $tekst)

Parses the OPT &lb; and &rb; entities in the $text and returns the modified version.

See also:

7.5. Opt_Compiler_Class class
7.5.27. processor()
7.5.26. parseShortEntities()
« Previous
7.5.28. set()
Next »

7.5.27. processor()

ReferenceOpt_Compiler_Processor processor(string $name)

Returns the processor object with the specified $name. If the processor does not exist, the Opt_ObjectNotExists_Exception is thrown.

7.5. Opt_Compiler_Class class
7.5.28. set()
7.5.27. processor()
« Previous
7.5.29. setConversion()
Next »

7.5.28. set()

Referencevoid set(string $name, mixed $value)

Creates the new compiler state variable with the name $name and value $value.

7.5. Opt_Compiler_Class class
7.5.29. setConversion()
7.5.28. set()
« Previous
7.5.30. setFormatList()
Next »

7.5.29. setConversion()

Referencebool setConversion(string $original, string $replacement)

Defines the new conversion rule from the $original string into the $replacement. If the conversion for $original is already defined, it returns false.

See also:

7.5. Opt_Compiler_Class class
7.5.30. setFormatList()
7.5.29. setConversion()
« Previous
7.5.31. setInheritance()
Next »

7.5.30. setFormatList()

Referencevoid setFormatList(array $variables)

Exports the list of variables and the formats associated with them to the compiler. The $variables is an associative array of pairs variable => format description.

See also:

7.5. Opt_Compiler_Class class
7.5.31. setInheritance()
7.5.30. setFormatList()
« Previous
7.5.32. unsetConversion()
Next »

7.5.31. setInheritance()

Referencevoid setInheritance(array $templates)

Exports the dynamic inheritance information to the compiler. The $templates is an associative array of pairs extending template => extended template.

7.5. Opt_Compiler_Class class
7.5.32. unsetConversion()
7.5.31. setInheritance()
« Previous
7.6. Opt_Compiler_Format class
Next »

7.5.32. unsetConversion()

Referencebool unsetConversion(string $original)

Removes the conversion rule for the $original string. If no such rule was defined, it returns false.

See also:

7. API Reference
7.6. Opt_Compiler_Format class
7.5.32. unsetConversion()
« Previous
7.6.1. __construct()
Next »

7.6. Opt_Compiler_Format class

ConstructClass
Extended byOpt_Format_Array, Opt_Format_SingleArray, Opt_Format_Objective, Opt_Format_StaticGenerator, Opt_Format_RuntimeGenerator

The class provides an interface to create new data formats for the compiler.

7.6. Opt_Compiler_Format class
7.6.1. __construct()
7.6. Opt_Compiler_Format class
« Previous
7.6.2. _build()
Next »

7.6.1. __construct()

Statusconstructor
Reference__construct(Opt_Class $tpl, Opt_Compiler_Class $compiler [, string $name = ''])

Initializes the format object. The method is called automatically by Opt_Compiler_Class::createFormat().

See also:

7.6. Opt_Compiler_Format class
7.6.2. _build()
7.6.1. __construct()
« Previous
7.6.3. _getVar()
Next »

7.6.2. _build()

Statusabstract protected
Referencestring _build(string $name)

Builds a PHP code hook identified by the name $name. The list of the default code hooks supported by OPT can be found in the Opt_Compiler_Format::get() method documentation page.

See also:

7.6. Opt_Compiler_Format class
7.6.3. _getVar()
7.6.2. _build()
« Previous
7.6.4. action()
Next »

7.6.3. _getVar()

Statusfinal protected
Referencemixed _getVar($name)

Returns the variable $name assigned to the format. The method is intended to be used by the code hook generators in order to read required hook parameters.

See also:

7.6. Opt_Compiler_Format class
7.6.4. action()
7.6.3. _getVar()
« Previous
7.6.5. assign()
Next »

7.6.4. action()

Statusextendable public
Referencevoid action(string $name)

The format may overwrite the method in order to perform some extra actions identified by the $name.

Available actions of the default formats

In the section feature set, there is defined one action:

section:forceItemVariables
The section variable call like $section.foo may be converted to the PHP code in two ways. In the first one, the generated PHP code contains the iterator: $_sectsection_vals[$_sectNESTING_i]['foo'], typical for the ordinary for loop or $_sectsection_v['foo'], if the section is converted into foreach. In the data formats that generate the first version, the action allows to switch to the second one. The data format should save the used version in some private property and check it while processing the code hooks.
7.6. Opt_Compiler_Format class
7.6.5. assign()
7.6.4. action()
« Previous
7.6.6. decorate()
Next »

7.6.5. assign()

Statusfinal
Referencevoid assign(string $name, mixed $value)

Assigns a variable to the format object. The format objects use the variables mostly to customize the returned code hooks.

7.6. Opt_Compiler_Format class
7.6.6. decorate()
7.6.5. assign()
« Previous
7.6.7. defined()
Next »

7.6.6. decorate()

Referencevoid decorate(Opt_Compiler_Format $decoratedFormat)

Decorate the $decoratedFormat with the current format object. The decorating and decorated formats share the variable list.

See also:

7.6. Opt_Compiler_Format class
7.6.7. defined()
7.6.6. decorate()
« Previous
7.6.8. get()
Next »

7.6.7. defined()

Statusfinal
Referenceboolean defined(string $name)

Returns true, if the variable $name is already defined in the format object.

7.6. Opt_Compiler_Format class
7.6.8. get()
7.6.7. defined()
« Previous
7.6.9. getName()
Next »

7.6.8. get()

Statusfinal
Referencestring get(string $name)

Returns the PHP code hook with the specified name $name in both the specified format and the formats decorated by it. If the code hook cannot be found, it throws Opt_APIHookNotDefined_Exception.

This method is affected by PHP bug #40479 related to the Zend Engine memory management. It causes the segmentation fault of the interpreter when it attempts to throw an exception. In order not to crash the script, OPT displays the exception using simple die() command rather than throwing it.

Available code hooks

The code hooks below are used by OPT and are grouped into three feature sets (see Opt_Compiler_Format::supports()).

variable feature set

variable:main
The PHP code for the template variable call, for example $templateVariable. The variable name can be read from the format variable item.
variable:assign
The PHP code that assigns a new value to the variable. The value marker is provided in the format variable value.
Required, if the item:assign property is set to true.

item feature set

item:main
The PHP code for the container call, for example $container.item.subitem. The code hook is called for each of the container elements separately, because they may be processed by different data formats. The item name can be read from the format variable item.
item:assign
The PHP code that assigns a new value to the item. The value marker is provided in the format variable value.
Required, if the item:assign property is set to true.

section feature set

All the hooks in this feature set have the access to the format variable section that contains all the information on the current section.

section:init
The section initialization code hook. It should obtain the list data from the datasource attribute, the parent section, one of the template variables or some other place. The section data should be saved into the variable $_sectSECTIONNAME_vals.
section:isNotEmpty
The condition that checks whether the element list is not empty and whether it contains the valid data.
section:started
The PHP code hook executed after checking the condition.
section:finished
The PHP code hook executed before finishing the conditional block.
section:done
The PHP code hook executed after exiting the condition block.
section:loopBefore
The PHP code hook executed before entering the section loop.
section:startAscLoop
The section loop that iterates through the elements in the ascending order.
section:startDescLoop
The section loop that iterates through the elements in the descending order.
section:endLoop
The end of the section loop.
section:variable
The PHP code that retrieves the section item variable, for example $sectionName.variable. If the data format decorates another format, you should order that format to return the code hook item:item for the .variable.
section:variableAssign
The PHP code that assigns a new value to the section variable. The value marker is provided in the format variable value.
Required, if the section:variableAssign property is set to true.
section:item
The PHP code that returns the whole current section item.
section:itemAssign
The PHP code that assigns a new value to the item. The value marker is provided in the format variable value.
Required, if the section:itemAssign property is set to true.
section:reset
Resets the section back to the first item.
section:next
Moves to the next section item.
section:valid
Checks if the section item we have moved to, is valid (if it exists etc.)
section:populate
The code hook that moves the section item data from $_sectSECTIONNAME[$_sectNESTING_i] to $_sectSECTIONNAME_v.
section:count
The expression that returns the number of items in the section.
section:size
The expression that returns the number of the variables in the current section item.
section:iterator
The expression that returns the section iterator. The iterator should be a variable constructed with this pattern: $_sectNESTING_i. The section nesting level can be read from the section format variable.
section:isFirst
The expression that checks, if the current section item is the first on the list, according to the section order.
section:isLast
The expression that checks, if the current section item is the first on the list, according to the section order.
section:isExtreme
The expression that checks, if the current section item is the first or the last on the list.
7.6. Opt_Compiler_Format class
7.6.9. getName()
7.6.8. get()
« Previous
7.6.10. isDecorating()
Next »

7.6.9. getName()

Statusfinal
Referencestring getName()

Returns the format name created from the class name.

7.6. Opt_Compiler_Format class
7.6.10. isDecorating()
7.6.9. getName()
« Previous
7.6.11. property()
Next »

7.6.10. isDecorating()

Referenceboolean isDecorating()

Returns true if the format object decorates another format object.

See also:

7.6. Opt_Compiler_Format class
7.6.11. property()
7.6.10. isDecorating()
« Previous
7.6.12. resetVars()
Next »

7.6.11. property()

Statusfinal
Referencemixed property(string $property)

Returns the value of the specified format $property. The property values are stored in the $_properties protected class field.

7.6. Opt_Compiler_Format class
7.6.12. resetVars()
7.6.11. property()
« Previous
7.6.13. supports()
Next »

7.6.12. resetVars()

Statusfinal
Referencevoid resetVars()

Resets the list of variables assigned to the format object.

7.6. Opt_Compiler_Format class
7.6.13. supports()
7.6.12. resetVars()
« Previous
7.7. Opt_Compiler_Processor class
Next »

7.6.13. supports()

Referenceboolean supports(string $name)

Returns true, if the data format supports the specified feature. The feature list is stored in the $_supports protected array.

Available features

The default Open Power Template compiler and instruction set consists of the following data format features:

section
The data format implements the code hooks necessary for the sections.
variable
The data format implements the code hooks necessary to handle the template variables.
item
The data format implements the code hooks necessary to handle the container calls.

Third party add-ons may define their own feature sets.

See also:

7. API Reference
7.7. Opt_Compiler_Processor class
7.6.13. supports()
« Previous
7.7.1. __construct()
Next »

7.7. Opt_Compiler_Processor class

ConstructClass
Extended byOpt_Instruction_Attribute, Opt_Instruction_BaseSection, Opt_Instruction_Block, Opt_Instruction_Capture, Opt_Instruction_Component, Opt_Instruction_Cycle, Opt_Instruction_Dtd, Opt_Instruction_Dynamic, Opt_Instruction_Extend, Opt_Instruction_For, Opt_Instruction_Foreach, Opt_Instruction_Grid, Opt_Instruction_If, Opt_Instruction_Include, Opt_Instruction_Literal, Opt_Instruction_Prolog, Opt_Instruction_Put, Opt_Instruction_Repeat, Opt_Instruction_Root, Opt_Instruction_Separator, Opt_Instruction_Snippet, Opt_Instruction_Tag

The XML tags and attributes in the opt namespace are not parsed directly by the compiler, but by the instruction processors. They are special classes that extend the Opt_Compiler_Processor class and define, how to compile the XML tag into the valid PHP code. This class provides all the necessary prototypes and tools to write your own instruction processor.

Class fields

The available fields are:

Name Type Description
$_name string The unique instruction identifier. The value must be set by the child class.
$_tpl Opt_Class The Opt_Class object
$_compiler Opt_Compiler_Class The compiler object
7.7. Opt_Compiler_Processor class
7.7.1. __construct()
7.7. Opt_Compiler_Processor class
« Previous
7.7.2. _addAttributes()
Next »

7.7.1. __construct()

Statusconstructor
Reference__construct(Opt_Compiler_Class $compiler)

Initializes the processor, setting the protected fields $_tpl and $_compiler.

7.7. Opt_Compiler_Processor class
7.7.2. _addAttributes()
7.7.1. __construct()
« Previous
7.7.3. _addInstructions()
Next »

7.7.2. _addAttributes()

Statusfinal protected
Referencevoid _addAttributes(mixed $attributes)

Registers a single or a group of XML attributes that can be parsed with this particular processor. $attributes can be a string with the attribute name or an array of names. The compiler will redirect the processing of those attributes to the processor, if it finds any of them. The method must not be used outside Opt_Compiler_Processor::configure(). Example:

class Opt_Processor_Foo extends Opt_Compiler_Processor
{
    protected $_name = 'foo';
 
    public function configure()
    {
        $this->_addAttributes('opt:foo');
        $this->_addAttributes(array('opt:bar', 'opt:joe'));
    } // end configure();
} // end Opt_Processor_Foo;

See also:

7.7. Opt_Compiler_Processor class
7.7.3. _addInstructions()
7.7.2. _addAttributes()
« Previous
7.7.4. _enqueue()
Next »

7.7.3. _addInstructions()

Statusfinal protected
Referencevoid _addInstructions(mixed $instructions)

Registers a single or a group of XML tags that can be parsed with this particular processor. $instructions can be a string with the attribute name or an array of names. The compiler will redirect the processing of those tags to the processor, if it finds any of them. The method must not be used outside Opt_Compiler_Processor::configure(). Example:

class Opt_Processor_Foo extends Opt_Compiler_Processor
{
    protected $_name = 'foo';
 
    public function configure()
    {
        $this->_addInstructions('opt:foo');
        $this->_addInstructions(array('opt:bar', 'opt:joe'));
    } // end configure();
} // end Opt_Processor_Foo;

See also:

7.7. Opt_Compiler_Processor class
7.7.4. _enqueue()
7.7.3. _addInstructions()
« Previous
7.7.5. _extractAttributes()
Next »

7.7.4. _enqueue()

ConstructFinal accessor method
Visibilityfinal protected
Referencevoid _enqueue(Opt_Xml_Node $node)
Argument list
$node - Opt_Xml_Node
The node that should be enqueued for processing.
Versionssince 2.0.4

This method should be used only in Opt_Compiler_Processor::processNode(). It adds the specified $node to the compiler processing queue. The most common use is to inform the compiler to parse the specified tag:

public function processNode(Opt_Xml_Node $node)
{
    $custom = new Opt_Xml_Element('foo');
    $node->appendChild($custom);
 
    // Ignore the default nodes, and parse only our custom node
    // we have already created.
    $this->_enqueue($custom);
} // end processNode();

See also:

7.7. Opt_Compiler_Processor class
7.7.5. _extractAttributes()
7.7.4. _enqueue()
« Previous
7.7.6. _process()
Next »

7.7.5. _extractAttributes()

Statusfinal protected
Referencearray _extractAttributes(Opt_Xml_Element $tag, Array &$attributes)

This method simplifies the instruction tag attribute validation. As the first parameter, we pass the Opt_Xml_Element node we want to check, and the second one is a list of acceptable attributes and their definitions. It is passed by reference, because OPT replaces it with an associative array of attribute values.

$attributes is an associative array, where the index means the attribute name, and the value is a three-element array:

  1. Is the attribute required or optional: Opt_Xml_Attribute::REQUIRED or Opt_Xml_Attribute::OPTIONAL.
  2. Attribute type (described below).
  3. The default value for the attribute, if it is optional and the template designer has not specified it.

The available attribute types are divided in two groups. The first one are the attributes that their value must be known at the stage of compilation and it cannot be set with a template variable or the script. In this category, we can find:

  1. Opt_Xml_Attribute::ID - an identifier
  2. Opt_Xml_Attribute::HARD_STRING - a string
  3. Opt_Xml_Attribute::NUMBER - a number

In the second group, there are the types that send the attribute value to the expression parser and return a valid PHP expression that may be placed somewhere in the instruction output code. Note that the template designer may always choose one of the types by changing the namespace, so they are only a suggestion, which one is the default one, if the namespace is not defined.

  1. Opt_Xml_Attribute::STRING - compile the value as a PHP string by default.
  2. Opt_Xml_Attribute::EXPRESSION - compile the value as an expression by default.
  3. Opt_Xml_Attribute::ASSIGN_EXPR - compile the value as a PHP string by default. The assignment operator is allowed.

An example:

public function processNode(Opt_Xml_Node $node)
{
    $attr = array(
        // 'foo' is required
        'foo' => array(Opt_Xml_Attribute::REQUIRED, Opt_Xml_Attribute::EXPRESSION),
        // 'bar' is optional and its default value is "bar"
        'bar' => array(Opt_Xml_Attribute::OPTIONAL, Opt_Xml_Attribute::ID, 'bar')
    );
    $this->_extractAttributes($node, $attr);
 
    // Mark that we do not generate any PHP code for this node.
    $node->set('nophp', true);
 
    // Generate the output code for this tag.
    $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'The compiled expression is: '.$attr['foo'].'<br/>');
    if($attr['bar'] != 'bar')
    {
        $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'The optional attribute has been set<br/>');
    }
} // end processNode();

The method supports also undefined number of attributes. If we specify a special rule called __UNKNOWN__, all the attributes that are not declared in the array, are matched against it. The values of those attributes are returned by the method as an associative array. If __UNKNOWN__ is required, the template designer must specify at least one custom attribute.

public function processNode(Opt_Xml_Node $node)
{
    $attr = array(
        // Definiujemy atrybut wymagany
        'foo' => array(Opt_Xml_Attribute::REQUIRED, Opt_Xml_Attribute::EXPRESSION),
        // Definiujemy atrybut opcjonalny z domyślną wartością "bar"
        '__UNKNOWN__' => array(Opt_Xml_Attribute::OPTIONAL, Opt_Xml_Attribute::ID)
    );
    $other = $this->_extractAttributes($node, $attr);
 
    $node->set('nophp', true);
 
    $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'The compiled expression is: '.$attr['foo'].'<br/>');
 
    // Wyświetl nadmiarowe atrybuty
    foreach($other as $name => $value)
    {
        $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, $name.': '.$value.'<br/>');
    }
} // end processNode();

See also:

7.7. Opt_Compiler_Processor class
7.7.6. _process()
7.7.5. _extractAttributes()
« Previous
7.7.7. configure()
Next »

7.7.6. _process()

ConstructFinal accessor method
Visibilityfinal protected
Referencevoid _process(Opt_Xml_Scannable $node)
Argument list
$node - Opt_Xml_Scannable
The node whose children should be enqueued for processing.

This method should be used only in Opt_Compiler_Processor::processNode(). It adds the children of $node to the compiler processing queue. The most common use is to inform the compiler to parse the content of the instruction tag:

public function processNode(Opt_Xml_Node $node)
{
    // Parse the children, too.
    $this->_process($node);
} // end processNode();

The node itself is not enqueued. If you want to enqueue a certain node directly, please use _enqueue().

See also:

7.7. Opt_Compiler_Processor class
7.7.7. configure()
7.7.6. _process()
« Previous
7.7.8. getName()
Next »

7.7.7. configure()

ConstructOptional method for implementation by the programmer
Visibilitypublic
Referencevoid configure()

Performs an initial configuration of the instruction processor and informs about the tags and attributes parsed by it. The instruction programmer should extend this method and call there Opt_Compiler_Processor::_addInstructions() and Opt_Compiler_Processor::_addAttributes():

class Opt_Instruction_Foo extends Opt_Compiler_Processor
{
    protected $_name = 'foo';
 
    public function configure()
    {
        // Configure the processor
        $this->_addInstructions('opt:foo');
        $this->_addAttributes('opt:foo');
    } // end configure();
 
    public function processNode(Opt_Xml_Node $node)
    {
        // Process the tags.
    } // end processNode();
 
    public function processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
    {
        // Process the attributes.
    } // end processAttribute();
} // end Opt_Instruction_Foo;
7.7. Opt_Compiler_Processor class
7.7.8. getName()
7.7.7. configure()
« Previous
7.7.9. postprocessAttribute()
Next »

7.7.8. getName()

ConstructFinal method
Referencestring getName()
Returned valueThe processor name

Returns the name of the processor saved in the protected field $_name.

7.7. Opt_Compiler_Processor class
7.7.9. postprocessAttribute()
7.7.8. getName()
« Previous
7.7.10. postprocessNode()
Next »

7.7.9. postprocessAttribute()

ConstructOptional method for implementation by the programmer
Visibilitypublic
Referencevoid postprocessAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attribute)
Argument list
$node - Opt_Xml_Node
The node with the attribute to process.
$attribute - Opt_Xml_Attribute
The attribute to process.

Works much like Opt_Compiler_Processor::postprocessNode(), but it allows to postprocess the instruction XML attribute. In order to be executed by the compiler, the Opt_Compiler_Processor::processAttribute() must set the attribute variable postprocess to true:

public function processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
{
    // The attribute must be postprocessed after the node children are parsed.
    $attr->set('postprocess', true);
} // end processAttribute();
 
public function postprocessAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attr)
{
    echo 'Hello world!';
} // end postprocessAttribute();

See also:

7.7. Opt_Compiler_Processor class
7.7.10. postprocessNode()
7.7.9. postprocessAttribute()
« Previous
7.7.11. processAttribute()
Next »

7.7.10. postprocessNode()

ConstructOptional method for implementation by the programmer
Visibilitypublic
Referencevoid postprocessNode(Opt_Xml_Node $node)
Argument list
$node - Opt_Xml_Node
The node to process.

Processes the registered instruction tags after their children have already been parsed, too. You must extend this method, if you want to write your own implementation.

The compiler calls this method if and only if we set the XML node value postprocess to true.

public function processNode(Opt_Xml_Node $node)
{
    // The node must be postprocessed after the children nodes are parsed.
    $node->set('postprocess', true);
} // end processNode();
 
public function postprocessNode(Opt_Xml_Node $node)
{
    echo 'Hello world!';
} // end postprocessNode();

See also:

7.7. Opt_Compiler_Processor class
7.7.11. processAttribute()
7.7.10. postprocessNode()
« Previous
7.7.12. processNode()
Next »

7.7.11. processAttribute()

ConstructOptional method for implementation by the programmer
Visibilitypublic
Referencevoid processAttribute(Opt_Xml_Node $node, Opt_Xml_Attribute $attribute)
Argument list
$node - Opt_Xml_Node
The node with the attribute to process.
$attribute - Opt_Xml_Attribute
The attribute to process.

This method captures all the XML attributes registered by the processor and allows to parse them much like Opt_Compiler_Processor::processNode() does with XML tags. The taken arguments are the registered $attribute and the XML $node which the attribute is assigned to.

See also:

7.7. Opt_Compiler_Processor class
7.7.12. processNode()
7.7.11. processAttribute()
« Previous
7.7.13. processSystemVar()
Next »

7.7.12. processNode()

ConstructOptional method for implementation by the programmer
Visibilitypublic
Referencevoid processNode(Opt_Xml_Node $node)
Argument list
$node - Opt_Xml_Node
The node to process.

This method is used to implement, how to process the XML tags registered by the instruction processor. It is called automatically by the compiler every time it founds any matching tag and is executed before the tag children. As an argument, it takes the node to be processed.

OPT does not send the instruction node children to be processed by the compiler. The processor has to do it on its own with Opt_Compiler_Processor::_process().

In order to illustrate, how to implement the content of this method, we show one of the real implementations from OPT source code: the opt:for instruction:

public function processNode(Opt_Xml_Node $node)
{
    // Step 1
    $params = array(
        'begin' => array(0 => self::REQUIRED, self::ASSIGN_EXPR),
        'while' => array(0 => self::REQUIRED, self::ASSIGN_EXPR),
        'iterate' => array(0 => self::REQUIRED, self::ASSIGN_EXPR),
        'separator' => $this->getSeparatorConfig()
    );
    $this->_extractAttributes($node, $params);
 
    // Step 2
    $this->_nesting++;
 
    // Step 3
    $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, ' for('.$params['begin'].'; '.$params['while'].'; '.$params['iterate'].'){ ');
    $node->addAfter(Opt_Xml_Buffer::TAG_AFTER, ' } ');
 
    $this->processSeparator('$__for'.$this->_nesting, $params['separator'], $node);
 
    // Step 4
    $node->set('postprocess', true);
    $this->_process($node);
} // end processNode();

The description of the steps:

  1. As this processor registers only one tag, we are sure that $node always points to the opt:for. We begin the compilation with parsing the instruction attributes with Opt_Compiler_Processor::_extractAttributes().

  2. We modify some of the processor object fields. In this case, $this->_nesting represents the current opt:for nesting level. We need this value to create unique variable names for the very loop.

  3. With Opt_Xml_Buffer::addBefore() and Opt_Xml_Buffer::addAfter() we bind some PHP code that will replace the instruction tag in the output file to the node buffers. Note that we fill the parts of the for declaration with the attribute values that have already been parsed as OPT expressions.

  4. As the node content is not processed by default, we have to call Opt_Compiler_Processor::_process() on the node. This method registers its children in the processing queue. Moreover, once the children are processed, we have to decrement the nesting counter. We can do it by setting postprocess to true on the $node. It forces the compiler to send our opt:for again to the processor after it finishes with the children. However, the next processing will be performed by Opt_Compiler_Processor::postprocessNode().

Some instructions may require much more sophisticated implementations.

See also:

7.7. Opt_Compiler_Processor class
7.7.13. processSystemVar()
7.7.12. processNode()
« Previous
7.7.14. reset()
Next »

7.7.13. processSystemVar()

Statusextendable public
Referencevoid processSystemVar(Array $sys)

Parses the OPT special container $sys.processorName. You should extend it to provide your own implementation for this variable. The method takes the array of the indexes that create the variable call. For example, the $sys.foo.bar is processed by the foo processor and processSystemVar() receives an array of three values:

0 => sys
1 => foo
2 => bar

The expected returned value is the PHP code generated from the container.

$opt is an alias to $sys in the templates. In this case, the first element of the array is opt instead of sys.

7.7. Opt_Compiler_Processor class
7.7.14. reset()
7.7.13. processSystemVar()
« Previous
7.8. Opt_Xml_Attribute class
Next »

7.7.14. reset()

Statusextendable public
Referencevoid reset()

This method is executed after the finished compilation. It allows the processor to reset to the initial state so that it could handle another compilation process.

7. API Reference
7.8. Opt_Xml_Attribute class
7.7.14. reset()
« Previous
7.8.1. __construct()
Next »

7.8. Opt_Xml_Attribute class

ConstructClass
ExtendsOpt_Xml_Buffer

The class that represents the XML attribute.

7.8. Opt_Xml_Attribute class
7.8.1. __construct()
7.8. Opt_Xml_Attribute class
« Previous
7.8.2. getName()
Next »

7.8.1. __construct()

Statuspublic
Reference__construct(string $name, string $value)

Creates a new attribute object with the name $name and value $value. The $name may contain the XML namespace, using the notation namespace:name.

7.8. Opt_Xml_Attribute class
7.8.2. getName()
7.8.1. __construct()
« Previous
7.8.3. getNamespace()
Next »

7.8.2. getName()

Statuspublic
Referencestring getName()

Returns the XML attribute name (without the namespace).

See also:

7.8. Opt_Xml_Attribute class
7.8.3. getNamespace()
7.8.2. getName()
« Previous
7.8.4. getValue()
Next »

7.8.3. getNamespace()

Statuspublic
Referencestring getNamespace()

Returns the XML attribute namespace or NULL, if the namespace is not defined.

7.8. Opt_Xml_Attribute class
7.8.4. getValue()
7.8.3. getNamespace()
« Previous
7.8.5. getXmlName()
Next »

7.8.4. getValue()

Statuspublic
Referencestring getValue()

Returns the XML attribute value.

7.8. Opt_Xml_Attribute class
7.8.5. getXmlName()
7.8.4. getValue()
« Previous
7.8.6. setName()
Next »

7.8.5. getXmlName()

Statuspublic
Referencestring getXmlName()

Returns the full XML attribute name with the namespace separated with a colon.

See also:

7.8. Opt_Xml_Attribute class
7.8.6. setName()
7.8.5. getXmlName()
« Previous
7.8.7. setNamespace()
Next »

7.8.6. setName()

Statuspublic
Referencevoid setName(string $name)

Sets the new XML attribute name. The name may optionally contain the namespace with the notation namespace:name.

7.8. Opt_Xml_Attribute class
7.8.7. setNamespace()
7.8.6. setName()
« Previous
7.8.8. setValue()
Next »

7.8.7. setNamespace()

Statuspublic
Referencevoid setNamespace(string $namespace)

Sets the new XML attribute namespace.

7.8. Opt_Xml_Attribute class
7.8.8. setValue()
7.8.7. setNamespace()
« Previous
7.9. Opt_Xml_Buffer class
Next »

7.8.8. setValue()

Statuspublic
Referencevoid setValue(string $value)

Sets the new XML attribute value.

7. API Reference
7.9. Opt_Xml_Buffer class
7.8.8. setValue()
« Previous
7.9.1. addAfter()
Next »

7.9. Opt_Xml_Buffer class

ConstructAbstract class
Extended byOpt_Xml_Attribute, Opt_Xml_Node

Opt_Xml_Buffer provides the basis for all the XML tree classes. It implements the most important tools that allow to generate the PHP code from the template:

  1. Code buffers
  2. State variables

Code buffers

The XML nodes are not replaced with the PHP code line by line. The instruction processor may add the new code snippet to every node, even if it has already been parsed by other processor. The nodes contain a special data structure to keep the PHP snippets assigned to them. The snippets are grouped into buffers that indicate where the code is located in the final file (i.e. before the opening tag). The compilation is nothing more but generating the PHP code for the node buffers.

Although there are many available buffers, the nodes may use only a part of them. They are identified by the Opt_Xml_Buffer constants:

  1. TAG_BEFORE
  2. TAG_AFTER
  3. TAG_OPENING_BEFORE
  4. TAG_OPENING_AFTER
  5. TAG_CONTENT_BEFORE
  6. TAG_CONTENT
  7. TAG_CONTENT_AFTER
  8. TAG_CLOSING_BEFORE
  9. TAG_CLOSING_AFTER
  10. TAG_SINGLE_BEFORE
  11. TAG_SINGLE_AFTER
  12. TAG_NAME
  13. TAG_ATTRIBUTES_BEFORE
  14. TAG_ATTRIBUTES_AFTER
  15. TAG_BEGINNING_ATTRIBUTES
  16. TAG_ENDING_ATTRIBUTES
  17. ATTRIBUTE_NAME
  18. ATTRIBUTE_VALUE
  19. ATTRIBUTE_BEGIN
  20. ATTRIBUTE_END

The pictures below show, how they surround the XML tag.

Location of the code buffers around the XML tag

We see that TAG_BEFORE and TAG_AFTER enclose the whole content, including the opening and closing tags. They are used the most often, because here we can locate the loop or condition that affects the entire tag. Next, TAG_OPENING_BEFORE and TAG_OPENING_AFTER allow to put some code around the opening tag, and there are similar buffers for the closures. Moreover, we have some buffers that influence the content only. TAG_CONTENT_BEFORE and TAG_CONTENT_AFTER are used if the tag contains at least one child node. Otherwise, the code in TAG_CONTENT may be used to generate it during the runtime.

The situation changes a bit, if we have a single tag:

Location of the code buffers around the single XML tag

There is no content and there are TAG_SINGLE_BEFORE and TAG_SINGLE_AFTER. Note that in this way we have two types of code buffer that surround the tag. This is not a mistake as there is important difference. The linker always adds the TAG_BEFORE snippets, whereas TAG_SINGLE_BEFORE only if the tag is single.

Note that the text and OPT expression nodes are surrounded only with TAG_BEFORE and TAG_AFTER.

We can find the code buffers around the XML attributes, too:

Location of the code buffers around XML attributes

State variables

For each node, we can assign state variables that bind some extra information to it. They are used for some purposes:

  1. Setting messages for the compiler.
  2. Temporary information store - if the processor parses the same node more than once, we can save some data in order not to generate them again.
  3. Setting messages for other instruction processors.

The template compiler recognizes the following state variables in the nodes:

dynamic
(boolean) If set to true, the content of this node must keep dynamic even if the cache system is used.
hidden
(boolean) If set to true, the linker ignores this tag and its children - they will not appear in the output code. By default, all the nodes in the XML tree have hidden set to true. The compiler automatically changes its state, if it is going to process it.
postprocess
(boolean) By default, the compiler allows the instruction processor to parse the tags before going to their children. If the processor wants to do something also after the children are processed, it must set this variable to true. The compiler returns to the node then by calling Opt_Compiler_Processor::postprocessNode() or Opt_Compiler_Processor::postprocessAttribute() after the children parsing is completed.
7.9. Opt_Xml_Buffer class
7.9.1. addAfter()
7.9. Opt_Xml_Buffer class
« Previous
7.9.2. addBefore()
Next »

7.9.1. addAfter()

Statuspublic
Referencevoid addAfter(int $buffer, string $code)

Appends the code snippet $code to the end of the specified buffer $buffer.

class Opt_Processor_Foo extends Opt_Compiler_Processor
{
    protected $_name = 'foo';
 
    public function processNode(Opt_Xml_Node $node)
    {
        $node->addAfter(Opt_Xml_Buffer::TAG_BEFORE, 'echo \'hello\';');
    } // end processNode();
} // end Opt_Processor_Foo;

To generate a PHP code with curly brackets and be sure that it is used in the correct order, we have to both this method and Opt_Xml_Buffer::addBefore(). The opening bracket must be added with the first one, and the closing with the second. There are two possible combinations:

  1. The brackets enclose the tag, but they may appear within other brackets generated by other snippets:

    $node->addAfter(Opt_Xml_Buffer::TAG_BEFORE, ' if($foo){ ');
    $node->addBefore(Opt_Xml_Buffer::TAG_AFTER, ' } ');
  2. The brackets enclose both the tag and the rest of the currently added code snippets assigned to this node:

    $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, ' if($foo){ ');
    $node->addAfter(Opt_Xml_Buffer::TAG_AFTER, ' } ');

See also:

7.9. Opt_Xml_Buffer class
7.9.2. addBefore()
7.9.1. addAfter()
« Previous
7.9.3. bufferSize()
Next »

7.9.2. addBefore()

Statuspublic
Referencevoid addBefore(int $buffer, string $code)

Adds the code snippet $code to the beginning of the buffer $buffer.

class Opt_Processor_Foo extends Opt_Compiler_Processor
{
    protected $_name = 'foo';
 
    public function processNode(Opt_Xml_Node $node)
    {
        $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'echo \'hello\';');
    } // end processNode();
} // end Opt_Processor_Foo;

See the description of Opt_Xml_Buffer::addAfter() to get to know more information about this method.

See also:

7.9. Opt_Xml_Buffer class
7.9.3. bufferSize()
7.9.2. addBefore()
« Previous
7.9.4. buildCode()
Next »

7.9.3. bufferSize()

Statuspublic
Referenceint bufferSize(int $buffer)

Returns the number of code snippets assigned to the buffer $buffer.

7.9. Opt_Xml_Buffer class
7.9.4. buildCode()
7.9.3. bufferSize()
« Previous
7.9.5. clear()
Next »

7.9.4. buildCode()

Statuspublic
Referencestring buildCode(...)

This method is used only by the code linker. It concatenates the code snippets in the specified buffers and returns the complete PHP code.

7.9. Opt_Xml_Buffer class
7.9.5. clear()
7.9.4. buildCode()
« Previous
7.9.6. copyBuffer()
Next »

7.9.5. clear()

Statuspublic
Referencevoid clear(int $buffer)

Clears the specified buffer $buffer.

7.9. Opt_Xml_Buffer class
7.9.6. copyBuffer()
7.9.5. clear()
« Previous
7.9.7. get()
Next »

7.9.6. copyBuffer()

Statuspublic
Referencevoid copyBuffer(Opt_Xml_Buffer $node, int $sourceBuffer, int $destBuffer)

Copies the buffer $sourceBuffer from the node $node to the buffer $destBuffer in the current node.

7.9. Opt_Xml_Buffer class
7.9.7. get()
7.9.6. copyBuffer()
« Previous
7.9.8. getBuffer()
Next »

7.9.7. get()

Statuspublic
Referencemixed get(string $name)

Returns the value of the state variable $name. If the variable does not exist, it returns NULL.

7.9. Opt_Xml_Buffer class
7.9.8. getBuffer()
7.9.7. get()
« Previous
7.9.9. set()
Next »

7.9.8. getBuffer()

Statuspublic
Referencearray getBuffer(int $bufferId)

Returns the array with all the code snippets assigned to the buffer $bufferId. If the buffer is empty, it returns an empty array.

7.9. Opt_Xml_Buffer class
7.9.9. set()
7.9.8. getBuffer()
« Previous
7.10. Opt_Xml_Cdata class
Next »

7.9.9. set()

Statuspublic
Referencevoid set(string $name, mixed $value)

Sets the state variable $name to $value.

7. API Reference
7.10. Opt_Xml_Cdata class
7.9.9. set()
« Previous
7.10.1. appendData()
Next »

7.10. Opt_Xml_Cdata class

ConstructClass
ExtendsOpt_Xml_Node
Extended byOpt_Xml_Comment

Opt_Xml_Cdata objects represent a static text within the tags. They may be children of Opt_Xml_Text only, together with Opt_Xml_Expression.

These nodes support the optional state variable cdata (for more information on state variables, see Opt_Xml_Buffer). If it is set to true, the text stored in this tag becomes a CDATA section.

To disable CDATA in all the descendants of the specified node, we can use the public method of Opt_Instruction_Literal processor:

$compiler->processor('literal')->disableCDATA($node);
7.10. Opt_Xml_Cdata class
7.10.1. appendData()
7.10. Opt_Xml_Cdata class
« Previous
7.10.2. deleteData()
Next »

7.10.1. appendData()

Statuspublic
Referencevoid appendData(string $text)

Appends the $text to the end of the text stored in the current node.

See also:

7.10. Opt_Xml_Cdata class
7.10.2. deleteData()
7.10.1. appendData()
« Previous
7.10.3. insertData()
Next »

7.10.2. deleteData()

Statuspublic
Referencevoid deleteData(int $offset, int $count)

Deletes the $count characters from the node, starting from $offset.

See also:

7.10. Opt_Xml_Cdata class
7.10.3. insertData()
7.10.2. deleteData()
« Previous
7.10.4. length()
Next »

7.10.3. insertData()

Statuspublic
Referencevoid insertData(int $offset, string $text)

Inserts the text $text to the node, starting from the $offset position. The starting position is 0.

See also:

7.10. Opt_Xml_Cdata class
7.10.4. length()
7.10.3. insertData()
« Previous
7.10.5. replaceData()
Next »

7.10.4. length()

Statuspublic
Referenceint length()

Returns the length of the text stored in the CDATA node.

7.10. Opt_Xml_Cdata class
7.10.5. replaceData()
7.10.4. length()
« Previous
7.10.6. substringData()
Next »

7.10.5. replaceData()

Statuspublic
Referencevoid replaceData(int $offset, int $count, string $text)

Replaces the $count characters in the node, starting from $offset, with the beginning of $text.

See also:

7.10. Opt_Xml_Cdata class
7.10.6. substringData()
7.10.5. replaceData()
« Previous
7.11. Opt_Xml_Comment class
Next »

7.10.6. substringData()

Statuspublic
Referencestring substringData(int $offset, int $count)

Returns the specified substring.

7. API Reference
7.11. Opt_Xml_Comment class
7.10.6. substringData()
« Previous
7.12. Opt_Xml_Dtd class
Next »

7.11. Opt_Xml_Comment class

ConstructClass
ExtendsOpt_Xml_Cdata

The class represents the XML comments and provides an API to manipulate their contents. The class extends Opt_Xml_Cdata, so we can use the same manipulation methods, as in case of ordinary character data nodes.

Note that every XML node may become a comment by setting the node flag commented to true. Their contents, including the node itself, will be enclosed within <!-- and --> tags:

$node->set('commented', true);
7. API Reference
7.12. Opt_Xml_Dtd class
7.11. Opt_Xml_Comment class
« Previous
7.13. Opt_Xml_Element class
Next »

7.12. Opt_Xml_Dtd class

ConstructClass

The class represents the Document Type Definition. It is not a part of the XML tree itself, but can be assigned to the Opt_Xml_Root objects using the setDtd() method. Open Power Template does not provide a parser for the DTD itself, so the definition is stored as a plain text. However, the behavior may be extended in the future.

7. API Reference
7.13. Opt_Xml_Element class
7.12. Opt_Xml_Dtd class
« Previous
7.13.1. addAttribute()
Next »

7.13. Opt_Xml_Element class

ConstructClass
ExtendsOpt_Xml_Scannable

This class represents the XML tag nodes. It provides the support for tag names, namespaces and attributes.

7.13. Opt_Xml_Element class
7.13.1. addAttribute()
7.13. Opt_Xml_Element class
« Previous
7.13.2. countAttributes()
Next »

7.13.1. addAttribute()

Statuspublic
Referencebool addAttribute(Opt_Xml_Attribute $attribute)

Adds the new XML attribute $attribute to the tag. If the tag already contains an attribute with the specified name, the method returns false.

See also:

7.13. Opt_Xml_Element class
7.13.2. countAttributes()
7.13.1. addAttribute()
« Previous
7.13.3. getAttribute()
Next »

7.13.2. countAttributes()

Statuspublic
Referenceint countAttributes()

Returns the total number of XML attributes.

See also:

7.13. Opt_Xml_Element class
7.13.3. getAttribute()
7.13.2. countAttributes()
« Previous
7.13.4. getAttributes()
Next »

7.13.3. getAttribute()

Statuspublic
ReferenceOpt_Xml_Attribute getAttribute(string $attribute)

Returns the attribute object with the specified name associated to this tag or null, if it has not been found.

See also:

7.13. Opt_Xml_Element class
7.13.4. getAttributes()
7.13.3. getAttribute()
« Previous
7.13.5. getName()
Next »

7.13.4. getAttributes()

Statuspublic
Referencearray getAttributes()

Returns an associative array with all the tag attribute objects.

See also:

7.13. Opt_Xml_Element class
7.13.5. getName()
7.13.4. getAttributes()
« Previous
7.13.6. getNamespace()
Next »

7.13.5. getName()

Statuspublic
Referencestring getName()

Returns the tag name (without the namespace).

See also:

7.13. Opt_Xml_Element class
7.13.6. getNamespace()
7.13.5. getName()
« Previous
7.13.7. getXmlName()
Next »

7.13.6. getNamespace()

Statuspublic
Referencestring getNamespace()

Returns the current tag namespace or null, if it is not specified.

7.13. Opt_Xml_Element class
7.13.7. getXmlName()
7.13.6. getNamespace()
« Previous
7.13.8. hasAttributes()
Next »

7.13.7. getXmlName()

Statuspublic
Referencestring getXmlName()

Returns the full XML name of the tag with the namespace separated with a colon.

See also:

7.13. Opt_Xml_Element class
7.13.8. hasAttributes()
7.13.7. getXmlName()
« Previous
7.13.9. removeAttribute()
Next »

7.13.8. hasAttributes()

Statuspublic
Referencebool hasAttributes()

Returns true, if the tag contains any attributes.

See also:

7.13. Opt_Xml_Element class
7.13.9. removeAttribute()
7.13.8. hasAttributes()
« Previous
7.13.10. setName()
Next »

7.13.9. removeAttribute()

Statuspublic
Referencebool removeAttribute(mixed $attribute)

Removes the specified attribute. The $attribute may be either the attribute name written in the XML notation: namespace:name or the object of Opt_Xml_Attribute.

See also:

7.13. Opt_Xml_Element class
7.13.10. setName()
7.13.9. removeAttribute()
« Previous
7.13.11. setNamespace()
Next »

7.13.10. setName()

Statuspublic
Referencevoid setName(string $name)

Sets the new name to the current node. Optionally, we are able to set both tag name and namespace using the XML notation: namespace:name. An example:

$node->setName('opt:name');
7.13. Opt_Xml_Element class
7.13.11. setNamespace()
7.13.10. setName()
« Previous
7.14. Opt_Xml_Expression class
Next »

7.13.11. setNamespace()

Statuspublic
Referencevoid setNamespace(string $namespace)

Sets the new namespace to the current tag.

7. API Reference
7.14. Opt_Xml_Expression class
7.13.11. setNamespace()
« Previous
7.14.1. __construct()
Next »

7.14. Opt_Xml_Expression class

ConstructClass
ExtendsOpt_Xml_Node

This class represents the OPT expressions enclosed in the curly brackets within the text nodes. The nodes of this type may be children of Opt_Xml_Text only.

7.14. Opt_Xml_Expression class
7.14.1. __construct()
7.14. Opt_Xml_Expression class
« Previous
7.14.2. getExpression()
Next »

7.14.1. __construct()

Statuspublic
Reference__construct(string $expression)

Initializes the expression node. It takes the OPT expression as an argument.

7.14. Opt_Xml_Expression class
7.14.2. getExpression()
7.14.1. __construct()
« Previous
7.14.3. setExpression()
Next »

7.14.2. getExpression()

Statuspublic
Referencestring getExpression()

Returns the OPT expression associated with this node.

7.14. Opt_Xml_Expression class
7.14.3. setExpression()
7.14.2. getExpression()
« Previous
7.15. Opt_Xml_Node class
Next »

7.14.3. setExpression()

Statuspublic
Referencevoid setExpression(string $expression)

Sets the new OPT $expression.

7. API Reference
7.15. Opt_Xml_Node class
7.14.3. setExpression()
« Previous
7.15.1. dispose()
Next »

7.15. Opt_Xml_Node class

ConstructAbstract class
ExtendsOpt_Xml_Buffer
Extended byOpt_Xml_Scannable, Opt_Xml_Cdata, Opt_Xml_Expression

Opt_Xml_Node contains the basic API for all the nodes that may create the XML tree in OPT. It provides the support for parent nodes and checking the node type, however it is not responsible for managing the child nodes.

7.15. Opt_Xml_Node class
7.15.1. dispose()
7.15. Opt_Xml_Node class
« Previous
7.15.2. getParent()
Next »

7.15.1. dispose()

Statuspublic
Referencevoid dispose()
Versionssince 2.0-beta2

The PHP garbage collector does not recognize cycles between objects, causing problems with freeing the XML tree memory with ordinary unset(). In order to destroy the tree, you should use this very method:

$tree->dispose();
unset($tree);
7.15. Opt_Xml_Node class
7.15.2. getParent()
7.15.1. dispose()
« Previous
7.15.3. getType()
Next »

7.15.2. getParent()

Statuspublic
ReferenceOpt_Xml_Node getParent()

Returns the current node parent.

See also:

7.15. Opt_Xml_Node class
7.15.3. getType()
7.15.2. getParent()
« Previous
7.15.4. setParent()
Next »

7.15.3. getType()

Statuspublic
Referencestring getType()

Returns the name of the current node type. The available values are:

  1. Opt_Xml_Cdata
  2. Opt_Xml_Text
  3. Opt_Xml_Element
  4. Opt_Xml_Expression
  5. Opt_Xml_Root
7.15. Opt_Xml_Node class
7.15.4. setParent()
7.15.3. getType()
« Previous
7.16. Opt_Xml_Prolog class
Next »

7.15.4. setParent()

Statuspublic
Referencevoid setParent(Opt_Xml_Node $node)

Sets the new parent of the current node.

Usually, you do not have to call this method on your own. The methods in Opt_Xml_Scannable that change the tree structure, automatically set the correct parent.

See also:

7. API Reference
7.16. Opt_Xml_Prolog class
7.15.4. setParent()
« Previous
7.17. Opt_Xml_Root class
Next »

7.16. Opt_Xml_Prolog class

ConstructClass

The class represents the XML prolog. It is not a part of the XML tree itself, but can be assigned to the Opt_Xml_Root objects using the setProlog() method. The class provides a simple interface that allows to manage the three XML prolog attributes:

  1. version
  2. encoding
  3. standalone

The values of the attributes can be either static or dynamic.

7. API Reference
7.17. Opt_Xml_Root class
7.16. Opt_Xml_Prolog class
« Previous
7.17.1. getDtd()
Next »

7.17. Opt_Xml_Root class

ConstructClass
ExtendsOpt_Xml_Scannable

This class represents the root of the XML node and nothing more.

7.17. Opt_Xml_Root class
7.17.1. getDtd()
7.17. Opt_Xml_Root class
« Previous
7.17.2. getProlog()
Next »

7.17.1. getDtd()

Statuspublic
ReferenceOpt_Xml_Dtd getDtd()

Returns the Opt_Xml_Dtd node assigned to the root node or null, if there is no such node.

See also:

7.17. Opt_Xml_Root class
7.17.2. getProlog()
7.17.1. getDtd()
« Previous
7.17.3. hasDtd()
Next »

7.17.2. getProlog()

Statuspublic
ReferenceOpt_Xml_Prolog getProlog()

Returns the Opt_Xml_Prolog node assigned to the root node or null, if there is no such node.

See also:

7.17. Opt_Xml_Root class
7.17.3. hasDtd()
7.17.2. getProlog()
« Previous
7.17.4. hasProlog()
Next »

7.17.3. hasDtd()

Statuspublic
Referencebool hasDtd()

Returns true, if there is a Document Type Definition assigned to the root node.

See also:

7.17. Opt_Xml_Root class
7.17.4. hasProlog()
7.17.3. hasDtd()
« Previous
7.17.5. setDtd()
Next »

7.17.4. hasProlog()

Statuspublic
Referencebool hasProlog()

Returns true, if there is a prolog assigned to the root node.

See also:

7.17. Opt_Xml_Root class
7.17.5. setDtd()
7.17.4. hasProlog()
« Previous
7.17.6. setProlog()
Next »

7.17.5. setDtd()

Statuspublic
Referencevoid setDtd(Opt_Xml_Dtd $dtd)

Sets the XML prolog of the XML tree represented by the root node.

See also:

7.17. Opt_Xml_Root class
7.17.6. setProlog()
7.17.5. setDtd()
« Previous
7.18. Opt_Xml_Scannable class
Next »

7.17.6. setProlog()

Statuspublic
Referencevoid setProlog(Opt_Xml_Prolog $prolog)

Sets the XML prolog of the XML tree represented by the root node.

See also:

7. API Reference
7.18. Opt_Xml_Scannable class
7.17.6. setProlog()
« Previous
7.18.1. _testNode()
Next »

7.18. Opt_Xml_Scannable class

ConstructClass
ExtendsOpt_Xml_Node
ImplementsIterator
Extended byOpt_Xml_Element, Opt_Xml_Root, Opt_Xml_Text

This abstract class provides complete XML node manipulation API. Each node that is able to contain children, must extend it. The programming interface is based on DOM, so everyone familiar with this API should have no problems with using it.

The class allows to disable adding particular node types as children. In this case the methods may generate Opt_InvalidNodeType_Exception.

7.18. Opt_Xml_Scannable class
7.18.1. _testNode()
7.18. Opt_Xml_Scannable class
« Previous
7.18.2. appendChild()
Next »

7.18.1. _testNode()

Statusprotected
Referencebool _testNode(Opt_Xml_Node $node)

This internal method should be overloaded in the descendant classes. It returns true, if the specified $node may be a child of the current node type. The default implementation always returns true.

7.18. Opt_Xml_Scannable class
7.18.2. appendChild()
7.18.1. _testNode()
« Previous
7.18.3. bringToEnd()
Next »

7.18.2. appendChild()

Statuspublic
Referencevoid appendChild(Opt_Xml_Node $node)

Appends the $node to the end of the child list in the current node.

7.18. Opt_Xml_Scannable class
7.18.3. bringToEnd()
7.18.2. appendChild()
« Previous
7.18.4. countChildren()
Next »

7.18.3. bringToEnd()

Statuspublic
Referencevoid bringToEnd(mixed $node)

Moves the specified node $node to the end of the child list. The $node may be either a node number or the object.

7.18. Opt_Xml_Scannable class
7.18.4. countChildren()
7.18.3. bringToEnd()
« Previous
7.18.5. getChildren()
Next »

7.18.4. countChildren()

Statuspublic
Referenceint countChildren()

Returns the total number of the current node children.

7.18. Opt_Xml_Scannable class
7.18.5. getChildren()
7.18.4. countChildren()
« Previous
7.18.6. getElementsByTagName()
Next »

7.18.5. getChildren()

Statuspublic
Referencearray getChildren()

Returns the array of the current node children. The array keeps the children order.

7.18. Opt_Xml_Scannable class
7.18.6. getElementsByTagName()
7.18.5. getChildren()
« Previous
7.18.7. getElementsByTagNameNS()
Next »

7.18.6. getElementsByTagName()

Statuspublic
Referencearray getElementsByTagName(string $name [, $recursive = true])

Returns an array of all the descendant nodes with the name $name. If the optional argument $recursive is set to false, the matching nodes are searched only within the direct children of the current node.

See also:

7.18. Opt_Xml_Scannable class
7.18.7. getElementsByTagNameNS()
7.18.6. getElementsByTagName()
« Previous
7.18.8. getElementsExt()
Next »

7.18.7. getElementsByTagNameNS()

Statuspublic
Referencearray getElementsByTagNameNS(string $namespace, string $name [, $recursive = true])

Returns an array of all the descendant nodes with the name $name and namespace $namespace. If the optional argument $recursive is set to false, the matching nodes are searched only within the direct children of the current node.

If $name is *, the method searches only the nodes within the specified namespace. If $namespace is *, the method behaves like Opt_Xml_Scannable::getElementsByTagName().

See also:

7.18. Opt_Xml_Scannable class
7.18.8. getElementsExt()
7.18.7. getElementsByTagNameNS()
« Previous
7.18.9. getLastChild()
Next »

7.18.8. getElementsExt()

Statuspublic
Referencearray getElementsExt(string $namespace, string $name)
Versionssince 2.0-dev8

This method works much like Opt_Xml_Scannable::getElementsByTagNameNS(), however it does not check the descendants of matching nodes. Its use is very common in OPT. Suppose we are writing the new instruction processor. The main instruction tag is opt:foo, but it must have also a descendant opt:bar:

<opt:foo>
    <div>
        <opt:bar>Hello world!</opt:bar>
    </div>
</opt:foo>

While parsing opt:foo we need to find the opt:bar for some reason and moreover simply to check if it is defined exactly once:

$barNodes = $node->getElementsByTagNameNS('opt', 'bar');
if(sizeof($nodes) != 1)
{
    throw new Opt_InstructionTooManyItems_Exception('opt:bar', 'opt:foo', 'One');
}

The first impression is that this code is correct. However, let's take a look at the following situation:

<opt:foo>
    <div>
        <opt:bar>
 
            <opt:foo>
                <div>
                    <opt:bar>Hello world!</opt:bar>
                </div>
            </opt:foo>
 
        </opt:bar>
    </div>
</opt:foo>

In this case the code above will surely cause error, even if it is easily noticeable that the second opt:bar should be connected to the nested opt:foo! Here, we must use getElementsExt():

$barNodes = $node->getElementsExt('opt', 'bar');
if(sizeof($nodes) != 1)
{
    throw new Opt_InstructionTooManyItems_Exception('opt:bar', 'opt:foo', 'One');
}

As this method does not visit the descendants of opt:bar, everything starts to work.

See also:

7.18. Opt_Xml_Scannable class
7.18.9. getLastChild()
7.18.8. getElementsExt()
« Previous
7.18.10. hasChildren()
Next »

7.18.9. getLastChild()

Statuspublic
ReferenceOpt_Xml_Node getLastChild()

Returns the last child of the current node or NULL, if the node does not have children.

7.18. Opt_Xml_Scannable class
7.18.10. hasChildren()
7.18.9. getLastChild()
« Previous
7.18.11. insertBefore()
Next »

7.18.10. hasChildren()

Statuspublic
Referencebool hasChildren()

Returns true, if the current node contains any children.

7.18. Opt_Xml_Scannable class
7.18.11. insertBefore()
7.18.10. hasChildren()
« Previous
7.18.12. moveChildren()
Next »

7.18.11. insertBefore()

Statuspublic
Referencevoid insertBefore(Opt_Xml_Node $newNode [, mixed $refNode = null [, $appendOnError = true]])

Inserts the specified $newNode before the child $refNode. $refNode may be specified either by the order number or by the object. By default, if $refNode cannot be found, the new node is added to the end of the children list, like in Opt_Xml_Scannable::appendChild(). However, if we set $appendOnError to false, it throws Opt_NodeNotFound_Exception in this case.

7.18. Opt_Xml_Scannable class
7.18.12. moveChildren()
7.18.11. insertBefore()
« Previous
7.18.13. removeChild()
Next »

7.18.12. moveChildren()

Statuspublic
Referencevoid moveChildren(Opt_Xml_Scannable $node)

Moves the children of the current node to $node. The method checks the types of the nodes in order to detect if they are allowed to be children of $node. In case of any problems, it throws Opt_APIInvalidNodeType_Exception.

7.18. Opt_Xml_Scannable class
7.18.13. removeChild()
7.18.12. moveChildren()
« Previous
7.18.14. removeChildren()
Next »

7.18.13. removeChild()

Statuspublic
Referencebool removeChild(mixed $node)

Removes the $node from the child list of the current node. $node may be specified either by the order number or by the object. It returns true, if the node is successfully removed.

7.18. Opt_Xml_Scannable class
7.18.14. removeChildren()
7.18.13. removeChild()
« Previous
7.18.15. replaceChild()
Next »

7.18.14. removeChildren()

Statuspublic
Referencevoid removeChildren()

Removes all the children of the current node.

7.18. Opt_Xml_Scannable class
7.18.15. replaceChild()
7.18.14. removeChildren()
« Previous
7.18.16. sort()
Next »

7.18.15. replaceChild()

ConstructFinal accessor method
Visibilitypublic
Referencebool replaceChild(Opt_Xml_Scannable $newNode, mixed $refNode [, $dispose = false ])
Argument list
$newNode - Opt_Xml_Scannable
The new node to insert
$refNode - mixed
The node to be replaced
$dispose - boolean
Dispose the old node?
Returned valueTrue on success

Replaces the child node $refNode with the $newNode. $refNode may be specified either by the order number or by the object. The method returns true, if the replacement is successful. The third, optional argument, $dispose controls disposing the memory of the old node. If the value is set to true, the old node is destroyed after replacing.

$dispose argument is available since OPT 2.0.1.

7.18. Opt_Xml_Scannable class
7.18.16. sort()
7.18.15. replaceChild()
« Previous
7.19. Opt_Xml_Text class
Next »

7.18.16. sort()

Statuspublic
Referencevoid sort(Array $prototypes)

Changes the order of child nodes to follow the specified rules. It takes an array of tag names in the requested order. It must also include the element * to mark the location of the undefined nodes. An example:

$node->sort(Array(
    'opt:foo',
    'opt:bar',
    '*'
));

After the execution of the call above, in the first place, we will have opt:foo tags, later opt:bar and at last, the remaining tags. sort() is widely used in complex instructions to provide the correct order of important sub-tags. For example, opt:if uses the following code to make sure that opt:elseif and opt:else are located at the end of child list:

$node->sort(array('*' => 0, 'opt:elseif' => 1, 'opt:else' => 2));

If the element * is not specified in $prototypes, the method generates Opt_InvalidAttributes_Exception.

The method uses a stable sorting algorithm. It means that it keeps the order of equal nodes.

7. API Reference
7.19. Opt_Xml_Text class
7.18.16. sort()
« Previous
7.19.1. __construct()
Next »

7.19. Opt_Xml_Text class

ConstructClass
ExtendsOpt_Xml_Scannable

Opt_Xml_Text is a container for Opt_Xml_Cdata and Opt_Xml_Expression nodes. It is a node that allows easier text block manipulation by grouping character data and OPT expressions associated to them.

7.19. Opt_Xml_Text class
7.19.1. __construct()
7.19. Opt_Xml_Text class
« Previous
7.19.2. appendData()
Next »

7.19.1. __construct()

Statuspublic
Reference__construct([string $text])

The default node constructor that allows to specify the optional initial $text. In this case, the constructor creates also a new Opt_Xml_Cdata node and initializes it with $text.

7.19. Opt_Xml_Text class
7.19.2. appendData()
7.19.1. __construct()
« Previous
7.20. Opt_Block_Interface
Next »

7.19.2. appendData()

ConstructFinal accessor method
Visibilitypublic
Referencevoid appendData(string $text)

Appends the $text to the last Opt_Xml_Cdata child. If the last child is Opt_Xml_Expression, the method creates the new Opt_Xml_Cdata node.

7. API Reference
7.20. Opt_Block_Interface
7.19.2. appendData()
« Previous
7.20.1. onClose()
Next »

7.20. Opt_Block_Interface

ConstructInterface

This interface allows you to use blocks in your scripts. The general idea is basically the same, like in components, but the offered interface is smaller and simpler.

See also:

7.20. Opt_Block_Interface
7.20.1. onClose()
7.20. Opt_Block_Interface
« Previous
7.20.2. onOpen()
Next »

7.20.1. onClose()

ConstructAbstract method
Visibilitypublic
Referencevoid onClose()

Represents the action that must be performed for a block port closing tag:

<opt:block from="$var"> 
    ...
</opt:block> <!-- here -->

The method is executed even if the onOpen() method ordered not to display the content.

See also:

7.20. Opt_Block_Interface
7.20.2. onOpen()
7.20.1. onClose()
« Previous
7.20.3. onSingle()
Next »

7.20.2. onOpen()

ConstructAbstract method
Visibilitypublic
Referenceboolean onOpen(Array $attributes)
Argument list
$attributes - array
The associative array of block tag attributes.

Represents the action that must be performed for a block port opening tag:

<opt:block from="$var"> <!-- here -->
    ...
</opt:block>

The method may return true or false to specify, whether the contents of the tag should be displayed or not.

See also:

7.20. Opt_Block_Interface
7.20.3. onSingle()
7.20.2. onOpen()
« Previous
7.20.4. setView()
Next »

7.20.3. onSingle()

ConstructAbstract method
Visibilitypublic
Referencevoid onSingle(Array $attributes)
Argument list
$attributes - array
The associative array of block tag attributes.

Represents the action that must be performed for a block port single tag:

<opt:block from="$var" /> <!-- here -->

See also:

7.20. Opt_Block_Interface
7.20.4. setView()
7.20.3. onSingle()
« Previous
7.21. Opt_Caching_Interface
Next »

7.20.4. setView()

ConstructAbstract method
Visibilitypublic
Referencevoid setView(Opt_View $view)
Argument list
$view - Opt_View
The view the block is deployed in.

This method is called by the executed template, when the block is going to be deployed in the port. OPT passes the view object that processes the template as an argument.

7. API Reference
7.21. Opt_Caching_Interface
7.20.4. setView()
« Previous
7.21.1. templateCacheStart()
Next »

7.21. Opt_Caching_Interface

ConstructInterface

Open Power Template does not provide its own caching solutions. Instead, it simply leaves an interface that allows you to integrate any external caching system with the library. The interface consists of two methods and is managed automatically by OPT to check, whether the requested template is cached and optionally capture the template output.

See also:

7.21. Opt_Caching_Interface
7.21.1. templateCacheStart()
7.21. Opt_Caching_Interface
« Previous
7.21.2. templateCacheStop()
Next »

7.21.1. templateCacheStart()

ConstructAbstract method
Visibilitypublic
Referenceboolean templateCacheStart(Opt_View $view)
Argument list
$view - Opt_View
The cached view object.
Returned valueThe information whether to execute the view or not.

Checks if the template requested by the view is already cached and optionally prints the cached content to the output. The method must return true, if it read the content from the cache and false if the original template should be executed to rebuild the cache. To capture the template output, you may use the output buffering functions available in PHP.

7.21. Opt_Caching_Interface
7.21.2. templateCacheStop()
7.21.1. templateCacheStart()
« Previous
7.22. Opt_Component_Interface
Next »

7.21.2. templateCacheStop()

ConstructAbstract method
Visibilitypublic
Referencevoid templateCacheStop(Opt_View $view)
Argument list
$view - Opt_View
The cached view object.

Finalizes capturing the template output. The method is executed only if Opt_Caching_Interface::templateCacheStart() returned false.

7. API Reference
7.22. Opt_Component_Interface
7.21.2. templateCacheStop()
« Previous
7.22.1. __construct()
Next »

7.22. Opt_Component_Interface

ConstructInterface

This is the PHP interface to write OPT component-compatible classes. It has to be implemented in our class, if its objects are going to be used by the components. Note that the class does not have to be registered in OPT unless we create defined components.

7.22. Opt_Component_Interface
7.22.1. __construct()
7.22. Opt_Component_Interface
« Previous
7.22.2. defined()
Next »

7.22.1. __construct()

ConstructClass constructor
Visibilitypublic
Reference__construct([string $name = ''])
Argument list
$name - string
The component name.

The component constructor. For user convenience, it might take an optional argument called $name that can be used to initialize one of the component parameters. For example, you can use the following constructor:

class myComponent implements Opt_Component_Interface
{
    private $_params = array();
 
    public function __construct($name = '')
    {
        if($name != '')
        {
            $this->set('name', $name);
        }
    } // end __construct();
 
    // Other code...
} // end myComponent;

Now, to specify the component name, we can pass it directly in the constructor:

// You can write
$component = new myComponent('someName');
 
// or
$component = new myComponent();
$component->set('name', 'someName');
7.22. Opt_Component_Interface
7.22.2. defined()
7.22.1. __construct()
« Previous
7.22.3. display()
Next »

7.22.2. defined()

ConstructAbstract method
Visibilitypublic
Referenceboolean defined(string $name)
Argument list
$name - string
The component parameter name
Returned valueA boolean value specifying, if the parameter is defined in the component

Returns true, if the component parameter $name is set in this component.

See also:

7.22. Opt_Component_Interface
7.22.3. display()
7.22.2. defined()
« Previous
7.22.4. get()
Next »

7.22.3. display()

ConstructAbstract method
Visibilitypublic
Referencevoid display([array $attributes = array()])
Argument list
$attributes - array
The list of attribute values set to the opt:display tag in the template.

display() should display the component. It is launched by the view in the place of opt:display tag. OPT passes the associative array of opt:display tag attributes as the $attributes argument. To render the HTML code, simply use echo statements, for example:

public function display($attributes = array())
{
    echo '<input type="text"';
    foreach($attributes as $name=>$value)
    {
        echo ' '.$name.'="'.$value.'"';
    }
    echo '/>';
} // end display();

Remember that the main purpose you should write components is the automation. Your components should be able to generate all the necessary attributes and tags without the help of the component users. Use the component parameters or even call your script API functions to get the necessary data.

7.22. Opt_Component_Interface
7.22.4. get()
7.22.3. display()
« Previous
7.22.5. manageAttributes()
Next »

7.22.4. get()

ConstructAbstract method
Visibilitypublic
Referencemixed get(string $name)
Argument list
$name - string
The component parameter name
Returned valueComponent parameter value

Returns the component parameter $name value.

See also:

7.22. Opt_Component_Interface
7.22.5. manageAttributes()
7.22.4. get()
« Previous
7.22.6. processEvent()
Next »

7.22.5. manageAttributes()

ConstructAbstract method
Visibilitypublic
Referencearray manageAttributes(string $name, array $attributes)
Argument list
$name - string
The tag name.
$attributes - array
The initial assotiative array of tag attribute values
Returned valueModified list of tag attribute values

manageAttributes() handles the attribute lists of the XML tags with the opt:component-attributes attribute or within the com namespace in the component port. It allows the component to extend the tag with some extra attributes (or modify their default values), for example to configure its look according to the current component status. The attributes are passed as an associative array, where the index is the attribute name, and the method should return the same, but modified array. The $name argument contains the tag name and it can be used to identify the tag we are going to process. The value of opt:component-attributes is added to the tag name and concatenated with the # symbol, for example: div#default.

Let's take a look at the following component port:

<opt:component from="$component">
    <div class="field" opt:component-attributes="default">
        <p>{$opt.component.title}</p>
        <span><opt:display /></span>
        <opt:onEvent name="error">
            <p>An error occurred: {$errorMessage}</p>
        </opt:onEvent>
    </div>
</opt:component>

If the component is in the invalid state (for example, the user entered the invalid value in the form field), we wish to change the CSS class of the entire <div>. Because it contains the opt:component-attributes attribute, OPT will capture this tag and send its attributes to manageAttributes(). It can modify the CSS class then:

public function manageAttributes($name, Array $attributes)
{
    if(!$this->valid)
    {
        $attributes['class'] .= 'error';
    }
    return $attributes;
} // end manageAttributes();

If your component does not provide any support for opt:component-attributes, the method should return the $attributes argument.

7.22. Opt_Component_Interface
7.22.6. processEvent()
7.22.5. manageAttributes()
« Previous
7.22.7. set()
Next »

7.22.6. processEvent()

ConstructAbstract method
Visibilitypublic
Referenceboolean processEvent($event)
Argument list
$event - $string
The event name
Returned valueThe boolean value indicating if the specified event occured or not.

processEvent() is called to ask the component if the specified $event is going to happen. The method must return true in order to display the content associated to the specified event, or false otherwise. Optionally, it can do some extra stuff. In the example below, we are handling the error event. If it happens, the component creates an extra template variable with the error message:

public function processEvent($event) 
{
    if($event == 'error')
    {
        if(!$this->valid)
        {
            // An error occurred
            $this->view->errorMessage = $this->errorMessage;
            return true;
        }
    }
    return false;
} // end processEvent();

Now we can use it in the template:

<opt:component from="$component">
    ...
    <opt:onEvent name="error">
        <p>An error occurred: {$errorMessage}</p>
    </opt:onEvent>
</opt:component>

For the unsupported events, processEvent() should return false.

7.22. Opt_Component_Interface
7.22.7. set()
7.22.6. processEvent()
« Previous
7.22.8. setDatasource()
Next »

7.22.7. set()

ConstructAbstract method
Visibilitypublic
Referencevoid set(string $name, mixed $value)
Argument list
$name - string
The component parameter name
$value - mixed
The new component parameter value

Sets the $name component parameter to $value.

See also:

7.22. Opt_Component_Interface
7.22.8. setDatasource()
7.22.7. set()
« Previous
7.22.9. setView()
Next »

7.22.8. setDatasource()

ConstructAbstract method
Visibilitypublic
Referencevoid setDatasource(mixed $datasource)
Argument list
$datasource - mixed
The data passed from the datasource to the component.

setDatasource() captures the value from the datasource attribute of the component port.

Prior to OPT 2.0-beta3 the $datasource argument was passed by reference which has been removed later.

7.22. Opt_Component_Interface
7.22.9. setView()
7.22.8. setDatasource()
« Previous
7.23. Opt_Generator_Interface
Next »

7.22.9. setView()

ConstructAbstract method
Visibilitypublic
Referencevoid setView(Opt_View $view)
Argument list
$view - Opt_View
The view the component is deployed in.

This method is called by the executed template, when the component is going to be deployed in the port. OPT passes the view object that processes the template as an argument.

7. API Reference
7.23. Opt_Generator_Interface
7.22.9. setView()
« Previous
7.23.1. generate()
Next »

7.23. Opt_Generator_Interface

ConstructInterface

The interface is used by StaticGenerator and RuntimeGenerator section data formats. It allows to lazy-load the section data with those generators. The generator object must be provided in place of the ordinary section data.

See also:

7.23. Opt_Generator_Interface
7.23.1. generate()
7.23. Opt_Generator_Interface
« Previous
7.24. Opt_Output_Interface class
Next »

7.23.1. generate()

ConstructAbstract method
Visibilitypublic
Referencemixed generate(string $what)
Argument list
$what - string
The section name that the generator was used in.
Returned valueThe section data.

Generates the data for the section. The data format provides the section name with the $what attribute, so that one class may support different sections.

A sample generator:

class myGenerator implements Opt_Generator_Interface
{
    public function generate($what)
    {
        return array(0 =>
            array('item' => 'Item 1'),
            array('item' => 'Item 2'),
            array('item' => 'Item 3'),
        );
    } // end generate();
} // end myGenerator;
 
$view = new Opt_View('view.tpl');
$view->section = new myGenerator();
$view->setFormat('section', 'RuntimeGenerator/Array');

The template:

<?xml version="1.0" ?>
<opt:root>
    <opt:section name="section">
        <p>{$section.item}</p>
    </opt:section>
</opt:root>
7. API Reference
7.24. Opt_Output_Interface class
7.23.1. generate()
« Previous
7.24.1. getName()
Next »

7.24. Opt_Output_Interface class

ConstructInterface
Implemented byOpt_Output_Http, Opt_Output_Return

In OPT, the views are executed by outputs. This interface provides a generic output API for OPT.

7.24. Opt_Output_Interface class
7.24.1. getName()
7.24. Opt_Output_Interface class
« Previous
7.24.2. render()
Next »

7.24.1. getName()

ConstructAbstract method
Visibilitypublic
Referencestring getName()
Returned valueThe output system name

This method must return the output system name. It is used mostly for debug purposes. Sample implementation:

public function getName()
{
    return 'My output';
} // end getName();
7.24. Opt_Output_Interface class
7.24.2. render()
7.24.1. getName()
« Previous
8. Appendix
Next »

7.24.2. render()

ConstructAbstract method
Visibilitypublic
Referencemixed render(Opt_View $view)
Argument list
$view - Opt_View
The view to render.
Returned valueThe returned value may depend on the output needs.

Executes the Opt_View::_parse() method in Opt_View class in order to execute the template. The most trivial implementation:

public function render(Opt_View $view)
{
    $view->_parse($this, true);
} // end render();

The second argument in _parse() indicates that the method should throw an exception, if the requested template does not exist. The library does not require the method to return any particular value, however the output system may return such a value in render().

Table of Contents
8. Appendix
7.24.2. render()
« Previous
A. Resources
Next »

8. Appendix

This chapter contains some extra information about the project.

8. Appendix
A. Resources
8. Appendix
« Previous
B. Error messages
Next »

Appendix A. Resources

Perhaps you wonder, where to find more tutorials, tips and tricks for Open Power Template.

  1. Invenzzia Wiki - this is community-driven repository of tutorials, tips and other useful stuff about OPT and other Invenzzia project.
  2. Invenzzia Resource Pages - official tutorials and devblog entries.
  3. Artykuly.zyxist.com - the website of one of OPT main developers also contains some stuff about OPT (however, you must know Polish to understand it).
8. Appendix
B. Error messages
A. Resources
« Previous
C. Possible problems
Next »

Appendix B. Error messages

This appendix contains the list of exceptions generated by OPT:

API Exceptions

Opt_Initialization_Exception
Your script tries to perform an action that can be done only if OPT is initialized/uninitialized (see the error message for details).
Opt_ContentType_Exception
The output system received the invalid content type value.
Opt_ObjectNotExists_Exception
The specified item you try to use is not registered in OPT.
Opt_TemplateNotFound_Exception
OPT could not find the specified template within the specified resource.
Opt_OutputOverloaded_Exception
The HTTP output system has already sent the XML content to the browser. The attempt of sending another template content to the browser will surely produce an invalid output document and has been blocked.
Opt_FilesystemAccess_Exception
Permission error: OPT is not able to read/write the specified directory.
Opt_InvalidEntityName_Exception
The specified string is not a valid entity name.
Opt_TreeInvalidDepth_Exception
The opt:tree instruction received invalid depth argument on the element list.

Template exceptions

Opt_XmlNoProlog_Exception
The template must have an XML prolog.
The exception does not occur, if the prologRequired configuration option is set to false.
Opt_XmlInvalidAttribute_Exception
Syntax error in the tag attribute list.
Opt_XmlInvalidProlog_Exception
Syntax/semantic error in the XML prolog.
Opt_XmlInvalidDoctype_Exception
Syntax error in the Doctype tag. Please note that OPT does not check the DTD section syntax.
Opt_XmlInvalidTagStructure_Exception
Invalid use of one of XML elements. For example, if you try to specify attributes for the closing tag, OPT throws this exception.
Opt_XmlRootElement_Exception
The template must contain a single root element.
The exception does not occur, if the singleRootNode configuration option is set to false.
Opt_XmlInvalidCharacter_Exception
Cannot use XML special characters like < and > in the static text. You have to encode them with entities.
Opt_XmlInvalidOrder_Exception
The tags have been closed in the invalid order.
Opt_XmlComment_Exception
The exception occurs, if the comments contain the content that is forbidden by the XML standard.
Opt_InvalidExpressionModifier_Exception
There are only two expression modifiers in OPT: e: and u:. Your template tried to use another one.
Opt_InvalidAttributeType_Exception
The value specified in one of OPT instruction attributes is invalid.
Opt_FormatNotFound_Exception
The specified data format has not been found. Check if you entered the name correctly and if it is installed in OPT.
Opt_FormatNotSupported_Exception
The specified data format does not support the specified functionality.
Opt_AssignNotSupported_Exception
The specified data format does not support assigning the new values to the specified variable.
Opt_FormatNotDecorated_Exception
The specified data format must decorate another data format in order to work properly in the specified place.
Opt_Expression_Exception
OPT expression error. The message identifies the unexpected token.
Opt_FunctionArgument_Exception
Missing argument in the function.
Opt_ExpressionOptionDisabled_Exception
The specified expression syntax option is not available in this place.
Opt_ItemNotAllowed_Exception
The specified item is not registered in OPT and cannot be used in templates.
Opt_SysVariableLength_Exception
The system variable call is invalid. OPT expects it to be longer/shorter.
Opt_SysVariableUnknown_Exception
Unknown system variable action.
Opt_SysVariableInvalidUse_Exception
The specified system variable call cannot be used in this place.
Opt_AttributeNotDefined_Exception
Missing required XML attribute.
Opt_AttributeEmpty_Exception
The required XML attribute is empty.
Opt_InvalidCallback_Exception
The callback registered in OPT is invalid.
Opt_UnknownEntity_Exception
The specified XML entity is not registered in OPT and cannot be parsed.

Compiler exceptions

These exceptions are usually caused by buggy instructions or the compiler code.

Opt_UnknownProcessor_Exception
No instruction processor has been found for the specified postprocessed XML tag.
Opt_CompilerLocked_Exception
The compiler can compile only one template at the same time.
Opt_CompilerCodeBufferConflict_Exception
The specified code buffer permits a limited number of PHP code snippets only.
Opt_APINoWildcard_Exception
Thrown by Opt_Xml_Scannable::sort()
The requested order list must contain a wildcard *.
Opt_APIInvalidBorders_Exception
Undocumented.
Opt_APIInvalidNodeType_Exception
The nodes of the specified type do not accept the specified nodes as their children.
Opt_APIHookNotDefined_Exception
Data format error: the specified hook is not defined in the data format.
Opt_APIMissingDefaultValue_Exception
Thrown by Opt_Compiler_Processor::_extractAttributes().
The optional attribute definition does not contain a default value.
Opt_APINoDataReturned_Exception
The instruction processor requested some data and did not receive them.

Instruction exceptions

The following exceptions are thrown by the instruction processors.

Opt_InstructionInvalidParent_Exception
Invalid parent of the specified tag. Check the instruction documentation.
Opt_InstructionTooManyItems_Exception
Too many occurrences of the specified tag in the instruction. Check the instruction documentation.
Opt_SectionExists_Exception
The specified section is already registered.
Opt_SectionNotFound_Exception
The specified section has not been found.
Opt_InstructionInvalidLocation_Exception
The specified tag must be nested somehow in another tag in order to work properly.
Opt_TreeContent_Exception
Thrown by opt:tree
Informs that the opt:content has been nested in an OPT tag that generates some dynamic PHP code. This may lead to generate an invalid compiler output.
Opt_SnippetRecursion_Exception
Infinite snippet recursion detected.
Opt_CompilerRecursion_Exception
Infinite include recursion detected.
Opt_InheritanceRecursion_Exception
Infinite template inheritance recursion detected.
Opt_IncludeNoAttributes
Thrown by opt:include
The opt:include instruction expects to have either file or from attribute.
Opt_ComponentNotActive_Exception
The specified item cannot be used outside the component context.
Opt_CycleNoValues_Exception
Trying to declare a cycle without values. Please simply add some values to the cycle.
Opt_AttributeInvalidNamespace_Exception
Thrown by opt:single attribute.
This attribute cannot change the state of the tags in the specified namespace.

Other exceptions

Opt_NotImplemented_Exception
The specified feature is not implemented yet.
This exception should not occur in the stable releases.
Opt_NotSupported_Exception
The specified feature is not supported by the current OPT version.
8. Appendix
C. Possible problems
B. Error messages
« Previous
D. Support
Next »

Appendix C. Possible problems

After update, my templates generate PHP errors

This may happen, if there were changes in the structure of the PHP code generated by OPT. We recommend to clean the template compilation cache (pointed by compileDir configuration option) after updating OPT and let the compiler rebuild it.

The script data are not displayed

OPT does not report such events like undefined template variable. Be sure that you do not make a mistake in the section/variable name both in the template and in the script. You can also change the default error reporting level for the template execution errorReporting:

$tpl->errorReporting = E_ALL | E_NOTICE;

However, in this case the templates might still display some internal notices.

After running a script with OPT I see the blank page.

In 99% cases the reason are some strange settings of Gzip compression in your web server. Try simply to disable such compression in OPT:

$tpl->gzipCompression = false;

I get a message "Exception thrown without a stack frame".

Neither OPT nor any other script that throws exceptions should be used in destructors. If the destructor is called during the finishing the script and some code throws an exception then, we get this message. There are two possible solutions:

public function __destruct()
{
    try
    {
        // Some dangerous code.
    }
    catch(Opt_Exception $e)
    {
        Opt_Error_Handler($e);
    }
} // end __destruct();
8. Appendix
D. Support
C. Possible problems
« Previous
E. Reporting bugs
Next »

Appendix D. Support

If you have a problem with the OPT library and do not know how to solve it, take a look at our discussion board, forums.invenzzia.org - we will try to help you find the solution and talk about many other things.

Support rules

8. Appendix
E. Reporting bugs
D. Support
« Previous
F. Authors and license
Next »

Appendix E. Reporting bugs

If you found a bug, please visit our bugtracker: bugs.invenzzia.org and report it. You will help developing the library.

How to use the bugtracker?

  1. We use the same bugtracker for various projects. Be sure that you add the report to the correct project.
  2. If it is possible, the report should contain a standalone test case that is ready to run. This will speed up reproducing the bug on our computers and creating the patch. If we cannot reproduce the bug, probably it will not be fixed.
  3. Include the information on OPT and PHP versions, as well as other projects that could interfere with the library.
  4. Please describe the expected behavior and the actual result.
  5. We include the full error message, if it is provided.
  6. The report must be written in English.
  7. Remember: if we need extra information, we will ask for them.
8. Appendix
F. Authors and license
E. Reporting bugs
« Previous

Appendix F. Authors and license

Open Power Template 2 was designed and created by the Invenzzia programming group which is also the holder of the copyrights. The people that worked on this:

We would like to thank all the users of both OPT 1 and OPT 2. We create them for you.

Copyright © Invenzzia Group 2008-2009 - www.invenzzia.org

License

Open Power Template 2.0 is available under the terms of new BSD license. It is included in the library archives.

This documentation is available under the terms of GNU Free Documentation License 1.2. Its content is included in the library archives and the source files are available to download on the Invenzzia website.