Archive: Namaste PHP AMQP framework v1.0 (2017-2020)
952 days continuous production uptime, 40k+ tp/s single node. Original corpo Bitbucket history not included — clean archive commit.
This commit is contained in:
225
autoloader.php
Normal file
225
autoloader.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* This autoloader takes care of class loading dynamically 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 un-catchable fatals)
|
||||
* 5) maximally optimized awesomeness factor (not really, but it sounds nice)
|
||||
***********************************************************************************
|
||||
*
|
||||
* HISTORY:
|
||||
* ========
|
||||
* 06-15-17 mks Initial check-in
|
||||
* 01-17-20 mks DB-150: PHP7.4 refactor, also got rid of the native logging which didn't work and replaced it
|
||||
* with consoleLog, exception wrappers on all the things, and general code clean-up
|
||||
*
|
||||
*/
|
||||
/** @noinspection PhpUnused */
|
||||
|
||||
/**
|
||||
* _import()
|
||||
*
|
||||
* validates the input parameter.
|
||||
*
|
||||
* registers the class directory with the application so that future class
|
||||
* instantiations are autoload'd from the registered directory.
|
||||
*
|
||||
* @param $namespace - enforces naming convention for class files: *.class.inc
|
||||
*/
|
||||
|
||||
function _import($namespace)
|
||||
{
|
||||
$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 array $registry = array();
|
||||
private static array $file_registry = array();
|
||||
private static array $file_types = array('.class.inc');
|
||||
public static string $base_dir = '.';
|
||||
private static bool $initialized = false;
|
||||
public static bool $verbose = false;
|
||||
private static string $res = 'AUTO: ';
|
||||
private static bool $debug = false; // change this to true for more output verbosity
|
||||
|
||||
|
||||
// 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 path'd
|
||||
// class in it's own place, deletes itself and triggers removal of it's own class
|
||||
// definition... sexy, eh?
|
||||
private static bool $class_path_verify_mode = false; //Let me reiterate: this doesn't work yet, DO NOT USE
|
||||
|
||||
public static function initialize() :void
|
||||
{
|
||||
spl_autoload_register(array('Autoloader', 'load'));
|
||||
Autoloader::$base_dir = getcwd();
|
||||
Autoloader::$initialized = true;
|
||||
}
|
||||
|
||||
public static function register_directory(string $directory, int $depth=0):void
|
||||
{
|
||||
if (!Autoloader::$initialized) {
|
||||
try {
|
||||
Autoloader::initialize();
|
||||
} catch (TypeError $t) {
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_ERROR, $t->getMessage());
|
||||
exit();
|
||||
}
|
||||
}
|
||||
//examine the stack to get the calling file
|
||||
$stacktrace = debug_backtrace();
|
||||
if (array_key_exists($depth, $stacktrace) && array_key_exists('file', $stacktrace[$depth])) {
|
||||
$calling_file = $stacktrace[$depth]['file'];
|
||||
if (static::$debug) {
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_SYSTEM, 'Registering ' . realpath(Autoloader::$base_dir . '/' . $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(string $directory, $class_name) : ?string
|
||||
{
|
||||
foreach(Autoloader::$file_types as $type) {
|
||||
//$class_path = realpath(Autoloader::$base_dir.'/'.$directory.'/'.$class_name.$type);
|
||||
$class_path = realpath($directory . '/' . $class_name . $type);
|
||||
if (static::$debug) {
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __LINE__));
|
||||
consoleLog(static::$res, CON_SYSTEM, 'Checking for class ' . $class_name . ' in directory: ' . $directory);
|
||||
consoleLog(static::$res, CON_SYSTEM, 'Class Path: (' . $class_path . ')');
|
||||
}
|
||||
if (file_exists($class_path)) {
|
||||
if (static::$debug) {
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __LINE__));
|
||||
consoleLog(static::$res, CON_SYSTEM, 'Found class ' . $class_name);
|
||||
}
|
||||
return $class_path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function load(string $class_name, int $depth = 1) :void
|
||||
{
|
||||
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(array_key_exists($calling_file, Autoloader::$file_registry) && is_array(Autoloader::$file_registry[$calling_file])) {
|
||||
foreach(Autoloader::$file_registry[$calling_file] as $directory) {
|
||||
if (static::$debug) {
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_SYSTEM, 'Local Seek [' . $directory . ']');
|
||||
}
|
||||
try {
|
||||
$definition = Autoloader::find_class_definition($directory, $class_name);
|
||||
} catch (TypeError $t) {
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_ERROR, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_ERROR, $t->getMessage());
|
||||
exit();
|
||||
}
|
||||
if (!is_null($definition)) {
|
||||
try {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once($definition);
|
||||
} catch (Throwable $t) {
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_ERROR, $t->getMessage());
|
||||
exit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
$checked_dirs[] = $directory;
|
||||
}
|
||||
}
|
||||
//TODO: chain scopes so you have proper scope inheritance (not just local to the calling file)
|
||||
// foreach depth we trim one link off the stack, then we walk through the stack. looking for scope
|
||||
// attempt to load from the global context
|
||||
foreach(Autoloader::$registry as $directory) {
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_SYSTEM,'Global Seek ['.$directory.']');
|
||||
try {
|
||||
$definition = Autoloader::find_class_definition($directory, $class_name);
|
||||
if (!is_null($definition)) {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once($definition);
|
||||
return;
|
||||
}
|
||||
} catch (TypeError $t) {
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_ERROR, $t->getMessage());
|
||||
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
|
||||
consoleLog(static::$res, CON_ERROR, 'Could not find the class: ' . $directory . SLASH . $class_name . ' Creating a dummy.');
|
||||
try {
|
||||
Autoloader::load_text(Autoloader::create_crash_dummy($class_name), $class_name);
|
||||
} catch (TypeError $t) {
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_ERROR, $t->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static function load_text(string $class_text, ?string $class=null, ?string $namespace=null) :void
|
||||
{
|
||||
try {
|
||||
eval('?>' . $class_text . '<?php ');
|
||||
//require_once($class_text);
|
||||
consoleLog(static::$res, CON_SYSTEM,'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 ');
|
||||
if (static::$debug)
|
||||
consoleLog(static::$res, CON_ERROR, 'Loaded package specific class definition ['.$class_with_package_text.']');
|
||||
}
|
||||
} catch (Throwable $t) {
|
||||
if (static::$debug) {
|
||||
consoleLog(static::$res, CON_SYSTEM, sprintf(INFO_LOC, basename(__METHOD__), __FILE__));
|
||||
consoleLog(static::$res, CON_ERROR, $t->getMessage());
|
||||
consoleLog(static::$res, CON_ERROR, $class_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function create_crash_dummy(string $class_name): string {
|
||||
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!");
|
||||
}
|
||||
}
|
||||
?>';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user