From ba3b3218424fcd6fe5f0e62613682f0d24df00e4 Mon Sep 17 00:00:00 2001 From: Gyula Sallai Date: Tue, 25 Oct 2011 17:28:19 +0200 Subject: [PATCH] [ClassLoader] Added a class map file generator utility fixed cs Small refactoring for Finder support If class name found, return Find multiple classes and namespaces in the same file fixed problems with inheritance and non-php files Renamed ClassMapDumper to ClassMapGenerator fixed error with splfileinfo --- .../ClassLoader/ClassMapGenerator.php | 135 ++++++++++++++++++ .../ClassLoader/ClassMapGeneratorTest.php | 73 ++++++++++ .../Fixtures/classmap/SomeClass.php | 8 ++ .../Fixtures/classmap/SomeInterface.php | 8 ++ .../Fixtures/classmap/SomeParent.php | 8 ++ .../Fixtures/classmap/multipleNs.php | 11 ++ .../Fixtures/classmap/notAClass.php | 3 + .../Fixtures/classmap/notPhpFile.md | 1 + .../classmap/sameNsMultipleClasses.php | 6 + 9 files changed, 253 insertions(+) create mode 100644 src/Symfony/Component/ClassLoader/ClassMapGenerator.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/ClassMapGeneratorTest.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/SomeClass.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/SomeInterface.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/SomeParent.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/multipleNs.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/notAClass.php create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/notPhpFile.md create mode 100644 tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/sameNsMultipleClasses.php diff --git a/src/Symfony/Component/ClassLoader/ClassMapGenerator.php b/src/Symfony/Component/ClassLoader/ClassMapGenerator.php new file mode 100644 index 0000000000..5730eb0821 --- /dev/null +++ b/src/Symfony/Component/ClassLoader/ClassMapGenerator.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * ClassMapGenerator + * + * @author Gyula Sallai + */ +class ClassMapGenerator +{ + /** + * Generate a class map file + * + * @param array|string $dirs Directories or a single path to search in + * @param string $file The name of the class map file + */ + static public function dump($dirs, $file) + { + $dirs = (array) $dirs; + $maps = array(); + + foreach ($dirs as $dir) { + $maps = array_merge($maps, static::createMap($dir)); + } + + file_put_contents($file, sprintf('isFile()) { + continue; + } + + $path = $file->getRealPath(); + + if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { + continue; + } + + $classes = self::findClasses($path); + + foreach ($classes as $class) { + $map[$class] = $path; + } + + } + + return $map; + } + + /** + * Extract the classes in the given file + * + * @param string $path The file to check + * + * @return array The found classes + */ + static private function findClasses($path) + { + $contents = file_get_contents($path); + $tokens = token_get_all($contents); + + $classes = array(); + + $namespace = ''; + for ($i = 0, $max = count($tokens); $i < $max; $i++) { + $token = $tokens[$i]; + + if (is_string($token)) { + continue; + } + + $class = ''; + + switch ($token[0]) { + case T_NAMESPACE: + $namespace = ''; + // If there is a namespace, extract it + while (($t = $tokens[++$i]) && is_array($t)) { + if (in_array($t[0], array(T_STRING, T_NS_SEPARATOR))) { + $namespace .= $t[1]; + } + } + $namespace .= '\\'; + break; + case T_CLASS: + case T_INTERFACE: + // Find the classname + while (($t = $tokens[++$i]) && is_array($t)) { + if (T_STRING === $t[0]) { + $class .= $t[1]; + } else if ($class !== '' && T_WHITESPACE == $t[0]) { + break; + } + } + + if (empty($namespace)) { + $classes[] = $class; + } else { + $classes[] = $namespace . $class; + } + break; + default: + break; + } + } + + return $classes; + } +} diff --git a/tests/Symfony/Tests/Component/ClassLoader/ClassMapGeneratorTest.php b/tests/Symfony/Tests/Component/ClassLoader/ClassMapGeneratorTest.php new file mode 100644 index 0000000000..922db32d01 --- /dev/null +++ b/tests/Symfony/Tests/Component/ClassLoader/ClassMapGeneratorTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\ClassLoader; + +use Symfony\Component\ClassLoader\ClassMapGenerator; + +class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @dataProvider getTestCreateMapTests + */ + public function testCreateMap($directory, $expected) + { + $this->assertEquals($expected, ClassMapGenerator::createMap($directory)); + } + + public function getTestCreateMapTests() + { + return array( + array(__DIR__.'/Fixtures/Namespaced', array( + 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', + 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', + 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', + ) + ), + array(__DIR__.'/Fixtures/beta/NamespaceCollision', array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + )), + array(__DIR__.'/Fixtures/Pearlike', array( + 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', + 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', + 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', + )), + array(__DIR__.'/Fixtures/classmap', array( + 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', + 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', + 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', + )), + ); + } + + public function testCreateMapFinderSupport() + { + if (!class_exists('Symfony\\Component\\Finder\\Finder')) { + $this->markTestSkipped('Finder component is not available'); + } + + $finder = new \Symfony\Component\Finder\Finder(); + $finder->files()->in(__DIR__ . '/Fixtures/beta/NamespaceCollision'); + + $this->assertEquals(array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + ), ClassMapGenerator::createMap($finder)); + } +} diff --git a/tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/SomeClass.php b/tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/SomeClass.php new file mode 100644 index 0000000000..cc6f9ab7b6 --- /dev/null +++ b/tests/Symfony/Tests/Component/ClassLoader/Fixtures/classmap/SomeClass.php @@ -0,0 +1,8 @@ +