| David Copperfield Wasn't This __magical: __autoload() is Awesome! [message #2107] |
Wed, 16 July 2008 13:31 |
dcousineau Messages: 6 Registered: July 2008 Location: College Station |
Shiny and New |
|
|
Is it possible that I could get any cornier with my post titles?
For anyone developing a moderately-to-highly object oriented program (as we should all be doing), balancing the need to keep KISS by separating classes/interfaces into their own files with having to make sure those files have been loaded properly can be tedious at best.
While you could take a scorched earth policy and just load up every file, does every execution (page load in web terms) require the same classes and are we wasting processing overhead?
As with most things, PHP already has a native, built-in solution waiting for us: The magic function __autoload().
When PHP 5.x comes across a reference to a class that's not loaded (say new Class(); or Class::CONST), it actually passes the offending class name to a user-defined function called __autoload() which is supposed to attempt to find and include() the class.
Now, the more perceptive readers will ask themselves "what about applications where I'm using third party libraries like Doctrine or Dwoo that might possibly use __autoload(), what happens to mine?" The SPL comes to the rescue in the form of spl_autoload_register().
You can use spl_autoload_register() to add your function to a universal __autoload(), managed by the SPL of course.
Now when you decide to use __autoload(), you will want to optimize how your application is laid out as you shouldn't waste cycles searching for a file (defeats the purpose of __autoload()), rather you should have a uniform name-to-path mapping.
The easiest (and best, IMHO) method is when using PEAR/PHP style namespacing (Class_Names_Like_This) is to just put each namespace in it's own directory (Class/Names/Like/This). The final namespace is the filename (This.php) and therefore Class_Names_Like_This should be stored in ~/Class/Names/Like/This.php.
Now, when working with third party libraries (or even purely my own code), I prefer to prefix all my classes with their own namespace. For example, if I was writing an application I wanted to call Foo, all my classes would begin with 'Foo_'. This helps out in (A) making sure if I include third party code none of the classes will conflict with mine, and (B) it gives me an easy way to make sure my __autoload() functions do not try and process anything but my own classes!
So, time for some code examples: Lets begin assuming we are calling our app "Foo" and we have already setup our root path to Foo as FOO_ROOT.
<?php
function foo_autoload($classname)
{
$pieces = explode('_', strtolower($classname));
//If the classname doesn't begin with Foo then exit this function
if( !$pieces || $pieces[0] !== "foo" )
return;
$file = array_pop($pieces) . ".php";
$path = implode(DIRECTORY_SEPARATOR, $pieces);
//If $path isn't empty add a trailing DIRECTORY_SEPARATOR
if( $path !== "" ) $path .= DIRECTORY_SEPARATOR;
$full_path = FOO_PATH . DIRECTORY_SEPARATOR . $path . $file;
if( file_exists( $full_path ) )
{
require_once($full_path);
}
}
spl_autoload_register('foo_autoload');
?>
Notice I named my __autoload() function foo_autoload(). I did this because spl_autoload_register() handles the actually __autoload(), so I give it a name to prevent any naming conflicts.
I lowercased the class name in the explode function to make sure the file names on the disk are consistent even in their casing. I don't like to risk the possibility that my script will run on a server with strict file name casing and be SOL.
Another thing to notice is that I return null when either there's a problem with $pieces or the class isn't prefixed with Foo_. Returning from the function allows the SPL to move on to the next __autoload() function (if it has one).
And finally you'll notice I actually check for the existence of the file before I attempt to require the file. I pick the require because obviously we've referenced the class (as we are in an __autoload() function), but I check the existence of the file to either give any other __autoloads() another chance to find the particular class. Or, if I so choose, I can put in error handling into my __autoload() function to be more explicit to the user that the particular file cannot be found.
A word of warning, with 5.3 and name spaces on the horizon, this code will only work with the old school method of PHP name spacing. From what I've gathered you can just explode on a '::' as the potential name space for a class is passed as a string, however I'm not sure on this.
Daniel Cousineau
Project Manager at Net Perspective, LLC.
My Blog of Pure Awesome
|
|
|