X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=framework%2Floader%2Fclass_ClassLoader.php;h=9e0e4bacf06e35c18356a0df568735a3ed29861a;hb=498e6b065ce47804bff4e1073592a2cc8e28f8ef;hp=f4a69ab6e6297889384d583b9811a43886dc2d40;hpb=e2c64b8dbc2e98f397b93152bab1f601df9c758e;p=core.git diff --git a/framework/loader/class_ClassLoader.php b/framework/loader/class_ClassLoader.php index f4a69ab6..9e0e4bac 100644 --- a/framework/loader/class_ClassLoader.php +++ b/framework/loader/class_ClassLoader.php @@ -1,22 +1,23 @@ * @version 1.5.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.shipsimu.org * @@ -61,12 +62,12 @@ class ClassLoader { /** * Array with all found classes */ - private $foundClasses = array(); + private $foundClasses = []; /** * List of loaded classes */ - private $loadedClasses = array(); + private $loadedClasses = []; /** * Suffix with extension for all class files @@ -82,38 +83,43 @@ 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) + * Debug this class loader? (true = yes, false = no) */ - private $debug = FALSE; + private $debug = false; /** * Whether the file list is cached */ - private $listCached = FALSE; + private $listCached = false; /** * Wethe class content has been cached */ - private $classesCached = FALSE; + private $classesCached = false; /** - * Filename for the list cache + * SplFileInfo for the list cache */ - private $listCacheFQFN = ''; + private $listCacheFile = NULL; /** - * Cache for class content + * SplFileInfo for class content */ - private $classCacheFQFN = ''; + private $classCacheFile = NULL; /** * Counter for loaded include files */ private $total = 0; + /** + * By default the class loader is strict with naming-convention check + */ + private static $strictNamingConvention = true; + /** * Framework/application paths for classes, etc. */ @@ -128,7 +134,7 @@ class ClassLoader { * Registered paths where test classes can be found. These are all relative * to base_path . */ - private static $testPaths = array(); + private static $testPaths = []; /** * The protected constructor. Please use the factory method below, or use @@ -152,23 +158,35 @@ class ClassLoader { } // END - if // Skip here if already cached - if ($this->listCached === FALSE) { + if ($this->listCached === false) { // Writes the cache file of our list away $cacheContent = json_encode($this->foundClasses); - file_put_contents($this->listCacheFQFN, $cacheContent); + + // Open cache instance + $fileObject = $this->listCacheFile->openFile('w'); + + // And write whole list + $fileObject->fwrite($cacheContent); } // END - if // Skip here if already cached - if ($this->classesCached === FALSE) { + if ($this->classesCached === false) { // Generate a full-cache of all classes $cacheContent = ''; - foreach (array_keys($this->loadedClasses) as $fqfn) { + foreach (array_keys($this->loadedClasses) as $fileInstance) { + // Open file + $fileObject = $fileInstance->openFile('r'); + // Load the file - $cacheContent .= file_get_contents($fqfn); + // @TODO Add some uglifying code (compress) here + $cacheContent .= $fileObject->fread($fileInstance->getSize()); } // END - foreach + // Open file + $fileObject = $this->classCacheFile->openFile('w'); + // And write it away - file_put_contents($this->classCacheFQFN, $cacheContent); + $fileObject->fwrite($cacheContent); } // END - if } @@ -198,16 +216,40 @@ class ClassLoader { // Trace message //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); - // Cache loader instance + // Get loader instance $loaderInstance = self::getSelfInstance(); + // Get config instance + $configInstance = FrameworkBootstrap::getConfigurationInstance(); + // Load all classes foreach (self::$frameworkPaths as $shortPath) { // Debug message //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath); + // Generate full path from it + $realPathName = realpath(sprintf( + '%smain%s%s%s', + $configInstance->getConfigEntry('framework_base_path'), + 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? + if (is_bool($realPathName)) { + // Skip this + continue; + } elseif (!is_readable($realPathName)) { + // @TODO Throw exception instead of break + break; + } + // Try to load the framework classes - $loaderInstance->scanClassPath(sprintf('framework/main/%s/', $shortPath)); + $loaderInstance->scanClassPath($realPathName); } // END - foreach // Trace message @@ -223,8 +265,11 @@ class ClassLoader { // Trace message //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); + // Get loader instance + $loaderInstance = self::getSelfInstance(); + // Get config instance - $cfg = FrameworkConfiguration::getSelfInstance(); + $configInstance = FrameworkBootstrap::getConfigurationInstance(); // Load all classes for the application foreach (self::$frameworkPaths as $shortPath) { @@ -233,9 +278,11 @@ class ClassLoader { // Create path name $pathName = realpath(sprintf( - '%s/%s/%s', - $cfg->getConfigEntry('application_path'), - $cfg->getConfigEntry('app_name'), + '%s%s%s%s%s', + $configInstance->getConfigEntry('application_base_path'), + DIRECTORY_SEPARATOR, + $configInstance->getConfigEntry('detected_app_name'), + DIRECTORY_SEPARATOR, $shortPath )); @@ -245,7 +292,7 @@ class ClassLoader { // Is the path readable? if (is_dir($pathName)) { // Try to load the application classes - ClassLoader::getSelfInstance()->scanClassPath($pathName); + $loaderInstance->scanClassPath($pathName); } // END - if } // END - foreach @@ -263,27 +310,34 @@ class ClassLoader { //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__); // Get config instance - $cfg = FrameworkConfiguration::getSelfInstance(); + $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); - // Create path name - $pathName = realpath(sprintf( - '%s/%s', - $cfg->getConfigEntry('base_path'), + // 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' . PHP_EOL, __METHOD__, __LINE__, gettype($pathName), $pathName); + //* 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); // Is the path readable? - if (is_dir($pathName)) { + if ((is_dir($realPathName)) && (is_readable($realPathName))) { // Try to load the application classes - ClassLoader::getSelfInstance()->scanClassPath($pathName); + ClassLoader::getSelfInstance()->scanClassPath($realPathName); } // END - if } // END - foreach @@ -291,6 +345,16 @@ class ClassLoader { //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); } + /** + * Enables or disables strict naming-convention tests on class loading + * + * @param $strictNamingConvention Whether to strictly check naming-convention + * @return void + */ + 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). @@ -298,9 +362,15 @@ class ClassLoader { * @param $relativePath Relative path to test classes * @return void */ - public static function registerTestsPath ($relativePath) { + public static function registerTestsPath (string $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__); } /** @@ -309,9 +379,15 @@ class ClassLoader { * @param $className Name of the class to load * @return void */ - public static function autoLoad ($className) { + public static function autoLoad (string $className) { + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d]: className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className); + // Try to include this class self::getSelfInstance()->loadClassFile($className); + + // Trace message + //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__); } /** @@ -323,7 +399,7 @@ class ClassLoader { // Is the instance there? if (is_null(self::$selfInstance)) { // Get a new one - self::$selfInstance = ClassLoader::createClassLoader(FrameworkConfiguration::getSelfInstance()); + self::$selfInstance = ClassLoader::createClassLoader(FrameworkBootstrap::getConfigurationInstance()); } // END - if // Return the instance @@ -333,13 +409,13 @@ class ClassLoader { /** * 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 'base_path' constant for all classes + * @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 */ - public function scanClassPath ($basePath, array $ignoreList = array() ) { + public function scanClassPath (string $basePath, array $ignoreList = [] ) { // Is a list has been restored from cache, don't read it again - if ($this->listCached === TRUE) { + if ($this->listCached === true) { // Abort here return; } // END - if @@ -360,8 +436,8 @@ class ClassLoader { */ $basePath2 = realpath($basePath); - // If the basePath is FALSE it is invalid - if ($basePath2 === FALSE) { + // If the basePath is false it is invalid + if ($basePath2 === false) { /* @TODO: Do not exit here. */ exit(__METHOD__ . ': Cannot read ' . $basePath . ' !' . PHP_EOL); } else { @@ -378,14 +454,11 @@ class ClassLoader { // Get current entry $currentEntry = $iteratorInstance->current(); - // Get filename from iterator - $fileName = $currentEntry->getFileName(); - - // Get the "FQFN" (path and file name) - $fqfn = $currentEntry->getRealPath(); + // Get filename from iterator which is the class' name (according naming-convention) + $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)) || (filesize($fqfn) < 100)) { + // 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)) { // Advance to next entry $iteratorInstance->next(); @@ -398,11 +471,11 @@ class ClassLoader { //* NOISY-DEBUG: */ printf('[%s:%d] FOUND: %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,fqfn=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $fqfn); - $this->foundClasses[$fileName] = $fqfn; + //* NOISY-DEBUG: */ printf('[%s:%d] ADD: %s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry); + $this->foundClasses[$fileName] = $currentEntry; } else { // Not added - //* NOISY-DEBUG: */ printf('[%s:%d] NOT ADDED: %s,fqfn=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $fqfn); + //* NOISY-DEBUG: */ printf('[%s:%d] NOT ADDED: %s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry); } // Advance to next entry @@ -412,28 +485,28 @@ class ClassLoader { } /** - * Load extra config files + * Getter for total include counter * - * @return void + * @return $total Total loaded include files */ - public function loadExtraConfigs () { - // Backup old prefix - $oldPrefix = $this->prefix; - - // Set new prefix (temporary!) - $this->prefix = 'config-'; - - // Set base directory - $basePath = $this->configInstance->getConfigEntry('base_path') . 'framework/config/'; - - // Load all classes from the config directory - $this->scanClassPath($basePath); + public final function getTotal () { + return $this->total; + } - // Include these extra configs now - $this->includeExtraConfigs(); + /** + * 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 - // Set back the old prefix - $this->prefix = $oldPrefix; + // And return it + return $includeList; } /** @@ -448,8 +521,8 @@ class ClassLoader { // Construct the FQFN for the cache if (!defined('DEVELOPER')) { - $this->listCacheFQFN = $this->configInstance->getConfigEntry('local_db_path') . 'list-' . $this->configInstance->getConfigEntry('app_name') . '.cache'; - $this->classCacheFQFN = $this->configInstance->getConfigEntry('local_db_path') . 'class-' . $this->configInstance->getConfigEntry('app_name') . '.cache'; + $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 // Set suffix and prefix from configuration @@ -464,25 +537,22 @@ class ClassLoader { return; } // END - if - // IS the cache there? - if (BaseFrameworkSystem::isReadableFile($this->listCacheFQFN)) { - // Get content - $cacheContent = file_get_contents($this->listCacheFQFN); - - // And convert it - $this->foundClasses = json_decode($cacheContent); + // Is the cache there? + if (FrameworkBootstrap::isReadableFile($this->listCacheFile)) { + // Load and convert it + $this->foundClasses = json_decode(file_get_contents($this->listCacheFile->getPathname())); // List has been restored from cache! - $this->listCached = TRUE; + $this->listCached = true; } // END - if // Does the class cache exist? - if (BaseFrameworkSystem::isReadableFile($this->listCacheFQFN)) { + if (FrameworkBootstrap::isReadableFile($this->classCacheFile)) { // Then include it - require($this->classCacheFQFN); + FrameworkBootstrap::loadInclude($this->classCacheFile); // Mark the class cache as loaded - $this->classesCached = TRUE; + $this->classesCached = true; } // END - if } @@ -493,39 +563,38 @@ class ClassLoader { * * @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 (count($classNameParts) < 3) { + 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: Project\Package[\SubPackage...]\SomeFooBar', $className)); + 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 $shortClassName = array_pop($classNameParts); // Create a name with prefix and suffix - $fileName = $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]]))) { + if ((isset($this->foundClasses[$fileName])) && (!isset($this->loadedClasses[$this->foundClasses[$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); - require($this->foundClasses[$fileName]); + FrameworkBootstrap::loadInclude($this->foundClasses[$fileName]); //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: %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]] = TRUE; + $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); @@ -534,8 +603,8 @@ class ClassLoader { // Developer mode excludes caching (better debugging) if (!defined('DEVELOPER')) { // Reset cache - //* NOISY-DEBUG: */ printf('[%s:%d] classesCached=FALSE' . PHP_EOL, __METHOD__, __LINE__); - $this->classesCached = FALSE; + //* NOISY-DEBUG: */ printf('[%s:%d] classesCached=false' . PHP_EOL, __METHOD__, __LINE__); + $this->classesCached = false; } // END - if } else { // Not found @@ -543,51 +612,4 @@ class ClassLoader { } } - /** - * Includes all extra config files - * - * @return void - */ - private function includeExtraConfigs () { - // Run through all class names (should not be much) - foreach ($this->foundClasses as $fileName => $fqfn) { - // Is this a config? - if (substr($fileName, 0, strlen($this->prefix)) == $this->prefix) { - // Then include it - //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: %s - START' . PHP_EOL, __METHOD__, __LINE__, $fileName); - require($fqfn); - //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: %s - END' . PHP_EOL, __METHOD__, __LINE__, $fileName); - - // Remove it from the list - //* NOISY-DEBUG: */ printf('[%s:%d] UNSET: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName); - unset($this->foundClasses[$fileName]); - } // END - if - } // END - foreach - } - - /** - * 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; - } - }