Archive for the ‘Zend Framework’ Category.

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