3 namespace Org\Mxchange\CoreFramework\Loader;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
10 use \InvalidArgumentException;
11 use \RecursiveDirectoryIterator;
12 use \RecursiveIteratorIterator;
14 use \UnexpectedValueException;
17 * This class loads class include files with a specific prefix and suffix
19 * @author Roland Haeder <webmaster@shipsimu.org>
21 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
22 * @license GNU GPL 3.0 or any newer version
23 * @link http://www.shipsimu.org
25 * This program is free software: you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation, either version 3 of the License, or
28 * (at your option) any later version.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with this program. If not, see <http://www.gnu.org/licenses/>.
38 * ----------------------------------
40 * - "Cached" more like config instance and root/application base path for
41 * shorter call stacks and lesser methods invoked
42 * - More debug logging
44 * - This class loader is now 100% singleton, no other instance is really
45 * required, therefore the factory method can be removed safely
46 * - renamed initLoader() to initClassLoader()
47 * - An instance of a FrameworkConfiguration is no longer set here as this
48 * violated the rule that there shall be no instance set of that class
49 * - .htaccess is marked as deprecated as this should be no longer done
50 * - scanClassPath() is now protected, please use other scan*() methods
52 * - Namespace scheme Project\Package[\SubPackage...] is fully supported and
53 * throws an InvalidArgumentException if not present. The last part will be
54 * always the class' name.
56 * - Some comments improved, other minor improvements
58 * - Constructor is now empty and factory method 'createClassLoader' is created
59 * - renamed loadClasses to scanClassPath
60 * - Added initLoader()
62 * - ClassLoader rewritten to PHP SPL's own RecursiveIteratorIterator class
64 * - loadClasses rewritten to fix some notices
67 * ----------------------------------
69 final class ClassLoader {
71 * Instance of this class
73 private static $selfInstance = NULL;
76 * Instance of a FrameworkConfiguration class
78 private static $configInstance = NULL;
81 * Cached configuration entry 'is_developer_mode_enabled'
83 private static $developerModeEnabled = NULL;
86 * Array with all valid but pending for loading file names (class,
87 * interfaces, traits must start with below prefix).
89 private $pendingFiles = [];
92 * List of loaded classes
94 private $loadedClasses = [];
97 * Suffix with extension for all class files
99 private $prefix = 'class_';
102 * Suffix with extension for all class files
104 private $suffix = '.php';
107 * A list for directory names (no leading/trailing slashes!) which not be scanned by the path scanner
110 private $ignoreList = [];
113 * Debug this class loader? (true = yes, false = no)
115 private $debug = false;
118 * Whether the file list is cached
120 private $listCached = false;
123 * Wethe class content has been cached
125 private $classesCached = false;
128 * SplFileInfo for the list cache
130 private $listCacheFile = NULL;
133 * SplFileInfo for class content
135 private $classCacheFile = NULL;
138 * Counter for loaded include files
143 * By default the class loader is strict with naming-convention check
145 private static $strictNamingConvention = true;
148 * Framework/application paths for classes, etc.
150 private static $frameworkPaths = [
151 'classes', // Classes
152 'exceptions', // Exceptions
153 'interfaces', // Interfaces
154 'middleware', // The middleware
159 * Registered paths where test classes can be found. These are all relative
162 private static $testPaths = [];
165 * Cached includes that needs to be flushed
167 private $flushCache = [];
170 * The protected constructor. Please use the factory method below, or use
171 * getSelfInstance() for singleton
175 private function __construct () {
176 // Is developerModeEnabled set?
177 //* NOISY-DEBUG: */ printf('[%s:%d]: CONSTRUCTED!' . PHP_EOL, __METHOD__, __LINE__);
178 if (is_null(self::$developerModeEnabled)) {
179 // Cache config instance
180 self::$configInstance = FrameworkBootstrap::getConfigurationInstance();
182 // Cache config entry
183 self::$developerModeEnabled = self::$configInstance->isEnabled('developer_mode');
187 //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d - EXIT!' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled));
191 * The destructor makes it sure all caches got flushed
195 public function __destruct () {
196 // Skip here if dev-mode
197 //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d - DESTRUCTED!' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled));
198 if (self::$developerModeEnabled) {
199 // Is enabled, don't cache
200 //* NOISY-DEBUG: */ printf('[%s:%d]: Developer mode enabled, not caching classes - EXIT!' . PHP_EOL, __METHOD__, __LINE__);
207 // Skip here if already cached
208 //* NOISY-DEBUG: */ printf('[%s:%d]: this->listCached=%d' . PHP_EOL, __METHOD__, __LINE__, intval($this->listCached));
209 if ($this->listCached === false) {
210 // Writes the cache file of our list away
211 $cacheContent = json_encode($this->pendingFiles);
213 // Open cache instance
214 //* NOISY-DEBUG: */ printf('[%s:%d]: cacheContent()=%d' . PHP_EOL, __METHOD__, __LINE__, strlen($cacheContent));
215 $fileObject = $this->listCacheFile->openFile('w');
217 // And write whole list
218 $fileObject->fwrite($cacheContent);
221 //* NOISY-DEBUG: */ printf('[%s:%d]: Closing file %s ...' . PHP_EOL, __METHOD__, __LINE__, $fileObject->getPathName());
228 // Skip here if already cached
229 //* NOISY-DEBUG: */ printf('[%s:%d]: this->classesCached=%d' . PHP_EOL, __METHOD__, __LINE__, intval($this->classesCached));
230 if ($this->classesCached === false) {
231 // Generate a full-cache of all classes
232 //* NOISY-DEBUG: */ printf('[%s:%d]: this->flushCache()=%d' . PHP_EOL, __METHOD__, __LINE__, count($this->flushCache));
233 foreach ($this->flushCache as $key => $fileInstance) {
235 //* NOISY-DEBUG: */ printf('[%s:%d]: key=%s,fileInstance[]=%s' . PHP_EOL, __METHOD__, __LINE__, $key, gettype($fileInstance));
236 $fileObject = $fileInstance->openFile('r');
239 // @TODO Add some uglifying code (compress) here
240 //* NOISY-DEBUG: */ printf('[%s:%d]: Adding fileInstance->size=%d bytes ...' . PHP_EOL, __METHOD__, __LINE__, $fileInstance->getSize());
241 $cacheContent .= $fileObject->fread($fileInstance->getSize());
245 //* NOISY-DEBUG: */ printf('[%s:%d]: cacheContent()=%d' . PHP_EOL, __METHOD__, __LINE__, strlen($cacheContent));
246 $fileObject = $this->classCacheFile->openFile('w');
249 $fileObject->fwrite($cacheContent);
252 //* NOISY-DEBUG: */ printf('[%s:%d]: Closing file %s ...' . PHP_EOL, __METHOD__, __LINE__, $fileObject->getPathName());
257 //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
261 * Scans for all framework classes, exceptions and interfaces.
265 public static function scanFrameworkClasses () {
266 // Get loader instance
267 //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
268 $loaderInstance = self::getSelfInstance();
270 // "Cache" configuration instance and framework base path
271 $frameworkBasePath = self::$configInstance->getConfigEntry('framework_base_path');
274 //* NOISY-DEBUG: */ printf('[%s:%d]: frameworkBasePath=%s,self::$frameworkPaths()=%d,' . PHP_EOL, __METHOD__, __LINE__, $frameworkBasePath, count(self::$frameworkPaths));
275 foreach (self::$frameworkPaths as $shortPath) {
276 // Generate full path from it
277 //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($shortPath), $shortPath);
278 $realPathName = realpath(sprintf(
286 // Is it not false and accessible?
287 //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName), $realPathName);
288 if (is_bool($realPathName)) {
289 // Skip this, it is not accessible
291 } elseif (!is_readable($realPathName)) {
292 // @TODO Throw exception instead of break
296 // Try to load the framework classes
297 $loaderInstance->scanClassPath($realPathName);
301 //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
305 * Scans for application's classes, etc.
308 * @throws UnexpectedValueException If a given path isn't one or not readable
310 public static function scanApplicationClasses () {
311 // Get loader instance
312 //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
313 $loaderInstance = self::getSelfInstance();
315 // "Cache" application base path
316 $basePath = self::$configInstance->getConfigEntry('application_base_path');
318 // Load all classes for the application
319 //* NOISY-DEBUG: */ printf('[%s:%d]: self::frameworkPaths()=%d,basePath=%s' . PHP_EOL, __METHOD__, __LINE__, count(self::$frameworkPaths), $basePath);
320 foreach (self::$frameworkPaths as $shortPath) {
322 //* NOISY-DEBUG: */ printf('[%s:%d]: shortPath=%s' . PHP_EOL, __METHOD__, __LINE__, $shortPath);
323 $realPathName = realpath(sprintf(
327 FrameworkBootstrap::getDetectedApplicationName(),
332 // Is the path readable?
333 //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[%s]=%s - AFTER!' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName), $realPathName);
334 if (!is_string($realPathName)) {
336 //* NOISY-DEBUG: */ printf('[%s:%d]: realPathName[]=%s - SKIPPED!' . PHP_EOL, __METHOD__, __LINE__, gettype($realPathName));
338 } elseif (!is_dir($realPathName)) {
339 // Is not a directory
340 throw new UnexpectedValueException(sprintf('realPathName=%s is not a directory', $realPathName), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
341 } elseif (!is_readable($realPathName)) {
343 throw new UnexpectedValueException(sprintf('realPathName=%s cannot be read from', $realPathName), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
346 // Try to load the application classes
347 //* NOISY-DEBUG: */ printf('[%s:%d]: Scanning for classes/interfaces at realPathName=%s ...' . PHP_EOL, __METHOD__, __LINE__, $realPathName);
348 $loaderInstance->scanClassPath($realPathName);
352 //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
356 * Enables or disables strict naming-convention tests on class loading
358 * @param $strictNamingConvention Whether to strictly check naming-convention
361 public static function enableStrictNamingConventionCheck (bool $strictNamingConvention = true) {
362 self::$strictNamingConvention = $strictNamingConvention;
368 * @param $className Name of the class to load
370 * @throws InvalidArgumentException If the class' name does not contain a namespace: Tld\Domain\Project is AT LEAST recommended!
372 public static function autoLoad (string $className) {
373 // Validate parameter
374 //* NOISY-DEBUG: */ printf('[%s:%d] className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className);
375 if (empty($className)) {
376 // Should not be empty
377 throw new InvalidArgumentException('Parameter "className" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
380 // The class name MUST be at least Tld\Domain\Project\Package\SomeFooBar so split at them
381 $classNameParts = explode("\\", $className);
383 // At least 3 parts should be there
384 //* NOISY-DEBUG: */ printf('[%s:%d]: self::strictNamingConvention=%d,classNameParts()=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$strictNamingConvention), count($classNameParts));
385 if ((self::$strictNamingConvention === true) && (count($classNameParts) < 5)) {
386 // Namespace scheme is: Tld\Domain\Project\Package[\SubPackage...]
387 throw new InvalidArgumentException(sprintf('Class name "%s" is not conform to naming-convention: Tld\Domain\Project\Package[\SubPackage...]\SomeFooBar', $className));
390 // Try to include this class
391 //* NOISY-DEBUG: */ printf('[%s:%d]: Invoking self->loadClassFile(%s) ...' . PHP_EOL, __METHOD__, __LINE__, $className);
392 self::getSelfInstance()->loadClassFile($className);
395 //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
399 * Singleton getter for an instance of this class
401 * @return $selfInstance A singleton instance of this class
403 public static final function getSelfInstance () {
404 // Is the instance there?
405 //* NOISY-DEBUG: */ printf('[%s:%d]: self::selfInstance[]=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, gettype(self::$selfInstance));
406 if (is_null(self::$selfInstance)) {
407 // Get a new one and initialize it
408 //* NOISY-DEBUG: */ printf('[%s:%d]: Initializing class loader ...' . PHP_EOL, __METHOD__, __LINE__);
409 self::$selfInstance = new ClassLoader();
410 self::$selfInstance->initClassLoader();
413 // Return the instance
414 //* NOISY-DEBUG: */ printf('[%s:%d]: self::selfInstance=%s - EXIT!' . PHP_EOL, __METHOD__, __LINE__, get_class(self::$selfInstance));
415 return self::$selfInstance;
419 * Getter for total include counter
421 * @return $total Total loaded include files
423 public final function getTotal () {
428 * Getter for a printable list of included main/interfaces/exceptions
430 * @param $includeList A printable include list
432 public function getPrintableIncludeList () {
435 foreach (array_keys($this->loadedClasses) as $classFile) {
436 $includeList .= basename($classFile) . '<br />' . PHP_EOL;
444 * Scans recursively a local path for class files which must have a prefix and a suffix as given by $this->suffix and $this->prefix
446 * @param $basePath The relative base path to 'framework_base_path' constant for all classes
447 * @param $ignoreList An optional list (array forced) of directory and file names which shall be ignored
449 * @throws InvalidArgumentException If a parameter is invalid
451 protected function scanClassPath (string $basePath, array $ignoreList = [] ) {
452 // Is a list has been restored from cache, don't read it again
453 //* NOISY-DEBUG: */ printf('[%s:%d] basePath=%s,ignoreList()=%d - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $basePath, count($ignoreList));
454 if (empty($basePath)) {
456 throw new InvalidArgumentException('Parameter "basePath" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
457 } elseif ($this->listCached === true) {
459 //* NOISY-DEBUG: */ printf('[%s:%d] this->listCache=true - EXIT!' . PHP_EOL, __METHOD__, __LINE__);
463 // Keep it in class for later usage, but flip index<->value
464 $this->ignoreList = array_flip($ignoreList);
467 * Set base directory which holds all our classes, an absolute path
468 * should be used here so is_dir(), is_file() and so on will always
469 * find the correct files and dirs.
471 $basePath = realpath($basePath);
473 // If the basePath is false it is invalid
474 //* NOISY-DEBUG: */ printf('[%s:%d] basePath[%s]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype($basePath), $basePath);
475 if (!is_string($basePath)) {
476 /* @TODO: Do not exit here. */
477 exit(__METHOD__ . ': Cannot read ' . $basePath . ' !' . PHP_EOL);
480 // Get a new iterator
481 //* NOISY-DEBUG: */ printf('[%s:%d] basePath=%s' . PHP_EOL, __METHOD__, __LINE__, $basePath);
482 $iteratorInstance = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basePath), RecursiveIteratorIterator::CHILD_FIRST);
485 while ($iteratorInstance->valid()) {
487 $currentEntry = $iteratorInstance->current();
489 // Get filename from iterator which is the class' name (according naming-convention)
490 //* NOISY-DEBUG: */ printf('[%s:%d] currentEntry=%s,currentEntry->size=%d' . PHP_EOL, __METHOD__, __LINE__, $currentEntry->__toString(), $currentEntry->getSize());
491 $fileName = $currentEntry->getFilename();
493 // Current entry must be a file, not smaller than 100 bytes and not on ignore list
494 //* NOISY-DEBUG: */ printf('[%s:%d] fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
495 if (!$currentEntry->isFile() || isset($this->ignoreList[$fileName]) || $currentEntry->getSize() < 100) {
496 // Advance to next entry
497 $iteratorInstance->next();
499 // Skip non-file entries
500 //* NOISY-DEBUG: */ printf('[%s:%d] SKIP: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
504 // Is this file wanted?
505 //* NOISY-DEBUG: */ printf('[%s:%d] FOUND: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
506 if ((substr($fileName, 0, strlen($this->prefix)) == $this->prefix) && (substr($fileName, -strlen($this->suffix), strlen($this->suffix)) == $this->suffix)) {
507 // Add it to the list
508 //* NOISY-DEBUG: */ printf('[%s:%d] ADD: fileName=%s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry);
509 $this->pendingFiles[$fileName] = $currentEntry;
512 //* NOISY-DEBUG: */ printf('[%s:%d] NOT ADDED: fileName=%s,currentEntry=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName, $currentEntry);
515 // Advance to next entry
516 //* NOISY-DEBUG: */ printf('[%s:%d] NEXT: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
517 $iteratorInstance->next();
521 //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
525 * Initializes our loader class
529 private function initClassLoader () {
530 // Set suffix and prefix from configuration
531 //* NOISY-DEBUG: */ printf('[%s:%d]: CALLED!' . PHP_EOL, __METHOD__, __LINE__);
532 $this->suffix = self::$configInstance->getConfigEntry('class_suffix');
533 $this->prefix = self::$configInstance->getConfigEntry('class_prefix');
536 //* NOISY-DEBUG: */ printf('[%s:%d]: this->suffix=%s,this->prefix=%s' . PHP_EOL, __METHOD__, __LINE__, $this->suffix, $this->prefix);
537 self::$selfInstance = $this;
539 // Skip here if no dev-mode
540 //* NOISY-DEBUG: */ printf('[%s:%d]: self::developerModeEnabled=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled));
541 if (self::$developerModeEnabled) {
542 // Developer mode is enabled
543 //* NOISY-DEBUG: */ printf('[%s:%d]: Developer mode is enabled - EXIT!' . PHP_EOL, __METHOD__, __LINE__);
547 // Init cache instances
548 //* NOISY-DEBUG: */ printf('[%s:%d]: Initializing cache file instances ...' . PHP_EOL, __METHOD__, __LINE__);
549 $this->listCacheFile = new SplFileInfo(self::$configInstance->getConfigEntry('local_database_path') . 'list-' . FrameworkBootstrap::getDetectedApplicationName() . '.cache');
550 $this->classCacheFile = new SplFileInfo(self::$configInstance->getConfigEntry('local_database_path') . 'class-' . FrameworkBootstrap::getDetectedApplicationName() . '.cache');
552 // Is the cache there?
553 //* NOISY-DEBUG: */ printf('[%s:%d]: Checking this->listCacheFile=%s ...' . PHP_EOL, __METHOD__, __LINE__, $this->listCacheFile);
554 if (FrameworkBootstrap::isReadableFile($this->listCacheFile)) {
555 // Load and convert it
556 //* NOISY-DEBUG: */ printf('[%s:%d]: Loading %s ...' . PHP_EOL, __METHOD__, __LINE__, $this->listCacheFile);
557 $this->pendingFiles = json_decode(file_get_contents($this->listCacheFile->getPathname()));
559 // List has been restored from cache!
560 $this->listCached = true;
563 // Does the class cache exist?
564 //* NOISY-DEBUG: */ printf('[%s:%d]: Checking this->classCacheFile=%s ...' . PHP_EOL, __METHOD__, __LINE__, $this->classCacheFile);
565 if (FrameworkBootstrap::isReadableFile($this->classCacheFile)) {
567 //* NOISY-DEBUG: */ printf('[%s:%d]: Invoking FrameworkBootstrap::loadInclude(%s) ...' . PHP_EOL, __METHOD__, __LINE__, $this->classCacheFile);
568 FrameworkBootstrap::loadInclude($this->classCacheFile);
570 // Mark the class cache as loaded
571 $this->classesCached = true;
575 //* NOISY-DEBUG: */ printf('[%s:%d]: EXIT!' . PHP_EOL, __METHOD__, __LINE__);
579 * Tries to find the given class in our list. It will ignore already loaded
580 * (to the program available) class/interface/trait files. But you SHOULD
581 * save below "expensive" code by pre-checking this->loadedClasses[], if
584 * @param $className The class that shall be loaded
587 private function loadClassFile (string $className) {
588 // The class name should contain at least 2 back-slashes, so split at them
589 //* NOISY-DEBUG: */ printf('[%s:%d]: className=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className);
590 $classNameParts = explode("\\", $className);
593 //* NOISY-DEBUG: */ printf('[%s:%d]: classNameParts()=%d' . PHP_EOL, __METHOD__, __LINE__, count($classNameParts));
594 $shortClassName = array_pop($classNameParts);
596 // Create a name with prefix and suffix
597 //* NOISY-DEBUG: */ printf('[%s:%d]: this->prefix=%s,shortClassName=%s,this->suffix=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $this->prefix, $shortClassName, $this->suffix);
598 $fileName = sprintf('%s%s%s', $this->prefix, $shortClassName, $this->suffix);
600 // Now look it up in our index
601 //* DEBUG-DIE: */ die(sprintf('[%s:%d]: this->pendingFiles=%s', __METHOD__, __LINE__, print_r($this->pendingFiles, TRUE)));
602 //* NOISY-DEBUG: */ printf('[%s:%d] ISSET: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
603 if ((isset($this->pendingFiles[$fileName])) && (!isset($this->loadedClasses[$this->pendingFiles[$fileName]->getPathname()]))) {
604 // File is found and not loaded so load it only once
605 //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s - START' . PHP_EOL, __METHOD__, __LINE__, $fileName);
606 FrameworkBootstrap::loadInclude($this->pendingFiles[$fileName]);
607 //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s - END' . PHP_EOL, __METHOD__, __LINE__, $fileName);
609 // Count this loaded class/interface/exception
612 // Mark this class as loaded for other purposes than loading it.
613 //* NOISY-DEBUG: */ printf('[%s:%d] LOAD: fileName=%s marked as loaded ...' . PHP_EOL, __METHOD__, __LINE__, $fileName);
614 $this->loadedClasses[$this->pendingFiles[$fileName]->getPathname()] = true;
616 // Developer mode excludes caching (better debugging)
617 //* NOISY-DEBUG: */ printf('[%s:%d] self::developerModeEnabled=%d' . PHP_EOL, __METHOD__, __LINE__, intval(self::$developerModeEnabled));
618 if (!self::$developerModeEnabled) {
619 // Reset cache and mark file for flushing
620 //* NOISY-DEBUG: */ printf('[%s:%d] Setting this->classesCached=false ...' . PHP_EOL, __METHOD__, __LINE__);
621 $this->classesCached = false;
622 $this->flushCache[$fileName] = $this->pendingFiles[$fileName];
625 // Remove it from classes list so it won't be found twice.
626 //* NOISY-DEBUG: */ printf('[%s:%d] UNSET: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
627 unset($this->pendingFiles[$fileName]);
630 //* NOISY-DEBUG: */ printf('[%s:%d] 404: fileName=%s' . PHP_EOL, __METHOD__, __LINE__, $fileName);
634 //* NOISY-DEBUG: */ printf('[%s:%d] EXIT!' . PHP_EOL, __METHOD__, __LINE__);