From 8f8461659416f096c1add3e9b5277b5bd0332720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Tue, 21 Dec 2021 14:43:15 +0100 Subject: [PATCH 1/2] bugfix: allow the class loader to be absent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With composer 2.2.0, some autoloaders are loaded even before the `vendor/autoload.php` file is dumped. This leads to installation crashes in some circumstances due to the fact that this component throws a `RuntimeException` in case the autoloader could not be found. Due to the fact, that this works properly after the autoloader got dumped, we can safely not register the prepend/append autoloader registration for this case. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- psalm-baseline.xml | 8 -------- src/Autoloader.php | 39 +++++++++++++++++++++++++++++---------- test/AutoloaderTest.php | 24 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 4a186e5..c054410 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -31,18 +31,10 @@ $loaded[$class] - - ClassLoader - $namespaces[$check] $namespaces[$check] - - include __DIR__ . '/../../../autoload.php' - include __DIR__ . '/../vendor/autoload.php' - include getenv('COMPOSER_VENDOR_DIR') . '/autoload.php' - getenv('COMPOSER_VENDOR_DIR') getenv('COMPOSER_VENDOR_DIR') diff --git a/src/Autoloader.php b/src/Autoloader.php index 261bb88..629e07f 100644 --- a/src/Autoloader.php +++ b/src/Autoloader.php @@ -41,10 +41,15 @@ class Autoloader public static function load() { $loaded = new ArrayObject([]); + $classLoader = self::getClassLoader(); + + if ($classLoader === null) { + return; + } spl_autoload_register(self::createPrependAutoloader( RewriteRules::namespaceReverse(), - self::getClassLoader(), + $classLoader, $loaded ), true, true); @@ -54,25 +59,39 @@ public static function load() )); } - /** - * @return ClassLoader - * @throws RuntimeException - */ - private static function getClassLoader() + private static function getClassLoader(): ?ClassLoader { if (getenv('COMPOSER_VENDOR_DIR') && file_exists(getenv('COMPOSER_VENDOR_DIR') . '/autoload.php')) { - return include getenv('COMPOSER_VENDOR_DIR') . '/autoload.php'; + /** @psalm-suppress MixedAssignment */ + $loader = include getenv('COMPOSER_VENDOR_DIR') . '/autoload.php'; + if (!$loader instanceof ClassLoader) { + return null; + } + + return $loader; } if (file_exists(__DIR__ . '/../../../autoload.php')) { - return include __DIR__ . '/../../../autoload.php'; + /** @psalm-suppress MixedAssignment */ + $loader = include __DIR__ . '/../../../autoload.php'; + if (!$loader instanceof ClassLoader) { + return null; + } + + return $loader; } if (file_exists(__DIR__ . '/../vendor/autoload.php')) { - return include __DIR__ . '/../vendor/autoload.php'; + /** @psalm-suppress MixedAssignment */ + $loader = include __DIR__ . '/../vendor/autoload.php'; + if (!$loader instanceof ClassLoader) { + return null; + } + + return $loader; } - throw new RuntimeException('Cannot detect composer autoload. Please run composer install'); + return null; } /** diff --git a/test/AutoloaderTest.php b/test/AutoloaderTest.php index 8292541..ae247d8 100644 --- a/test/AutoloaderTest.php +++ b/test/AutoloaderTest.php @@ -3,14 +3,20 @@ namespace LaminasTest\ZendFrameworkBridge; use Laminas\LegacyTypeHint; +use Laminas\ZendFrameworkBridge\Autoloader; use PHPUnit\Framework\TestCase; use function class_exists; +use function clearstatcache; +use function file_exists; use function get_class; use function interface_exists; +use function rename; class AutoloaderTest extends TestCase { + private const PATH_TO_AUTOLOADER = __DIR__ . '/../vendor/autoload.php'; + /** * @return array[] */ @@ -139,4 +145,22 @@ public function testReverseAliasCreated($actual, $legacy) self::assertTrue(class_exists($actual)); self::assertTrue(class_exists($legacy)); } + + public function testCanHandleNonExistentAutoloadFile(): void + { + self::assertTrue(file_exists(self::PATH_TO_AUTOLOADER)); + $pathToAutoloaderBackup = sprintf('%s.bak', self::PATH_TO_AUTOLOADER); + rename(self::PATH_TO_AUTOLOADER, $pathToAutoloaderBackup); + clearstatcache(); + self::assertFalse(file_exists(self::PATH_TO_AUTOLOADER)); + + try { + Autoloader::load(); + } finally { + rename($pathToAutoloaderBackup, self::PATH_TO_AUTOLOADER); + } + + clearstatcache(); + self::assertTrue(file_exists(self::PATH_TO_AUTOLOADER)); + } } From e71bcbb8ba8c87f767db25db96e26c6b382d595f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Tue, 21 Dec 2021 14:57:52 +0100 Subject: [PATCH 2/2] qa: optimize code for better readability and avoid multiple `getenv` calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- psalm-baseline.xml | 10 --------- src/Autoloader.php | 54 +++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c054410..76d61cd 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -12,9 +12,6 @@ $class $class - - include __DIR__ . '/../../../autoload.php' - load @@ -35,13 +32,6 @@ $namespaces[$check] $namespaces[$check] - - getenv('COMPOSER_VENDOR_DIR') - getenv('COMPOSER_VENDOR_DIR') - - - include getenv('COMPOSER_VENDOR_DIR') . '/autoload.php' - diff --git a/src/Autoloader.php b/src/Autoloader.php index 629e07f..84f9406 100644 --- a/src/Autoloader.php +++ b/src/Autoloader.php @@ -11,6 +11,7 @@ use function class_exists; use function explode; use function file_exists; +use function getenv; use function interface_exists; use function spl_autoload_register; use function strlen; @@ -23,6 +24,9 @@ */ class Autoloader { + private const UPSTREAM_COMPOSER_VENDOR_DIRECTORY = __DIR__ . '/../../..'; + private const LOCAL_COMPOSER_VENDOR_DIRECTORY = __DIR__ . '/../vendor'; + /** * Attach autoloaders for managing legacy ZF artifacts. * @@ -61,37 +65,13 @@ public static function load() private static function getClassLoader(): ?ClassLoader { - if (getenv('COMPOSER_VENDOR_DIR') && file_exists(getenv('COMPOSER_VENDOR_DIR') . '/autoload.php')) { - /** @psalm-suppress MixedAssignment */ - $loader = include getenv('COMPOSER_VENDOR_DIR') . '/autoload.php'; - if (!$loader instanceof ClassLoader) { - return null; - } - - return $loader; - } - - if (file_exists(__DIR__ . '/../../../autoload.php')) { - /** @psalm-suppress MixedAssignment */ - $loader = include __DIR__ . '/../../../autoload.php'; - if (!$loader instanceof ClassLoader) { - return null; - } - - return $loader; - } - - if (file_exists(__DIR__ . '/../vendor/autoload.php')) { - /** @psalm-suppress MixedAssignment */ - $loader = include __DIR__ . '/../vendor/autoload.php'; - if (!$loader instanceof ClassLoader) { - return null; - } - - return $loader; + $composerVendorDirectory = getenv('COMPOSER_VENDOR_DIR'); + if (is_string($composerVendorDirectory)) { + return self::getClassLoaderFromVendorDirectory($composerVendorDirectory); } - return null; + return self::getClassLoaderFromVendorDirectory(self::UPSTREAM_COMPOSER_VENDOR_DIRECTORY) + ?? self::getClassLoaderFromVendorDirectory(self::LOCAL_COMPOSER_VENDOR_DIRECTORY); } /** @@ -182,4 +162,20 @@ class_alias($alias, $class); } }; } + + private static function getClassLoaderFromVendorDirectory(string $composerVendorDirectory): ?ClassLoader + { + $filename = rtrim($composerVendorDirectory, '/') . '/autoload.php'; + if (!file_exists($filename)) { + return null; + } + + /** @psalm-suppress MixedAssignment */ + $loader = include $filename; + if (!$loader instanceof ClassLoader) { + return null; + } + + return $loader; + } }