PHP – Autoloading classes with Standard PHP Library functions
In this topic, I will discuss about the power of PHPs autoloding functions and it’s capabilities to handle any situation smoothly.
GOAL:
Minimum number of include/require statements into your PHP project
BASICS:
When you have a long project with you, there are more chances that if you look to the starting of a file; you may find lot of include/require statements to include those different files/classes. This can be a hurdle if you have many classes defined in separate files. Thanks to PHPs autoloading functions that allows superb functionality to handle this situation. PHP has a spl_autoload_register function that can make any function an autoloading function. So if you provide spl_autoload_register(“foo::bar”) then a static function ‘bar’ of foo class would treat as an autoload function.
IMPLEMENTATION:
All you need is a class that handles the autoloading functionality. I created such class called autoloader.php which is shown below. The class has four functions namely the constructor, init, autoLoad and recursiveAutoLoad. You need to include this file in your project’s start-up environment and call the function autoload::init(), that’s all, you are all set
. From then onwards you don’t need any include or require statements throughout your project runtime.
CODE SNIPPET:
<?php
/**
* @author Bhargav Vadher - 2010-10-12
*
* An AutoLoader class for ANY level of files in
* a project hierarchy
*
*/
define('__ROOT', realpath ( dirname ( __FILE__)) . '/classes' ) . '/';
define('DS', DIRECTORY_SEPARATOR);
define('PS', PATH_SEPARATOR);
class Autoloader
{
private static $__loader;
// ----- The awesome constructor ----- //
private function __construct ()
{
spl_autoload_register ( array ( $this, 'autoLoad' ) );
}
// ----- The fire up call for an autoloader ----- //
public static function init ()
{
if ( self::$__loader == NULL )
{
self::$__loader = new self();
}
return self::$__loader;
}
// ----- The autoloder registered function ----- //
public function autoLoad( $class )
{
$exts = array ('.php', '.class.php', '.inc');
spl_autoload_extensions("'" . implode ( ',', $exts ) . "'");
set_include_path(get_include_path() . PATH_SEPARATOR . __ROOT);
foreach ($exts as $extention)
{
if( is_readable ( $path = __ROOT . $class . $ext) )
{
require_once $path ;
return TRUE;
}
}
self::recursiveAutoLoad( $class, __ROOT);
}
// ----- Recursive autoLoader, only called from autoLoder function ----- //
private static function recursiveAutoLoad( $class, $path )
{
if( is_dir ( $path))
{
if ( ($handle = opendir ( $path)) !== FALSE )
{
while (( $resource = readdir($handle) ) !== FALSE)
{
if( ($resource == '..') OR ($resource == '.'))
{
continue ;
}
if( is_dir ( $dir = $path . DS . $resource))
{
self::recursiveAutoLoad($class, $dir);
}
else if( is_readable ( $file = $path . DS . $resource))
{
require_once $file;
}
}
closedir( $handle);
}
}
}
}
UNDERSTANDING:
When you call autoloader::init() function, first it checks whether the instance of the class is already created, if yes then it returns that instance otherwise it calls a class constructor. As you can see in the constructor that it has only one function and that is PHPs SPL (Standard PHP Library) function spl_autoload_register with argument as an array of $this and ‘autoLoad’. So it will call the same file’s autoLoad function and register it as an autoloading function for later object creation.
Alright, let’s say you included this file in an index.php file and then you fire up the function init() of autoloader. Right after that you want to create an object/instance of the class `foo` which is somewhere deep down in directories that you don’t know
. As soon as the PHPs engine parse the new keyword, it will wake-up the autoLoad function of autoloder.php that has been registered by the init() function of the same file. Now you are at the core part. The autoLoad function is for immediate level of file/class to the path we provided as __ROOT constant. If the file is there directly inside __ROOT directory, this function will happily include that file at runtime. But what if the file is somewhere else let’s say at __ROOT/dir1/dir11/file111.php ? Well, for this we have recursiveAutoLoad function to take care and now you are at inner-core part of autoloader
.
Basically the recursiveAutoload function includes all the files that are not at the immediate level to the __ROOT path. It start with the check of whether $path or __ROOT is a directory or not, if it is then it will create a handle for that $path and then recursively gets all the resources from that handle. The condition for the while loop is ‘until we get the resources from $handle using readdir() function’. Inside the while loop first thing we care about is skipping the current and parent directories that is ‘.’ and ‘..’ respectively. After that, it checks for whether it’s a directory or a file. If it is a directory again, no worries, call the same function again using recursion with appending the directory at the end of original path. And if it’s not a directory it must be a file, so simply include that file and keep going on. You should notice the function is_readable() that is very useful function for I/O, it basically checks whether the file path exists and it’s readable too so two shots with one arrow. Finally close the handle and you are done. This autoloder will be invoked only once throughout the runtime of any given PHP script.
USE:
Create a file called index.php and paste the following code, execute it and see what happens
<?php require_once 'autoLoader.php'; Autoloader::init(); $obj = new some_class(); print_r(get_included_files());