Using the ClassMap Autoloader for Better Performance

Want to improve Zend Framework 2 performance with minimal effort? If so, come learn about the ClassMap autoloader.


Do you want a simple way to improve the performance of your Zend Framework 2 application? One that you don’t need to code and can be maintained for you at deploy or commit time? If so, come learn about the ClassMap autoloader.

Zend Framework 2’s been critiqued many times as being slow, at least slower than some of the other leading PHP frameworks. And to be fair, sometimes it’s true. But it doesn’t need to be and there are simple things you can do to improve performance of your applications.

So this post will be the first in a multi-part series looking at ways in which you can improve the performance of your Zend Framework 2 application, with only a minimum of effort.

Today, we’re looking at the 2 autoloaders which are available in Zend Framework 2; these being the StandardAutoloader and ClassMapAutoloader.

What is an Autoloader?

If you’re not familiar with autoloaders, they’ve been around since PHP 5 and allow classes to be found by PHP when either the new() or class_exists() functions are called. You almost no longer need to make use of the require() and include() functions any longer.

PSR-0

Both the Standard and ClassMap autoloaders support PSR-0 compliant autoloading. Quoting the standard, that means:

  • A fully-qualified namespace and class must have the following structure ()*
  • Each namespace must have a top-level namespace (“Vendor Name”).
  • Each namespace can have as many sub-namespaces as it wishes.
  • Each namespace separator is converted to a DIRECTORY_SEPARATOR when loading from the file system.
  • Each _ character in the CLASS NAME is converted to a DIRECTORY_SEPARATOR. The _ character has no special meaning in the namespace.
  • The fully-qualified namespace and class is suffixed with .php when loading from the file system.
  • Alphabetic characters in vendor names, namespaces, and class names may be of any combination of lower case and upper case.

So if I’d created a module called VideoManager and in that had requested VideoManager\Notify\Video\EmailNotifier, assuming VideoManager is a module I’ve created, then the class will be found in module/VideoManager/src/VideoManager/Notify/Video/EmailNotifier.php. By default, the Zend Framework libraries will be found under vendor/zendframework/zendframework/library.

The Standard Autoloader

As the name infers, it’s the standard autoloader and if no other is used it will be the fallback one used to look up the class in the available paths.

This is fine in development, as you’re experimenting and testing ideas and performance isn’t an issue. But in production, you could have a rather heavy performance penalty if you continue to use it.

The reason is that it has to search all the paths which are available to it, such as the include_path and all the module paths, when attempting to resolve the file. Depending on the number of paths available and the directory depth , this could take some time.

The Configuration

By default it’s setup for each module in Module.php in the getAutoloaderConfig() function, which I’ve got a sample of below.

php
public function getAutoloaderConfig()
{
    return array(
        'ZendLoaderStandardAutoloader' => array(
            'namespaces' => array(
                __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
            ),
            'fallback_autoloader' => true,
        ),
    );
}

Here you see that it returns an array of autoloaders, specifying only one, Zend\Loader\StandardAutoloader. It specifies a namespace, which is the current module, and a path where the files for that namespace can be found, which will be module/VideoManager/src, assuming the module name is VideoManager.

Let’s run Apache Bench on that configuration and see how it performs. The configuration I’m using is 200 concurrent users over 15 seconds, using the following command: ab -c 200 -t 15 http://localhost:8080/.

I’m using the PHP cli server with only a simple site and connecting to a SQLite3 database. So there’s not a lot of overhead. Here’s the results.

Finished 118 requests
Document Length:        5584 bytes
Time taken for tests:   15.114 seconds
Complete requests:      118
Failed requests:        0
Write errors:           0
Total transferred:      695020 bytes
HTML transferred:       658912 bytes
Requests per second:    7.81 [#/sec] (mean)
Time per request:       25617.441 [ms] (mean)
Time per request:       128.087 [ms] (mean, across all concurrent requests)
Transfer rate:          44.91 [Kbytes/sec] received

You can see that it’s ok at 7.81 requests per second. Let’s see if that can be improved.

The ClassMap Autoloader

This autoloader is a much higher performing autoloader than the standard. The reason being, is because it doesn’t search the filesystem to attempt to resolve a class. Instead, as the name implies, it uses a class map, or a map of classes to their file paths to resolve the file. Here’s a small sample of what one looks like:

<?php
return array(
 'VideoManagerTablesFactoriesVideoTablegatewayFactory' => __DIR__ . '//src/VideoManager/Tables/Factories/VideoTablegatewayFactory.php',
  'VideoManagerTablesVideoTable' => __DIR__ . '//src/VideoManager/Tables/VideoTable.php',
);

On the left you see the class, on the right you see the path. The array can then be searched with a isset(). In production, you could expect this to be significantly faster.

Enabling the ClassMap Autoloader

To enable it, in getAutoloaderConfig() we add the following to the start of the returned array:

'ZendLoaderClassMapAutoloader' => array(
    array(
        __DIR__ . '/autoload_classmap.php'
    )
),

This will add it as the first autoloader to be used and tell it to use the classmap which we’ve just generated.

Generating the Class Map

But how to keep the file up to date? You don’t want to do this by hand, for obvious reasons. But gladly you don’t have to. A utility is shipped with Zend Framework 2, which does this all for you, classmap_autoloader.php and is available in the bin directory.

This will scan a directory for all the class files and generate the classmap file automatically. Several options are available including:

  • How the file will be created. If it exists, it can be overwritten or appended to
  • Where to search for the class files
  • Where to write the classmap file to
  • Whether to sort the results

Assuming I’m in the module directory, running the command below will generate a new classmap file, called autoload_classmap.php in the root of the module.

../vendor/bin/classmap_generator.php --library VideoManager --output VideoManager/autoload_classmap.php --overwrite --sort

Let’s now re-run Apache Bench to see what the performance difference is.

Finished 123 requests
Document Length:        5584 bytes
Time taken for tests:   15.189 seconds
Complete requests:      123
Failed requests:        0
Write errors:           0
Total transferred:      724470 bytes
HTML transferred:       686832 bytes
Requests per second:    8.10 [#/sec] (mean)
Time per request:       24697.231 [ms] (mean)
Time per request:       123.486 [ms] (mean, across all concurrent requests)
Transfer rate:          46.58 [Kbytes/sec] received

Wrapping Up

Ok, so the differences weren’t mind boggling, 7.81 requests per/second versus 8.10. But this is a simple test setup. But I do encourage you to try it out and see what your results are like. If you’d like more comprehensive information about the classloaders and aren’t using them in the full MVC stack, then defeinitely check out Rob Allens excellent post or this post by Hari K T.

Otherwise, if you’ve got any thoughts or questions, add them in the comments.



Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.


Join the discussion

comments powered by Disqus