From ff5c428d602a6cf9a984e9737c994d5a8dd696e3 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Sun, 11 Nov 2012 18:30:31 +0100 Subject: [PATCH 1/3] [ClassMapGenerator] Refine the findClasses method The code could not throw --- src/Composer/Autoload/ClassMapGenerator.php | 60 +++++++++---------- .../Test/Autoload/ClassMapGeneratorTest.php | 13 ++++ 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 61cfae4a5..cfaa6bb0f 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -96,38 +96,38 @@ class ClassMapGenerator try { $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(?])(?class|interface'.$traits.') \s+ (?\S+) - | \b(?])(?namespace) (?\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) { throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e); } + + 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(?])(?class|interface'.$traits.') \s+ (?\S+) + | \b(?])(?namespace) (?\s+[^\s;{}\\\\]+(?:\s*\\\\\s*[^\s;{}\\\\]+)*)? \s*[\{;] + ) + }ix', $contents, $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; + } } diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index f1394fd44..1196ee5dc 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -84,6 +84,19 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase ), ClassMapGenerator::createMap($finder)); } + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Could not scan for classes inside + */ + public function testThrowsWhenFileDoesNotExist() + { + $r = new \ReflectionClass('Composer\\Autoload\\ClassMapGenerator'); + $find = $r->getMethod('findClasses'); + $find->setAccessible(true); + + $find->invoke(null, __DIR__.'/no-file'); + } + protected function assertEqualsNormalized($expected, $actual, $message = null) { foreach ($expected as $ns => $path) { From ab481145314e952a97df578db1fcabf8b6996f76 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Sun, 11 Nov 2012 18:31:17 +0100 Subject: [PATCH 2/3] [ClassMapGenerator] Improve error message when the path does not exist i.e. the composer.json has a typo --- src/Composer/Autoload/ClassMapGenerator.php | 31 ++++++++++++------- .../Test/Autoload/ClassMapGeneratorTest.php | 11 ++++++- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index cfaa6bb0f..e2518a721 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -40,42 +40,49 @@ class ClassMapGenerator /** * 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 * * @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_file($dir)) { - $dir = array(new \SplFileInfo($dir)); + if (is_string($path)) { + if (is_file($path)) { + $path = array(new \SplFileInfo($path)); + } else if (is_dir($path)) { + $path = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); } 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(); - foreach ($dir as $file) { + foreach ($path as $file) { if (!$file->isFile()) { continue; } - $path = $file->getRealPath(); + $filePath = $file->getRealPath(); - if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { + if (pathinfo($filePath, PATHINFO_EXTENSION) !== 'php') { continue; } - if ($whitelist && !preg_match($whitelist, strtr($path, '\\', '/'))) { + if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) { continue; } - $classes = self::findClasses($path); + $classes = self::findClasses($filePath); foreach ($classes as $class) { - $map[$class] = $path; + $map[$class] = $filePath; } } diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 1196ee5dc..751aa6a38 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -88,7 +88,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase * @expectedException \RuntimeException * @expectedExceptionMessage Could not scan for classes inside */ - public function testThrowsWhenFileDoesNotExist() + public function testFindClassesThrowsWhenFileDoesNotExist() { $r = new \ReflectionClass('Composer\\Autoload\\ClassMapGenerator'); $find = $r->getMethod('findClasses'); @@ -97,6 +97,15 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase $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) { foreach ($expected as $ns => $path) { From 86bb1be61f03897c0a3a39533bed7e71be1e7271 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 12 Nov 2012 09:15:35 +0100 Subject: [PATCH 3/3] [ClassMapGeneratot] Filter out non php code Otherwise files like https://github.com/propelorm/Propel/blob/master/generator/lib/behavior/i18n/templates/queryUseI18nQuery.php would fail ("class" keyword would not be filtered out by php_strip_whitespace()) --- src/Composer/Autoload/ClassMapGenerator.php | 16 +++++++++++----- .../Test/Autoload/ClassMapGeneratorTest.php | 1 + .../Autoload/Fixtures/template/template_1.php | 6 ++++++ .../Autoload/Fixtures/template/template_2.php | 8 ++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/template/template_1.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/template/template_2.php diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index e2518a721..0fe9b5cf7 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -107,21 +107,27 @@ class ClassMapGenerator throw new \RuntimeException('Could not scan for classes inside '.$path.": \n".$e->getMessage(), 0, $e); } - 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); + // 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(?])(?class|interface'.$traits.') \s+ (?\S+) | \b(?])(?namespace) (?\s+[^\s;{}\\\\]+(?:\s*\\\\\s*[^\s;{}\\\\]+)*)? \s*[\{;] ) - }ix', $contents, $matches); + }ix', $phpContents, $matches); $classes = array(); $namespace = ''; diff --git a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php index 751aa6a38..779104567 100644 --- a/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php +++ b/tests/Composer/Test/Autoload/ClassMapGeneratorTest.php @@ -53,6 +53,7 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', )), + array(__DIR__.'/Fixtures/template', array()), ); if (version_compare(PHP_VERSION, '5.4', '>=')) { diff --git a/tests/Composer/Test/Autoload/Fixtures/template/template_1.php b/tests/Composer/Test/Autoload/Fixtures/template/template_1.php new file mode 100644 index 000000000..4a706f317 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/template/template_1.php @@ -0,0 +1,6 @@ +/* + * class templateClass_1 + * interface templateInterface_1 + * trait temlpateTrait_1 + */ +