PHx

From MODx Wiki
Jump to: navigation, search

Contents

Introduction

PHx (Placeholders Xtended) will add the capability of output modifiers when using placeholders, content tags (including template variables) and settings tags. The recursive parser allows for nested tags. Custom modifiers can be added by creating "mini-snippets" in the MODx resource manager.

Downloads

Download the latest version of PHx from MODx repository. The current version is 2.1.4-GA.

Installation

New install

  1. Download and extract the archive.
  2. Create a directory called "phx" in your [MODx Directory]/assets/plugins directory
  3. FTP or copy the files into [MODx Directory]/assets/plugins/phx
  4. Create a new plugin in the manager called "PHx" and copy/paste the contents of phx.plugin.txt into the code field.
  5. Check "OnParseDocument" at the System Events tab

Update

  1. Download and extract the archive.
  2. Backup and delete the contents of [MODx Directory]/assets/snippets/phx
  3. Create a directory called "phx" in your [MODx Directory]/assets/plugins directory
  4. FTP or copy the files into [MODx Directory]/assets/plugins/phx
  5. Update the "PHx" plugin in the manager and copy/paste the contents of phx.plugin.txt into the code field.
  6. Check "OnParseDocument" at the System Events tab

Configuration

On the Configuration tab -> Plugin configuration enter:

&phxdebug=Log events;int;0 &phxmaxpass=Max. Passes;int;50

Advanced Users

You can change the default values of the PHx parser

  • Log Events
0 = Disabled
1 = Enabled logging of PHx
When enabled PHx creates a detailed processing log for every page load in the MODx System Event log ( Manager->Reports->System Events)
  • Max. Passes
Enter a number which represents the maximum depth of your nested tags. I recommend leaving this on the default value of 50.

Description

PHx (Placeholders Xtended) extends the use of placeholders, content tags (including template variables) and settings tags so you can easily format how the final output should look. When enabled it plugs into the MODx parser, extending it with modifiers, conditionals and as a bonus makes it truly recursive. PHx will also eliminate the need for lesser parameter snippets where you'd use them to format a value for display.

Great for templating!

Transforming a value is is as easy as adding :modifier.


Supported tags

When enabled, PHx supports extensions of the following MODx tags:

[+placeholder+]
[*content tag/TV*]
[(setting)]

Snippets supporting PHx

  • Ditto (as of 1.1 Beta 2)
  • Jot
  • MaxiGallery

You can also use PHx in chunks of other snippets that are not in the above list but it may require another approach (see the Tips & Tricks section).

Usage

A normal placeholder like [+placeholder+] can be extended to a PHx placeholder: [+placeholder:esc+]. The same applies for content tags like:

[*createdby*]

Some modifiers take options like:

[*createdby:date=`%a %B %d, %Y at %H:%M`*]

Multiple modifiers can be placed into one placeholder/template variables and will be processed from left to right.

somevar:esc:nl2br:strip

Advanced usage

There is a special placeholder called 'phx' which acts as a dummy placeholder to start a sequence without an actual variable.

[+phx:if=`[+this+]`:is=`[+that+]`:then=`do this`:else=`do that`+]

Also, with some modifiers this dummy will automatically have a specific default value. In case of the "userinfo" modifier it takes on the value of the current userid.

[+phx:userinfo=`username`+]

Known issues

Syntax

It seems logical but I mention this as an issue because it's easy to take into account. Try to avoid the following character combinations in your template if they are not part of a MODx tag:

[+
[*
[(
+]
*]
)]
]]

They might throw the parser and MODx off guard. In normal circumstances this wouldn't be a problem. But in the case of javascript in your template a situation like this:

array[counter++]

... might trigger weird behaviour because of the +]. Also, using a closing CDATA tag like this:

/* ]]> */

... will create issues as well.

Just remember that you can't break your website by using faulty PHx syntax, the worst thing that could happen is that your layout comes out the wrong way.

Modifiers

String

lcase

Returns current value with all alphabetic characters converted to lowercase.

Example:
[+string:lcase+]
  • Input:
    This is a string
  • Returns:
    this is a string

ucase

Returns current value with all alphabetic characters converted to uppercase.

Example:
[+string:ucase+]
  • Input:
    This is a string
  • Returns:
    THIS IS A STRING

ucfirst

Returns current value's first character as uppercase

Example:
[+string:ucfirst+]
  • Input:
    this is a string
  • Returns:
    This is a string

length | len

Returns length of the current value.

Example:
[+string:len+]
  • Input:
    this is a string
  • Returns:
    16

notags

Strips html tags from the current value.

Example:
[+string:notags+]
  • Input:
    this <b>is</b> a <i>string</i>
  • Returns:
    this is a string

esc

Escapes html and the bracket characters.

htmlent

Converts the input to html entities.

nl2br

Converts new lines to breaks.

Example:
[+string:nl2br+]
  • Input:
    1. this is
    2. a string
  • Returns:
    this is <br />a string

strip

Strip newlines (\n), tabs (\t) and multiple spaces.

Example:
[+string:strip+]
  • Input:
    1. this is
    2. a
    3. string
  • Returns:
    this is a string

Other

  • reverse
    Reverses the current value.
  • wordwrap(=`length`)
    length - characters
    Breaks words in the current value longer than the given length of characters by putting a space in between.
    Default: 70 characters.
  • limit(=`length`)
    Returns the first X characters from the current value.
    Default: 100 characters.

Special

date(=`dateformat`)

  • dateformat: Like PHP strftime
  • Converts unix timestamps to the format specified.

Example:

[*createdon:date=`%d.%m.%Y`*]

To change the output of date according to the language of the website, insert setlocale at the beginning of the PHx-plugin (this is an example for German language):

 setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'de', 'ge');

md5

  • Creates a MD5 hash from the current value.

userinfo=`field`

  • field: Formats a userid value (webuser have negative ids) to a field specified. Similar to the fields used in the MODx database user_attributes table (eg: username, useremail):
    • cachepwd : Cache password
    • comment : Comment
    • country : Country
    • dob : Date of birth, in UNIX time format
    • email : Email
    • fax : Fax
    • fullname : Full name
    • gender : Gender
    • internalKey : User internal key
    • lastlogin : Last login, in UNIX time format
    • logincount : Number of logins
    • mobilephone : Mobile phone
    • password : Password
    • phone : Phone
    • photo : Photo
    • role : Role
    • state : State
    • thislogin : This login, in UNIX time format
    • username : User name
    • zip : Zip code

math=`calculation`

  • Use simple calculations like - * + /.
  • The "?" character is replaced by the current value of the extension but you can also use nested tags.
  • Example calculation: ?+1+(2+3)+4/5*6

ifempty=`other value`

  • Use "other value" when the output of the placeholder/templatevar is empty.

select=`options`

Translates the value of placeholder/templatevar to a specified output.

  • options like: value1=output1&value2=output2

Example:

  • Input: 1
[+placeholder:select=`0=OFF&1=ON&2=UNKNOWN`+]
  • Returns ON

Conditional operators

is

  • alias: eq

is equal to (==)

ne

  • alias: isnot, isnt

is not equal to (!=)

eg

  • alias: isgt

is equal or greater than (>=)

el

  • alias: islt

is equal or lower than (<=)

gt

is greater than (>)

lt

is lower than (<)

mo=`Webgroups`

  • aliases: isinrole, ir, memberof

Takes a comma-separated list of webgroup names and returns true/false depending on the user being in the group or not (replaces the "inrole" modifier which had to be combined with a conditional operator).

Example:
[+phx:mo=`myWebgroup`:then=`I'm a member`:else=`I'm NOT a member`+]

if=`value`

Takes a new value. Will normally be used before :or or :and

or

Previous OR next statement

[+phx:if=`[*id*]`:is=`2`:or:is=`3`:then=`{ {Chunk} }`:else=`{ {OtherChunk} }`+]

and

Previous AND next statement

[+phx:if=`[!UltimateParent!]`:is=`1`:and:isnot=`[*id*]`:then=`{ {ChildChunk} }`:else=`{ {ParentChunk} }`+]

then=`template`

template : This is shown if conditions are true. Template can be a { {chunk} }, [ [ snippet] ] or just plain html.

else=`template`

template : This is shown if conditions are false. Template can be a { {chunk} }, [ [ snippet] ] or just plain html.

show

Similar to then except that it uses the original input as 'template' when the condition is true.

[+myplaceholder:len:gt=`3`:show+]
  • Will return the value of myplaceholder if it contains more then 3 characters.

More examples

Example 1

myplaceholder is set to myvalue

  • [+myplaceholder:is=`myvalue`:then=`Correct`:else=`Incorrect`+]
    will return: Correct
  • [+myplaceholder:isnot=`myvalue`:then=`Correct`:else=`Incorrect`+]
  • [+myplaceholder:is=`othervalue`:then=`Correct`:else=`Incorrect`+]
    will both return: Incorrect
Example 2
  • myplaceholder is set to 2
  • someplaceholder is set to 3
  • otherplaceholder is set to 1
  1. [+myplaceholder:is=`2`:then=`
  2. {{ChunkGood}}
  3. `:else=`
  4. {{ChunkBad}}
  5. `+]

Will display the contents of chunk called ChunkGood.

  • [+myplaceholder:gt=`1`:then=`Yes`:else=`No`+]
  • [+myplaceholder:lt=`3`:and:gt=`1`:then=`Yes`:else=`No`+]
  • [+myplaceholder:lt=`[+someplaceholder+]`:then=`Yes`:else=`No`+]
  • [+myplaceholder:islt=`2`:then=`Yes`:else=`No`+]
  • [+myplaceholder:isnot=`2`:or:lt=`3`:then=`Yes`:else=`No`+]

Will all return Yes.

  • [+myplaceholder:isnot=`2`:then=`Yes`:else=`No`+]
  • [+myplaceholder:gt=`[+someplaceholder+]`:then=`Yes`:else=`No`+]
  • [+myplaceholder:lt=`2`:then=`Yes`:else=`No`+]
  • [+myplaceholder:gt=`2`:then=`Yes`:else=`No`+]
  • [+myplaceholder:lt=`1`:then=`Yes`:else=`No`+]

Will all return No.

Custom modifiers

A modifier is in fact a replacement for a simple snippet to process a given value to anything you like. It's possible to create your own custom modifiers/mini-snippets by adding a new snippet into the MODx resource manager.

Because a modifier doesn't hold complicated code you don't need parameters other than what a modifier in PHx gets from the parser. There are two main variables:

  1. $output
    the current value of the variable that is getting "modified"
  2. $options
    (optional) an optionial string that a modifier could have

To bring this into perspective here are some short examples assuming myplaceholder is set to "test":


[+myplaceholder:mymodifier+]
  • $output contains the value "test".
  • $options contains nothing because they aren't specified.

[+myplaceholder:mymodifier=`my options`+]
  • $output still contains the value "test".
  • $options contains the value "my options".

Other variables (advanced users)

  • $input contains the original input (starting value).
  • $condition an array containing the elements that make up a conditional statement (0, 1, || and &&).

Creating a custom modifier

Example: I love MODx

With this knowledge we are going to create a new custom modifier. In this example I want to create a modifier (without options) that adds the string " because I love MODx" to a variable.

  1. Login to the MODx manager
  2. Go to Resources -> Manage Resources -> Snippets
  3. Click "new snippet"
  4. For the snippet name we enter "'phx:love'"
    For PHx to know about the custom modifier all snippets created for PHx should be prefixed with "phx:" the string (containing NO spaces) after the prefix will be the actual modifier name. In this case our modifier will be triggered by adding :love to the placeholder like [+myplaceholder:love+].
  5. Now we are going to enter the modifier code into the snippet code field. For this example we create it like this:
    1. <?php
    2. $newvalue = $output. " because I love MODx";
    3. return $newvalue;
    4. ?>
  6. Click "Save" and your custom modifier (:love) is ready for use!

Example: I love MODx even more

Similar to example 1 we are now going to create a modifier that will do the same BUT if there are options specified it will take the options value as the string to append.

  1. Login to the MODx manager
  2. Go to Resources -> Manage Resources -> Snippets
  3. Click "new snippet"
  4. For the snippet name we enter "phx:love2"
    For PHx to know about the custom modifier all snippets created for PHx should be prefixed with "phx:" the string (containing NO spaces) after the prefix will be the actual modifier name. In this case our modifier will be triggered by adding :love to the placeholder like [+myplaceholder:love2+].
  5. Now we are going to enter the modifier code into the snippet code field. For this example we create it like this:
    1. <?php
    2. $defaultValue = " because I do love MODx";
    3. if (strlen($options)>0) {
    4. $newvalue = $output . $options;
    5. } else {
    6. $newvalue = $output . $defaultValue;
    7. }
    8. return $newvalue;
    9. ?>
  6. Click "Save" and your custom modifier (:love2) is ready for use!

Contributed

Check PHx/CustomModifiers for a list of custom modifiers you can install.

Tips and Tricks

Using PHx in non-phx snippet templates

Most snippets render their templates "locally" before they are passed to MODx. Unless the snippet's description states it's using PHx to render the templates it's not possible to use the modifiers directly, however you still CAN use PHx by using a special modifier called phx:input.

This example will not work with a non-PHx snippet:

[+myplaceholder:modifier1:modifier2+]

Try this method instead:

[+phx:input=`[+myplaceholder+]`:modifier1:modifier2+]

Or:

[*phx:input=`[+myplaceholder+]`:modifier1:modifier2*]

This example will work with a non-PHx snippet. Some restrictions may apply depending on the snippet logic.

Technical Documentation

This part of the documentation is not intended for the average user but is just here to give some background information of how PHx works.

PHx makes use of the OnParseDocument (MODx API) event to process the documentOutput. OnParseDocument is triggered before the default MODx elements are processed so PHx is activated on the raw template version of the output and goes trough the following phases:

  1. We only start if we did not reach maximum passes yet.
  2. A hash is made of the current content of the document to check for changes in the process later on.
  3. The document is prepared with some filters to escape the rogue bracket characters (the ones that are not part of a MODx tag).
  4. The document now enters the main parser function:
    1. A hash is made of the current content of the document to check for changes in the process later on.
    2. Chunks are merged using mergeChunkContent (MODx API).
    3. Snippets are detected using an advanced regular expression that takes nested tags into account:
      1. Snippet calls which contain no nested tags are matched.
      2. Each matching call is checked for (non-)cached output, prepared and passed to evalSnippets (MODx API).
    4. Snippet output is merged with the document.
    5. The rest of the MODx tags are detected using an advanced regular expression that takes nested tags into account:
      1. Document/Template Variable output is gathered from mergeDocumentContent (MODx API).
      2. Setting Variables output is gathered from mergeSettingsContent (MODx API).
      3. Placeholder output is gathered in the following order PHx, MODx.
      4. If the requested placeholder is not set it is skipped for the current pass (replaced with original).
    6. For each detected tag, the ones which also contains modifiers are passed to the filter that will modify the value as given.
    7. The final output for all tags is then merged into the document.
    8. A hash is made of the content of the merged document and compared to the initial hash to check if there were changes.
    9. If we did not reach maximum passes AND if the document content has changed we repeat this entire level (pass+1).
  5. The document's rogue bracket characters are restored to their non-escaped versions.
  6. The tags that are still present (and thus unresolved for the entire document) are removed from the output.
  7. A hash is made of the new document content and compared to the initial hash to check if there were changes.
  8. If the document content has changed let it repeat this entire level.
  9. Log the parser steps into the Event Log if required.
  10. Return the new documentOutput to MODx.

After the entire PHx logic is through, it's 99% certain that MODx does not have any tags left to parse. You could say that PHx replaces the core function of MODx that handles the merging (with added flexibility by allowing nesting). However, the different types of elements are still processed by the responsible MODx functions.

Personal tools