Textpattern Tags in Tags: The Gordian Knot

23. August 2006

(Diesen Artikel gibt’s auch auf Deutsch.)

Much of the information Textpattern tags need to build page templates and other functionality is transferred by the means of attributes. For instance, the tag <txp:section /> uses its title attribute’s value to output either a section’s title or name:

<txp:section title="1" /> [...]

Textpattern tags can be used without further thoughts as an attribute value for HTML elements. A frequent use case is the assignment of individual classes to a page’s body element. I have acquainted the habit of deriving this class name from the active section as returned by <txp:section />:

<body class="<txp:section title="0" />"> [...]

XML purists may sigh at the sight of such nestings, and it requires a certain amount of practice to keep all those quotes and angle brackets in an appropriate balance. Anyhow, it works due to the simple search’n‘replace algorithm Textpattern employs to transform tags into their “real” values.

A Gordian Textpattern Knot

Things turn really bad and Textpattern fails to decode tag patterns at occasions where one Textpattern tag would be required as an attribute for another enclosing tag.

Usage example: Reading an e-mail address from an article’s custom field #1, a useful facility for a website collecting contributions from many authors, enabling them to leave their e-mail address with the articles they write by jotting it into a custom field. A naive approach would require this tag arrangement:

<txp:email email="<txp:custom_field name="custom1" />" />

Alas, Textpattern stops its examination of those tags on the first occurrence of “/>” and therefore fails miserably.

Tags in tags

Implementing such “Tags in Tags” requires a dash of PHP, the immediate invocation of tag handler functions and the application of basic knowledge regarding Textpattern’s attribute passing methods. Though this sounds complicated, one can apply a small set of reproducable steps:

Step 1: Start at the center, find your way out

Using the example from above the innermost tag is txp:custom_field, embedded into txp:email. Additional outer layers are detectable using the same method.

Step 2: Build a PHP snippet to invoke tag handlers according to this determined sequence

Tag handler functions are named like their counterpart tags (stripping the txp: prefix). txp:custom_field is therefore handled by a PHP function named custom_field, txp:email by email. Attribute values are passed by arrays of 'name' => 'value' pairs.

So the innermost part equals this PHP block:

custom_field (array(
    'name' => 'custom1'

Using the value returned by this block we can feed the outer layers. To keep things tidy we use an intermediate storage for the e-mail address ($a). Pass other attributes by comma separated 'name' => 'value' pairs in any random order.

$a = custom_field (array(
   'name' => 'custom1'
email (array(
    'email' => $a,
    'linktext' => 'mail me'

Step 3: Package it nicely into a form

Add a new “misc” type form to keep this PHP code and apply it where needed by txp:output_form. Bearing a little loss of tidiness the whole sequence can be included into an article form as well. Output the result of this whole shebang with echo.

$a = custom_field (array ('name' => 'custom1'));
echo email (array(
    'email' => $a,
    'linktext' => 'mail me'


One single pitfall

Textpattern tags without any attribute require an empty array for the tag handler function:

$a = section (array ());

In case it doesn’t work out in the first place…

If the actual results are rather disappointing, dmp() can be used to dump intermediate results. Try comparing your expected results with those written on the page. Sticking with our example, you would expect to find the contents of custom field 1 read into $a:

$a = custom_field ( array ('name' => 'custom1') );
dmp ($a);
echo email (array('email' => $a, 'linktext' => 'mail me'));

Applying this method is a way of reducing the need for a specialized plug in, covering Textpattern applications diverging and expanding the common static tag arrangements.

(Rope image by sxc.hu)


  1. Wet,
    you are really gifted here man. Not just in doing the PHP, but in helping us understand it. I think I owe you another beer? How many are we up to now?

  2. Matthew,

    glad you liked it. Regarding the beers: I stopped counting at about 20. Agree?

  3. Nice one Wet. Obvious when someone explains it as well as you do. I was often frustrated over TXPs lack of tag nesting, but I’ll be frustrated no more. Many thanks.

    I’d offer my gratitude in German, but sadly the only phrase I know (and yes, my endings probably don’t agree) is: “Ich habe einen grossen Gummitintenfisch in meinen Unterhosen.”

    As I’m sure you’ll agree, this sentence has limited appeal :-)

  4. Stef, your first paragraph gives me a warm feeling from having reached my target.

    While the second paragraph quickly lets it shrink again ;-) “Thanks for the fish”. So to speak…

  5. Ganz wunderbar, herzlichen Dank fuer diesen schoen erklaerten work around!
    Jetzt scheint der erwaehnte Gummitintenfisch direkt verschwunden zu sein.