Page 1 of 1

__autoload vs class configuration

Posted: Tue Jun 03, 2008 8:34 am
by mindplay
I've been messing about with __autoload() which turns out to be surprisingly effective.

I had heard rumors saying that __autoload() was terribly inefficient and caused all sorts of problems. But it's working pretty good for me so far.

What I would like to share here, is a problem (and solution) related to __autoload(), although you may also find this useful for PHP programming in general.

To the point:

Most people, when they create a class library, the library comes with a configuration file of some sort - typically, this comes in the form of a script that you copy and edit, which is filled with define() statements:

Code: Select all

define("MYCLASS_PATH", "/var/www/someapp/files");
define("MYCLASS_URL", "http://www.somewhere.com/something.php");
// etc.
This is all fine and works well.

However, as I was experimenting with autoloading, I came across a problem. Normally, I used to have a main file (header.php or something similar) that you're supposed to load, which includes the configuration file and the core classes.

But with autoload, the configuration file is no longer loaded. The impulsive way to sort this out, is to just add require_once() statements to the top of every class file, to ensure the configuration file is always loaded whenever a class autoloads.

Trouble is, the net result of that, is even more unnecessary include/require statements than you had before you started autoloading.

So, I thought, how can I get the class configuration to autoload?

Simply, my configuration file now looks like this:

Code: Select all

abstract class MyClassConfig {
   const PATH = "/var/www/someapp/files";
   const URL = "http://www.somewhere.com/something.php";
   // etc.
}
Because my configuration is contained in a class, it can autoload - and an added benefit is, the code that referes to these configuration values becomes a bit more legible, since MyClassConfig::PATH actually tells you where to look for the configuration value.

So, I thought, what is the performance penalty for adding and loading the extra class, and performing the static class resolution every time you need a configuration value?

I created two simple test scripts.

The classical way:

Code: Select all

$start = microtime(true);
 
define("CONFIG_0", "0");
define("CONFIG_1", "1");
define("CONFIG_2", "2");
define("CONFIG_3", "3");
define("CONFIG_4", "4");
define("CONFIG_5", "5");
define("CONFIG_6", "6");
define("CONFIG_7", "7");
define("CONFIG_8", "8");
define("CONFIG_9", "9");
 
echo CONFIG_0."<br/>";
echo CONFIG_1."<br/>";
echo CONFIG_2."<br/>";
echo CONFIG_3."<br/>";
echo CONFIG_4."<br/>";
echo CONFIG_5."<br/>";
echo CONFIG_6."<br/>";
echo CONFIG_7."<br/>";
echo CONFIG_8."<br/>";
echo CONFIG_9."<br/>";
 
echo number_format(1000*(microtime(true)-$start),8);
and what I call the classy way ;-)

Code: Select all

$start = microtime(true);
 
abstract class Config {
    const _0 = "0";
    const _1 = "1";
    const _2 = "2";
    const _3 = "3";
    const _4 = "4";
    const _5 = "5";
    const _6 = "6";
    const _7 = "7";
    const _8 = "8";
    const _9 = "9";
}
 
echo Config::_0."<br/>";
echo Config::_1."<br/>";
echo Config::_2."<br/>";
echo Config::_3."<br/>";
echo Config::_4."<br/>";
echo Config::_5."<br/>";
echo Config::_6."<br/>";
echo Config::_7."<br/>";
echo Config::_8."<br/>";
echo Config::_9."<br/>";
 
echo number_format(1000*(microtime(true)-$start),8);
The perhaps surprising result, is that using a class instead of flat define()'s is approximately twice as fast!

Nope, declaring the class, and resolving the constants, does not result in any performance penalty whatsoever, quite the contrary.

The explanation is, that regular define()'d constants exist in the global PHP namespace, which is already littered with built-in constants, constants defined by various modules, etc. - whereas your own isolated little island configuration class has a much smaller scope to search for the necessary constant. Hence, faster.

Nice!

Whether you use (or like) autoloading, this is definitely not a bad technique. I would highly recommend using classes as configuration files in the future, as this results in a cleaner namespace, more legible code, and, apparently, even a minute performance bonus! :-)

Re: __autoload vs class configuration

Posted: Tue Jun 03, 2008 8:10 pm
by Ambush Commander
mindplay, the performance downturn from autoload comes when you start loading lots of PHP files, since autoload is architected with one file per class in mind. The fact that autoload makes it difficult for opcode caches to perform efficiently exacerbates the problem (although nostat certainly comes in handy, in the case of APC).

However, it's not surprising that class constants are faster than define() constants; define is known to be notoriously slow, and class constants have the benefit of being evaluated compile time.

Re: __autoload vs class configuration

Posted: Wed Jun 04, 2008 9:17 am
by mindplay
Ambush Commander wrote:mindplay, the performance downturn from autoload comes when you start loading lots of PHP files, since autoload is architected with one file per class in mind. The fact that autoload makes it difficult for opcode caches to perform efficiently exacerbates the problem (although nostat certainly comes in handy, in the case of APC).
It seems like that overhead is there regardless of whether you use autoload or not - as most people structure their codebase with one php-file per class, to keep things organized, anyway.

It's a different discussion altogether, but I am curious to hear how you propose to solve that then? :-) ... let's pretend that autoload doesn't exist, how would you go about this problem, roll all your classes into single files? That would make things rather unmanageable in my opinion - you would run into lots of problems with CVS merge, for example...

Re: __autoload vs class configuration

Posted: Wed Jun 04, 2008 5:35 pm
by Ambush Commander
Actually, that's exactly what we do over at HTML Purifier. All the dev work happens in individual files, then we use a little script to glob all the scripts together, and package/distribute that. It's fairly easy to do.

Also, as mentioned previously, opcode caches mitigate the performance penalty of many files.

Re: __autoload vs class configuration

Posted: Fri Jun 06, 2008 1:29 am
by mindplay
Ambush Commander wrote:Actually, that's exactly what we do over at HTML Purifier. All the dev work happens in individual files, then we use a little script to glob all the scripts together, and package/distribute that. It's fairly easy to do.

Also, as mentioned previously, opcode caches mitigate the performance penalty of many files.
Still, if you have class libraries with 50+ classes, I bet there's still considerable overhead you could save.

Just curious, do you update the packaged script manually, or do you have some sort of filemtime() check on all the packaged files? I'm guessing the latter would probably cause as much (if not more) overhead than simply including the individual files, so if that's what you're doing, I guess you have a flag that turns this check on/off?