Every PHP function and method is a Textpattern CMS template tag
Every PHP function and method is a Textpattern CMS template tag.
$ composer require rah/rah_function
<rah::fn function thing parameter1="value" parameter2="value"> Contained statement </rah::fn>
The plugin introduces a
<rah::fn /> tag to Textpattern’s arsenal. It’s a multi-purpose tag that enables using public PHP functions and methods as Textpattern tags.
Rah_function is almost like a bridge between PHP and Textpattern’s tag syntax. It allows calling almost any PHP function as a Textpattern tag. Want to encode something? You can. Want use PHP’s string functions to truncate, count or otherwise bend strings at your will? You can. All without needing to write any code for new tags or add raw PHP blocks in page templates.
The tag takes a
call attribute which sets the function that is called by the tag. If omitted, the first boolean attribute name is used the called function. Apart from a
thing, all attributes used in a tag are passed to the called PHP function as its arguments in the given order. The tag then processes the request and returns the results.
<rah::fn /> are as follows:
Name of the function or the class method you want to use with the tag. Comma-separated if multiple. If omitted, the fist boolean attribute is used as the called function instead.
Defines the argument position of a container tag’s contained statement. If a
thing is the last defined attribute in the tag, then the contained statement is used as the last argument too for the PHP function. If
thing is left undefined, the contained statement is used as the first argument.
foo="bar" thing bar="foo" Default: undefined.
Converts the tag into a conditional. If set, the tag’s results are compared against the value of the
_is attribute. If they match, the tag’s contained statement is shown.
_is="FALSE" Default: undefined.
Creates a variable containing the tag’s returned value. The value is used as the variable’s name.
_assign="variable" Default: undefined.
parameter1="" , parameter2="" , parameter3="",[..]
Zero or more parameters passed to the called function as arguments. Parameters are assigned to the function in the order they are defined in the tag. These additional tag attributes can be named anything. A valid attribute name can contain letters (A-z) and numbers (0-9). The names
call are reserved.
foo2="bar" foo1="bar" foo4="bar" Default: undefined.
Apart from the tag’s own reserved attributes, including
call, all attributes are passed to the called function as arguments in the order in which they are defined. If no additional attributes are used, then the called PHP function is used as is without arguments.
When calling md5, the first attribute would be used as the string from which a hash is calculated.
<rah::fn md5 str="apple" />
1f3870be274f6c49b3e31a0c6728957f, a MD5 hash of an apple.
Rah_function supports both container and self-closing, single tag use.
The tag supports both a container and self-closing usage. When using the tag as a container tag, the contained statement is used in the parameter position specified by the tag’s
thing attribute. If the
thing attribute is undefined, the contained statement is used as the first parameter of the PHP function.
<rah::fn str_replace from="Hello" to="Hi" thing> Hello World! </rah::fn>
In the snippet above, the contained statement
Hi World! is used as the third parameter of the str_replace function because that is where the
thing is ordered. The snippet returns
Hello World!, as expected. If
thing attribute wasn’t used, the contained statement would be applied as first argument, leading to different results.
<rah::fn str_replace from="Hello" to="Hi"> Hello World! </rah::fn>
Still looks pretty much the same, but unlike the previous example where
thing was used, now the tag returns
Hello. Instead of the
to attribute, the contained statement would be used as the searched needle in str_replace.
A container translates seamlessly to a singular self-closing tag. The contained statement is a function parameter just like tag attributes, and as such, can be substituted with a tag attribute. The following does exactly same thing as the previous str_replace examples that used containers.
<rah::fn str_replace from="Hi" to="Hello" string="Hi World!"/>
These two htmlspecialchars snippets give the same results, but do it with different syntaxes. As a self-closing tag, a string would be passed to the function as a tag attribute.
<rah::fn htmlspecialchars string="<p class=""msg"">Hello World!</p>" />
But that string can also be passed as a contained statement. Since the string is htmlspecialchars function’s first parameter, the tag doesn’t need a
<rah::fn htmlspecialchars> <p class="msg">Hello World!</p> </rah::fn>
Gives a relatively readable formatting and avoids quote escaping that comes with tag attributes.
A rah_function tag can transform into a conditional by applying the
_is attribute. When
_is is used, the tag’s results are compared against the attribute’s value. If they match, the tag’s contained statement is shown. If compared values do not match, an else statement is shown, if defined.
<rah::fn cs name="theme" _is="blue"> Theme is set as blue. <txp:else/> No blue? </rah::fn>
The above snippet checks if an HTTP cookie named
theme is set as
Since version 0.5, a single tag can call multiple functions. This allows to further process output returned by a function with a second or more functions. Calling multiple functions with a single tag instance is done by simply using comma-separated (
,) list of functions in the
<rah::fn call="strip_tags, trim"> <p>Some markup to <strong>strip</strong> and surrounding white-space to trim.</p> </rah::fn>
The first function in the list acts as the primary one. Its output is passed by reference to the following functions and all tag attributes apply only to it. The second or later functions do not get passed any attributes and the output from the first function is assigned as the consecutive functions’ first parameter.
Calling multiple functions works only if the following functions expect a single parameter. If they require more than a one parameter, more than one
<rah::fn /> tag is needed.
<rah::fn str_replace from="Hi" to="Hello"> <rah::fn substr string="Hello World!" start="0" end="7"/> </rah::fn>
Due to how Textpattern’s template language and PHP work, not every function’s output can be returned to the template in its original format. The limitation comes in the form of types.
Textpattern’s markup language expects strings, and that is what we must give it. Rah_function returns values of types integer, float and string to the template as is, in their true presentation, with the expectation that the returned type is converted to a string by Textpattern.
The last from the scalars, boolean, is converted to an uppercase string
FALSE. This is to allow differentiating an empty string (
0 and booleans from one another, otherwise in Textpattern’s template context you would have no idea which is which.
For instance, PHP’s strpos returns an integer starting from zero or a boolean
FALSE when no matches are found. When the output gets converted to a string, that zero and FALSE become the same, and there would be no way of knowing whether there were any matches.
<rah::fn strpos value="ABC, zero or false?" search="A"/>
If no conversion was done, the above would return a zero/empty, but so it would act as if there were no matches. Through the conversion, the position and boolean are distinguishable.
<rah::fn strpos value="ABC, zero or false?" search="A" _assign="matches"/>
<txp:if_variable name="matches" value="FALSE"> No matches. <txp:else /> First match at <txp:variable name="matches"/> </txp:if_variable>
<rah::fn explode delimiter=", " values="value1, value2, value3" />
The rest of types, including object, resource, NULL and callable will be discarded and a notice will be issued. Discarding is done to prevent issues. The types that are not returned do not translate to the markup language and are not usable in string context. Thus, they are not returned. While the illegal output will not be returned, the functions will still be executed. For instance, a class method that returns an object can still be executed just fine with rah_function. There just won’t be any output apart from a harmless, informative error message.
<rah::fn _boolAttr="boolean" _nullAttr="NULL" _intAttr="integer" _arrayAttr="array"/>
To get around template language’s type juggling limitations, rah_function packs a couple special attribute prefixes. These prefixes can be added before tag attribute names, allowing to cast values to different types before passing the attributes to the called function as arguments. These special attribute prefixes are
If a tag attribute is prefixed with
_bool, the value is converted to a boolean type. The value is converted to
FALSE if it is empty (
0 or uppercase string
FALSE. If it’s anything else, it becomes
<rah::fn function _boolsilent="I become TRUE" _boolfalse="FALSE"/>
_null prefix converts the value to NULL, no matter what the supplied value is.
<rah::fn function _nullvalue="Always NULL, no matter what"/>
_int prefix converts the value to integer, ensuring that the value is a safe, valid integer. Numeric values will be rounded towards zero and non-numerical strings will be converted based on the initial portion of the string. If the attribute value starts with valid numeric data, this will be used as the value. Otherwise, the value becomes
<rah::fn function _intid="I will be zero" _intnumericval="247"/>
_array prefix is used to generate and pass arrays. An attribute prefixed with an
<rah::fn implode _arrayValue="[1, 2, 3]"/>
Considering the plugin’s nature, it comes with few options to limit its access. Limiting extends to both what a rah_function tag can do and where the tag can be used. The plugin has a whitelisting option for enabling only certain functions, and it follows Textpattern’s PHP evaluation rules and user privileges, the same rules that affect php tags.
User group privileges limit which users can use
<rah::fn/> tags within articles. Only user-groups that have privileges granted access to
article.php resource can use the tags in an article body, custom fields or excerpt. By default only the two highest groups, Publishers and Managing Editors (groups with ID 1 and 2) have access to
article.php and will be able to publish articles with the plugin’s tags in them.
Rah_function follows the two PHP options that can be found from Textpattern’s Preferences panel, Allow PHP in pages and Allow PHP in articles. When Allow PHP in pages option is disabled, a rah_function tag can not be used in a page template or a form partial. When Allow PHP in articles option is disabled, rah_function tag can not be used in articles. The tags won’t be executed, no matter what permissions the article’s author has.
For added optional security, certain functions can be explicitly whitelisted. If the whitelisting option is defined, then only those whitelisted functions can be called with a rah_function tag.
Whitelisting options can be set from Textpattern’s
config.php file. Rah_function expects a constant named
rah_function_whitelist, populated with a comma-separated list of function names. Function names and class methods should be formatted in the same way as when used in a rah_function tag’s
define('rah_function_whitelist', 'gps, ps, htmlspecialchars, str_replace, Class::StaticMethod, Class->Method');
Class::Method would be the allowed functions and class methods.
This whitelisting option is completely optional, and it doesn’t need to be configured. It should only be used if you want to enable certain functions for added security.
<rah::fn str_replace search="Hello" replace="Hi" subject="Hello world!"/>
A contained statement is used as str_replace subject parameter. Wrapped content is positioned to the correct location by using
thing in the tag.
<rah::fn str_replace search="Hello" replace="Hi" thing> Hello world! </rah::fn>
Returning GET/POST values and sanitizing the output can be done with a single rah_function tag instance by using the plugin’s multi-function call feature. First we would use Textpattern’s
gps function to get a specific value, e.g.
theme, and then prepare it for the page template by converting HTML’s special characters to entities with htmlspecialchars.
<rah::fn call="gps, htmlspecialchars" name="theme" />
The above would return HTTP GET/POST param’s, named
theme, value. If the requested value is
?theme=I<3TXP, the above would return a safe string of
get_pref() function can be used to return site’s preference settings. Following would return site’s production status.
<rah::fn get_pref name="production_status" />
The above can be used as a conditional by applying
<rah::fn get_pref name="production_status" _is="debug"> Site is in debugging mode. <txp:else /> Either in Testing or Live. </rah::fn>
PHP comes with a couple useful functions for removing whitespace and other characters from the beginning and end of a string: trim, ltrim and rtrim. Trim removes characters from both ends, while ltrim only touches the beginning and rtrim() wants to be all rightful. All three can take up to two arguments. The first one is the string which will be trimmed, and the second, a list of characters that are stripped. If no characters are specified, white-space is stripped.
Wrapped content is passed to ltrim, which is set to strip zeros (
0) from the beginning of the string. The original value
000150 is converted to
<rah::fn ltrim strip="0">000150</rah::fn>
When no extra arguments are given to trim, it will strip any whitespace from the beginning and the end.
<rah::fn trim> Hello World! </rah::fn>
Hello World! without the indentation or linefeed at the end.
<rah::fn do_list> value1, value2, value3 </rah::fn>
The above returns
escape_js() and other is
Or for JSON:
<rah::fn json_encode> Soon to be valid for JSON. </rah::fn>
<rah::fn safe_strftime> %Y </rah::fn>
<rah::fn fetch what="last_access" from="txp_users" where="user_id" is="1" />
Returns: Last access time for site admin with user ID of 1.
Counting is done with Textpattern’s
<rah::fn safe_count table="textpattern" where="Status IN(4, 5)" />
Returns: number of articles.
- Register the tag.
- Called function name can be set with the first boolean attribute if
- Supports boolean attributes as arguments.
- Now requires Textpattern 4.7.0 or newer.
- Fixed: error in composer.json file that prevented the plugin from being installed with Composer.
- Changed: Updated the Composer package to use textpattern/installer.
- Released as a composer package.
- Updated: Help file (readme). Thanks you, Ralitza.
- Added: Ability to call class methods (
- Added: Multiple functions can be called with a single tag instance (
call="func1, func2, func3"). Output is passed by reference from function to function. First function in the list is treated as the primary and given tag attributes only apply to it.
- Added: Arrays are returned as JSON string and can be passed from one tag instance to other. JSON can be used as a value by prefixing used tag attribute name with
- Added: Converts returned booleans to uppercase strings,
FALSE. This makes it possible to identify integers from booleans (e.g. strpos).
- Added: Function whitelisting option.
- Improved: Prevent passing non-scalars to the template.
- Improved: Moved away from sanitization and
eval(). Now uses callbacks.
- Improved: Show some error messages when needed.
- Updated: Help file (readme). Thanks to Tye for help.
- Improved: Do not use attributes real names in the function call, but use an temp array. Makes sure attributes get resolved as valid variables no matter what is passed by Textpattern’s parser to the plugin.
- Fixed: Now an empty, nothing-at-all string can be used as the container-mode’s wrapped statement.
- Added: Now makes sure that the called function is really defined before executing anything.
- Added attribute:
thing. Thanks you Ruud for the suggestion.
- First release