Zen Coding for PHP and WordPress

Zen Coding in Action
Zen Coding in Action

I’ve been using Zen Coding for about a year now, and I can’t understate how great the idea is. Its value should be immediately apparent to anyone who has experience writing HTML by hand. But in the past year I’ve started doing a lot more server side programming and a lot less HTML by hand, so I don’t get to take advantage of Zen Coding as much as I once did.

Sill, I constantly find myself in situations where I need to programmatically create a bunch of nested HTML elements in PHP, and I wonder: why isn’t there a PHP function that can take a Zen Coding selector and automatically expand it?

Well, when you wonder something enough times, eventually you decide to actually do something about it.

I’ve created a PHP class called PW_Zen_Coder, and I’ve put the source on github if anyone wants to grab it or contribute. I’ve also set it up as a WordPress plugin, so you can drop it into your plugins directory, activate it, and then use PW_Zen_Coder in any of your own code.

Documentation

PW_Zen_Coder has only one public method, expand(). This may grow in the future as Zen Coding itself changes, but at the the moment all it does is expand a Zen Coding selector into valid HTML ready for output.

expand( )

Consumes a Zen Coding selector, along with whatever passed values, and returns a valid string of HTML code ready for output.

public string PW_Zen_Coder::expand( string $selector , mixed $text [, array $...] )

Parameters

selector
(String) A selector according to Zen Coding syntax. See below for a list of specific selectors not supported.
text
(Mixed) A string that will be the innerHTML value of the last element in the selector. If element multiplication (e.g. “li*4″) is applied and you want different values for each element, pass an array of values that correspond to each iteration of the multiplied element.
(Array) Pass additional arrays that correspond to indexed values (e.g. {%1}, {%2}, etc.) within the selector. The passed array(s) should be a list of key/value pairs that correspond to HTML attribute/properties respectively. If element multiplication is used, pass an array of such arrays.

Return Value

Returns the HTML string produced.

Examples

All examples below assume the existence of a PW_Zen_Coder instance variable named $zc .

$zc = new PW_Zen_Coder;

A Simple Example

This is an example of very basic usage, but even here the process of creating an element with a class and a sub-element is significantly simplified.

echo $zc->expand('.wrapper>p', 'Hello World');

Becomes:

<div class="wrapper">
  <p>Hello World</p>
</div>

A Complex Example

Here is a much more complex example. While the selector is somewhat difficult to read, imagine how much easier it would be to edit the markup if down the road you decided to change the structure slightly.

echo $zc->expand(
  '#page.js>#header>.wrapper>h1[title="Slogan"]>a[href="http://google.com"]',
  'Hello World'
);

Becomes:

<div id="page">
  <div id="header">
    <div class="wrapper>
      <h1 title="Slogan"><a href="http://google.com">Hello World</a></h1>
    </div>
  </div>
</div>

Element Multiplication

Adding *N to an element outputs it N times. The $ character in an ID or class is replaced with the iteration number. Notice how this time an array is pass to $text instead of a string.

echo $zc->expand(
  'ul>li.item$*6',
  array("Red","Orange","Yellow","Green","Blue","Violet")
);

Becomes:

<ul>
  <li class="item1">Red</li>
  <li class="item2">Orange</li>
  <li class="item3">Yellow</li>
  <li class="item4">Green</li>
  <li class="item5">Blue</li>
  <li class="item6">Violet</li>
</ul>

Element Multiplication (dynamically and with multiple $’s)

The real power of Zen Coding in evident when you start constructing the selector dynamically. Here is a basic example of doing just that. Also notice how $$ becomes 01, 02, 03 instead of 1, 2, 3.

$numbers = array("One","Two","Three");
echo $zc->expand(
  'ul>li.item$$*' . count($numbers),
  $numbers
);

Becomes:

<ul>
  <li class="item01">One</li>
  <li class="item02">Two</li>
  <li class="item03">Three</li>
</ul>

Passing HTML Attributes to Elements

If you want to apply additional HTML attributes to an element, include {%N} in your selector and pass another argument in the method call.

echo $zc->expand(
  '#sample>pre{%1}>code',
  array('title'=>'code', 'width'=>'600px'),
  'Pre-formatted Code Block'
);

Becomes:

<div id="sample">
  <pre width="600px" title="code">
    <code>Pre-formatted Code Block</code>
  </pre>
</div>

Passing HTML Attributes to Elements with Element Multiplication

If you pass an array of attributes to an element that has been multiplied, each element will possess the exact same set of attributes. If you want them to possess different attributes, simply pass an array of arrays instead of just one array. This example shows both methods:

echo $zc->expand(
  'ul{%1}>li*3{%2}>span',
  array('style'=>'background-color:#eee'),
  array(
    array('style'=>'color:red'),
    array('style'=>'color:green'),
    array('style'=>'color:blue'),
  ),
  array( 'red', 'green', 'blue' )
);

Becomes:

<ul style="background-color:#eee">
  <li style="color:red"><span>red</span></li>
  <li style="color:green"><span>green</span></li>
  <li style="color:blue"><span>blue</span></li>
</ul>

Differences from Zen Coding

Some of the elements in the Zen Coding syntax do not make sense to be dynamically implemented, and some of the features I just haven’t decided how best to implement them. Here is a list of exactly what PW_Zen_Coder does and does not support.

Supported features

  • ID and class attributes: div#page.section.main
  • Custom attributes: a[title="Hello world"]
  • Element multiplication: li*5 will output <li> tag five times.
  • Item numbering with $ character: li.item$*3 will output <li> tag three times, replacing $ character with item number
  • Multiple ‘$’ characters in a row are used as zero padding, i.e.: li.item$$$ will output li.item001
  • div tag name can be omitted when writing element starting from ID or class: #content>.section is the same as div#content>div.section.

Features Not Supprted

  • Abbreviation groups with unlimited nesting: div#page>(div#header>ul#nav>li*4>a)+(div#page>(h1>span)+p*2)+div#footer.
    Note:
    You cannot use the + character to create sibling elements nor can you use parentheses to nest. If you want to nest or use siblings, just pass another PW_Zen_Coder instance as the $text argument.
  • Filters support

Partially Supported Features

  • Custom attributes: while a[title="Hello world"] is supported, a[title] and a[title="Hello world" rel] are not. The attribute must have both the name and value. To have multiple attributes, just pass them as a method argument.
  • Element multiplication: while element multiplication is possible, and it’s possible to do it multiple times within a selector, it is not always possible to do it multiple times within a selector while simultaneously passing the selector attribute values. For example: ul>li*3{%1}>p*2{%2} will probably not work as expected.

What’s next?

If you want to try PW_Zen_Coder, just download the source on github and test it out. There is an example file included that should give you an idea of how it works.

If you have any questions, comments, complaints, suggestions, or if you find any bugs, let me know in the comments below and I’ll try to address them as quickly as possible.