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.6.0
- * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2022 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.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
private static $selfInstance = NULL;
/**
- * Cached configuration entry 'developer_mode_enabled'
+ * Instance of a FrameworkConfiguration class
*/
- private static $developerModeEnabled = false;
+ 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,
*/
private static $testPaths = [];
+ /**
+ * Cached includes that needs to be flushed
+ */
+ private $flushCache = [];
+
/**
* The protected constructor. Please use the factory method below, or use
* getSelfInstance() for singleton
* @return void
*/
private function __construct () {
- // Cache config entry
- self::$developerModeEnabled = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('developer_mode_enabled');
+ // 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
+ //* 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;
}
+ // 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->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 $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());
}
// 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);
+
+ // Close it
+ //* NOISY-DEBUG: */ printf('[%s:%d]: Closing file %s ...' . PHP_EOL, __METHOD__, __LINE__, $fileObject->getPathName());
+ unset($fileObject);
}
+
+ // Trace message
+ //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
}
/**
$loaderInstance = self::getSelfInstance();
// "Cache" configuration instance and framework base path
- $configInstance = FrameworkBootstrap::getConfigurationInstance();
- $frameworkBasePath = $configInstance->getConfigEntry('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));
* Scans for application's classes, etc.
*
* @return void
+ * @throws UnexpectedValueException If a given path isn't one or not readable
*/
public static function scanApplicationClasses () {
// Get loader instance
//* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
$loaderInstance = self::getSelfInstance();
- // "Cache" configuration 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) {
// Create path name
//* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath);
- $pathName = realpath(sprintf(
+ $realPathName = realpath(sprintf(
'%s%s%s%s%s',
- $configInstance->getConfigEntry('application_base_path'),
+ $basePath,
DIRECTORY_SEPARATOR,
FrameworkBootstrap::getDetectedApplicationName(),
DIRECTORY_SEPARATOR,
));
// Is the path readable?
- //* NOISY-DEBUG: */ printf('[%s:%d]: pathName[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($pathName), $pathName);
- if (is_dir($pathName)) {
- // Try to load the application classes
- $loaderInstance->scanClassPath($pathName);
+ //* 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));
+ } elseif (!is_readable($realPathName)) {
+ // Not readable
+ throw new UnexpectedValueException(sprintf('realPathName=%s cannot be read from', $realPathName));
}
+
+ // 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
* Scans for test classes, etc.
*
* @return void
+ * @throws UnexpectedValueException If a given path isn't one or not readable
*/
public static function scanTestsClasses () {
- // "Cache" configuration instance
+ // Get loader instance
//* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
- $configInstance = FrameworkBootstrap::getConfigurationInstance();
+ $loaderInstance = self::getSelfInstance();
+
+ // "Cache" root base path
+ $basePath = self::$configInstance->getConfigEntry('root_base_path');
// Load all classes for the application
+ //* NOISY-DEBUG: */ printf('[%s:%d]: self::testPaths()=%d,basePath=%s' . PHP_EOL, __METHOD__, __LINE__, count(self::$testPaths), $basePath);
foreach (self::$testPaths as $shortPath) {
// Construct path name
//* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath);
- $pathName = sprintf(
+ $realPathName = realpath(sprintf(
'%s%s%s',
- $configInstance->getConfigEntry('root_base_path'),
+ $basePath,
DIRECTORY_SEPARATOR,
$shortPath
- );
-
- // Try to find it
- //* NOISY-DEBUG: */ printf('[%s:%d]: pathName[%s]=%s - BEFORE!' . PHP_EOL, __METHOD__, __LINE__, gettype($pathName), $pathName);
- $realPathName = realpath($pathName);
+ ));
// Is the path readable?
//* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[%s]=%s - AFTER!' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName), $realPathName);
- if ((is_dir($realPathName)) && (is_readable($realPathName))) {
- // Try to load the application classes
- ClassLoader::getSelfInstance()->scanClassPath($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));
+ } elseif (!is_readable($realPathName)) {
+ // Not readable
+ throw new UnexpectedValueException(sprintf('realPathName=%s cannot be read from', $realPathName));
}
+
+ // 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
}
// Get real path from it
- $fullQualifiedPath = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('root_base_path') . $relativePath;
+ $fullQualifiedPath = self::$configInstance->getConfigEntry('root_base_path') . $relativePath;
// Is it there?
//* NOISY-DEBUG: */ printf('[%s:%d]: fullQualifiedPath=%s' . PHP_EOL, __METHOD__, __LINE__, $fullQualifiedPath);
*/
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 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;
}
public function getPrintableIncludeList () {
// Prepare the list
$includeList = '';
- foreach ($this->loadedClasses as $classFile) {
+ foreach (array_keys($this->loadedClasses) as $classFile) {
$includeList .= basename($classFile) . '<br />' . PHP_EOL;
}
* 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
- //* NOISY-DEBUG: */ printf('[%s:%d] basePath2[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($basePath2), $basePath2);
- 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)
+ //* 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
+ //* 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();
* @return void
*/
private function initClassLoader () {
- // Construct the FQFN for the cache
- if (!self::$developerModeEnabled) {
- // Init cache instances
- $this->listCacheFile = new SplFileInfo(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . 'list-' . FrameworkBootstrap::getDetectedApplicationName() . '.cache');
- $this->classCacheFile = new SplFileInfo(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . 'class-' . FrameworkBootstrap::getDetectedApplicationName() . '.cache');
- }
-
// Set suffix and prefix from configuration
- $this->suffix = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('class_suffix');
- $this->prefix = FrameworkBootstrap::getConfigurationInstance()->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
+ //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled));
if (self::$developerModeEnabled) {
return;
}
+ // 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->listCacheFile)) {
// Load and convert it
// Mark the class cache as loaded
$this->classesCached = true;
}
+
+ // Trace message
+ //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
}
/**
$fileName = sprintf('%s%s%s', $this->prefix, $shortClassName, $this->suffix);
// Now look it up in our index
+ //* 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
$this->total++;
// Mark this class as loaded for other purposes than loading it.
+ //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s marked as loaded ...' . PHP_EOL, __METHOD__, __LINE__, $fileName);
$this->loadedClasses[$this->pendingFiles[$fileName]->getPathname()] = true;
- // 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]);
-
// Developer mode excludes caching (better debugging)
+ //* NOISY-DEBUG: */ printf('[%s:%d] self::developerModeEnabled=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled));
if (!self::$developerModeEnabled) {
- // Reset cache
- //* NOISY-DEBUG: */ printf('[%s:%d] classesCached=false' . PHP_EOL, __METHOD__, __LINE__);
+ // Reset cache and mark file for flushing
+ //* NOISY-DEBUG: */ printf('[%s:%d] Setting this->classesCached=false ...' . PHP_EOL, __METHOD__, __LINE__);
$this->classesCached = false;
+ $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: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);