rah/rah_function

Every PHP function and method is a Textpattern tag

Installs: 13

Dependents: 0

Stars: 4

Watchers: 1

Forks: 1

Language: PHP

0.7.2 2014-03-20 15:18 UTC

README

Download | Packagist | Twitter | Issues | Support forum | Donate

Every function is a <txp:tag />. Rah_function allows you to call any PHP function in Textpattern’s tag syntax. No need for PHP snippets, ending nor closing PHP tags — the only thing you need is a XML like tag.

List of features

  • Call any PHP function with a single Textpattern tag.
  • Freely use functions attributes, flags and parameters.
  • Supports both a container- and a single-tag format.

Requirements

Rah_function’s minimum requirements:

  • Textpattern 4.0.7 or newer.
  • PHP 5.2.0 or newer.

Install

Using Composer:

$ composer require rah/rah_function:*

Or download a plugin package.

Basics

<txp:rah_function call="function" thing="here" parameter1="value" parameter2="value">
    Contained statement
</txp:rah_function>

The plugin introduces a <txp:rah_function /> 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. Apart from a thing, all attributes used in a tag are passed to the called PHP function as its arguments. The tag then processes the request and returns the results.

Tag attributes

Attributes for <txp:rah_function /> are as follows:

call
Name of the function or the class method you want to use with the tag. Comma-separated if multiple. This attribute is required.
Example: call="base64_encode" Default: ""

thing
Defines the argument position of a container tag’s contained statement. If a thing="here" 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="here" 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.

<txp:rah_function call="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="here" attribute. If the thing attribute is undefined, the contained statement is used as the first parameter of the PHP function.

<txp:rah_function call="str_replace" from="Hello" to="Hi" thing="here">
    Hello World!
</txp:rah_function>

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="here" 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.

<txp:rah_function call="str_replace" from="Hello" to="Hi">
    Hello World!
</txp:rah_function>

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.

<txp:rah_function 
    call="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.

<txp:rah_function call="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.

<txp:rah_function call="htmlspecialchars">
    <p class="msg">Hello World!</p>
</txp:rah_function>

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.

<txp:rah_function call="cs" name="theme" _is="blue">
    Theme is set as blue.
<txp:else />
    No blue?
</txp:rah_function>

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.

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

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 <txp:rah_function /> tag is needed.

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

<txp:rah_function call="str_replace" from="Hi" to="Hello">
    <txp:rah_function call="substr" string="Hello World!" start="0" end="7" />
</txp:rah_function>

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.

<txp:rah_function
    call="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.

<txp:rah_function
    call="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.

<txp:rah_function call="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

<txp:rah_function 
    _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.

<txp:rah_function call="function" _boolsilent="I become TRUE" _boolfalse="FALSE" />

_null

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

<txp:rah_function call="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).

<txp:rah_function call="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.

<txp:rah_function call="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 <txp:rah_function /> tags within articles. Only user-groups that have privileges granted an article.php level 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. For more information about user-privileges, please see:

Advanced PHP preferences

Rah_function follows the two PHP options that can be found from Textpattern’s Advanced 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 (e.g. /textpattern/config.php) file. Setting a whitelist up doesn’t take more than adding a single line to the config. 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

<txp:rah_function call="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="here" in the tag.

<txp:rah_function call="str_replace" search="Hello" replace="Hi" thing="here">
    Hello world!
</txp:rah_function>

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.

<txp:rah_function 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.

<txp:rah_function call="get_pref" name="production_status" />

The above can be used as a conditional by applying _is attribute:

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

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.

<txp:rah_function call="ltrim" strip="0">000150</txp:rah_function>
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.

<txp:rah_function call="trim">
    Hello World!
</txp:rah_function>

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.

<txp:rah_function call="do_list">
    value1, value2, value3
</txp:rah_function>

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.

<txp:rah_function call="escape_js">
    Soon to be valid JavaScript string.
</txp:rah_function>

Or for JSON:

<txp:rah_function call="json_encode">
    Soon to be valid for JSON.
</txp:rah_function>

Returning time using safe_strftime()

<txp:rah_function call="safe_strftime">
    %Y
</txp:rah_function>

Returns: 2009.

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

<txp:rah_function call="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.

<txp:rah_function call="safe_count" table="textpattern" where="Status IN(4, 5)" />

Returns: number of articles.

Changelog

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