<?php
// Own namespace
-namespace CoreFramework\Loader;
+namespace Org\Mxchange\CoreFramework\Loader;
// Import framework stuff
-use CoreFramework\Bootstrap\FrameworkBootstrap;
-use CoreFramework\Configuration\FrameworkConfiguration;
+use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
+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 <webmaster@shipsimu.org>
- * @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
*
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ----------------------------------
- * 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
* 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)
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
/**
* 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
*
* @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));
}
/**
*/
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);
- file_put_contents($this->listCacheFQFN, $cacheContent);
- } // END - if
+ $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);
+
+ // 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 $fqfn) {
+ //* 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
- $cacheContent .= file_get_contents($fqfn);
- } // END - foreach
+ // @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());
+ }
- // And write it away
- file_put_contents($this->classCacheFQFN, $cacheContent);
- } // END - if
- }
+ // Open file
+ //* NOISY-DEBUG: */ printf('[%s:%d]: cacheContent()=%d' . PHP_EOL, __METHOD__, __LINE__, strlen($cacheContent));
+ $fileObject = $this->classCacheFile->openFile('w');
- /**
- * 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();
+ // And write it away
+ $fileObject->fwrite($cacheContent);
- // 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__);
}
/**
* @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 = FrameworkConfiguration::getSelfInstance();
+ // "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
// Try to load the framework classes
$loaderInstance->scanClassPath($realPathName);
- } // END - foreach
+ }
// Trace message
//* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
* 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 = FrameworkConfiguration::getSelfInstance();
+ // "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 = FrameworkConfiguration::getSelfInstance();
-
- // 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__);
* @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
*/
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(FrameworkConfiguration::getSelfInstance());
- } // 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) . '<br />' . 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
$currentEntry = $iteratorInstance->current();
// Get filename from iterator which is the class' name (according naming-convention)
- $fileName = $currentEntry->getFileName();
-
- // Get the "FQFN" (path and file name)
- $fqfn = $currentEntry->getRealPath();
+ //* 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)) || (filesize($fqfn) < 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,fqfn=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $fqfn);
- $this->foundClasses[$fileName] = $fqfn;
+ //* 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,fqfn=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $fqfn);
+ //* 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
+ }
+
+ // 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->listCacheFQFN = $this->configInstance->getConfigEntry('local_database_path') . 'list-' . $this->configInstance->getConfigEntry('detected_app_name') . '.cache';
- $this->classCacheFQFN = $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?
- if (FrameworkBootstrap::isReadableFile($this->listCacheFQFN)) {
+ // 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->listCacheFQFN));
+ //* 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?
- if (FrameworkBootstrap::isReadableFile($this->listCacheFQFN)) {
+ //* NOISY-DEBUG: */ printf('[%s:%d]: Checking this->classCacheFile=%s ...' . PHP_EOL, __METHOD__, __LINE__, $this->classCacheFile);
+ if (FrameworkBootstrap::isReadableFile($this->classCacheFile)) {
// Then include it
- FrameworkBootstrap::loadInclude($this->classCacheFQFN);
+ //* 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) < 3)) {
- // Namespace scheme is: Project\Package[\SubPackage...]
- throw new InvalidArgumentException(sprintf('Class name "%s" is not conform to naming-convention: 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
- $fileName = $this->prefix . $shortClassName . $this->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]]))) {
+ //* 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]] = 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);
}
- }
- /**
- * 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) . '<br />' . PHP_EOL;
- } // END - foreach
-
- // And return it
- return $includeList;
+ // Trace message
+ //* NOISY-DEBUG: */ printf('[%s:%d] EXIT!' . PHP_EOL, __METHOD__, __LINE__);
}
}