X-Git-Url: https://git.mxchange.org/?p=core.git;a=blobdiff_plain;f=framework%2Floader%2Fclass_ClassLoader.php;h=7a16aca721bef4d0702ad67fea31a1bdf8a44593;hp=5035d3f83cce41ea94ec6edf90f4a824a67f15e6;hb=HEAD;hpb=a60894f1d6ef33613d2d0351075aa07aa257f304 diff --git a/framework/loader/class_ClassLoader.php b/framework/loader/class_ClassLoader.php index 5035d3f8..3f4422cd 100644 --- a/framework/loader/class_ClassLoader.php +++ b/framework/loader/class_ClassLoader.php @@ -4,20 +4,21 @@ namespace Org\Mxchange\CoreFramework\Loader; // Import framework stuff use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap; -use Org\Mxchange\CoreFramework\Configuration\FrameworkConfiguration; +use Org\Mxchange\CoreFramework\Generic\FrameworkInterface; // Import SPL stuff use \InvalidArgumentException; use \RecursiveDirectoryIterator; use \RecursiveIteratorIterator; use \SplFileInfo; +use \UnexpectedValueException; /** * This class loads class include files with a specific prefix and suffix * * @author Roland Haeder - * @version 1.5.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team + * @version 1.7.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.shipsimu.org * @@ -35,39 +36,62 @@ use \SplFileInfo; * along with this program. If not, see . * * ---------------------------------- - * 1.5 + * 1.7.0 + * - "Cached" more like config instance and root/application base path for + * shorter call stacks and lesser methods invoked + * - More debug logging + * 1.6.0 + * - This class loader is now 100% singleton, no other instance is really + * required, therefore the factory method can be removed safely + * - renamed initLoader() to initClassLoader() + * - An instance of a FrameworkConfiguration is no longer set here as this + * violated the rule that there shall be no instance set of that class + * - .htaccess is marked as deprecated as this should be no longer done + * - scanClassPath() is now protected, please use other scan*() methods + * 1.5.0 * - Namespace scheme Project\Package[\SubPackage...] is fully supported and * throws an InvalidArgumentException if not present. The last part will be * always the class' name. - * 1.4 + * 1.4.0 * - Some comments improved, other minor improvements - * 1.3 + * 1.3.0 * - Constructor is now empty and factory method 'createClassLoader' is created * - renamed loadClasses to scanClassPath * - Added initLoader() - * 1.2 + * 1.2.0 * - ClassLoader rewritten to PHP SPL's own RecursiveIteratorIterator class - * 1.1 + * 1.1.0 * - loadClasses rewritten to fix some notices - * 1.0 + * 1.0.0 * - Initial release * ---------------------------------- */ -class ClassLoader { +final class ClassLoader { /** * Instance of this class */ private static $selfInstance = NULL; /** - * Array with all found classes + * Instance of a FrameworkConfiguration class */ - private $foundClasses = array(); + private static $configInstance = NULL; + + /** + * Cached configuration entry 'is_developer_mode_enabled' + */ + private static $developerModeEnabled = NULL; + + /** + * Array with all valid but pending for loading file names (class, + * interfaces, traits must start with below prefix). + */ + private $pendingFiles = []; /** * List of loaded classes */ - private $loadedClasses = array(); + private $loadedClasses = []; /** * Suffix with extension for all class files @@ -83,7 +107,7 @@ class ClassLoader { * A list for directory names (no leading/trailing slashes!) which not be scanned by the path scanner * @see scanLocalPath */ - private $ignoreList = array(); + private $ignoreList = []; /** * Debug this class loader? (true = yes, false = no) @@ -123,18 +147,24 @@ class ClassLoader { /** * Framework/application paths for classes, etc. */ - private static $frameworkPaths = array( + private static $frameworkPaths = [ + 'classes', // Classes 'exceptions', // Exceptions 'interfaces', // Interfaces - 'classes', // Classes - 'middleware' // The middleware - ); + 'middleware', // The middleware + 'traits', // Traits + ]; /** * Registered paths where test classes can be found. These are all relative * to base_path . */ - private static $testPaths = array(); + private static $testPaths = []; + + /** + * Cached includes that needs to be flushed + */ + private $flushCache = []; /** * The protected constructor. Please use the factory method below, or use @@ -142,8 +172,19 @@ class ClassLoader { * * @return void */ - protected function __construct () { - // This is empty for now + private function __construct () { + // Is developerModeEnabled set? + //* NOISY-DEBUG: */ printf('[%s:%d]: CONSTRUCTED!' . PHP_EOL, __METHOD__, __LINE__); + if (is_null(self::$developerModeEnabled)) { + // Cache config instance + self::$configInstance = FrameworkBootstrap::getConfigurationInstance(); + + // Cache config entry + self::$developerModeEnabled = self::$configInstance->isEnabled('developer_mode'); + } + + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d - EXIT!' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled)); } /** @@ -153,58 +194,67 @@ class ClassLoader { */ public function __destruct () { // Skip here if dev-mode - if (defined('DEVELOPER')) { + //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d - DESTRUCTED!' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled)); + if (self::$developerModeEnabled) { + // Is enabled, don't cache + //* NOISY-DEBUG: */ printf('[%s:%d]: Developer mode enabled, not caching classes - EXIT!' . PHP_EOL, __METHOD__, __LINE__); return; - } // END - if + } + + // Init content + $cacheContent = ''; // Skip here if already cached + //* NOISY-DEBUG: */ printf('[%s:%d]: this->listCached=%d' . PHP_EOL, __METHOD__, __LINE__, intval($this->listCached)); if ($this->listCached === false) { // Writes the cache file of our list away - $cacheContent = json_encode($this->foundClasses); + $cacheContent = json_encode($this->pendingFiles); // Open cache instance + //* NOISY-DEBUG: */ printf('[%s:%d]: cacheContent()=%d' . PHP_EOL, __METHOD__, __LINE__, strlen($cacheContent)); $fileObject = $this->listCacheFile->openFile('w'); // And write whole list $fileObject->fwrite($cacheContent); - } // END - if + + // Close it + //* NOISY-DEBUG: */ printf('[%s:%d]: Closing file %s ...' . PHP_EOL, __METHOD__, __LINE__, $fileObject->getPathName()); + unset($fileObject); + } + + // Init content + $cacheContent = ''; // Skip here if already cached + //* NOISY-DEBUG: */ printf('[%s:%d]: this->classesCached=%d' . PHP_EOL, __METHOD__, __LINE__, intval($this->classesCached)); if ($this->classesCached === false) { // Generate a full-cache of all classes - $cacheContent = ''; - foreach (array_keys($this->loadedClasses) as $fileInstance) { + //* NOISY-DEBUG: */ printf('[%s:%d]: this->flushCache()=%d' . PHP_EOL, __METHOD__, __LINE__, count($this->flushCache)); + foreach ($this->flushCache as $key => $fileInstance) { // Open file + //* NOISY-DEBUG: */ printf('[%s:%d]: key=%s,fileInstance[]=%s' . PHP_EOL, __METHOD__, __LINE__, $key, gettype($fileInstance)); $fileObject = $fileInstance->openFile('r'); // Load the file // @TODO Add some uglifying code (compress) here + //* NOISY-DEBUG: */ printf('[%s:%d]: Adding fileInstance->size=%d bytes ...' . PHP_EOL, __METHOD__, __LINE__, $fileInstance->getSize()); $cacheContent .= $fileObject->fread($fileInstance->getSize()); - } // END - foreach + } // Open file + //* NOISY-DEBUG: */ printf('[%s:%d]: cacheContent()=%d' . PHP_EOL, __METHOD__, __LINE__, strlen($cacheContent)); $fileObject = $this->classCacheFile->openFile('w'); // And write it away $fileObject->fwrite($cacheContent); - } // END - if - } - - /** - * Creates an instance of this class loader for given configuration instance - * - * @param $configInstance Configuration class instance - * @return void - */ - public static final function createClassLoader (FrameworkConfiguration $configInstance) { - // Get a new instance - $loaderInstance = new ClassLoader(); - // Init the instance - $loaderInstance->initLoader($configInstance); + // Close it + //* NOISY-DEBUG: */ printf('[%s:%d]: Closing file %s ...' . PHP_EOL, __METHOD__, __LINE__, $fileObject->getPathName()); + unset($fileObject); + } - // Return the prepared instance - return $loaderInstance; + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); } /** @@ -213,35 +263,30 @@ class ClassLoader { * @return void */ public static function scanFrameworkClasses () { - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); - // Get loader instance + //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); $loaderInstance = self::getSelfInstance(); - // Get config instance - $configInstance = FrameworkBootstrap::getConfigurationInstance(); + // "Cache" configuration instance and framework base path + $frameworkBasePath = self::$configInstance->getConfigEntry('framework_base_path'); // Load all classes + //* NOISY-DEBUG: */ printf('[%s:%d]: frameworkBasePath=%s,self::$frameworkPaths()=%d,' . PHP_EOL, __METHOD__, __LINE__, $frameworkBasePath, count(self::$frameworkPaths)); foreach (self::$frameworkPaths as $shortPath) { - // Debug message - //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath); - // Generate full path from it + //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($shortPath), $shortPath); $realPathName = realpath(sprintf( '%smain%s%s%s', - $configInstance->getConfigEntry('framework_base_path'), + $frameworkBasePath, DIRECTORY_SEPARATOR, $shortPath, DIRECTORY_SEPARATOR )); - // Debug message - //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName=%s' . PHP_EOL, __METHOD__, __LINE__, $realPathName); - // Is it not false and accessible? + //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName), $realPathName); if (is_bool($realPathName)) { - // Skip this + // Skip this, it is not accessible continue; } elseif (!is_readable($realPathName)) { // @TODO Throw exception instead of break @@ -250,7 +295,7 @@ class ClassLoader { // Try to load the framework classes $loaderInstance->scanClassPath($realPathName); - } // END - foreach + } // Trace message //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); @@ -260,86 +305,48 @@ class ClassLoader { * Scans for application's classes, etc. * * @return void + * @throws UnexpectedValueException If a given path isn't one or not readable */ public static function scanApplicationClasses () { - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); - // Get loader instance + //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); $loaderInstance = self::getSelfInstance(); - // Get config instance - $configInstance = FrameworkBootstrap::getConfigurationInstance(); + // "Cache" application base path + $basePath = self::$configInstance->getConfigEntry('application_base_path'); // Load all classes for the application + //* NOISY-DEBUG: */ printf('[%s:%d]: self::frameworkPaths()=%d,basePath=%s' . PHP_EOL, __METHOD__, __LINE__, count(self::$frameworkPaths), $basePath); foreach (self::$frameworkPaths as $shortPath) { - // Debug message - //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath); - // Create path name - $pathName = realpath(sprintf( + //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath); + $realPathName = realpath(sprintf( '%s%s%s%s%s', - $configInstance->getConfigEntry('application_base_path'), + $basePath, DIRECTORY_SEPARATOR, - $configInstance->getConfigEntry('detected_app_name'), + FrameworkBootstrap::getDetectedApplicationName(), DIRECTORY_SEPARATOR, $shortPath )); - // Debug message - //* NOISY-DEBUG: */ printf('[%s:%d]: pathName[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($pathName), $pathName); - // Is the path readable? - if (is_dir($pathName)) { - // Try to load the application classes - $loaderInstance->scanClassPath($pathName); - } // END - if - } // END - foreach - - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); - } - - /** - * Scans for test classes, etc. - * - * @return void - */ - public static function scanTestsClasses () { - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); - - // Get config instance - $configInstance = FrameworkBootstrap::getConfigurationInstance(); - - // Load all classes for the application - foreach (self::$testPaths as $shortPath) { - // Debug message - //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath); - - // Construct path name - $pathName = sprintf( - '%s%s%s', - $configInstance->getConfigEntry('root_base_path'), - DIRECTORY_SEPARATOR, - $shortPath - ); - - // Debug message - //* NOISY-DEBUG: */ printf('[%s:%d]: pathName[%s]=%s - BEFORE!' . PHP_EOL, __METHOD__, __LINE__, gettype($pathName), $pathName); - - // Try to find it - $realPathName = realpath($pathName); - - // Debug message //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[%s]=%s - AFTER!' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName), $realPathName); + if (!is_string($realPathName)) { + // Skip this cone + //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[]=%s - SKIPPED!' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName)); + continue; + } elseif (!is_dir($realPathName)) { + // Is not a directory + throw new UnexpectedValueException(sprintf('realPathName=%s is not a directory', $realPathName), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE); + } elseif (!is_readable($realPathName)) { + // Not readable + throw new UnexpectedValueException(sprintf('realPathName=%s cannot be read from', $realPathName), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE); + } - // Is the path readable? - if ((is_dir($realPathName)) && (is_readable($realPathName))) { - // Try to load the application classes - ClassLoader::getSelfInstance()->scanClassPath($realPathName); - } // END - if - } // END - foreach + // Try to load the application classes + //* NOISY-DEBUG: */ printf('[%s:%d]: Scanning for classes/interfaces at realPathName=%s ...' . PHP_EOL, __METHOD__, __LINE__, $realPathName); + $loaderInstance->scanClassPath($realPathName); + } // Trace message //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); @@ -351,39 +358,37 @@ class ClassLoader { * @param $strictNamingConvention Whether to strictly check naming-convention * @return void */ - public static function enableStrictNamingConventionCheck ($strictNamingConvention = true) { + public static function enableStrictNamingConventionCheck (bool $strictNamingConvention = true) { self::$strictNamingConvention = $strictNamingConvention; } - /** - * Registeres given relative path where test classes reside. For regular - * framework uses, they should not be loaded (and used). - * - * @param $relativePath Relative path to test classes - * @return void - */ - public static function registerTestsPath ($relativePath) { - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: relativePath=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $relativePath); - - // "Register" it - self::$testPaths[$relativePath] = $relativePath; - - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); - } - /** * Autoload-function * * @param $className Name of the class to load * @return void + * @throws InvalidArgumentException If the class' name does not contain a namespace: Tld\Domain\Project is AT LEAST recommended! */ - public static function autoLoad ($className) { - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d]: className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className); + public static function autoLoad (string $className) { + // Validate parameter + //* NOISY-DEBUG: */ printf('[%s:%d] className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className); + if (empty($className)) { + // Should not be empty + throw new InvalidArgumentException('Parameter "className" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } + + // The class name MUST be at least Tld\Domain\Project\Package\SomeFooBar so split at them + $classNameParts = explode("\\", $className); + + // At least 3 parts should be there + //* NOISY-DEBUG: */ printf('[%s:%d]: self::strictNamingConvention=%d,classNameParts()=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$strictNamingConvention), count($classNameParts)); + if ((self::$strictNamingConvention === true) && (count($classNameParts) < 5)) { + // Namespace scheme is: Tld\Domain\Project\Package[\SubPackage...] + throw new InvalidArgumentException(sprintf('Class name "%s" is not conform to naming-convention: Tld\Domain\Project\Package[\SubPackage...]\SomeFooBar', $className)); + } // Try to include this class + //* NOISY-DEBUG: */ printf('[%s:%d]: Invoking self->loadClassFile(%s) ...' . PHP_EOL, __METHOD__, __LINE__, $className); self::getSelfInstance()->loadClassFile($className); // Trace message @@ -397,52 +402,79 @@ class ClassLoader { */ public static final function getSelfInstance () { // Is the instance there? + //* NOISY-DEBUG: */ printf('[%s:%d]: self::selfInstance[]=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, gettype(self::$selfInstance)); if (is_null(self::$selfInstance)) { - // Get a new one - self::$selfInstance = ClassLoader::createClassLoader(FrameworkBootstrap::getConfigurationInstance()); - } // END - if + // Get a new one and initialize it + //* NOISY-DEBUG: */ printf('[%s:%d]: Initializing class loader ...' . PHP_EOL, __METHOD__, __LINE__); + self::$selfInstance = new ClassLoader(); + self::$selfInstance->initClassLoader(); + } // Return the instance + //* NOISY-DEBUG: */ printf('[%s:%d]: self::selfInstance=%s - EXIT!' . PHP_EOL, __METHOD__, __LINE__, get_class(self::$selfInstance)); return self::$selfInstance; } + /** + * Getter for total include counter + * + * @return $total Total loaded include files + */ + public final function getTotal () { + return $this->total; + } + + /** + * Getter for a printable list of included main/interfaces/exceptions + * + * @param $includeList A printable include list + */ + public function getPrintableIncludeList () { + // Prepare the list + $includeList = ''; + foreach (array_keys($this->loadedClasses) as $classFile) { + $includeList .= basename($classFile) . '
' . PHP_EOL; + } + + // And return it + return $includeList; + } + /** * Scans recursively a local path for class files which must have a prefix and a suffix as given by $this->suffix and $this->prefix * * @param $basePath The relative base path to 'framework_base_path' constant for all classes * @param $ignoreList An optional list (array forced) of directory and file names which shall be ignored * @return void + * @throws InvalidArgumentException If a parameter is invalid */ - public function scanClassPath ($basePath, array $ignoreList = array() ) { + protected function scanClassPath (string $basePath, array $ignoreList = [] ) { // Is a list has been restored from cache, don't read it again - if ($this->listCached === true) { + //* NOISY-DEBUG: */ printf('[%s:%d] basePath=%s,ignoreList()=%d - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $basePath, count($ignoreList)); + if (empty($basePath)) { + // Throw IAE + throw new InvalidArgumentException('Parameter "basePath" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); + } elseif ($this->listCached === true) { // Abort here + //* NOISY-DEBUG: */ printf('[%s:%d] this->listCache=true - EXIT!' . PHP_EOL, __METHOD__, __LINE__); return; - } // END - if - - // Keep it in class for later usage - $this->ignoreList = $ignoreList; + } - /* - * Ignore .htaccess by default as it is for protection of directories - * on Apache servers. - */ - array_push($ignoreList, '.htaccess'); + // Keep it in class for later usage, but flip index<->value + $this->ignoreList = array_flip($ignoreList); /* * Set base directory which holds all our classes, an absolute path * should be used here so is_dir(), is_file() and so on will always * find the correct files and dirs. */ - $basePath2 = realpath($basePath); + $basePath = realpath($basePath); // If the basePath is false it is invalid - if ($basePath2 === false) { + //* NOISY-DEBUG: */ printf('[%s:%d] basePath[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($basePath), $basePath); + if (!is_string($basePath)) { /* @TODO: Do not exit here. */ exit(__METHOD__ . ': Cannot read ' . $basePath . ' !' . PHP_EOL); - } else { - // Set base path - $basePath = $basePath2; } // Get a new iterator @@ -455,163 +487,151 @@ class ClassLoader { $currentEntry = $iteratorInstance->current(); // Get filename from iterator which is the class' name (according naming-convention) + //* NOISY-DEBUG: */ printf('[%s:%d] currentEntry=%s,currentEntry->size=%d' . PHP_EOL, __METHOD__, __LINE__, $currentEntry->__toString(), $currentEntry->getSize()); $fileName = $currentEntry->getFilename(); // Current entry must be a file, not smaller than 100 bytes and not on ignore list - if ((!$currentEntry->isFile()) || (in_array($fileName, $this->ignoreList)) || ($currentEntry->getSize() < 100)) { + //* NOISY-DEBUG: */ printf('[%s:%d] fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + if (!$currentEntry->isFile() || isset($this->ignoreList[$fileName]) || $currentEntry->getSize() < 100) { // Advance to next entry $iteratorInstance->next(); // Skip non-file entries - //* NOISY-DEBUG: */ printf('[%s:%d] SKIP: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + //* NOISY-DEBUG: */ printf('[%s:%d] SKIP: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); continue; - } // END - if + } // Is this file wanted? - //* NOISY-DEBUG: */ printf('[%s:%d] FOUND: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + //* NOISY-DEBUG: */ printf('[%s:%d] FOUND: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); if ((substr($fileName, 0, strlen($this->prefix)) == $this->prefix) && (substr($fileName, -strlen($this->suffix), strlen($this->suffix)) == $this->suffix)) { // Add it to the list - //* NOISY-DEBUG: */ printf('[%s:%d] ADD: %s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry); - $this->foundClasses[$fileName] = $currentEntry; + //* NOISY-DEBUG: */ printf('[%s:%d] ADD: fileName=%s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry); + $this->pendingFiles[$fileName] = $currentEntry; } else { // Not added - //* NOISY-DEBUG: */ printf('[%s:%d] NOT ADDED: %s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry); + //* NOISY-DEBUG: */ printf('[%s:%d] NOT ADDED: fileName=%s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry); } // Advance to next entry - //* NOISY-DEBUG: */ printf('[%s:%d] NEXT: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + //* NOISY-DEBUG: */ printf('[%s:%d] NEXT: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); $iteratorInstance->next(); - } // END - while - } - - /** - * Getter for total include counter - * - * @return $total Total loaded include files - */ - public final function getTotal () { - return $this->total; - } - - /** - * Getter for a printable list of included main/interfaces/exceptions - * - * @param $includeList A printable include list - */ - public function getPrintableIncludeList () { - // Prepare the list - $includeList = ''; - foreach ($this->loadedClasses as $classFile) { - $includeList .= basename($classFile) . '
' . PHP_EOL; - } // END - foreach + } - // And return it - return $includeList; + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); } /** * Initializes our loader class * - * @param $configInstance Configuration class instance * @return void */ - private function initLoader (FrameworkConfiguration $configInstance) { - // Set configuration instance - $this->configInstance = $configInstance; - - // Construct the FQFN for the cache - if (!defined('DEVELOPER')) { - $this->listCacheFile = new SplFileInfo($this->configInstance->getConfigEntry('local_database_path') . 'list-' . $this->configInstance->getConfigEntry('detected_app_name') . '.cache'); - $this->classCacheFile = new SplFileInfo($this->configInstance->getConfigEntry('local_database_path') . 'class-' . $this->configInstance->getConfigEntry('detected_app_name') . '.cache'); - } // END - if - + private function initClassLoader () { // Set suffix and prefix from configuration - $this->suffix = $configInstance->getConfigEntry('class_suffix'); - $this->prefix = $configInstance->getConfigEntry('class_prefix'); + //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); + $this->suffix = self::$configInstance->getConfigEntry('class_suffix'); + $this->prefix = self::$configInstance->getConfigEntry('class_prefix'); // Set own instance + //* NOISY-DEBUG: */ printf('[%s:%d]: this->suffix=%s,this->prefix=%s' . PHP_EOL, __METHOD__, __LINE__, $this->suffix, $this->prefix); self::$selfInstance = $this; // Skip here if no dev-mode - if (defined('DEVELOPER')) { + //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled)); + if (self::$developerModeEnabled) { + // Developer mode is enabled + //* NOISY-DEBUG: */ printf('[%s:%d]: Developer mode is enabled - EXIT!' . PHP_EOL, __METHOD__, __LINE__); return; - } // END - if + } + + // Init cache instances + //* NOISY-DEBUG: */ printf('[%s:%d]: Initializing cache file instances ...' . PHP_EOL, __METHOD__, __LINE__); + $this->listCacheFile = new SplFileInfo(self::$configInstance->getConfigEntry('local_database_path') . 'list-' . FrameworkBootstrap::getDetectedApplicationName() . '.cache'); + $this->classCacheFile = new SplFileInfo(self::$configInstance->getConfigEntry('local_database_path') . 'class-' . FrameworkBootstrap::getDetectedApplicationName() . '.cache'); // Is the cache there? + //* NOISY-DEBUG: */ printf('[%s:%d]: Checking this->listCacheFile=%s ...' . PHP_EOL, __METHOD__, __LINE__, $this->listCacheFile); if (FrameworkBootstrap::isReadableFile($this->listCacheFile)) { // Load and convert it - $this->foundClasses = json_decode(file_get_contents($this->listCacheFile->getPathname())); + //* NOISY-DEBUG: */ printf('[%s:%d]: Loading %s ...' . PHP_EOL, __METHOD__, __LINE__, $this->listCacheFile); + $this->pendingFiles = json_decode(file_get_contents($this->listCacheFile->getPathname())); // List has been restored from cache! $this->listCached = true; - } // END - if + } // Does the class cache exist? + //* NOISY-DEBUG: */ printf('[%s:%d]: Checking this->classCacheFile=%s ...' . PHP_EOL, __METHOD__, __LINE__, $this->classCacheFile); if (FrameworkBootstrap::isReadableFile($this->classCacheFile)) { // Then include it + //* NOISY-DEBUG: */ printf('[%s:%d]: Invoking FrameworkBootstrap::loadInclude(%s) ...' . PHP_EOL, __METHOD__, __LINE__, $this->classCacheFile); FrameworkBootstrap::loadInclude($this->classCacheFile); // Mark the class cache as loaded $this->classesCached = true; - } // END - if + } + + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); } /** - * Tries to find the given class in our list. This method ignores silently - * missing classes or interfaces. So if you use class_exists() this method - * does not interrupt your program. + * Tries to find the given class in our list. It will ignore already loaded + * (to the program available) class/interface/trait files. But you SHOULD + * save below "expensive" code by pre-checking this->loadedClasses[], if + * possible. * * @param $className The class that shall be loaded * @return void - * @throws InvalidArgumentException If strict-checking is enabled and class name is not following naming-convention */ - private function loadClassFile ($className) { - // Trace message - //* NOISY-DEBUG: */ printf('[%s:%d] className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className); - + private function loadClassFile (string $className) { // The class name should contain at least 2 back-slashes, so split at them + //* NOISY-DEBUG: */ printf('[%s:%d]: className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className); $classNameParts = explode("\\", $className); - // At least 3 parts should be there - if ((self::$strictNamingConvention === true) && (count($classNameParts) < 5)) { - // Namespace scheme is: Project\Package[\SubPackage...] - throw new InvalidArgumentException(sprintf('Class name "%s" is not conform to naming-convention: Tld\Domain\Project\Package[\SubPackage...]\SomeFooBar', $className)); - } // END - if - // Get last element + //* NOISY-DEBUG: */ printf('[%s:%d]: classNameParts()=%d' . PHP_EOL, __METHOD__, __LINE__, count($classNameParts)); $shortClassName = array_pop($classNameParts); // Create a name with prefix and suffix + //* NOISY-DEBUG: */ printf('[%s:%d]: this->prefix=%s,shortClassName=%s,this->suffix=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $this->prefix, $shortClassName, $this->suffix); $fileName = sprintf('%s%s%s', $this->prefix, $shortClassName, $this->suffix); // Now look it up in our index - //* NOISY-DEBUG: */ printf('[%s:%d] ISSET: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); - if ((isset($this->foundClasses[$fileName])) && (!isset($this->loadedClasses[$this->foundClasses[$fileName]->getPathname()]))) { + //* DEBUG-DIE: */ die(sprintf('[%s:%d]: this->pendingFiles=%s', __METHOD__, __LINE__, print_r($this->pendingFiles, TRUE))); + //* NOISY-DEBUG: */ printf('[%s:%d] ISSET: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + if ((isset($this->pendingFiles[$fileName])) && (!isset($this->loadedClasses[$this->pendingFiles[$fileName]->getPathname()]))) { // File is found and not loaded so load it only once - //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: %s - START' . PHP_EOL, __METHOD__, __LINE__, $fileName); - FrameworkBootstrap::loadInclude($this->foundClasses[$fileName]); - //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: %s - END' . PHP_EOL, __METHOD__, __LINE__, $fileName); + //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s - START' . PHP_EOL, __METHOD__, __LINE__, $fileName); + FrameworkBootstrap::loadInclude($this->pendingFiles[$fileName]); + //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s - END' . PHP_EOL, __METHOD__, __LINE__, $fileName); // Count this loaded class/interface/exception $this->total++; // Mark this class as loaded for other purposes than loading it. - $this->loadedClasses[$this->foundClasses[$fileName]->getPathname()] = true; - - // Remove it from classes list so it won't be found twice. - //* NOISY-DEBUG: */ printf('[%s:%d] UNSET: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); - unset($this->foundClasses[$fileName]); + //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s marked as loaded ...' . PHP_EOL, __METHOD__, __LINE__, $fileName); + $this->loadedClasses[$this->pendingFiles[$fileName]->getPathname()] = true; // Developer mode excludes caching (better debugging) - if (!defined('DEVELOPER')) { - // Reset cache - //* NOISY-DEBUG: */ printf('[%s:%d] classesCached=false' . PHP_EOL, __METHOD__, __LINE__); + //* NOISY-DEBUG: */ printf('[%s:%d] self::developerModeEnabled=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled)); + if (!self::$developerModeEnabled) { + // Reset cache and mark file for flushing + //* NOISY-DEBUG: */ printf('[%s:%d] Setting this->classesCached=false ...' . PHP_EOL, __METHOD__, __LINE__); $this->classesCached = false; - } // END - if + $this->flushCache[$fileName] = $this->pendingFiles[$fileName]; + } + + // Remove it from classes list so it won't be found twice. + //* NOISY-DEBUG: */ printf('[%s:%d] UNSET: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + unset($this->pendingFiles[$fileName]); } else { // Not found - //* NOISY-DEBUG: */ printf('[%s:%d] 404: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); + //* NOISY-DEBUG: */ printf('[%s:%d] 404: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName); } + + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d] EXIT!' . PHP_EOL, __METHOD__, __LINE__); } }