Make Module Configs Cacheable with the ZF2 Factory Interface

Zend Framework 2

For the longest time, I’ve been using closures in my Zend Framework 2 Modules Module class. I know they’re not always the best approach, but they’re not necessarily wrong either.

But after reviewing Gary Hockin’s recent talk at PHP Conference UK, I was reminded that outside of APC and OPCache, closures aren’t cacheable.

Depending on your application, this may not be a problem. But as most projects, invariably, grow over time, it’s likely they’ll inevitably become a performance bottleneck.

Gladly, he reminded me that there are alternative approaches which do allow for caching with Memcached, Redis and so on; specifically – if your configuration is based on a class which implements Zend\ServiceManager\FactoryInterface.

So in today’s tutorial, I’m going to show you a simple example of how to migrate from closures using this approach.

Standard Closure Approach

Ok, firstly here’s what a sample configuration in an app I’m making looks like:

class Module
{
    public function getServiceConfig()
    {
	return array(
	    'factories' => array(
                'Form\Feed\DeleteForm' => function ($sm) {
                    $form = new DeleteFeedRecordForm();
                    $form->setValidationGroup(
			array('feedId')
		    );
		    $form->setInputFilter(
			new DeleteFeedRecordInputFilter()
		    );
	            return $form;
            	},
	    )
    	);
    }
}

You can see that I’ve setup a service manager service entry, Form\Feed\DeleteForm, by using a simple closure, instantiating the form, specifying a validation group and input filter, then returning the instantiated form.

For the purposes of today’s tutorial, this will work just nicely. Let’s see how we could migrate it.

Using The Factory Interface

To implement the FactoryInterface approach, we’ll need to create a class which implements Zend\ServiceManager\FactoryInterface. Below is my class. Have a look, then let’s go over it.

<?php
namespace BabyMonitor\ServiceManager\Form\Feed;

use Zend\ServiceManager\FactoryInterface,
    Zend\ServiceManager\ServiceLocatorInterface,
    BabyMonitor\Form\DeleteFeedRecordForm as DeleteForm,
    BabyMonitor\InputFilter\DeleteFeedRecordInputFilter as DeleteFilter;

class DeleteFormFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
    	$form = new DeleteForm();
        $form->setValidationGroup(array('feedId'))
             ->setInputFilter(new DeleteFilter());
        return $form;
    }
}

As it’s configuring a service for the ServiceManager, providing a Form, managing deletion of feeds, BabyMonitor\ServiceManager\Form\Feed\DeleteFormFactory, the class namespace is semi-intuitive.

The factory interface only defines one required function, createService. I’ve included the use statements for the required classes and largely copied the code, verbatim, from the closure. Nice and simple.

Note: I’ve aliased the classes only for the purposes of readability and formatting. No real requirement otherwise.

Updating Module.php

With the class defined, I can now update getServiceConfig() by replacing our existing configuration with the one below:

'Form\Feed\DeleteForm' => 'BabyMonitor\ServiceManager\Form\Feed\DeleteFormFactory',

That’s it! Just one class (per/closure), and update the Module configuration. Assuming this was the only only configuration needing migration, we could then enable caching, as Akrabat explains.

Winding Up

Admittedly these were trivial examples, and depending on your configurations, you may need to go to a bit of effort to migrate your closures. But I hope you see that the migration’s quite straight-forward to do.

Intermediate Modules Tutorial