From 54d20f23293028e565750a1913cd81b4c1284246 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 3 Jan 2011 18:08:10 -0500 Subject: [PATCH] Fix UniversalClassLoader matching collisions. The current `loadClass()` implementation tries to load a class from the first matching prefix then stops, producing false-negative results. This is especially evident in groups of related libraries, such as Doctrine: Doctrine Doctrine\Common Doctrine\Common\DataFixtures Doctrine\DBAL Doctrine\DBAL\Migrations Each of these libraries is submoduled into a different vendor directory. Depending on what order these libraries are added to a UniversalClassLoader instance, classes may or may not actually be loaded. This fix continues searching registered namespaces and prefixes if the first partial match is negative. --- .../HttpFoundation/UniversalClassLoader.php | 10 +- .../alpha/NamespaceCollision/A/Bar.php | 8 ++ .../alpha/NamespaceCollision/A/Foo.php | 8 ++ .../Fixtures/alpha/PrefixCollision/A/Bar.php | 6 ++ .../Fixtures/alpha/PrefixCollision/A/Foo.php | 6 ++ .../beta/NamespaceCollision/A/B/Bar.php | 8 ++ .../beta/NamespaceCollision/A/B/Foo.php | 8 ++ .../Fixtures/beta/PrefixCollision/A/B/Bar.php | 6 ++ .../Fixtures/beta/PrefixCollision/A/B/Foo.php | 6 ++ .../UniversalClassLoaderTest.php | 101 +++++++++++++++++- 10 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/NamespaceCollision/A/Bar.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/NamespaceCollision/A/Foo.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/PrefixCollision/A/Bar.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/PrefixCollision/A/Foo.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/beta/NamespaceCollision/A/B/Bar.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/beta/NamespaceCollision/A/B/Foo.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/beta/PrefixCollision/A/B/Bar.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/Fixtures/beta/PrefixCollision/A/B/Foo.php diff --git a/src/Symfony/Component/HttpFoundation/UniversalClassLoader.php b/src/Symfony/Component/HttpFoundation/UniversalClassLoader.php index bbfd6a91591f7..b5f341ec41ed6 100644 --- a/src/Symfony/Component/HttpFoundation/UniversalClassLoader.php +++ b/src/Symfony/Component/HttpFoundation/UniversalClassLoader.php @@ -132,13 +132,12 @@ public function loadClass($class) $namespace = substr($class, 0, $pos); foreach ($this->namespaces as $ns => $dir) { if (0 === strpos($namespace, $ns)) { - $class = substr($class, $pos + 1); - $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; + $className = substr($class, $pos + 1); + $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; if (file_exists($file)) { require $file; + return; } - - return; } } } else { @@ -148,9 +147,8 @@ public function loadClass($class) $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; if (file_exists($file)) { require $file; + return; } - - return; } } } diff --git a/tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/NamespaceCollision/A/Bar.php b/tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/NamespaceCollision/A/Bar.php new file mode 100644 index 0000000000000..0f44fc2e99ae2 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/Fixtures/alpha/NamespaceCollision/A/Bar.php @@ -0,0 +1,8 @@ +loadClass() loads Pearlike_Bar class with a leading slash'), ); } -} + /** + * @dataProvider namespaceCollisionClassProvider + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerNamespaces($namespaces); + + $loader->loadClass($className); + $this->assertTrue(class_exists($className), $message); + } + + public static function namespaceCollisionClassProvider() + { + return array( + array( + array( + 'NamespaceCollision\\A' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + 'NamespaceCollision\\A\\B' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + ), + 'NamespaceCollision\A\Foo', + '->loadClass() loads NamespaceCollision\A\Foo from alpha.', + ), + array( + array( + 'NamespaceCollision\\A\\B' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + 'NamespaceCollision\\A' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + ), + 'NamespaceCollision\A\Bar', + '->loadClass() loads NamespaceCollision\A\Bar from alpha.', + ), + array( + array( + 'NamespaceCollision\\A' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + 'NamespaceCollision\\A\\B' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + ), + 'NamespaceCollision\A\B\Foo', + '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', + ), + array( + array( + 'NamespaceCollision\\A\\B' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + 'NamespaceCollision\\A' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + ), + 'NamespaceCollision\A\B\Bar', + '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', + ), + ); + } + + /** + * @dataProvider prefixCollisionClassProvider + */ + public function testLoadClassPrefixCollision($prefixes, $className, $message) + { + $loader = new UniversalClassLoader(); + $loader->registerPrefixes($prefixes); + + $loader->loadClass($className); + $this->assertTrue(class_exists($className), $message); + } + + public static function prefixCollisionClassProvider() + { + return array( + array( + array( + 'PrefixCollision_A_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + 'PrefixCollision_A_B_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + ), + 'PrefixCollision_A_Foo', + '->loadClass() loads PrefixCollision_A_Foo from alpha.', + ), + array( + array( + 'PrefixCollision_A_B_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + 'PrefixCollision_A_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + ), + 'PrefixCollision_A_Bar', + '->loadClass() loads PrefixCollision_A_Bar from alpha.', + ), + array( + array( + 'PrefixCollision_A_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + 'PrefixCollision_A_B_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + ), + 'PrefixCollision_A_B_Foo', + '->loadClass() loads PrefixCollision_A_B_Foo from beta.', + ), + array( + array( + 'PrefixCollision_A_B_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/beta', + 'PrefixCollision_A_' => __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures/alpha', + ), + 'PrefixCollision_A_B_Bar', + '->loadClass() loads PrefixCollision_A_B_Bar from beta.', + ), + ); + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy