From 328bf5f6910151332974e5ae4c2c492e17a39843 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 13 Oct 2023 14:50:58 +0800 Subject: [PATCH] MDL-79697 core: PSR-4 NS can be spread across multiple dirs The PSR-4 specification does not preclude a single namespaces from having multiple sources. This is the case for several PSR standards including standard packagist packages distributed by PSR: - PSR-7 - HTTP Message Interfaces: \Psr\Http\Message - PSR-17 - HTTP Factories: \Psr\Http\Message - PSR-15 - http-server-handler: \Psr\Http\Server - PSR-15 - http-server-middleware: \Psr\Http\Server --- lib/classes/component.php | 26 +++++++++++++++++--------- lib/tests/component_test.php | 25 ++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/classes/component.php b/lib/classes/component.php index b9ba8a82a31..d09361afa59 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -90,8 +90,8 @@ class core_component { 'Mustache' => 'lib/mustache/src/Mustache', 'CFPropertyList' => 'lib/plist/classes/CFPropertyList', ); - /** @var array associative array of PRS-4 namespaces and corresponding paths. */ - protected static $psr4namespaces = array( + /** @var array> associative array of PRS-4 namespaces and corresponding paths. */ + protected static $psr4namespaces = [ 'MaxMind' => 'lib/maxmind/MaxMind', 'GeoIp2' => 'lib/maxmind/GeoIp2', 'Sabberworm\\CSS' => 'lib/php-css-parser', @@ -110,13 +110,16 @@ class core_component { 'MyCLabs\\Enum' => 'lib/php-enum/src', 'PhpXmlRpc' => 'lib/phpxmlrpc', 'Psr\\Http\\Client' => 'lib/psr/http-client/src', - 'Psr\\Http\\Factory' => 'lib/psr/http-factory/src', - 'Psr\\Http\\Message' => 'lib/psr/http-message/src', + 'Psr\\Http\\Message' => [ + 'lib/psr/http-message/src', + 'lib/psr/http-factory/src', + ], + 'Psr\\EventDispatcher' => 'lib/psr/event-dispatcher/src', 'GuzzleHttp\\Psr7' => 'lib/guzzlehttp/psr7/src', 'GuzzleHttp\\Promise' => 'lib/guzzlehttp/promises/src', 'GuzzleHttp' => 'lib/guzzlehttp/guzzle/src', 'Kevinrob\\GuzzleCache' => 'lib/guzzlehttp/kevinrob/guzzlecache/src', - ); + ]; /** * Class loader for Frankenstyle named classes in standard locations. @@ -175,10 +178,15 @@ class core_component { */ protected static function psr_classloader($class) { // Iterate through each PSR-4 namespace prefix. - foreach (self::$psr4namespaces as $prefix => $path) { - $file = self::get_class_file($class, $prefix, $path, array('\\')); - if (!empty($file) && file_exists($file)) { - return $file; + foreach (self::$psr4namespaces as $prefix => $paths) { + if (!is_array($paths)) { + $paths = [$paths]; + } + foreach ($paths as $path) { + $file = self::get_class_file($class, $prefix, $path, ['\\']); + if (!empty($file) && file_exists($file)) { + return $file; + } } } diff --git a/lib/tests/component_test.php b/lib/tests/component_test.php index 91693c3e8c0..22f68d2a032 100644 --- a/lib/tests/component_test.php +++ b/lib/tests/component_test.php @@ -631,7 +631,8 @@ class component_test extends advanced_testcase { // This is not in the spec, but may come up in some libraries using both namespaces and PEAR-style class names. // If problems arise we can remove this test, but will need to add a warning. // Normalise to forward slash for testing purposes. - $directory = str_replace('\\', '/', $CFG->dirroot) . "/lib/tests/fixtures/component/"; + $dirroot = str_replace('\\', '/', $CFG->dirroot); + $directory = "{$dirroot}/lib/tests/fixtures/component/"; $psr0 = [ 'psr0' => 'lib/tests/fixtures/component/psr0', @@ -702,6 +703,28 @@ class component_test extends advanced_testcase { 'classname' => 'overlap_subnamespace_example2', 'file' => "{$directory}overlap/subnamespace/example2.php", ], + 'PSR-4 namespaces can come from multiple sources - first source' => [ + 'psr0' => $psr0, + 'psr4' => [ + 'Psr\\Http\\Message' => [ + 'lib/psr/http-message/src', + 'lib/psr/http-factory/src', + ], + ], + 'classname' => 'Psr\Http\Message\ServerRequestInterface', + 'includedfiles' => "{$dirroot}/lib/psr/http-message/src/ServerRequestInterface.php", + ], + 'PSR-4 namespaces can come from multiple sources - second source' => [ + 'psr0' => [], + 'psr4' => [ + 'Psr\\Http\\Message' => [ + 'lib/psr/http-message/src', + 'lib/psr/http-factory/src', + ], + ], + 'classname' => 'Psr\Http\Message\ServerRequestFactoryInterface', + 'includedfiles' => "{$dirroot}/lib/psr/http-factory/src/ServerRequestFactoryInterface.php", + ], ]; }