From b0f3b484908ae438464e83e5098be815acf571c6 Mon Sep 17 00:00:00 2001 From: Belle Aerni Date: Wed, 7 Jun 2023 23:44:00 -0700 Subject: [PATCH] Minor improvements, bump to next release (#3) * Code improvements + prepend autoloader option * Updates to the readme, updated benchmark info * Remove the need to have PHPStan ignore a few things * Fix readme --- README.md | 12 +++--- composer.lock | 46 +++++++++++------------ src/AntLoader.php | 87 ++++++++++++++++++++++---------------------- tests/LoaderTest.php | 8 +++- tests/Pest.php | 21 ++--------- 5 files changed, 84 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index e2462ef..b94d615 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ $loader = new AntCMS\AntLoader(); // Create AntLoader with it's default options. $loader->addNamespace('', 'somepath', 'psr0'); //Add a path for a PSR-0 autoloader, by providing an empty string it'll search for all classes in this path. $loader->addNamespace('Example\\Class\\', 'someotherpath'); //Add a path for a PSR-4 autoloader, which will only search in that directory for the "Example\Class" namespace. $loader->checkClassMap(); // Create a new classmap if it doesn't already exist. If it does, load it now. -$loader->register(); // Register the autoloader within PHP. +$loader->register(); // Register the autoloader within PHP. Optionally pass 'true' to this to prepend AntLoader to the start of the autoloader list. PHP will then use AntLoader first when attempting to load classes. $loader->resetClassMap(); // Reset the classmap, clearing the existing one out from whatever is the current caching method. Will not regenerate one automatically. ``` @@ -80,10 +80,12 @@ Here are some details about the filesystem caching method: ## Notes ### Performance -While it's not strictly necessary to use the classmap functionality, we strongly recommend doing so for optimal performance. In our tests, we found that using the classmap resulted in significant speed improvements: - -- Software RAID 0 SSD Array: 85% faster, reducing the time it took to instance 1000 random classes from 0.0691 seconds to 0.01 seconds. -- Standard HDD: 91% faster, reducing the time it took to instance 1000 random classes from 0.0796 seconds to 0.0072 seconds. + - While it's not strictly necessary to use the classmap functionality, we strongly recommend doing so for optimal performance. + - Testing shows that it reduces the time to find and load `1000` random classes by `95%` (from `0.0699` seconds to `0.0037` seconds) + - Depending on the setup, prepending AntLoader may speed up the performance of your application. + - For example, if you are using composer's autoloader and have a lot of composer packages, that may delay the time it takes to load classes within your application. + - In this example, the classes inside of your application will load slightly faster and classes loaded through composer will be slightly slower. + - Potential improvements are highly dependent on the specific application and enviroment. In most situations, the difference is likely to be very minimal. So, we encourage you to take advantage of the classmap feature to get the best performance out of your application. diff --git a/composer.lock b/composer.lock index 86f649f..8df7798 100644 --- a/composer.lock +++ b/composer.lock @@ -415,16 +415,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -465,9 +465,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "nunomaduro/collision", @@ -835,16 +835,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.14", + "version": "1.10.18", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "d232901b09e67538e5c86a724be841bea5768a7c" + "reference": "52b6416c579663eebdd2f1d97df21971daf3b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c", - "reference": "d232901b09e67538e5c86a724be841bea5768a7c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/52b6416c579663eebdd2f1d97df21971daf3b43f", + "reference": "52b6416c579663eebdd2f1d97df21971daf3b43f", "shasum": "" }, "require": { @@ -893,7 +893,7 @@ "type": "tidelift" } ], - "time": "2023-04-19T13:47:27+00:00" + "time": "2023-06-07T22:00:43+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1215,16 +1215,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.7", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -1298,7 +1298,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -1314,7 +1314,7 @@ "type": "tidelift" } ], - "time": "2023-04-14T08:58:40+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "psr/container", @@ -1719,16 +1719,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1773,7 +1773,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { @@ -1781,7 +1781,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", diff --git a/src/AntLoader.php b/src/AntLoader.php index 312508a..4ece12f 100644 --- a/src/AntLoader.php +++ b/src/AntLoader.php @@ -31,7 +31,7 @@ class AntLoader /** * Creates a new instance of AntLoader. * - * @param array $config (optional) Configuration options for AntLoader. + * @param array{mode?:string,path?:string,key?:string,ttl?:int,stopIfNotFound?:bool} $config (optional) Configuration options for AntLoader. * Available keys: * - 'mode': What mode to use for storing the classmap. Can be 'auto', 'filesystem', 'apcu', or 'none'. * - 'path': Where to save the classmap to. By default, this will be saved to a random temp file. @@ -54,15 +54,15 @@ public function __construct(array $config = []) $config = array_merge($defaultConfig, $config); if (empty($config['key'])) { - $generatedID = 'AntLoader_' . hash('md5', __DIR__); + $generatedID = 'AntLoader_' . hash('md5', __FILE__); } else { - $generatedID = strval($config['key']); // @phpstan-ignore-line + $generatedID = $config['key']; } if (empty($config['path'])) { $this->classMapPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $generatedID; } else { - $this->classMapPath = strval($config['path']); // @phpstan-ignore-line + $this->classMapPath = $config['path']; } $cacheOptions = [ @@ -81,7 +81,6 @@ public function __construct(array $config = []) ] ]; - // @phpstan-ignore-next-line if (array_key_exists($config['mode'], $cacheOptions)) { $this->cacheType = intval($cacheOptions[$config['mode']]['type']); $this->cacheKey = strval($cacheOptions[$config['mode']]['key'] ?? ''); @@ -89,7 +88,7 @@ public function __construct(array $config = []) throw new \Exception("Unsupported cache mode. Please ensure you are specifying 'auto', 'filesystem', 'apcu', or 'none'."); } - $this->cacheTtl = intval($config['ttl']); // @phpstan-ignore-line + $this->cacheTtl = $config['ttl']; $this->stopIfNotFound = (bool) $config['stopIfNotFound']; } @@ -99,33 +98,32 @@ public function __construct(array $config = []) */ public function checkClassMap(): void { - if ($this->cacheType === self::noCache) { - return; - } - - if ($this->cacheType === self::fileCache) { - // If the classmap doesn't yet exist, generate a new one now. - if (!file_exists($this->classMapPath)) { - $classMap = $this->generateMap(); - $this->classMap = $classMap->getMap(); - $this->saveMap(); + switch ($this->cacheType) { + case self::noCache: return; - } - - // Otherwise, load the existing one. - $this->classMap = include $this->classMapPath; - } else { - if (apcu_exists($this->cacheKey)) { - $map = apcu_fetch($this->cacheKey); - if (is_array($map)) { - $this->classMap = $map; + case self::fileCache: + // If the classmap doesn't yet exist, generate a new one now. + if (!file_exists($this->classMapPath)) { + $classMap = $this->generateMap(); + $this->classMap = $classMap->getMap(); + $this->saveMap(); + } else { + // Otherwise, load the existing one. + $this->classMap = include $this->classMapPath; + } + return; + case self::apcuCache: + if (apcu_exists($this->cacheKey)) { + $map = apcu_fetch($this->cacheKey); + if (is_array($map)) { + $this->classMap = $map; + } + } else { + $classMap = $this->generateMap(); + $this->classMap = $classMap->getMap(); + $this->saveMap(); } - } else { - $classMap = $this->generateMap(); - $this->classMap = $classMap->getMap(); - $this->saveMap(); return; - } } } @@ -142,15 +140,18 @@ public function resetClassMap(): void @unlink($this->classMapPath); break; } + $this->classMap = []; } /** * Registers the autoloader. + * @param bool $prepend (optional) Set to true to cause the autoloader to be added to the start of the PHP autoloader list. + * This will make AntLoader take priority over other autoloaders. */ - public function register(): void + public function register(bool $prepend = false): void { - spl_autoload_register(array($this, 'autoload')); + spl_autoload_register(array($this, 'autoload'), true, $prepend); } /** @@ -203,7 +204,6 @@ public function autoload(string $class): void return; } - // If this configuration option is set to 'true', AntLoader will stop searching for a class after checking the classmap. if ($this->stopIfNotFound) { return; } @@ -275,16 +275,17 @@ private function getFile(string $class, string $path, string $namespace): string */ private function saveMap(): void { - if ($this->cacheType === self::noCache) { - return; - } - - if ($this->cacheType === self::fileCache) { - $output = 'classMap, true) . ';'; - @file_put_contents($this->classMapPath, $output); - } else { - apcu_store($this->cacheKey, $this->classMap, $this->cacheTtl); + switch ($this->cacheType) { + case self::noCache: + return; + case self::fileCache: + $output = 'classMap, true) . ';'; + @file_put_contents($this->classMapPath, $output); + return; + case self::fileCache: + apcu_store($this->cacheKey, $this->classMap, $this->cacheTtl); + return; } } diff --git a/tests/LoaderTest.php b/tests/LoaderTest.php index dc96b43..6dd49d2 100644 --- a/tests/LoaderTest.php +++ b/tests/LoaderTest.php @@ -29,7 +29,7 @@ $loader = setupLoader(); // Test class loading without class map - removeClassMap(); + $loader->resetClassMap(); $start = microtime(true); foreach ($classes as $class) { expect(class_exists($class))->toBeTrue(); @@ -44,7 +44,11 @@ // Test class loading with class map deleteRandomClasses(); $classes = createRandomClasses(1000); + $loader = setupLoader(true); + $loader->resetClassMap(); // Ensure we don't have an old class map + $loader->checkClassMap(); + $start = microtime(true); foreach ($classes as $class) { expect(class_exists($class))->toBeTrue(); @@ -56,7 +60,7 @@ $withMap = $totalTime / 10; // Clean up and output result - removeClassMap(); + $loader->resetClassMap(); deleteRandomClasses(); expect($withMap)->toBeLessThan($withoutMap); }); diff --git a/tests/Pest.php b/tests/Pest.php index 29bf611..16d7b80 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -3,32 +3,19 @@ function setupLoader(bool $cache = false) { $pathClasses = __DIR__ . DIRECTORY_SEPARATOR . 'Classes' . DIRECTORY_SEPARATOR; - if ($cache) { - $config = [ - 'mode' => 'auto', - ]; - } else { - $config = [ - 'mode' => 'none', - ]; - } + $config = [ + 'mode' => $cache ? 'auto' :'none', + ]; $loader = new AntCMS\AntLoader($config); $loader->addNamespace('', $pathClasses . 'PSR0', 'psr0'); $loader->addNamespace('', $pathClasses . 'PSR4'); $loader->addNamespace('', $pathClasses . 'Random'); $loader->checkClassMap(); - $loader->register(); + $loader->register(true); return $loader; } -function removeClassMap() -{ - $loader = new AntCMS\AntLoader(); - $loader->resetClassMap(); - $loader->unRegister(); -} - /** * Generates any number of random classes. Default is 250 classes, which is probably more than most applications will see. But this is a test to just show that the class map is infact optimizing things.