Search code examples
phpcomposer-phpfastcgi

PHP - preload all classes/functions


Is there a way to preload (as opposed to autoload) all classes/functions for a PHP project?

I'm thinking about a web application that runs with PHPFastCGI (so we can bootstrap part of it and reuse this bit between requests), and that uses composer (could its classmap be used for this purpose?).

Here's some context:

"Regular" PHP applications handle a single request and then they get killed. Preloading all classes/functions means having a bigger booting time, autoloading only classes used for the request is a clever optimization but it could be further improved (they require many "read filesystem" operations which are slow), usually by grouping in a single file classes that are likely to be used for all requests (this is what ClassPreloader solves).

However booting an application to handle only one request and then kill it is not the only option: with PHP FastCGI it is possible to keep the application alive between requests. This allows us to shave booting time off the Request/Response time (e.g. instantiate all services from the DIC once).

While profiling my applications, I've noticed that autoloading always appear in the top 10 most expensive exclusive function calls. In a PHP FastCGI application it might make sense to move class loading in the boot phase to remove it entirely from the Request/Response time. I'm trying to find out (I'll do some benchmarking and publish the result).

For more information about this "exotic" way of running PHP applications, see:


Solution

  • If you're using composer, the return value of your composer include statement will be an instance of Composer\Autoload\ClassLoader. Calling the loadClass() method on this object will include the class file. Thus, when you actually need the class, PHP will already know its definition and will not need to call the callback that composer registers with 'spl_autoload_register'.

    <?php
    
    /** @var Composer\Autoload\ClassLoader **/
    $classLoader = include 'vendor/autoload.php';
    
    $classLoader->loadClass('Path\\To\\MyClass');
    $classLoader->loadClass('Path\\To\\OtherClass');
    // ...
    

    Edit: Note that what is probably more useful in your scenario is preloading services. Actually instantiating the classes your application requires is probably more costly than getting PHP to read their definitions. How to preload services would be dependent on your dependency injection container, but simply 'getting' the service would force the container to instantiate it and remember it.

    OP edit: Here's the experiment, done on an "empty" symfony application with ReactPHP.

    1. Benchmark using preloaded classes, loss of 8% performances: https://github.com/gnugat-examples/bench-sf-standard/commit/c23d681cddc105b3a78f05679b2cffa84657f742
    2. Benchmark using preloaded services on top of preloaded classes, same performances as without proloaded classes/services: https://github.com/gnugat-examples/bench-sf-standard/commit/79a16cd3a7184aea6ed35461c4368dec32289ac9

    So the conclusion is surprisingly that warming up services or classes before starting a server brings no performance improvement.