Functions versus operators
The template language has two kinds of named building blocks. A function is a tag: {if}, {foreach},
{include}, {cache-block}. An operator produces a value inside an expression: wash, fetch, concat,
count. They are registered in separate lists, implemented by different methods and compiled differently. Knowing
which is which explains most "why does this not work here" questions.
Contents
- At a glance
- The same task both ways
- From function output to an operator
- Registration
- The PHP side
- Interpreted and compiled
- Which one to write
- Related pages
At a glance
| Function | Operator | |
|---|---|---|
| Written as | a tag: {name params}, often with {/name} | in an expression: name( ... ) or |name( ... ) |
| Parameters | name=value pairs, or its own grammar (foreach ... as ...) | positional, in parentheses |
| Produces | output text, variables, control flow | a value |
| Can have children | yes, block functions (if, foreach, cache-block) | no |
| Usable inside an expression | no | yes, nested and chained |
| Registered in | $eZTemplateFunctionArray | $eZTemplateOperatorArray |
| Main PHP method | process() | modify() |
| Compile hook | templateNodeTransformation() | operatorTemplateHints() and a transformation method |
On the site siteaccess | 48 registered | 395 registered |
The same task both ways
Many tasks can be done with either, and the result is the same text:
def $tags = array( 'news', 'sport', 'weather' )
function: foreach $tags as $t$tdelimiter, /delimiter/foreach
operator: $tags|implode( ', ' )
function: if $tags|count|gt( 2 )manyelsefew/if
operator: cond( $tags|count|gt( 2 ), 'many', 'few' )Output:
function: news, sport, weatheroperator: news, sport, weather
function: manyoperator: manyThe function form is easier to read when the branches or the loop body hold markup; the operator form fits inside a parameter or an attribute value.
From function output to an operator
The output of functions is text, and set-block turns text back into a value that operators can work on:
set-block variable=listforeach array( 'a', 'b', 'c' ) as $x<$x>/foreach/set-block
[$list] [$list|count_chars] [$list|upcase]Output:
[<a><b><c>] [9] [<A><B><C>]A function cannot be used where a value is expected. Inside an expression the parser only knows operators, so if( ... )
is looked up as an operator:
[concat( 'a', if( true() ) )]Output:
[a]Logged by the template engine:
WARNING : Operator 'if' is not registeredRegistration
Both kinds are listed in an eztemplateautoload.php file that eZTemplate::autoload() includes from every path of
site.ini [TemplateSettings] AutoloadPathList and every extension in ExtensionAutoloadPath
(<extension>/autoloads/eztemplateautoload.php). The template library's own file,
lib/eztemplate/classes/eztemplateautoload.php, shows both forms:
$eZTemplateOperatorArray[] = array( 'class' => 'eZTemplateTextOperator',
'operator_names' => array( 'concat', 'indent' ) );
$eZTemplateFunctionArray[] = array( 'class' => 'eZTemplateIfFunction',
'function_names' => array( 'if' ),
'function_attributes' => array( 'elseif',
'else' ) );function_attributes names the child tags that belong to the function (elseif and else of if; delimiter,
break, continue and skip of the loops; case of switch). Entries are only remembered at first; the class is
loaded the first time one of its names is used (loadAndRegisterOperators(), loadAndRegisterFunctions()). A name
registered twice keeps the entry read last, which is how an extension can replace a kernel operator; the
page Overriding kernel operators covers that.
The kernel's view functions (node_view_gui, attribute_view_gui and the other *_gui functions) are all one class,
eZObjectForwarder in kernel/common/ezobjectforwarder.php, configured by the rules in
kernel/common/eztemplateautoload.php.
The PHP side
An operator class answers:
operatorList(): the names it handles;namedParameterPerOperator()andnamedParameterList(): the parameters, withtype,requiredanddefault;modify( $tpl, $operatorName, $operatorParameters, $rootNamespace, $currentNamespace, &$operatorValue, $namedParameters, $placement ): reads the input from$operatorValueand writes the result back into it.
A function class answers:
functionList(): the names it handles;hasChildren(): whether it is a block (true, false, or an array per name);process( $tpl, &$textElements, $functionName, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace ): appends output to$textElements, sets variables, and processes its children with$tpl->processNode()as often as it likes; this is how loops repeat their body.
Interpreted and compiled
When site.ini [TemplateSettings] TemplateCompile is enabled (the default), each template is turned into a PHP file
once (lib/eztemplate/classes/eztemplatecompiler.php). Both kinds can take part:
- A function with
'tree-transformation' => trueinfunctionTemplateHints()replaces its node by generated PHP intemplateNodeTransformation().foreach,if,defandsetdo this. A function without it is called throughprocess()from the compiled file. - An operator with
'element-transformation' => trueinoperatorTemplateHints()generates PHP for its call, through the method named inelement-transformation-func(for examplephpMapTransformationforupcase). Others are called throughmodify()at run time.
Either way the output must be the same as when the template is interpreted. The examples of these pages were rendered both ways on a development installation and compared; the real differences found are noted on Sections versus foreach and in the foreach reference.
Which one to write
- Write an operator to compute, format or look up a value: it composes with every other operator and can be used in any parameter. Most extension needs are operators.
- Write a function only to control output: to repeat or skip a block, to capture or cache it, or to render something with its own template. Functions are harder to write well, because they handle children, namespaces and compilation.
- Write a fetch function (in a module's
function_definition.php) to give templates data from the database:fetch( 'yourmodule', 'yourfunction', hash( ... ) ). See Writing your own fetch function.
Related pages
- Previous: Operators in expressions
- Next: Control structures reference
- Function reference and Operator reference
- Writing template operators
- A custom template operator
- Free documentation: this page is free to copy, translate and improve under the GNU FDL 1.3.
Become an Exponential Partner
Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.