8000 [ClassLoader] Added a class map file generator utility · symfony/symfony@ba3b321 · GitHub
[go: up one dir, main page]

Skip to content

Commit ba3b321

Browse files
committed
[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
1 parent 6e60967 commit ba3b321

File tree

9 files changed

+253
-0
lines changed

9 files changed

+253
-0
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ClassLoader;
13+
14+
/**
15+
* ClassMapGenerator
16+
*
17+
* @author Gyula Sallai <salla016@gmail.com>
18+
*/
19+
class ClassMapGenerator
20+
{
21+
/**
22+
* Generate a class map file
23+
*
24+
* @param array|string $dirs Directories or a single path to search in
25+
* @param string $file The name of the class map file
26+
*/
27+
static public function dump($dirs, $file)
28+
{
29+
$dirs = (array) $dirs;
30+
$maps = array();
31+
32+
foreach ($dirs as $dir) {
33+
$maps = array_merge($maps, static::createMap($dir));
34+
}
35+
36+
file_put_contents($file, sprintf('<?php return %s;', var_export($maps, true)));
37+
}
38+
39+
/**
40+
* Iterate over all files in the given directory searching for classes
41+
*
42+
* @param Iterator|string $dir The directory to search in or an iterator
43+
*
44+
* @return array A class map array
45+
*/
46+
static public function createMap($dir)
47+
{
48+
if (is_string($dir)) {
49+
$dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
50+
}
51+
52+
$map = array();
53+
54+
foreach ($dir as $file) {
55+
if (!$file->isFile()) {
56+
continue;
57+
}
58+
59+
$path = $file->getRealPath();
60+
61+
if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') {
62+
continue;
63+
}
64+
65+
$classes = self::findClasses($path);
66+
67+
foreach ($classes as $class) {
68+
$map[$class] = $path;
69+
}
70+
71+
}
72+
73+
return $map;
74+
}
75+
76+
/**
77+
* Extract the classes in the given file
78+
*
79+
* @param string $path The file to check
80+
*
81+
* @return array The found classes
82+
*/
83+
static private function findClasses($path)
84+
{
85+
$contents = file_get_contents($path);
86+
$tokens = token_get_all($contents);
87+
88+
$classes = array();
89+
90+
$namespace = '';
91+
for ($i = 0, $max = count($tokens); $i < $max; $i++) {
92+
$token = $tokens[$i];
93+
94+
if (is_string($token)) {
95+
continue;
96+
}
97+
98+
$class = '';
99+
100+
switch ($token[0]) {
101+
case T_NAMESPACE:
102+
$namespace = '';
103+
// If there is a namespace, extract it
104+
while (($t = $tokens[++$i]) && is_array($t)) {
105+
if (in_array($t[0], array(T_STRING, T_NS_SEPARATOR))) {
106+
$namespace .= $t[1];
107+
}
108+
}
109+
$namespace .= '\\';
110+
break;
111+
case T_CLASS:
112+
case T_INTERFACE:
113+
// Find the classname
114+
while (($t = $tokens[++$i]) && is_array($t)) {
115+
if (T_STRING === $t[0]) {
116+
$class .= $t[1];
117+
} else if ($class !== '' && T_WHITESPACE == $t[0]) {
118+
break;
119+
}
120+
}
121+
122+
if (empty($namespace)) {
123+
$classes[] = $class;
124+
} else {
125+
$classes[] = $namespace . $class;
126+
}
127+
break;
128+
default:
129+
break;
130+
}
131+
}
132+
133+
return $classes;
134+
}
135+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Tests\Component\ClassLoader;
13+
14+
use Symfony\Component\ClassLoader\ClassMapGenerator;
15+
16+
class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
17+
{
18+
19+
/**
20+
* @dataProvider getTestCreateMapTests
21+
*/
22+
public function testCreateMap($directory, $expected)
23+
{
24+
$this->assertEquals($expected, ClassMapGenerator::createMap($directory));
25+
}
26+
27+
public function getTestCreateMapTests()
28+
{
29+
return array(
30+
array(__DIR__.'/Fixtures/Namespaced', array(
31+
'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php',
32+
'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php',
33+
'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php',
34+
)
35+
),
36+
array(__DIR__.'/Fixtures/beta/NamespaceCollision', array(
37+
'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php',
38+
'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php',
39+
)),
40+
array(__DIR__.'/Fixtures/Pearlike', array(
41+
'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php',
42+
'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php',
43+
'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php',
44+
)),
45+
array(__DIR__.'/Fixtures/classmap', array(
46+
'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
47+
'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
48+
'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
49+
'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
50+
'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
51+
'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
52+
'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php',
53+
'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php',
54+
'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php',
55+
)),
56+
);
57+
}
58+
59+
public function testCreateMapFinderSupport()
60+
{
61+
if (!class_exists('Symfony\\Component\\Finder\\Finder')) {
62+
$this->markTestSkipped('Finder component is not available');
63+
}
64+
65+
$finder = new \Symfony\Component\Finder\Finder();
66+
$finder->files()->in(__DIR__ . '/Fixtures/beta/NamespaceCollision');
67+
68+
$this->assertEquals(array(
69+
'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php',
70+
'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php',
71+
), ClassMapGenerator::createMap($finder));
72+
}
73+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace ClassMap;
4+
5+
class SomeClass extends SomeParent implements SomeInterface
6+
{
7+
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace ClassMap;
4+
5+
interface SomeInterface
6+
{
7+
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace ClassMap;
4+
5+
abstract class SomeParent
6+
{
7+
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Alpha {
4+
class A {}
5+
class B {}
6+
}
7+
8+
namespace Beta {
9+
class A {}
10+
class B {}
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
$a = new stdClass();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This file should be skipped.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
namespace Foo\Bar;
4+
5+
class A {}
6+
class B {}

0 commit comments

Comments
 (0)
0