OW Simple Operator: limitations and risks
OW Simple Operator saves code by giving every operator the same generic definition. That generic definition is
also where its limits come from. Everything below is read from extension/owsimpleoperator/autoloads/owsimpleoperator.php
and autoloads/eztemplateautoload.php and, where an example is shown, rendered on a development installation.
Contents
- The pipe form drops an argument
- Null arguments are left out
- No parameter checks
- No compile-time code
- Name clashes
- Cached output
- Security of permitted PHP functions
- When to write a full operator class instead
The pipe form drops an argument
OWSimpleOperator::modify() handles a piped value like this:
// If there are an operator value, we switch it with our parameter
if ( $operator_value !== null )
{
array_pop( $method_arguments );
array_unshift( $method_arguments, $operator_value );
}For a class method the argument list always has ten entries (one per declared parameter), so the entry that
is removed is the tenth, unused one, and {1|example_sum( 2 )} works as example_sum( 1, 2 )
(example).
For a PHP function the list only holds the arguments you gave (see the next section), so the removed entry
is your last argument. {'This is a great example!'|str_replace( 'great', 'very good' )} calls
str_replace( 'This is a great example!', 'great' ), and PHP throws an ArgumentCountError; see
the example on the first page. With a function that has
optional parameters there is no error but a wrong result, which is worse. Rule: call PHP function operators in
the call form with every argument in the parentheses, and pipe only into functions with exactly one argument
({$text|str_rot13}).
A piped value of null counts as no piped value at all (the test is $operator_value !== null), so
{$missing|str_rot13} with an unset value calls str_rot13() without arguments.
Null arguments are left out
For PHP functions, modify() copies only the arguments that are set:
foreach( $named_parameters as $index => $parameter )
{
if( isset( $parameter ) )
{
$method_arguments[$index] = $parameter;
}
}and then calls call_user_func_array() with that list. An argument whose value is null is not passed, and
every argument after it moves one place forward into the wrong parameter of the PHP function. Make sure the
values you pass are set (first_set() gives a fallback, see
Control operators).
For class methods the opposite happens: all ten values are passed, so a parameter you leave out arrives as null
and never gets your PHP default (Optional arguments).
No parameter checks
namedParameterList() declares ten parameters of type mixed, none required, default null, for every operator.
The template engine therefore never reports a missing or wrong argument for these operators; PHP does, at run
time, and the template engine does not catch PHP's errors:
str_rot13()Output:
(no output)Logged by the template engine:
EXCEPTION ArgumentCountError: str_rot13() expects exactly 1 argument, 0 givenOperators of the template library check their parameters themselves and log a template warning instead, for
example Missing required parameter: start from substr in eZTemplateStringsOperator
(PHP string functions).
If a name is listed in owsimpleoperator.ini but is neither a method of the class nor a PHP function, modify()
writes The method "<name>" doesn't exists. to the debug output and returns null.
No compile-time code
OWSimpleOperator has no operatorTemplateHints(). The template compiler (eZTemplateCompiler::operatorHint())
then has no hints for these operators and cannot turn them into inline PHP, so compiled templates call the
operator object at run time, every time. The operators of the template library declare hints and many of them are
compiled into plain PHP (see The operator class interface and
The template compiler). For an operator called a few times per
page this does not matter; inside a loop over hundreds of items it can.
Name clashes
Every PHP function you permit and every method you register becomes a global operator name. On a development installation,
str_replace is registered by both extension swark and owsimpleoperator; owsimpleoperator's
eztemplateautoload.php is read later and wins. A permitted name that matches an operator of a class with several
operators (for example strlen of eZTemplateStringsOperator) is worse: the template library's class registers
all its names again when it loads, and takes the name back in the middle of a request
(Registration order and overrides).
Use names that no other operator has.
Cached output
Operator results end up in the output like any other text, so they are stored in the view cache and in
cache blocks. {time()} inside a cached full view shows the time the cache was written, not the time of the
request. The extension's README says the same: use cache-block with the right keys or expiry, or keep such
calls out of cached templates (Template caches).
Security of permitted PHP functions
A template can call a permitted function with any argument. Templates are written by designers, edited through
the admin's template editor and shipped by extensions, so treat PermittedFunctionList[] like code:
- list only functions without side effects that read nothing outside their arguments (the rule in the shipped settings file);
- never file, process, network, configuration or code-evaluation functions (
file_get_contents,exec,getenv,ini_get,call_user_func, ... the extension's test lists the ones it refuses); - escape what an operator returns when it contains user content (
|wash), because nothing escapes it for you.
When to write a full operator class instead
Write an ordinary operator class (Creating an operator, step by step) when you need any of these:
- required parameters with clear template warnings, or typed parameters;
- an operator that works on its piped input and also takes several parameters;
- compile-time optimization (
operatorTemplateHints()); - registration in your own extension without touching
owsimpleoperator.ini; - no dependency on a third extension being active.
OW Simple Operator stays a good fit for small helpers with one or two arguments that you call in the call form.
Related pages
- OW Simple Operator: PHP functions as template operators
- Your own operators with OW Simple Operator
- Registration order and overrides
- 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.