Game Designer, Programmer, Choreographer

Zend_View helpers in include path

Posted on

N.B. This article applies to version 0.7 of the Zend Framework. The first solution suggested below is now incorporated in version 0.8 of the framework.

The Problem

I'm using the Zend Framework at the moment and came across a problem with creating custom helpers for the Zend_View class. All is fine if you place your helpers either within the zend framework's own helpers folder (not a good idea) or within a directory in your site structure. The problem arises if you place the helpers in a folder in the PHP include path and don't know the full path to the folder.

Why would that happen? Well, here's my set-up - on each server I have a folder that is added to the include path. This folder has the same structure on all my servers - development, testing and production - but may be in a different location on some of them. That doesn't matter because I can add the correct folder to PHP's include path in the php.ini configuration file on each server.

In the include folder I place a number of common files, including the Zend framework itself and my custom files for use with the framework. To keep things simple I duplicate the structure of the Zend Framework library files within my own custom files, as recommended in the Zend Framework documentation. My own files are placed within a fiolder called Bigroom, just as the Zend files are placed within a folder called Zend.

So, my Zend_View helpers are in the folder at Bigroom/View/Helper within the include path. The helpers are all named accordingly too, e.g. Bigroom_View_Helper_SelectCountry for a select box listing all countries. So far so good and in keeping with the recommendations in the framework documentation.

However, to add my helpers to the set of helpers in the Zend Framework, I have to know the full path to the helpers, not the path relative to the PHP include path, and not just the class name. This is not a bug, it's how the helpers part of the framework is designed. It's just a bit odd when compared to the rest of the framework. The code to add the helper directory to the helpers might look like this on my production and test servers -

$view = new Zend_View();
$view->addHelperPath( '/home/web/phpincludes/Bigroom/View/Helper',
                      'Bigroom_View_Helper' );

But on my development system, which is windows based, it would look like this -

$view = new Zend_View();
$view->addHelperPath( 'C:\php\includes\Bigroom\View\Helper',
                      'Bigroom_View_Helper' );

This seems to break with the normal behaviour of the Zend Framework, in which classes are automatically found from within the include path based on their class name. It also makes it difficult to create portable code since one must ensure that the include path is exactly the same on all systems using the code.

I have two solutions to this. The first involves altering the code in the Zend Framework, while the second, my preferred option, does not.

Solution 1

In the first solution, we make two small modifications to the Zend_View_Abstract class.

First, in the _loadClass method replace the line

} elseif (is_readable($dir . $file)) {

with

} elseif (Zend::_isReadable($dir . $file)) {

If you do all your work on the same platform, this will suffice. But for cross platform use we also need to check that the directory separators are the same. So the second change is in method _addPath where you need to add the statement

$dir = str_replace( array('/', '\\'), DIRECTORY_SEPARATOR, $dir );

after

$dir = rtrim($dir, '\\/' . DIRECTORY_SEPARATOR) 
                . DIRECTORY_SEPARATOR;

Now you can add helper paths using a path relative to the include path and with either / or \ as the directory separator. So portable code to add my helpers becomes

$view = new Zend_View();
$view->addHelperPath( 'Bigroom/View/Helper',
                      'Bigroom_View_Helper' );

There are, however, two drawbacks to this approach. First it involves modifying the Zend framework itself. That's not a good idea since you have to remember to redo the modifications when upgrading to a new version of the framework. And secondly, Zend::isReadable is significantly slower than is_readable, and here it's being used every time you use a helper. The speed hit probably won't be significant but it would be nice to avoid it.

Solution 2

My second solution to the problem is to extent the Zend_View class with my own Bigroom_View class that implements a new function for adding helper paths based on the class prefix only and looks for the files within the include path. This is in keeping with the normal operation of the Zend Framework where a class name is all that's needed to find that class within the include paths.

Here's the code

require_once 'Zend/View.php';

class Bigroom_View extends Zend_View
{
    public function addHelperPrefix( $classPrefix )
    {
        $path = DIRECTORY_SEPARATOR
              . str_replace('_', DIRECTORY_SEPARATOR, $classPrefix);
        $dirs = explode( PATH_SEPARATOR, get_include_path() );
        foreach( $dirs as $dir )
        {
            $fullpath = $dir . $path;
            if( is_readable( $fullpath ) )
            {
                $this->addHelperPath( $fullpath, $classPrefix );
            }
        }
    }
}

Now I use Bigroom_View instead of Zend_View and add helpers as follows.

$view = new Bigroom_View();
$view->addHelperPrefix( 'Bigroom_View_Helper' );

Which seems a lot neater to me.

Share this post or a comment online -


Also in the collection PHP