VGTech is a blog where the developers and devops of Norways most visited website share code and tricks of the trade… Read more



Are you brilliant? We're hiring. Read more

Lazy Loading Resources with Zend Framework Bootstrap

PHP

The Bootstrapping process in Zend Framework isn’t perfect. You’ll often end up bootstrapping a lot of resources that you don’t need to complete the request. Depending on the resources this can be really expensive and hurt your overall performance.

The worst kind are resources that open connections to external services like MySQL and Memcached. Both Memcache::connect() and MySQLi::__construct() will try to connect to the server immediately and this can, and usually will be, “slow”. Even worse, if the services are down they will end up blocking the execution.

An initialization of a memcache resource would typically look something like this:

Show code
public function _initMemcache() {
    $config = $this->getOption('memcache');

    $memcache = new Memcache();
    $memcache->connect($config['host']);

    return $memcache;
}

If your Memcached server is down this will end up blocking the entire application, even if you don’t actually use the resource.

Solve it with lazy loading!

The solution to these problems is lazy loading. The following two methods provides an easy, efficient and small implementation of lazy loading that can be used in almost any Zend Framework bootstrap.

Show code
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
    /**
     * Retrieve a resource from the container.
     *
     * If the requested resource is a callable it will be exectued and the
     * result from the callable is returned instead of the callable itself.
     *
     * @see Zend_Application_Bootstrap_BootstrapAbstract::getResource()
     */
    public function getResource($name) {
        $resource = parent::getResource($name);

        if (is_callable($resource)) {
            return $resource();
        } else if ($resource !== null) {
            return $resource;
        }
    }

    /**
     * Method for creating closures which lazyload the resource.
     *
     * Creates an closure which will create the resource when called
     * and caches this resource so only one instance is created.
     *
     * @param callback $callable Callback which is able to create the resource.
     * @return mixed The instantiated resource, usually an object.
     */
    private function lazyload($callable) {
        return function () use ($callable) {
            static $object;

            if (is_null($object)) {
                $object = $callable();
            }

            return $object;
        };
    }
}

The usage of these methods is quite easy and only requires small changes to your existing resource initialization methods. If we want to add lazy loading to the memcache initialization method above, it can be accomplished like this:

Show code
public function _initMemcache() {
    $config = $this->getOption('memcache');

    return $this->lazyload(function() use ($config) {
        $memcache = new Memcache();
        $memcache->connect($config['host']);

        return $memcache;
    });
}

This will defer the creation of the Memcache object until someone actually performs a $bootstrap->getResource('memcache'); and therefore the connection will only be made when actually needed.

In-depth explanation of how this works.

The lazyload() method wraps your normal initialization, which is passed in as the anonymous function $callable, inside an anonymous function. This wrapper function contains a static $object variable which is unique to the created instance of the function. The first time this anonymous function is executed $object will be null and $callable will be executed, the result of calling $callable will be stored in $object. This makes sure you only initialize the resource once.

Just implementing lazyload() would cause getResource() to return a Closure object, containing the anonymous wrapper function. That is little use to us in our code so we extend the getResource() method as well. Our getResource() method checks if the requested resource is callable and then call it.

The entire code for this example is located in this gist.

Developer at VG. @androa


4 comments

  • Gerry

    Now _that's what I'm talking about. Well done.


  • Programowanie w PHP » Blog Archive » VG Tech Blog: Lazy Loading Resources with Zend Framework Bootstrap

    [...] the VG Tech blog today André Roaldseth has a new post showing how to lazy load in the Zend Framework bootstrap using two handy methods you can drop into your applications initialization. The Bootstrapping [...]


  • Vincent van Dijk

    Nice blog, just what I needed!

    The default resources (log, mail, etc) will still be loaded when they are defined in the config. Is there a possibility to also "lazy load" these resources?

    Thanks in advance!


  • KJ Prince

    An interesting approach. Glad I found this site while researching DNS Prefetch!


Leave your comment