Posts tagged ‘Zend Framework’

The Configuration Pattern in Zend Framework

Several components in Zend Framework such as Zend_Form and Zend_Layout, support what I’ve come to call the Configuration Pattern. Though it doesn’t appear to exist in any officially sanctioned capacity like traditional design patterns, and it may qualify more as a convention than a design pattern, it seems to me that it’s something worth knowing about.

Here’s how it works. Have a look at the constructor for Zend_Form. It accepts an $options parameter, which can be an associative array or Zend_Config instance. If it’s an array, setOptions() is called. If it’s a Zend_Config instance, setConfig() is called, which then converts the Zend_Config instance to an associative array and passes that to setOptions(). So, either way, you end up in the same method with the same type of data.

setOptions() then iterates over the associative array it receives. It takes the index of each element and looks for a corresponding setter method. For example, if the array contains an element with the index ‘viewScriptPath’, setOptions() would check the class definition for a method setViewScriptPath() using method_exists(). Since that method does exist in Zend_Form, it would be called and passed the value from the associative array corresponding to that index.

This alleviates the need to explicitly call a setter method for every value you want to set. While that approach is slightly more efficient — not using setOptions() means one less function call and no method_exists() calls, which are slightly expensive — it can make code using the class in question look overly verbose or cluttered.

Alternatively, the setOptions() method could be called after the class was instantiated. With respect to that approach, passing an array or Zend_Config instance to the constructor only saves you one line of code on the calling end.

To get a list of classes that use this pattern, you can issue the bash command shown below from the library directory of your Zend Framework installation.

grep "\$this->setOptions" * | sort | uniq

Zend_Form and Zend_Loader_PluginLoader SNAFU

I’ve spoken on this blog before about scaling Zend_Form for larger forms by having it use a central set of plugin loaders for all elements contained within a form, rather than allowing each element to implicitly create its own instances. Another way to accomplish the same goal is to have form elements use the same loaders that the form has already created for itself.

<php

class FastloadingForm extends Zend_Form
{
    public function addElement($element, $name = null, $options = null)
    {
        if (!is_array($options)) {
            $options = array();
        }

        // A plugin loader is implicitly created if default decorators are loaded
        $options['disableLoadDefaultDecorators'] = true;

        // Add the element to the form
        parent::addElement($element, $name, $options);

        // Configure the element to use the form's plugin loaders
        $element = $this->getElement($name);
        foreach ($this->_loaders as $type => $loader) {
            if ($type != 'ELEMENT') {
                $element->setPluginLoader($loader, $type);
            }
        }

        // Now load default decorators for the element
        $element->loadDefaultDecorators();

        return $this;
    }
}

However, this approach can cause an issue if subforms are being used because of a bug in Zend_Loader_PluginLoader.

The bug entails Zend_Loader_PluginLoader allowing multiple instances of the same path to be added per prefix. This becomes an issue because of how Zend_Form handles adding subforms: it automatically calls addPrefixPath() on the subform for each path contained in each of the form’s own plugin loaders. If both the form and subform are using the same plugin loaders, the bug causes the same paths to be added multiple times. When plugins are subsequently loaded, the duplicate paths are all searched individually, creating a bottleneck as more subforms are added.

The issue in the JIRA bug tracker for ZF has patches for both the issue itself and the unit test suite for the Zend_Loader_PluginLoader component. Please view the bug report and vote to ensure that the issue receives attention and is fixed in a release in the near future.

New Article Published

Just a quickie to mention that the January 2009 issue of php|architect magazine has an article by yours truly on my experiences in taking the Zend Framework Certified Engineer exam and that I was also among the Technical Editors for the issue. I’m very glad to have contributed in my own small way to the continued success of the most awesome professional PHP publication in the industry. Grab your copy today!

Scaling Zend_Form

An adage often exchanged between Zend Framework enthusiasts goes something like this: “The bad thing about Zend Framework is that there’s a dozen ways to do anything. The great thing about Zend Framework is that there’s a dozen ways to do anything.” To a degree, this is a boon for the project. I think it’s fair to say that it’s one of the more flexible framework projects out there when it comes to how to do things with it.

I came across an instance using Zend_Form recently where the level of flexibility offered was a bit of a double-edged sword. In order to provide maximum flexibility per form element instance, each has not only their own filter, validator, and decorator instances, but also a plugin loader instance for each of these three types of plugins. These add up quickly when you have a form with several hundred elements in it.

But maybe you don’t have a reason for needing each element to have its own plugin loaders. I honestly can’t see a use case for that, but I’ve heard it claimed that one exists. For large forms, you can improve performance and memory usage by manually instantiating a plugin loader for each type of plugin, configuring them, and then having all elements added to the form use those plugin loader instances. To do this, subclass Zend_Form like so:

require_once 'Zend/Form.php';
require_once 'Zend/Loader/PluginLoader.php';

class Custom_Form extends Zend_Form
{
    private $_elementLoaders;

    public function init()
    {
        // Clear default form decorator paths so elements don't inherit them
        $this->getPluginLoader('decorator')->clearPaths();

        // Instantiate and configure central plugin loaders for elements
        $this->_elementLoaders = array();

        $this->_elementLoaders['decorator'] = new Zend_Loader_PluginLoader();
        // $this->_elementLoaders['decorator']->addPrefixPath( ... );

        $this->_elementLoaders['validate'] = new Zend_Loader_PluginLoader();
        // $this->_elementLoaders['validate']->addPrefixPath( ... );

        $this->_elementLoaders['filter'] = new Zend_Loader_PluginLoader();
        // $this->_elementLoaders['filter']->addPrefixPath( ... );
    }

    public function addElement($element, $name = null, $options = null)
    {
        if (!is_array($options)) {
            $options = array();
        }

        // A plugin loader is implicitly created if default decorators are loaded
        $options['disableLoadDefaultDecorators'] = true;

        // Add the element to the form
        parent::addElement($element, $name, $options);

        // Configure the element to use the central plugin loaders
        $element = $this->getElement($name);
        foreach ($this->_elementLoaders as $type => $loader) {
            $element->setPluginLoader($loader, $type);
        }

        // Now load default decorators for the element
        $element->loadDefaultDecorators();

        return $this;
    }
}

I find the Internationalization of Zend_Form page in the Reference Guide to be a bit misleading. While no i18n is done by default, that doesn’t mean that Zend_Translate components are not still loaded by default. In my opinion, Zend_Form should have been designed such that you would enable this feature if you needed it rather than disabling it if you didn’t. Be that as it may, here’s how you can handle turning it off if you don’t need it to gain a little extra performance.

class Custom_Form extends Zend_Form
{
    public function init()
    {
        $this->setDisableTranslator(true);
    }

    public function addElement($element, $name = null, $options = null)
    {
        if (!is_array($options)) {
            $options = array();
        }
        $options['disableTranslator'] = true;
        return parent::addElement($element, $name, $options);
    }
}

If you are currently running ZF 1.6.2, changeset 12201 was committed recently to address a performance issue with translation (which applies whether it’s disabled or not) of multi elements. It should be included in 1.7, but in the meantime the patch is easy to apply and shouldn’t conflict with any existing code using it.

The only other bottleneck that I noticed was how quickly calls to render individual form elements added up. I’m not sure if there’s any way around this or if any particular area of the task is causing a bottleneck, but it’s something that I hope to investigate further in the future. For the time being, I hope the changes I’ve mentioned here are helpful to someone. If you have any relevant comments, please feel free to post them to this entry. Thanks in advance for your contributions to the discussion!

ZendCon 2008 Slides Up

The slides from my talks at ZendCon 2008 are now up for your viewing pleasure. I’ll try to have the traditional conference wrap-up post up a little later on this weekend.

Creating Web Services with Zend Framework

Web Scraping

Simplifying Zend_View_Helper_Url

When I first began working with Zend_View on a project at work, I noticed that the Url view helper was a bit of a pain to use. It was rare that I didn’t want to specify one or more of the action, controller, and module in my call along with the other Route assembly parameters. Having to label them with an array index made the calls to the helper ugly and more difficult to read. As well, I often wanted to refer to actions in the same controller or controllers in the same module with respect to the current action.

To get around this, I wrote a view helper that extended Zend_View_Helper_Url and had three optional parameters specifically for the action, controller, and module. Everything else was passed into the fourth parameter, an associative array of any other parameters I needed to include. If any component of the route is excluded or specified as null, I simply get its value from the current request.

My friend Andy Best took this a step further and added in support for specifying custom routes, which he’s using for a project of his own but I didn’t need myself because I only use the default routes provided by the standard rewrite router. The result is the code below.

<?php

require_once 'Zend/Controller/Front.php';
require_once 'Zend/View/Helper/Url.php';

class App_View_Helper_Route extends Zend_View_Helper_Url
{
    public function route($action = null, $controller = null, 
        $module = null, $params = array(), $route = null)
    {
        $front = Zend_Controller_Front::getInstance();
        $request = $front->getRequest();
        $router = $front->getRouter();
        
        if ($action === null) {
            $action = $request->getActionName();
        }
        if ($controller === null) {
            $controller = $request->getControllerName();
        }
        if ($module === null) {
            $module = $request->getModuleName();
        }
        
        $urlOptions = array_merge($params, array(
            'action' => $action,
            'controller' => $controller,
            'module' => $module
        ));
        
        if ($router->hasRoute($controller) && $route === null) {
            $route = $controller;
        } else if ($route === null) {
            $route = 'default';
        }
        
        return $this->url($urlOptions, $route, true);
    }
}

Speaking at ZendCon

It appears the schedule for ZendCon is at least partly up. I’d been reserving this announcement mostly out of a personal sense of superstitution, but since it seems to be official now, so I’ll go ahead and pipe up: I’m speaking at ZendCon.

Out of four proposals, one managed to make it onto the conference schedule, and that was Pick Your Protocol: Creating Web Services with Zend Framework. It won’t be my first time at ZendCon, but it will be my first time there as a speaker. I’m looking forward to being among their ranks as well as meeting friends new and old.

See you all in September!

Output Filters in Zend_View

A feature of Zend Framework MVC that isn’t currently very well documented is output filters. They’re mentioned in passing in the Zend_View documentation, but not reviewed in detail anywhere in the Reference Guide as of version 1.5.2. I was curious enough about how to implement markup minification that I decided to trace through the Zend_View source code in attempt to discern how output filters actually worked. As it turns out, it’s actually pretty simple.

First, you need to get a reference to the current Zend_View instance. If you’re using the Zend_Layout MVC integration, you can get this by calling $this->_helper->layout->getLayoutInstance within your Zend_Controller_Action class to get the current Zend_Layout instance and then getView on that to get your Zend_View instance. Otherwise, the Zend_View instance is available via the view property of Zend_Controller_Action instance.

Next, call addFilterPath or setFilterPath on your Zend_View instance from your Zend_Controller_Action class. Pass in a path to the directory to contain your output filter classes and a naming prefix that all of your output filter classes will use. I’m not sure why the class prefix defaults to “Zend_View_Filter_” since no such classes exist. In my opinion, it would have made more sense to derive the prefix based on the provided directory path. Anyway, create the directory you’ve specified if it doesn’t already exist and create a new class file within that directory. In my case, I named the directory Vendor/View/Filter, the file Minify.php, and the class contained in the file Vendor_View_Filter_Minify.

Within this class, you must implement at least one method, filter. This method should accept a single parameter, which will be a string containing the view ouput to be filtered, and should return the filtered version of that string. Optionally, if your filter requires access to the related Zend_View instance, you can also declare a setView method that accepts the Zend_View instance as its only parameter and it will automatically be passed in when your output filter class is instantiated. Within setView, you can store the Zend_View instance in an instance property of the output filter class so it can be referred to later in the filter method.

Once you’ve finished your output filter class, you need to explicitly add it to the output filters in use from your Zend_Controller_Action class. You can use addFilter or setFilter for this. Pass in the name of your output filter class without the class prefix. In my case, I passed in “Minify.” At this point, the filter should be used when rendering your page. I poked around in the DOM and Tidy PHP extension documentation, but couldn’t find a feature for markup minification, so I ended up using the PCRE extension to do the job. Below is the final source code for my output filter class.

Vendor/View/Filter/Minify.php

class Vendor_View_Filter_Minify
{
    public function filter($string)
    {
        return preg_replace(
            array('/>\s+/', '/\s+</', '/[\x0A\x0D]+/'),
            array('>', '<', ' '),
            $string
        );
    }
}

Vendor/Controller.php

class Vendor_Controller extends Zend_Controller_Action
{
    public init()
    {
        $this->_helper->layout->getLayoutInstance()->getView()
            ->addFilterPath('Vendor/View/Filter', 'Vendor_View_Filter_')
            ->addFilter('Minify');
    }
}

Zend Framework and Remember The Milk

I’ve posted a few times on Twitter related to my latest project and a few people have already asked me about it, so I figured it was worth a blog post.

My first project for the Zend Framework was Zend_Service_Simpy, a service module providing a lightweight wrapper around the API for the Simpy social bookmarking service.

My latest project is another service module for the Zend Framework. This time, though, it’s for the Remember The Milk API. RTM is basically a TODO list on serious steroids. It’s the Swiss Army Knife of task management. It allows you to manage multiple lists of tasks. You can add them easily from a variety of mediums, tag them, prioritize them, set deadlines for them, have them repeat, get reminders for them, tie them to physical real world locations, and share them. RTM offers great support for integration with Google applications including Google Calendar, iGoogle, and Gmail (plus offline access powered by Google Gears). They’re also very big into supporting mobile devices, including those running on Windows Mobile as well as the iPhone.

If you like, you can check out my original proposal for this module. I can already say that the API will end up changing a little, though, but it’s good enough to give you a general idea of what the capabilities of the finished service module will be. I only actively started implementation recently and things are progressing at a fairly rapid pace. I still have unit tests and documentation to handle, but hopefully there’s a shot at seeing it moved to core within the next two releases of the framework.