On one hand it’s kind of disheartening how much resistance and fear there is towards objects from segments of the PHP community. (hilighted by the recent hostility towards PHP5’s adding of something resembling a working object model) On the other hand can you blame them when popular tools like Smarty punish you for using object?

Transparent, Unified Access

In the ideal world you could pass an array or an object to Smarty and have the access be transparent. Being able to call methods on objects from your template is immensley useful. Besides reinforcing your data model you also: 1. don’t have to write all those toArray() method to dump your data to Smarty

  1. can lazy load data as needed
  2. can provide intelligent formatting without the need to write a Smarty plugin

And being able to do it transparently means it is painless to migrate from using arrays to using objects (e.g. as your traffic grows, you switch to lazy loading objects), or from objects to arrays. (another way to say it is, having to know the underlying data storage implementation is a clear case of a leaky abstraction) (fyi this “ideal world” is also known as Template Toolkit)

SPL

There is however a way, with a little effort, to achieve this behaviour with Smarty and PHP5. One of the most useful, and least discussed new features of PHP5, is the SPL, “a collection of interfaces and classes that are meant to solve standard problems”, including allowing objects to behave like arrays, using the ArrayAccess interface. ### A quick example (there are probably better ones)

Say you had a class that you used to help you page through a list of results, and at the bottom of each page you wanted to add a slug like “Widgets 21-30 of 142”.

One solution would be to assign {$objectType}, {$showingMin}, {$showingMax}, {$totalToShow} (or some such) variables to Smarty, and then have something like the following in your template:

<pre class="code">
{$objectType} {$showingMin}-{$showingMax} of {$totalToShow} in

But to my eye that seems to clutter up the paging code with assigns, and clutter up the templates with unintuitive markup. (and this is a simple case)

Another solution would be to build the string at some point in your code, and pass that to Smarty (making your eventual I18N process just that much more challenging)

The third solution is to have your Pager class pretend to be an array, allowing you to add {$page.showStatus} to your templates which Smarty will compile into $this-><em>tpl</em>vars['page']['showStatus'], and the SPL will intercept and transform into $page->offsetGet('showStatus');

So whats does the Pager class look like? Well the relevant bits look like:


class Pager implements ArrayAccess {
   ...
   function offsetGet($k) {
    if ($this->offsetExists($k)) {
                return $this->$k();
        }
   }

<p>function offsetExists($k) {
       return method_exists($this, $k);
   }</p>

<p>/* set and unset don't do anything */<br></br>
   function offsetSet($k, $v) {}
   function offsetUnset($k) {}</p>

<p>function showStatus() {
      ...construct status string and return it....
   }
}
</p>

The offsetGet() method is the intersting piece. It says, “take the array access key, and call the method of the same name on that object”. Voila, you can now pass objects to templates, and call methods on them. ### A Word About Security

Generally you trust your template editors, but if calling arbitrary code worries you there are a couple of things you can do. - strip all non-word characters from your access key like so: $key = preg_replace('/\W/', '', $key);

  • the method calls are already namespaced to the object, but you could quarantine them further by appending a string to them (e.g. ‘tmpl*‘), in which case your offsetGet() becomes ```

     function offsetGet($k) {
          $k = preg   ```* replace('/\\W/', '', $k); $f = "tmpl*$f"; return $this-&gt;$f(); } (alternately you could use Relection API to limit offsetGet to only exposing methods inherited from some base class, or whatever other creative solution strikes your fancy)*
    

(Note most of this code was sketched out on my way home tonight, and my spiral bound notebook’s PHP syntax hilighting is pretty weak not to mention lacks PHP5 support, so there might be some bugs in the above code)

Smarty and Iterators

Okay, so all that above is about using SPL’s array interface to allow method access, but what if you want pass iterators to Smarty? Should just work right? Unfortunately not. You see in Smarty’s compile<em>foreach</em>start, arrays are accessed with an explicit array cast, making it impossible to use an iterator. The quick and dirty solution is to remove this explicit cast. However Smarty’s vmethod’s list .first, and .last won’t be available. For that you need this patch sent to the Smarty Dev list. (hasn’t tested it yet)