* - Initial release
* ----------------------------------
*/
-class ClassLoader {
+final class ClassLoader {
/**
* Instance of this class
*/
private static $selfInstance = NULL;
/**
- * Array with all found classes
+ * Array with all valid but pending for loading file names (class,
+ * interfaces, traits must start with below prefix).
*/
- private $foundClasses = [];
+ private $pendingFiles = [];
/**
* List of loaded classes
// Skip here if already cached
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
$fileObject = $this->listCacheFile->openFile('w');
//* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
$loaderInstance = self::getSelfInstance();
- // "Cache" configuration instance
+ // "Cache" configuration instance and framework base path
$configInstance = FrameworkBootstrap::getConfigurationInstance();
+ $frameworkBasePath = $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) {
// Generate full path from it
- //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath);
+ //* 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
));
// Is it not false and accessible?
- //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName=%s' . PHP_EOL, __METHOD__, __LINE__, $realPathName);
+ //* 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
* @return void
*/
public static function scanTestsClasses () {
- // Trace message
- //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
-
// "Cache" configuration instance
+ //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
$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
+ //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath);
$pathName = sprintf(
'%s%s%s',
$configInstance->getConfigEntry('root_base_path'),
* @throws InvalidArgumentException If the class' name does not contain a namespace: Tld\Domain\Project is AT LEAST recommended!
*/
public static function autoLoad (string $className) {
- // The class name should contain at least 2 back-slashes, so split at them
+ // 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');
+ }
+
+ // 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
if ((self::$strictNamingConvention === true) && (count($classNameParts) < 5)) {
- // Namespace scheme is: Project\Package[\SubPackage...]
+ // 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]: Calling self->loadClassFile(%s) ...' . PHP_EOL, __METHOD__, __LINE__, $className);
self::getSelfInstance()->loadClassFile($className);
// Trace message
*/
protected function scanClassPath (string $basePath, array $ignoreList = [] ) {
// Is a list has been restored from cache, don't read it again
+ /* NOISY-DEBUG: */ printf('[%s:%d] basePath=%s,ignoreList()=%d - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $basePath, count($ignoreList));
if ($this->listCached === true) {
// Abort here
+ /* NOISY-DEBUG: */ printf('[%s:%d] this->listCache=true - EXIT!' . PHP_EOL, __METHOD__, __LINE__);
return;
}
// Keep it in class for later usage
$this->ignoreList = $ignoreList;
- /*
- * Ignore .htaccess by default as it is for protection of directories
- * on Apache servers.
- *
- * @deprecated
- */
- array_push($ignoreList, '.htaccess');
-
/*
* 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
$basePath2 = 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) {
/* @TODO: Do not exit here. */
exit(__METHOD__ . ': Cannot read ' . $basePath . ' !' . PHP_EOL);
$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;
}
// 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();
}
}
// Is the cache there?
if (FrameworkBootstrap::isReadableFile($this->listCacheFile)) {
// Load and convert it
- $this->foundClasses = json_decode(file_get_contents($this->listCacheFile->getPathname()));
+ $this->pendingFiles = json_decode(file_get_contents($this->listCacheFile->getPathname()));
// List has been restored from cache!
$this->listCached = true;
}
/**
- * 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
$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()]))) {
+ //* 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;
+ $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: %s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
- unset($this->foundClasses[$fileName]);
+ //* NOISY-DEBUG: */ printf('[%s:%d] UNSET: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
+ unset($this->pendingFiles[$fileName]);
// Developer mode excludes caching (better debugging)
if (!FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('developer_mode_enabled')) {
}
} 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__);
}
}