rah/rah_function

Every PHP function and method is a Textpattern CMS template tag

Fund package maintenance!
www.paypal.me/jukkasvahn

Installs: 20

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Watchers: 3

Forks: 0

Open Issues: 0

Type:textpattern-plugin

0.8.1 2023-02-24 23:00 UTC

This package is auto-updated.

Last update: 2024-03-25 01:09:31 UTC


README

Download | Packagist | Issues

Every PHP function and method is a Textpattern CMS template tag.

Install

Using Composer:

$ composer require rah/rah_function

Or download an installer package.

Basics

<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.

Attributes

Attributes for <rah::fn /> are as follows:

call
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.
Example: call="base64_encode" Default: ""

thing
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.
Example: foo="bar" thing bar="foo" Default: undefined.

_is
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.
Example: _is="FALSE" Default: undefined.

_assign
Creates a variable containing the tag’s returned value. The value is used as the variable’s name.
Example: _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 thing and call are reserved.
Example: foo2="bar" foo1="bar" foo4="bar" Default: undefined.

Function parameters

Apart from the tag’s own reserved attributes, including thing and 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" />

Above returns 1f3870be274f6c49b3e31a0c6728957f, a MD5 hash of an apple.

Containers and self-closing tags

Rah_function supports both container and self-closing, single tag use.

As a container tag

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.

As a self-closing tag

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 thing attribute.

<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.

Conditionals

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 blue.

Calling multiple functions at once

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 call attribute.

<rah::fn call="strip_tags, trim">
    <p>Some markup to <strong>strip</strong> and surrounding white-space to trim.</p>
</rah::fn>

Specified functions are processed from left to right. In the above snippet, strip_tags is ran first and its output is then passed on to trim.

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.

Calling both str_replace and substr would require two tags as both require two or more parameters.

<rah::fn str_replace from="Hi" to="Hello">
    <rah::fn substr string="Hello World!" start="0" end="7"/>
</rah::fn>

Returned values and legal types

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.

Integers, floats and strings

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.

Booleans

The last from the scalars, boolean, is converted to an uppercase string TRUE or FALSE. This is to allow differentiating an empty string (""), numbers 1, 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>

Arrays

Returned arrays will be converted to JSON representations. The following snippet would split the list of values into an array.

<rah::fn explode delimiter=", " values="value1, value2, value3" />

The numeric array returned by explode would be converted and returned as a literal JavaScript array.

["value1","value2","value3"]

Discarded

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.

Type casting and special attribute prefixes

<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 _bool, _null, _int and _array.

_bool

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 TRUE.

<rah::fn function _boolsilent="I become TRUE" _boolfalse="FALSE"/>

_null

The _null prefix converts the value to NULL, no matter what the supplied value is.

<rah::fn function _nullvalue="Always NULL, no matter what"/>

_int

The _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 0 (zero).

<rah::fn function _intid="I will be zero" _intnumericval="247"/>

_array

The _array prefix is used to generate and pass arrays. An attribute prefixed with an _array takes a JSON string (JavaScript Object Notation) and converts it to a PHP’s array.

<rah::fn implode _arrayValue="[1, 2, 3]"/>

Security related features

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.

Privileges in articles

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.

Advanced PHP preferences

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.

Function whitelisting

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 call attribute.

define('rah_function_whitelist', 'gps, ps, htmlspecialchars, str_replace, Class::StaticMethod, Class->Method');

Where gps, ps, htmlspecialchars, str_replace, Class::StaticMethod and 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.

Examples

Replacing content using str_replace in a single tag mode

<rah::fn str_replace search="Hello" replace="Hi" subject="Hello world!"/>

Returns: Hi world!

Replacing content using str_replace and containers

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>

Returns: Hi world!

Sanitizing and returning HTTP GET value

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 I&lt;3TXP.

Getting and checking site preferences

Textpattern’s 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 _is attribute:

<rah::fn get_pref name="production_status" _is="debug">
    Site is in debugging mode.
<txp:else />
    Either in Testing or Live.
</rah::fn>

Removing, appending and prepending whitespace or other characters

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.

Stripping zeros from the beginning

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 150.

<rah::fn ltrim strip="0">000150</rah::fn>

Stripping whitespace from the beginning and the end

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>

Returns Hello World! without the indentation or linefeed at the end.

Generating a valid JavaScript array

The following example generates a valid Javascript Array from a comma-separated list of values. The code splits the values to a PHP array using do_list(). Since the function returns array, rah_function encodes and returns as a valid JSON presentation. Rah_function returns any array as a JSON.

<rah::fn do_list>
    value1, value2, value3
</rah::fn>

The above returns ["value1","value2","value3"].

Generating a valid JavaScript string

Like in the previous example, encoding non-arrays is possible too. There are couple ways of doing it, if you want an valid and safe JavaScript string. One option is calling escape_js() and other is json_encode(). The first one strictly returns valid JavaScript while the latter takes JSON specifications into account.

<rah::fn escape_js>
    Soon to be valid JavaScript string.
</rah::fn>

Or for JSON:

<rah::fn json_encode>
    Soon to be valid for JSON.
</rah::fn>

Returning time using safe_strftime()

<rah::fn safe_strftime>
    %Y
</rah::fn>

Returns: 2009.

Fetching a single field from the database with fetch() function

<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 number of articles

Counting is done with Textpattern’s safe_count() function.

<rah::fn safe_count table="textpattern" where="Status IN(4, 5)" />

Returns: number of articles.

Changelog

Version 0.8.1 – 2023/02/25

  • PHP >= 8.0 compatibility. In PHP 8.0 or greater, the tag attributes would be passed down as named arguments to functions. This would cause fatal error in cases where the names did not match. To mitigate the issue, this release reverts back to pre-8.0 behaviour where the attributes are passed based on their order rather than argument name.

Version 0.8.0 – 2019/04/07

  • Register the tag for Textpattern >= 4.7.0 compatibility.
  • Called function name can be set with the first boolean attribute if call is omitted.
  • Supports boolean attributes as arguments.
  • Now requires Textpattern 4.7.0 or newer.

Version 0.7.2 – 2014/03/20

  • Fixed: error in composer.json file that prevented the plugin from being installed with Composer.

Version 0.7.1 – 2013/05/06

Version 0.7.0 – 2013/04/25

  • Added: _constant attribute prefix.
  • Added: _assign attribute.
  • Released as a composer package.

Version 0.6 – 2012/07/19

  • Updated: Help file (readme). Thanks you, Ralitza.

Version 0.5 – 2012/07/19

  • Added: Ability to call class methods (call="Class->Method" and call="Class::StaticMethod").
  • 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 _array.
  • Added: Converts returned booleans to uppercase strings, TRUE and 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.

Version 0.4 – 2011/12/16

  • 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.

Version 0.3 – 2011/07/08

  • 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.

Version 0.2 – 2009/11/28

  • Added attribute: thing. Thanks you Ruud for the suggestion.

Version 0.1.1 – 2009/11/21

  • Added has_privs() and allow_scripting checks.

Version 0.1 – 2009/11/21

  • First release