<?php
// ***********************************************************************************
// * Autoloader by Abbey Hawk Sparrow
// ***********************************************************************************
// * This autoloader takes care of classloading dynamicly as well as maintaining scope
// * and registration. All you have to do to use it is use the java style _import
// * EX: _import('path.to.package.*');
// * or the path based syntax
// * EX: Autoloader::register_directory('./path/to/package/');
// * multiple copies of a identically named class can exist, but only one can be
// * loaded during execution, so while this can be helpful when dealing with libraries
// * which have colliding namespaces, it is no panacea.
// ***********************************************************************************
// * KEY FEATURES *
// ****************
// * 1) auto-autoload (other than setting your class folders, you do nothing)
// * 2) namespace compartmentalization (use the same class name over and over)
// * 3) shorter class/folder hierarchy traversals (get to the right class faster)
// * 4) allows inline catching of class load errors (no uncatchable fatals)
// * 5) maximally optimized awesomeness factor (not really, but it sounds nice)
// ***********************************************************************************
function _import($namespace){ //enforces a naming convention of <class>.classes.php
$ns_parts = explode('.', $namespace);
if(end($ns_parts) == '*'){
unset($ns_parts[key($ns_parts)]);
$directory = implode('/', $ns_parts);
Autoloader::register_directory($directory, 1);
}
}
class Autoloader{
private static $registry = array();
private static $file_registry = array();
public static $base_dir = '.';
private static $initialized = false;
public static $verbose = false;
// the following mode is an experimental setting to shoehorn dummy classes
// that can mask multiple collided classes and resolve the linkage by the
// calling context, I have personal doubts this will ever be suitable for
// anything but tests, unless you have a very odd need to sandbox namespaces.
// it's way inefficient as it is an attempt to autoload *every* instantiation
// the idea is to define a new class that injects an instance of the fully pathed
// class in it's own place, deletes itself and triggers removal of it's own class
// definition... sexy, eh?
private static $class_path_verify_mode = false; //Let me reiterate: this doesn't work yet, DO NOT USE
private static function log($text){
if(Autoloader::$verbose) echo($text."<br/>\n");
}
private static function initialize(){
Autoloader::register_autoloader();
Autoloader::$base_dir = getcwd();
}
private static function register_autoloader(){
spl_autoload_register(array('Autoloader', 'load'));
}
public static function register_directory($directory, $depth=0){
if(!Autoloader::$initialized) Autoloader::initialize();
//examine the stack to get the calling file
$stacktrace = debug_backtrace();
$calling_file = $stacktrace[$depth]['file'];
Autoloader::log('Registering '.realpath($directory).' to '.$calling_file);
//register the directory to the calling file
Autoloader::$file_registry[$calling_file][] = $directory;
//register the directory globally
if(!in_array($directory, Autoloader::$registry)) Autoloader::$registry[] = $directory;
}
public static function find_class_definition($directory, $class_name){
$class_path = realpath(Autoloader::$base_dir.'/'.$directory.'/'.$class_name.'.class.php');
Autoloader::log('Checking for class '.$class_name.' in directory '.$directory.' ('.$class_path.')');
if(file_exists($class_path)){ Autoloader::log('Found class '.$class_name.' in directory '.$directory); return $class_path; }
return false;
}
public static function load($class_name, $depth = 1){
if(!Autoloader::$initialized) Autoloader::initialize();
//get the context file we called from
$stacktrace = debug_backtrace();
$calling_file = $stacktrace[$depth]['file'];
//attempt to load from the local context
$checked_dirs = array();
if(is_array(Autoloader::$file_registry[$calling_file])) foreach(Autoloader::$file_registry[$calling_file] as $directory){
Autoloader::log('Local Seek ['.$directory.']');
if($definition = Autoloader::find_class_definition($directory, $class_name)){ require_once($definition); return; }
$checked_dirs[] = $directory;
}
//TODO: chain scopes so you have proper scope inheritance (not just local to the calling file)
//attempt to load from the global context
foreach(Autoloader::$registry as $directory){
Autoloader::log('Global Seek ['.$directory.']');
if($definition = Autoloader::find_class_definition($directory, $class_name)){ require_once($definition); return; }
}
// uh oh, we can't find the class, we're going to have to return a clean crash-dummy, so we can catch the error
Autoloader::log('Could not find the class! Creating a dummy.');
Autoloader::load_text(Autoloader::create_crash_dummy($class_name), $class_name);
}
public static function load_text($class_text, $class=null, $namespace=null){
///*
eval('?>'.$class_text.'<?php '); // */
//require_once($class_text);
Autoloader::log('Loaded class definition ['.$class.']');
if(Autoloader::$class_path_verify_mode && $class != null && $namespace != null){
$class_with_package_text = preg_replace('~ '.$class.'~', ' '.$namespace.'_'.$class, $class_text);
eval('?>'.$class_with_package_text.'<?php ');
Autoloader::log('Loaded package specific class definition ['.$class_with_package_text.']');
}
}
public static function create_crash_dummy($class_name){
return '<?php
class '.$class_name.'{
function __construct($a=null, $b=null, $c=null, $d=null, $e=null, $f=null, $g=null, $h=null, $i=null, $j=null, $k=null, $l=null, $m=null, $n=null, $o=null, $p=null){
throw new Exception("AUTOLOADER: Class '.$class_name.' not found!");
}
}
?>';
}
}
?>