1
0
Fork 0

Merge remote-tracking branch 'vicb/classmap'

pull/1328/head
Jordi Boggiano 2012-11-12 10:34:53 +01:00
commit 8a0c8560b7
4 changed files with 92 additions and 42 deletions

View File

@ -40,42 +40,49 @@ class ClassMapGenerator
/** /**
* Iterate over all files in the given directory searching for classes * Iterate over all files in the given directory searching for classes
* *
* @param Iterator|string $dir The directory to search in or an iterator * @param Iterator|string $path The path to search in or an iterator
* @param string $whitelist Regex that matches against the file path * @param string $whitelist Regex that matches against the file path
* *
* @return array A class map array * @return array A class map array
*
* @throws \RuntimeException When the path is neither an existing file nor directory
*/ */
public static function createMap($dir, $whitelist = null) public static function createMap($path, $whitelist = null)
{ {
if (is_string($dir)) { if (is_string($path)) {
if (is_file($dir)) { if (is_file($path)) {
$dir = array(new \SplFileInfo($dir)); $path = array(new \SplFileInfo($path));
} else if (is_dir($path)) {
$path = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
} else { } else {
$dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)); throw new \RuntimeException(
'Could not scan for classes inside "'.$path.
'" which does not appear to be a file nor a folder'
);
} }
} }
$map = array(); $map = array();
foreach ($dir as $file) { foreach ($path as $file) {
if (!$file->isFile()) { if (!$file->isFile()) {
continue; continue;
} }
$path = $file->getRealPath(); $filePath = $file->getRealPath();
if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { if (pathinfo($filePath, PATHINFO_EXTENSION) !== 'php') {
continue; continue;
} }
if ($whitelist && !preg_match($whitelist, strtr($path, '\\', '/'))) { if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) {
continue; continue;
} }
$classes = self::findClasses($path); $classes = self::findClasses($filePath);
foreach ($classes as $class) { foreach ($classes as $class) {
$map[$class] = $path; $map[$class] = $filePath;
} }
} }
@ -96,38 +103,44 @@ class ClassMapGenerator
try { try {
$contents = php_strip_whitespace($path); $contents = php_strip_whitespace($path);
if (!preg_match('{\b(?:class|interface'.$traits.')\b}i', $contents)) {
return array();
}
// strip heredocs/nowdocs
$contents = preg_replace('{<<<\'?(\w+)\'?(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\1(?=\r\n|\n|\r|;)}s', 'null', $contents);
// strip strings
$contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}', 'null', $contents);
preg_match_all('{
(?:
\b(?<![\$:>])(?<type>class|interface'.$traits.') \s+ (?<name>\S+)
| \b(?<![\$:>])(?<ns>namespace) (?<nsname>\s+[^\s;{}\\\\]+(?:\s*\\\\\s*[^\s;{}\\\\]+)*)? \s*[\{;]
)
}ix', $contents, $matches);
$classes = array();
$namespace = '';
for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
$name = $matches['name'][$i];
if (!empty($matches['ns'][$i])) {
$namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\';
} else {
$classes[] = ltrim($namespace . $matches['name'][$i], '\\');
}
}
return $classes;
} catch (\Exception $e) { } catch (\Exception $e) {
throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e); throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e);
} }
// strip heredocs/nowdocs
$contents = preg_replace('{<<<\'?(\w+)\'?(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\1(?=\r\n|\n|\r|;)}s', 'null', $contents);
// strip strings
$contents = preg_replace('{"[^"\\\\]*(\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(\\\\.[^\'\\\\]*)*\'}', 'null', $contents);
// keep only php code
$phpContents = preg_match_all('{<\?(?:php)?(.*)\?>}s', $contents, $m) ? join($m[1], ' ') : '';
$contents = preg_replace('{<\?(php)?.*\?>}s', '', $contents);
if (preg_match('{<\?(?:php)?(.*)}s', $contents, $m)) {
$phpContents .= ' ' . $m[1];
}
if (!preg_match('{\b(?:class|interface'.$traits.')\b}i', $phpContents)) {
return array();
}
preg_match_all('{
(?:
\b(?<![\$:>])(?<type>class|interface'.$traits.') \s+ (?<name>\S+)
| \b(?<![\$:>])(?<ns>namespace) (?<nsname>\s+[^\s;{}\\\\]+(?:\s*\\\\\s*[^\s;{}\\\\]+)*)? \s*[\{;]
)
}ix', $phpContents, $matches);
$classes = array();
$namespace = '';
for ($i = 0, $len = count($matches['type']); $i < $len; $i++) {
if (!empty($matches['ns'][$i])) {
$namespace = str_replace(array(' ', "\t", "\r", "\n"), '', $matches['nsname'][$i]) . '\\';
} else {
$classes[] = ltrim($namespace . $matches['name'][$i], '\\');
}
}
return $classes;
} }
} }

View File

@ -53,6 +53,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php',
'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php',
)), )),
array(__DIR__.'/Fixtures/template', array()),
); );
if (version_compare(PHP_VERSION, '5.4', '>=')) { if (version_compare(PHP_VERSION, '5.4', '>=')) {
@ -84,6 +85,28 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
), ClassMapGenerator::createMap($finder)); ), ClassMapGenerator::createMap($finder));
} }
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Could not scan for classes inside
*/
public function testFindClassesThrowsWhenFileDoesNotExist()
{
$r = new \ReflectionClass('Composer\\Autoload\\ClassMapGenerator');
$find = $r->getMethod('findClasses');
$find->setAccessible(true);
$find->invoke(null, __DIR__.'/no-file');
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Could not scan for classes inside
*/
public function testCreateMapThrowsWhenDirectoryDoesNotExist()
{
ClassMapGenerator::createMap(__DIR__.'/no-file.no-foler');
}
protected function assertEqualsNormalized($expected, $actual, $message = null) protected function assertEqualsNormalized($expected, $actual, $message = null)
{ {
foreach ($expected as $ns => $path) { foreach ($expected as $ns => $path) {

View File

@ -0,0 +1,6 @@
/*
* class templateClass_1
* interface templateInterface_1
* trait temlpateTrait_1
*/
<?php echo $code

View File

@ -0,0 +1,8 @@
/*
* class templateClass_2
* interface templateInterface_2
* trait temlpateTrait_2
*/
<?php
echo $code
?>