Frameworks, PHP

Zend_Form Performance Issues

If you are using Zend_Form and your site increases in complexity and attracts more traffic, you are most likely to run into performance problems.

Of course, bigger and more complex projects result in more load on your servers. A typical scenario might be you’ve added a few new features to a Web application and now are seeing more server load and memory usage; thus, pages seem to load slower. To solve this you can throw bigger hardware at the problem or try to find the parts of your code that are causing the slowdowns and optimize them.

In this post I’ll explain the problems I faced when using Zend_Form and how I managed to optimize it and improve the performance of my application.

Update: Matthew Weier O’Phinney wrote:

“You’ll be happy to know that I’ve already made changes in the incubator to allow lazy loading of validators, filters, and decorators. These changes will be released with version 1.6.0.”

The Problem

Zend_Form heavily depends on other Zend Framework components, such as Zend_Validate, Zend_Filter, Zend_Loader_PluginLoader and, optionally, Zend_Config.

The Zend_Form_Elements class doesn’t check for the requested method and loads all the filters and validators on each request creating an unnecessary overhead. In an ideal world, these objects should only be loaded when the elements are checked against the data provided.

1. HTTP/1.1 GET

o- (request)
-> (new form)
-> (load filters and validators)
-> (response)

2. HTTP/1.1 POST

o- (request)
-> (new form)
-> (load filters and validators)
-> (process data)
-> (response)

In the first example, when a user requests the form, Zend_Form loads all the filters and validators, but never uses them.

The Test

Zend_Form

Requests per second 1:   36.51 [#/sec]
Requests per second 2:   36.72 [#/sec]
Requests per second 3:   36.54 [#/sec]

Zend_Form + Zend_Cache

Requests per second 1:   81.54 [#/sec]
Requests per second 2:   81.38 [#/sec]
Requests per second 3:   82.01 [#/sec]

HTML Form

Requests per second 1:   94.55 [#/sec]
Requests per second 2:   94.63 [#/sec]
Requests per second 3:   94.49 [#/sec]

Benchmarks and Test Data Results

The Solution

1. Modify Zend_Form_Elements so that it checks the requested method before loading all the dependencies, otherwise Zend_Form_Elements will use Reflection to allocate unnecessary objects into memory.

2. Cache the output of the Zend_Form object using Zend_Cache.

Example

Caching Zend_Form output:

class ZendFormCachedController extends Zend_Controller_Action
{
        protected $_formId = 'form';

        public function indexAction()
        {
                $frontend = array(
                        'lifetime' => 7200,
                        'automatic_serialization' => true);

                $backend = array('cache_dir' => '/tmp/');
                $cache = Zend_Cache::factory('Core', 'File', $frontend, $backend);

                if ($this->getRequest()->isPost()) {
                        $form = $this->getForm(new Zend_Form);
                } else if (! $form = $cache->load($this->_formId)) {
                        $form = $this->getForm(new Zend_Form);
                        $cache->save($form->__toString(), $this->_formId);
                }

                $this->getHelper('layout')->setLayout('zend-form');
                $this->view->form = $form;
        }

Conclusion

Make sure you cache and optimize your code! When you add a new component to your application, make sure you have a benchmark against which you can test your code to see how the changes affect performance and efficiency.

Zend_Form Benchmarks and Test Data Results

Related Articles

PHP framework comparison benchmarks

12 thoughts on “Zend_Form Performance Issues

  1. You’ll be happy to know that I’ve already made changes in the incubator to allow lazy loading of validators, filters, and decorators; basically, the plugin objects are only instantiated when you actually need them (for instance, validators are only loaded when you either call getValidator() or isValid(); filters are only loaded when you call getFilter() or getValue(); and decorators are only rendered if you call getDecorator() or render()). These changes will be released with version 1.6.0.

  2. Pingback: Веб-обзор
  3. Just a note — while lazy loading will be helpful in many situations, caching your forms is still a good idea.While the validators and filters may not need to load for GET requests, there are still many, many objects floating around, and a lot of logic that has to be performed for rendering. So the takeaway is that the root issue — objects that were never used were being loaded — is corrected, the techniques you describe here are still sound.

  4. Hopefully i am not too profane, this line:

    $cache->save($form, $this->_formId);

    throws an Uncaught exception ‘Zend_Cache_Exception’ with message ‘Invalid id or tag : must be a string’

  5. That exception is thrown whenever you pass an invalid cache ID to the save() method. Check your $form variable and make sure you pass it as a string. More info here: Zend_Cache_Core, line 404.

  6. geez i’m experiencing the slowness right now..

    i have some fairly large select boxes with +300 elements per box (using zlib compression too)

    box 1 = countries
    box 2 = timezones
    box 3 = languages

    I’m not concerned with internationalization so i’ve explicitly disabled translation on each form element

    $oFormElement->setDisableTranslator(true);

    This helps alot. And in addition cached the form variable in an APC user variable. That helps even more… Its still not as zippy as i’d like but its alot better without it

    Looking forward to seeing Matthew’s changes! *phew*

  7. Yeah it really is amazing how slow Zend_Form can be. Normally my relatively complicated zend pages (with MVC) take about .5 seconds to load, with some modest zend_forms on them, we’re talking about 1.5 – 2 seconds per page load! (no caching, on my laptop).

    I noticed a lot of translation calls, so hopefully setting the setDisableTranslator flag will help on that end.

    To speed up Zend_Form loading i cache the actual Zend_Form object, not the rendered HTML, and i think im seeing some speed results with that. The problem with caching just the HTML is that even though the initial GET request of a form will be fast, you still need to rebuild the object on the postback to enable things like validation.

  8. Caching objects in PHP doesn’t offer substantial benefits. The serialise function has its proper uses, but it’s a relatively slow process.

  9. Hi Federico,
    try change the line below:
    $cache->save($form, $this->_formId);
    to
    $cache->save($form->__toString(), $this->_formId);

    this will fix bug related by iongion.
    bye bye

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s