1
0
Fork 0

Merge remote-tracking branch 'trivago/add_exclude'

Conflicts:
	doc/04-schema.md
	src/Composer/Autoload/AutoloadGenerator.php
pull/4564/merge
Jordi Boggiano 2015-10-30 00:00:44 +00:00
commit 084f6de24e
5 changed files with 111 additions and 17 deletions

View File

@ -552,6 +552,22 @@ Example:
} }
``` ```
#### Exclude files from classmaps
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 test classes in your live environment, for example.
The classmap generator will ignore all files in the paths configured here.
Example:
```json
{
"autoload": {
"exclude-from-classmap": ["/Tests/", "/test/", "/tests/"]
}
}
### autoload-dev <span>([root-only](04-schema.md#root-package))</span> ### autoload-dev <span>([root-only](04-schema.md#root-package))</span>
This section allows to define autoload rules for development purposes. This section allows to define autoload rules for development purposes.
@ -730,7 +746,7 @@ override packages from it.
### config <span>([root-only](04-schema.md#root-package))</span> ### config <span>([root-only](04-schema.md#root-package))</span>
A set of configuration options. It is only used for projects. See A set of configuration options. It is only used for projects. See
[Config](06-config.md) for a description of each individual option. [Config](06-config.md) for a description of each individual option.
### scripts <span>([root-only](04-schema.md#root-package))</span> ### scripts <span>([root-only](04-schema.md#root-package))</span>
@ -791,7 +807,7 @@ Optional.
### non-feature-branches ### non-feature-branches
A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something), A list of regex patterns of branch names that are non-numeric (e.g. "latest" or something),
that will NOT be handled as feature branches. This is an array of strings. that will NOT be handled as feature branches. This is an array of strings.
If you have non-numeric branch names, for example like "latest", "current", "latest-stable" If you have non-numeric branch names, for example like "latest", "current", "latest-stable"

View File

@ -257,6 +257,10 @@
"files": { "files": {
"type": "array", "type": "array",
"description": "This is an array of files that are always required on every request." "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/\"]"
} }
} }
}, },

View File

@ -28,6 +28,8 @@ use Composer\Script\ScriptEvents;
*/ */
class AutoloadGenerator class AutoloadGenerator
{ {
const EXCLUDE_PATTERN = '.*%s';
/** /**
* @var EventDispatcher * @var EventDispatcher
*/ */
@ -193,6 +195,11 @@ EOF;
EOF; EOF;
} }
$blacklist = null;
if (!empty($autoloads['exclude-from-classmap'])) {
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
}
// flatten array // flatten array
$classMap = array(); $classMap = array();
if ($scanPsr0Packages) { if ($scanPsr0Packages) {
@ -215,21 +222,21 @@ EOF;
if (!is_dir($dir)) { if (!is_dir($dir)) {
continue; continue;
} }
$whitelist = sprintf( // $whitelist = sprintf(
'{%s/%s.+$}', // '{%s/%s.+$}',
preg_quote($dir), // preg_quote($dir),
($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : '' // ($psrType === 'psr-0' && strpos($namespace, '_') === false) ? preg_quote(strtr($namespace, '\\', '/')) : ''
); // );
$namespaceFilter = $namespace === '' ? null : $namespace; $namespaceFilter = $namespace === '' ? null : $namespace;
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $whitelist, $namespaceFilter, $classMap); $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $classMap);
} }
} }
} }
} }
foreach ($autoloads['classmap'] as $dir) { foreach ($autoloads['classmap'] as $dir) {
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, null, null, $classMap); $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, $classMap);
} }
ksort($classMap); ksort($classMap);
@ -277,9 +284,9 @@ EOF;
)); ));
} }
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $whitelist = null, $namespaceFilter = null, array $classMap = array()) private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, array $classMap = array())
{ {
foreach ($this->generateClassMap($dir, $whitelist, $namespaceFilter) as $class => $path) { foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter) as $class => $path) {
$pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n"; $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
if (!isset($classMap[$class])) { if (!isset($classMap[$class])) {
$classMap[$class] = $pathCode; $classMap[$class] = $pathCode;
@ -294,9 +301,9 @@ EOF;
return $classMap; return $classMap;
} }
private function generateClassMap($dir, $whitelist = null, $namespaceFilter = null) private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null)
{ {
return ClassMapGenerator::createMap($dir, $whitelist, $this->io, $namespaceFilter); return ClassMapGenerator::createMap($dir, $blacklist, $this->io, $namespaceFilter);
} }
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
@ -359,11 +366,18 @@ EOF;
$psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage); $psr4 = $this->parseAutoloadsType($packageMap, 'psr-4', $mainPackage);
$classmap = $this->parseAutoloadsType(array_reverse($sortedPackageMap), 'classmap', $mainPackage); $classmap = $this->parseAutoloadsType(array_reverse($sortedPackageMap), 'classmap', $mainPackage);
$files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage); $files = $this->parseAutoloadsType($sortedPackageMap, 'files', $mainPackage);
$exclude = $this->parseAutoloadsType($sortedPackageMap, 'exclude-from-classmap', $mainPackage);
krsort($psr0); krsort($psr0);
krsort($psr4); 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
);
} }
/** /**
@ -674,6 +688,22 @@ FOOTER;
} }
} }
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('\\<dirsep\\>', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '<dirsep>', $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;
}
$relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path; $relativePath = empty($installPath) ? (empty($path) ? '.' : $path) : $installPath.'/'.$path;
if ($type === 'files' || $type === 'classmap') { if ($type === 'files' || $type === 'classmap') {

View File

@ -50,14 +50,14 @@ 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 $path The path 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 $blacklist Regex that matches against the file path that exclude from the classmap.
* @param IOInterface $io IO object * @param IOInterface $io IO object
* @param string $namespace Optional namespace prefix to filter by * @param string $namespace Optional namespace prefix to filter by
* *
* @throws \RuntimeException When the path is neither an existing file nor directory * @throws \RuntimeException When the path is neither an existing file nor directory
* @return array A class map array * @return array A class map array
*/ */
public static function createMap($path, $whitelist = null, IOInterface $io = null, $namespace = null) public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null)
{ {
if (is_string($path)) { if (is_string($path)) {
if (is_file($path)) { if (is_file($path)) {
@ -81,7 +81,7 @@ class ClassMapGenerator
continue; continue;
} }
if ($whitelist && !preg_match($whitelist, strtr($filePath, '\\', '/'))) { if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) {
continue; continue;
} }

View File

@ -1261,6 +1261,50 @@ EOF;
$this->assertEquals($expectedPsr4, file_get_contents($this->vendorDir.'/composer/autoload_psr4.php')); $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/',
'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/', 'Exclude.php'),
));
$this->repository->expects($this->once())
->method('getCanonicalPackages')
->will($this->returnValue(array()));
$this->fs->ensureDirectoryExists($this->workingDir.'/composer');
$this->fs->ensureDirectoryExists($this->workingDir.'/src/Lala');
$this->fs->ensureDirectoryExists($this->workingDir.'/lib');
file_put_contents($this->workingDir.'/src/Lala/ClassMapMain.php', '<?php namespace Lala; class ClassMapMain {}');
$this->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', '<?php namespace Acme\Cake; class ClassMapBar {}');
$this->fs->ensureDirectoryExists($this->workingDir.'/composersrc');
$this->fs->ensureDirectoryExists($this->workingDir.'/composersrc/tests');
file_put_contents($this->workingDir.'/composersrc/foo.php', '<?php class ClassMapFoo {}');
// this classes should not be found in the classmap
file_put_contents($this->workingDir.'/composersrc/tests/bar.php', '<?php class ClassExcludeMapFoo {}');
file_put_contents($this->workingDir.'/composersrc/ClassToExclude.php', '<?php class ClassClassToExclude {}');
$this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1');
// Assert that autoload_classmap.php was correctly generated.
$this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap');
}
private function assertAutoloadFiles($name, $dir, $type = 'namespaces') private function assertAutoloadFiles($name, $dir, $type = 'namespaces')
{ {
$a = __DIR__.'/Fixtures/autoload_'.$name.'.php'; $a = __DIR__.'/Fixtures/autoload_'.$name.'.php';