From b0ec8ee0968c144c2966c8587e09da1c84023347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kerner?= Date: Mon, 27 Jan 2014 11:42:54 +0100 Subject: [PATCH 1/5] * added exclude-from-classmap feature * updated to latest master version --- res/composer-schema.json | 4 ++ src/Composer/Autoload/AutoloadGenerator.php | 43 +++++++++++++++---- src/Composer/Autoload/ClassMapGenerator.php | 6 +-- .../Test/Autoload/AutoloadGeneratorTest.php | 30 +++++++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index 905199247..6687de280 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -223,6 +223,10 @@ "files": { "type": "array", "description": "This is an array of files that are always required on every request." + }, + "exclude-from-classmap": { + "type": "array", + "description": "This is an array of patterns to exclude from autoload classmap generation. (e.g. \"exclude-from-classmap\": [\"/test/\", \"/tests/\", \"/Tests/\"]" } } }, diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2359265bd..c65980697 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -27,6 +27,8 @@ use Composer\Script\ScriptEvents; */ class AutoloadGenerator { + const EXCLUDE_PATTERN = '.*%s'; + /** * @var EventDispatcher */ @@ -154,6 +156,11 @@ EOF; EOF; } + $blacklist = ''; + if(!empty($autoloads['exclude-from-classmap'])) { + $blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}'; + } + // flatten array $classMap = array(); if ($scanPsr0Packages) { @@ -165,12 +172,7 @@ EOF; if (!is_dir($dir)) { continue; } - $whitelist = sprintf( - '{%s/%s.+(? $path) { + foreach (ClassMapGenerator::createMap($dir, $blacklist) as $class => $path) { if ('' === $namespace || 0 === strpos($class, $namespace)) { if (!isset($classMap[$class])) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); @@ -185,7 +187,7 @@ EOF; $autoloads['classmap'] = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($autoloads['classmap'])); foreach ($autoloads['classmap'] as $dir) { - foreach (ClassMapGenerator::createMap($dir) as $class => $path) { + foreach (ClassMapGenerator::createMap($dir, $blacklist) as $class => $path) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); $classMap[$class] = $path.",\n"; } @@ -285,11 +287,19 @@ EOF; $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage); $classmap = $this->parseAutoloadsType($sortedPackageMap, 'classmap', $mainPackage); $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage); + $exclude = $this->parseAutoloadsType($sortedPackageMap, 'exclude-from-classmap', $mainPackage); krsort($psr0); krsort($psr4); - return array('psr-0' => $psr0, 'psr-4' => $psr4, 'classmap' => $classmap, 'files' => $files); + return + array( + 'psr-0' => $psr0, + 'psr-4' => $psr4, + 'classmap' => $classmap, + 'files' => $files, + 'exclude-from-classmap' => $exclude + ); } /** @@ -597,6 +607,23 @@ FOOTER; $path = $package->getTargetDir() . '/' . $path; } + if ($type === 'exclude-from-classmap') { + // first escape user input + $path = sprintf(self::EXCLUDE_PATTERN, preg_quote($path)); + + if ($package === $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { + // remove target-dir from classmap entries of the root package + $targetDir = str_replace('\\', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '', $package->getTargetDir()))); + $path = ltrim(preg_replace('{^'.$targetDir.'}', '', ltrim($path, '\\/')), '\\/'); + } + elseif($package !== $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { + // add target-dir to exclude entries that don't have it + $path = preg_quote($package->getTargetDir()) . '/' . $path; + } + $autoloads[] = empty($installPath) ? $path : preg_quote($installPath) . '/' . $path; + continue; + } + if (empty($installPath)) { $autoloads[$namespace][] = empty($path) ? '.' : $path; } else { diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index e9142c15f..5d45eb482 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -42,13 +42,13 @@ class ClassMapGenerator * Iterate over all files in the given directory searching for classes * * @param Iterator|string $path The path to search in or an iterator - * @param string $whitelist Regex that matches against the file path + * @param string $blacklist Regex that matches against the file path that exclude from the classmap. * * @return array A class map array * * @throws \RuntimeException When the path is neither an existing file nor directory */ - public static function createMap($path, $whitelist = null) + public static function createMap($path, $blacklist = '') { if (is_string($path)) { if (is_file($path)) { @@ -72,7 +72,7 @@ class ClassMapGenerator continue; } - if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) { + if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) { continue; } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 499f4de0d..4f91903cf 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1016,6 +1016,36 @@ EOF; $this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php')); } + public function testExcludeFromClassmap() + { + $package = new Package('a', '1.0', '1.0'); + $package->setAutoload(array( + 'psr-0' => array( + 'Main' => 'src/', + ), + 'classmap' => array('composersrc/'), + 'exclude-from-classmap' => array('/tests/'), + )); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue(array())); + + $this->fs->ensureDirectoryExists($this->workingDir.'/composer'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src'); + + $this->fs->ensureDirectoryExists($this->workingDir.'/composersrc'); + file_put_contents($this->workingDir.'/composersrc/foo.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc/tests'); + file_put_contents($this->workingDir.'/composersrc/tests/bar.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_1'); + + // Assert that autoload_classmap.php was correctly generated. + $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); + } + private function assertAutoloadFiles($name, $dir, $type = 'namespaces') { $a = __DIR__.'/Fixtures/autoload_'.$name.'.php'; From dad6b05ca76231e3840d0516a88fd38f859ead64 Mon Sep 17 00:00:00 2001 From: msiebeneicher Date: Wed, 11 Feb 2015 18:04:57 +0100 Subject: [PATCH 2/5] Merge branches 'add_exclude' and 'master' of https://github.com/trivago/composer into add_exclude * Resolve conflicts and update unit test --- src/Composer/Autoload/AutoloadGenerator.php | 27 ++++--------------- src/Composer/Autoload/ClassMapGenerator.php | 7 +++-- .../Test/Autoload/AutoloadGeneratorTest.php | 19 ++++++++++--- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index b0ce14481..2cebad4bd 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -174,7 +174,7 @@ EOF; } $blacklist = ''; - if(!empty($autoloads['exclude-from-classmap'])) { + if (!empty($autoloads['exclude-from-classmap'])) { $blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}'; } @@ -189,38 +189,21 @@ EOF; if (!is_dir($dir)) { continue; } - $whitelist = sprintf( - '{%s/%s.+(?io, $namespaceFilter) as $class => $path) { + foreach (ClassMapGenerator::createMap($dir, $blacklist, $this->io, $namespaceFilter) as $class => $path) { if (!isset($classMap[$class])) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); $classMap[$class] = $path.",\n"; } } - /* - * RKERNER - * foreach (ClassMapGenerator::createMap($dir, $blacklist) as $class => $path) { - if ('' === $namespace || 0 === strpos($class, $namespace)) { - if (!isset($classMap[$class])) { - $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); - $classMap[$class] = $path.",\n"; - } - } - } - */ } } } } foreach ($autoloads['classmap'] as $dir) { - foreach (ClassMapGenerator::createMap($dir, null, $this->io) as $class => $path) { - //REKERNER foreach (ClassMapGenerator::createMap($dir, $blacklist) as $class => $path) { + foreach (ClassMapGenerator::createMap($dir, $blacklist, $this->io) as $class => $path) { $path = $this->getPathCode($filesystem, $basePath, $vendorPath, $path); $classMap[$class] = $path.",\n"; } @@ -644,7 +627,7 @@ FOOTER; if ($type === 'classmap' && $package !== $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { $path = $package->getTargetDir() . '/' . $path; } - +*/ if ($type === 'exclude-from-classmap') { // first escape user input $path = sprintf(self::EXCLUDE_PATTERN, preg_quote($path)); @@ -661,7 +644,7 @@ FOOTER; $autoloads[] = empty($installPath) ? $path : preg_quote($installPath) . '/' . $path; continue; } - +/* if (empty($installPath)) { $autoloads[$namespace][] = empty($path) ? '.' : $path; } else { diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 80e1fe8b4..1f0e1ceed 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -45,7 +45,7 @@ class ClassMapGenerator * Iterate over all files in the given directory searching for classes * * @param \Iterator|string $path The path to search in or an iterator - * @param string $whitelist Regex that matches against the file path + * @param string $blacklist Regex that matches against the file path that exclude from the classmap. * @param IOInterface $io IO object * @param string $namespace Optional namespace prefix to filter by * @@ -53,7 +53,7 @@ class ClassMapGenerator * * @throws \RuntimeException When the path is neither an existing file nor directory */ - public static function createMap($path, $whitelist = null, IOInterface $io = null, $namespace = null) + public static function createMap($path, $blacklist = '', IOInterface $io = null, $namespace = null) // RKERNER: public static function createMap($path, $blacklist = '') { if (is_string($path)) { @@ -78,8 +78,7 @@ class ClassMapGenerator continue; } - if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) { - // RKERNER: if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) { + if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) { continue; } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 11639fddc..efb9a012c 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1200,6 +1200,11 @@ EOF; $package->setAutoload(array( 'psr-0' => array( 'Main' => 'src/', + 'Lala' => array('src/', 'lib/'), + ), + 'psr-4' => array( + 'Acme\Fruit\\' => 'src-fruit/', + 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), ), 'classmap' => array('composersrc/'), 'exclude-from-classmap' => array('/tests/'), @@ -1210,17 +1215,25 @@ EOF; ->will($this->returnValue(array())); $this->fs->ensureDirectoryExists($this->workingDir.'/composer'); - $this->fs->ensureDirectoryExists($this->workingDir.'/src'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src/Lala'); + $this->fs->ensureDirectoryExists($this->workingDir.'/lib'); + file_put_contents($this->workingDir.'/src/Lala/ClassMapMain.php', 'fs->ensureDirectoryExists($this->workingDir.'/src-fruit'); + $this->fs->ensureDirectoryExists($this->workingDir.'/src-cake'); + $this->fs->ensureDirectoryExists($this->workingDir.'/lib-cake'); + file_put_contents($this->workingDir.'/src-cake/ClassMapBar.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc'); file_put_contents($this->workingDir.'/composersrc/foo.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc/tests'); file_put_contents($this->workingDir.'/composersrc/tests/bar.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_1'); + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); - // Assert that autoload_classmap.php was correctly generated. + // Assert that autoload_classmap.php was correctly generated. $this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); } From 98de300878b5b9143366f8144b3d92a91db197f3 Mon Sep 17 00:00:00 2001 From: msiebeneicher Date: Thu, 12 Feb 2015 09:49:42 +0100 Subject: [PATCH 3/5] * Small improvement of the testExcludeFromClassmap unit test --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index efb9a012c..99ef17fa4 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1207,7 +1207,7 @@ EOF; 'Acme\Cake\\' => array('src-cake/', 'lib-cake/'), ), 'classmap' => array('composersrc/'), - 'exclude-from-classmap' => array('/tests/'), + 'exclude-from-classmap' => array('/tests/', 'Exclude.php'), )); $this->repository->expects($this->once()) @@ -1225,11 +1225,12 @@ EOF; file_put_contents($this->workingDir.'/src-cake/ClassMapBar.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc'); + $this->fs->ensureDirectoryExists($this->workingDir.'/composersrc/tests'); file_put_contents($this->workingDir.'/composersrc/foo.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc/tests'); + // this classes should not be found in the classmap file_put_contents($this->workingDir.'/composersrc/tests/bar.php', 'workingDir.'/composersrc/ClassToExclude.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); From 3682a9f5a79787316756bbb4987aeb82e2959321 Mon Sep 17 00:00:00 2001 From: msiebeneicher Date: Thu, 12 Feb 2015 10:18:00 +0100 Subject: [PATCH 4/5] * Cleanup code comments --- src/Composer/Autoload/AutoloadGenerator.php | 15 +-------------- src/Composer/Autoload/ClassMapGenerator.php | 1 - 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2cebad4bd..3fceb443a 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -621,13 +621,7 @@ FOOTER; $path = $package->getTargetDir() . '/' . $path; } } - - /* RKERNER - * // add target-dir to classmap entries that don't have it - if ($type === 'classmap' && $package !== $mainPackage && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { - $path = $package->getTargetDir() . '/' . $path; - } -*/ + if ($type === 'exclude-from-classmap') { // first escape user input $path = sprintf(self::EXCLUDE_PATTERN, preg_quote($path)); @@ -644,13 +638,6 @@ FOOTER; $autoloads[] = empty($installPath) ? $path : preg_quote($installPath) . '/' . $path; continue; } -/* - if (empty($installPath)) { - $autoloads[$namespace][] = empty($path) ? '.' : $path; - } else { - $autoloads[$namespace][] = $installPath.'/'.$path; - } - */ $relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path; diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 1f0e1ceed..cbbd8e1dd 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -54,7 +54,6 @@ class ClassMapGenerator * @throws \RuntimeException When the path is neither an existing file nor directory */ public static function createMap($path, $blacklist = '', IOInterface $io = null, $namespace = null) - // RKERNER: public static function createMap($path, $blacklist = '') { if (is_string($path)) { if (is_file($path)) { From 636f93750cba0aac2673130877680ab7e3816fd6 Mon Sep 17 00:00:00 2001 From: msiebeneicher Date: Tue, 17 Feb 2015 15:56:34 +0100 Subject: [PATCH 5/5] add "exclude-from-classmap" description to 04-schema.md --- doc/04-schema.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/04-schema.md b/doc/04-schema.md index e422d5c38..2dbab14aa 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -554,6 +554,22 @@ Example: } ``` +#### Exclude files from classmap + +If you want to exclude some files or folders from the classmap you can use the 'exclude-from-classmap' property. +This might be useful to exclude UnitTest classes from the classmap in your live environment, for example. + +The classmap generator will ignore all files which match your configured regex pattern. +Please note that configured regex pattern will be interpreted as case sensitive. + +```json +{ + "autoload": { + "exclude-from-classmap": ["/Tests/", "/test/", "/tests/"] + } +} +``` + ### autoload-dev (root-only) This section allows to define autoload rules for development purposes.