. */ /** * This file contains the Core class. * The Core class handles the loading of modules and brings the toolkit together. * * @author Dinu Florin * @package Core */ /** * The core class for the toolkit. * This class is a static class. * * @author Dinu Florin * @package Core */ final class Core extends Pt { /** A list of loaded modules. */ private static $loadedModules = array(); /** A list of files in the autoload directory of every loaded module. */ private static $autoloadIndex = array(); /** When the class is initialized this is set to true. */ private static $initialized = false; /** A list of directory to search when loading a module */ private static $moduleDirs = array(); protected static $cwd = null; //Current working directory, this is set on initialization. //Signals /** * Signal emitted after a module has loaded. * * @signal * @param string $module The module path. */ private static function moduleLoad($module) {} /** * Signal emitted if a module is already loaded. * * @signal * @param string $module The module path. */ private static function moduleAlreadyLoaded($module) {} /** * Signal emitted if autoloading fails. * * @signal * @param string $className The name of the needed class. */ private static function autoloadFailed($className) {} /** * Signal emitted on script shutdown. * @signal */ private static function shutdown() {} /** * Get the path to a class file from the autoload index array. * * @return mixed String on success or boolean false if the class is not mapped. */ public static function getAutoloadClassFile($className) { assert('is_string($className)'); assert('!empty($className)'); if(!isset(self::$autoloadIndex[$className])) return false; else return self::$autoloadIndex[$className]; } /** * Autoload handler. Do not call this method directly. * * @param string $className The class to autoload. */ public static function autoload($className) { $file = self::getAutoloadClassFile($className); if($file !== false) { require $file; } else { // self::emit('Core', 'autoloadFailed', $className); if(!class_exists($className)) trigger_error("Can't load the class file for {$className}. Are you sure you loaded all the modules you need?", E_USER_WARNING); } } /** * Return the autoload index. * * @return array. */ public static function getAutoloadIndex() { return self::$autoloadIndex; } /** * Index the contents of a directory for autoload. * * @param string $dir The directory to index. */ public static function indexForAutoload($dir) { foreach(glob($dir.DIRECTORY_SEPARATOR.'*.class.php') as $class) { $className = basename($class, '.class.php'); if(isset(self::$autoloadIndex[$className])) { throw new CoreException("Class colision. {$className} is already defined in another loaded module."); } self::$autoloadIndex[$className] = $class; } } /** * Add a new module directory. This will let you load modules by name from a custom directory. * * @param string $path The directory to add. */ public static function addModDir($path) { if(!file_exists($path)) { throw new CoreException("Invalid path {$path}"); } if(!is_readable($path)) { throw new CoreException("Unreadable path {$path}"); } $path = Util::makeStdPath($path); self::$moduleDirs[] = $path; } /** * Load a module. * Use this method to load a module, it will load and initialize the requested module. * * @param string $name the module name. * @param mixed $arg,... additional parameters passed to the initialization script. * They will be available in initialize.php as $argv[]. * @throws CoreException */ final public static function load($name) { assert('is_string($name)'); assert('!empty($name)'); if(!self::$initialized) self::initialize(); $name = trim($name); if(file_exists(PT_MOD_PATH.DIRECTORY_SEPARATOR.$name)) //We have a standard module. { $path = PT_MOD_PATH.DIRECTORY_SEPARATOR.$name; } else //We have a custom module path. { foreach(self::$moduleDirs as $dir) { if(file_exists($dir.DIRECTORY_SEPARATOR.$name)) { $path = $dir.DIRECTORY_SEPARATOR.$name; break; } } if(empty($path)) { //Try to load the module directly. $path = @Util::makeStdPath($name); if(!file_exists($path)) { throw new CoreException("Invalid module path {$name}"); } } } if(!in_array($path, self::$loadedModules)) //Did we load this already? { //This is a new module $iniScript = $path.DIRECTORY_SEPARATOR.'load.php'; $autoloadDir = $path.DIRECTORY_SEPARATOR.'autoload'; if(file_exists($autoloadDir)) { self::indexForAutoload($autoloadDir); } //Run the initialization script if(file_exists($iniScript)) { if(is_readable($iniScript)) { // self::emit('Core', 'moduleLoad', $path); //This is used to isolate the load.php script's context from //the rest of the context and to create a separate scope for the initialization scripts. if(!function_exists('iniScriptWrapper')) { function iniScriptWrapper($argv, $iniScript) { $thisDir = Util::makeStdPath(dirname($iniScript)); require $iniScript; } } iniScriptWrapper(func_get_args(), $iniScript); } else { throw new CoreException("Module {$name} has an unreadable initialization script."); } } else { throw new CoreException("Module {$name} doesn't have an initialization script."); } self::$loadedModules[] = $path; } else { // self::emit('Core', 'moduleAlreadyLoaded', $path); } } /** * Initialize the core. * Call this method before using the class. */ public static function initialize() { if(self::$initialized) return; self::$initialized = true; //Set the timezone $timeZone = getenv('TZ'); if(!$timeZone) $timeZone = ini_get('date.timezone'); if(!$timeZone) $timeZone = 'GMT'; date_default_timezone_set($timeZone); define('PT_PATH', Util::getPtPath()); define('PT_MOD_PATH', PT_PATH.DIRECTORY_SEPARATOR.'mod'); //Register the autoload method. spl_autoload_register(array('Core', 'autoload')); self::$cwd = getcwd(); //Register any signals or slots // self::registerSignal('Core', 'moduleLoad'); // self::registerSignal('Core', 'autoloadFailed'); // self::registerSignal('Core', 'moduleAlreadyLoaded'); self::registerSignal('Core', 'shutdown'); register_shutdown_function(array('Core', 'shutdownHandler')); self::indexForAutoload(PT_PATH.DIRECTORY_SEPARATOR.'core'); } /** * Called on script shutdown to emmit the shutdown signal. * * @ignore */ public static function shutdownHandler() { chdir(self::$cwd); self::emit('Core', 'shutdown'); } /** * Constructor. This is a static class, do not call the constructor, it *will* trow an exception. * Because it extends Pt the constructor must be public. */ public function __construct() { throw new CoreException('You can\'t instantiate the core class'); } } /** * Usually these exceptions are fatal. * * @package Core */ class CoreException extends GenericException { protected $message = 'Core Exception'; } ?>