The Core Compile Stats helper for symfony

Introduction

I read on some web pages, that regrouping php files (aka fake compiling solution) into one big file can be really efficient, mainly because the server will do less I/O operations.

Let’s try to think about the different issues we might have.

  • Conditional include issue : file will not exist, if for instance, the merged script has been moved into a cache folder.
  • Require/Include statement issue: if you use a require statement to a file that is already in your merged file you will have a fatal error as you cannot redeclare classes or function.

The first conclusion is that you cannot use the compile feature in some situations. Now let’s try to think about frameworks and how we can get a list of files we need to merge. Modern PHP frameworks are now fully OOP (one file per class) and use the spl autoloader feature.

So mainly we can easily compile class files, other php files, such as templates or configuration files will be ignored as they are bound to a dynamic context.

Now, let’s try to apply this to your favorite framework. I made the assumption it is symfony ;)

The symfony framework provides a nice feature to compile “Core” files into one file. So by adding a core_compile.yml into your application configuration folder, you can add more classes to be merged. Great it is what we need ! The only bad point is that you need to type my hand class path …

However I have written a quick and dirty file class file to help us do the job ! To use this class properly you need to hack the autoloader class of your project. I will explain how to do that with symfony and Doctrine.

Step 1 : Edit your main php file.

Edit your frontend_dev.php file to initialize the class and the path

 // Initialize the swCompilerStats class  
 if($_compiler = false)  
 {  
  require_once(dirname(__FILE__).'/../plugins/swToolboxPlugin/lib/optimize/swCompilerStats.class.php');  
  $sw_compiler_stats = swCompilerStats::getInstance(  
    '/Users/thomas/Projects/your_project_name/stats.dump',  
    '/Users/thomas/Projects/your_project_name',  
    array()  
  );  
 }  

 require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');  
 $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'dev', true);  
 sfContext::createInstance($configuration)->dispatch();  

 // Save the stats and dump the core_compile.yml file  
 if($_compiler)  
 {  
  $sw_compiler_stats->save();  
  $sw_compiler_stats->dump('/Users/thomas/Projects/your_project_name/core_compile.yml', $limit = 1);  
 }  

Step 2 : edit the sfAutoload.class.php

now open the sfAutoload.class.php file and look up for the loadClass method, now replace the require statement by a swCompilerStats call.

 // we have a class path, let's include it  
 if (isset($this->classes[$class]))  
 {  

  swCompilerStats::getInstance()->requireFile($class, $this->classes[$class]);  
  //require($this->classes[$class]);  

  return true;  
 }

That’s it, swCompilerStats::requireFile required method includes the file and increments the stats. So now If you hit a page on your website, 2 new files will be added :

  • stats.dump : saved stats
  • core_compile.yml : the core_compile.yml ;)

Step 3 : edit the Doctrine.php file

Now let’s add this feature to Doctrine, open Doctrine.php file and find the autoload method and update the code like :

 if (file_exists($class)) {  
    //require $class;  
    swCompilerStats::getInstance()->requireFile($className, $class);  

    return true;  
 }  

 $loadedModels = self::$_loadedModelFiles;  

 if (isset($loadedModels[$className]) && file_exists($loadedModels[$className])) {  
    //require $loadedModels[$className];  

    swCompilerStats::getInstance()->requireFile($className, $loadedModels[$className]);  

    return true;  
 }

Now doctrine classes will be added to your core_compile.yml file. Now you can use your website, check all your pages, the swCompilerStats will keep a record of all class files included.

General points

The core_compile.yml file is only used on production environment so if you call index.php the core_compiled will be generated. A symfony cc is required !

Now the swCompilerStats might have included files which generated issues as explained in the introduction, so you might have a “blank screen of the death”. So check your web server log, remove the class which breaks the core_compile and clear you cache.

Important Note : the current script assumes that you have installed symfony into your project folder. I might update this point if I get positive feedbacks.

Conclusion

Send me back your comments if you have any speed improvement. You might need to play with the number of merged files to get the best improvement, big file is not good too …

You can install the swToolboxPlugin to get the swCompilerStats, for now the swToolboxPlugin is only accessible by svn at http://svn.symfony-project.com/plugins/swToolboxPlugin/sf1.2/trunk/