diff --git a/composer.json b/composer.json
index b72856837..ea526e9c7 100644
--- a/composer.json
+++ b/composer.json
@@ -39,6 +39,9 @@
"autoload": {
"psr-0": { "Composer": "src/" }
},
+ "autoload-dev": {
+ "psr-0": { "Composer\\Test": "tests/" }
+ },
"bin": ["bin/composer"],
"extra": {
"branch-alias": {
diff --git a/doc/04-schema.md b/doc/04-schema.md
index effec5386..ad8b53623 100644
--- a/doc/04-schema.md
+++ b/doc/04-schema.md
@@ -516,6 +516,27 @@ Example:
}
}
+### autoload-dev (root-only)
+
+This section allows to define autoload rules for development purpose.
+
+If you're generating classmaps from your PSR-0 namespaces, you're probably concerned
+about performance, if so, you'll also don't want your test classes to be mixed up
+with your regular classes in those classmaps.
+
+Therefore, it is a good idea to rely on a dedicated path for your unit tests.
+
+Example:
+
+ {
+ "autoload": {
+ "psr-0": { "MyLibrary": "src/" }
+ },
+ "autoload-dev": {
+ "psr-0": { "MyLibrary\\Tests": "tests/" }
+ }
+ }
+
### include-path
> **DEPRECATED**: This is only present to support legacy projects, and all new code
diff --git a/res/composer-schema.json b/res/composer-schema.json
index 905199247..69d4dc5a1 100644
--- a/res/composer-schema.json
+++ b/res/composer-schema.json
@@ -226,6 +226,30 @@
}
}
},
+ "autoload-dev": {
+ "type": "object",
+ "description": "Description of additional autoload rules for development purpose (eg. a test suite).",
+ "properties": {
+ "psr-0": {
+ "type": "object",
+ "description": "This is a hash of namespaces (keys) and the directories they can be found into (values, can be arrays of paths) by the autoloader.",
+ "additionalProperties": true
+ },
+ "psr-4": {
+ "type": "object",
+ "description": "This is a hash of namespaces (keys) and the PSR-4 directories they can map to (values, can be arrays of paths) by the autoloader.",
+ "additionalProperties": true
+ },
+ "classmap": {
+ "type": "array",
+ "description": "This is an array of directories that contain classes to be included in the class-map generation process."
+ },
+ "files": {
+ "type": "array",
+ "description": "This is an array of files that are always required on every request."
+ }
+ }
+ },
"archive": {
"type": ["object"],
"description": "Options for creating package archives for distribution.",
diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php
index 4fc900187..a0bf8c29f 100644
--- a/src/Composer/Autoload/AutoloadGenerator.php
+++ b/src/Composer/Autoload/AutoloadGenerator.php
@@ -32,11 +32,18 @@ class AutoloadGenerator
*/
private $eventDispatcher;
+ private $devMode = false;
+
public function __construct(EventDispatcher $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
+ public function setDevMode($devMode = true)
+ {
+ $this->devMode = (boolean) $devMode;
+ }
+
public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsr0Packages = false, $suffix = '')
{
$this->eventDispatcher->dispatchScript(ScriptEvents::PRE_AUTOLOAD_DUMP);
@@ -567,6 +574,9 @@ FOOTER;
list($package, $installPath) = $item;
$autoload = $package->getAutoload();
+ if ($this->devMode && $package === $mainPackage) {
+ $autoload = array_merge_recursive($autoload, $package->getDevAutoload());
+ }
// skip misconfigured packages
if (!isset($autoload[$type]) || !is_array($autoload[$type])) {
diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php
index d228fb150..cd3f1b71d 100644
--- a/src/Composer/Command/DumpAutoloadCommand.php
+++ b/src/Composer/Command/DumpAutoloadCommand.php
@@ -31,6 +31,7 @@ class DumpAutoloadCommand extends Command
->setDescription('Dumps the autoloader')
->setDefinition(array(
new InputOption('optimize', 'o', InputOption::VALUE_NONE, 'Optimizes PSR0 packages to be loaded with classmaps too, good for production.'),
+ new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables dev autoload.'),
))
->setHelp(<<php composer.phar dump-autoload
@@ -59,6 +60,8 @@ EOT
$output->writeln('Generating autoload files');
}
- $composer->getAutoloadGenerator()->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
+ $generator = $composer->getAutoloadGenerator();
+ $generator->setDevMode($input->getOption('dev'));
+ $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize);
}
}
diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php
index d373beb43..839fc45e4 100644
--- a/src/Composer/Installer.php
+++ b/src/Composer/Installer.php
@@ -290,6 +290,7 @@ class Installer
$this->io->write('Generating autoload files');
}
+ $this->autoloadGenerator->setDevMode($this->devMode);
$this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader);
if ($this->runScripts) {
diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php
index 6f1fb5095..631b035a7 100644
--- a/src/Composer/Package/AliasPackage.php
+++ b/src/Composer/Package/AliasPackage.php
@@ -245,6 +245,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->getAutoload();
}
+ public function getDevAutoload()
+ {
+ return $this->aliasOf->getDevAutoload();
+ }
public function getIncludePaths()
{
return $this->aliasOf->getIncludePaths();
diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php
index be94adec4..0a183742f 100644
--- a/src/Composer/Package/Dumper/ArrayDumper.php
+++ b/src/Composer/Package/Dumper/ArrayDumper.php
@@ -31,6 +31,7 @@ class ArrayDumper
'extra',
'installationSource' => 'installation-source',
'autoload',
+ 'devAutoload' => 'autoload-dev',
'notificationUrl' => 'notification-url',
'includePaths' => 'include-path',
);
diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php
index 3940bdeb0..6ef4e7c03 100644
--- a/src/Composer/Package/Loader/ArrayLoader.php
+++ b/src/Composer/Package/Loader/ArrayLoader.php
@@ -130,6 +130,10 @@ class ArrayLoader implements LoaderInterface
$package->setAutoload($config['autoload']);
}
+ if (isset($config['autoload-dev'])) {
+ $package->setDevAutoload($config['autoload-dev']);
+ }
+
if (isset($config['include-path'])) {
$package->setIncludePaths($config['include-path']);
}
diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php
index ba3f611c1..8fab59a8a 100644
--- a/src/Composer/Package/Package.php
+++ b/src/Composer/Package/Package.php
@@ -47,6 +47,7 @@ class Package extends BasePackage
protected $devRequires = array();
protected $suggests = array();
protected $autoload = array();
+ protected $devAutoload = array();
protected $includePaths = array();
protected $archiveExcludes = array();
@@ -440,6 +441,24 @@ class Package extends BasePackage
return $this->autoload;
}
+ /**
+ * Set the dev autoload mapping
+ *
+ * @param array $autoload Mapping of dev autoloading rules
+ */
+ public function setDevAutoload(array $devAutoload)
+ {
+ $this->devAutoload = $devAutoload;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getDevAutoload()
+ {
+ return $this->devAutoload;
+ }
+
/**
* Sets the list of paths added to PHP's include path.
*
diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php
index a3c8a2793..fd7393992 100644
--- a/src/Composer/Package/PackageInterface.php
+++ b/src/Composer/Package/PackageInterface.php
@@ -231,13 +231,25 @@ interface PackageInterface
*
* {"": {""}}
*
- * Type is either "psr-0" or "pear". Namespaces are mapped to directories
- * for autoloading using the type specified.
+ * Type is either "psr-4", "psr-0", "classmap" or "files". Namespaces are mapped to
+ * directories for autoloading using the type specified.
*
* @return array Mapping of autoloading rules
*/
public function getAutoload();
+ /**
+ * Returns an associative array of dev autoloading rules
+ *
+ * {"": {""}}
+ *
+ * Type is either "psr-4", "psr-0", "classmap" or "files". Namespaces are mapped to
+ * directories for autoloading using the type specified.
+ *
+ * @return array Mapping of dev autoloading rules
+ */
+ public function getDevAutoload();
+
/**
* Returns a list of directories which should get added to PHP's
* include path.
diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
index 9f1e2fb16..1bbf0b577 100644
--- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
+++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php
@@ -170,7 +170,75 @@ class AutoloadGeneratorTest extends TestCase
// Assert that autoload_classmap.php was correctly generated.
$this->assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap');
}
+
+ public function testMainPackageDevAutoloading()
+ {
+ $package = new Package('a', '1.0', '1.0');
+ $package->setAutoload(array(
+ 'psr-0' => array(
+ 'Main' => 'src/',
+ ),
+ ));
+ $package->setDevAutoload(array(
+ 'files' => array('devfiles/foo.php'),
+ ));
+ $this->repository->expects($this->once())
+ ->method('getCanonicalPackages')
+ ->will($this->returnValue(array()));
+
+ $this->fs->ensureDirectoryExists($this->workingDir.'/composer');
+ $this->fs->ensureDirectoryExists($this->workingDir.'/src/Main');
+ file_put_contents($this->workingDir.'/src/Main/ClassMain.php', 'fs->ensureDirectoryExists($this->workingDir.'/devfiles');
+ file_put_contents($this->workingDir.'/devfiles/foo.php', 'generator->setDevMode(true);
+ $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1');
+
+ // check standard autoload
+ $this->assertAutoloadFiles('main4', $this->vendorDir.'/composer');
+ $this->assertAutoloadFiles('classmap7', $this->vendorDir.'/composer', 'classmap');
+
+ // make sure dev autoload is correctly dumped
+ $this->assertAutoloadFiles('files2', $this->vendorDir.'/composer', 'files');
+ }
+
+ public function testMainPackageDevAutoloadingDisabledByDefault()
+ {
+ $package = new Package('a', '1.0', '1.0');
+ $package->setAutoload(array(
+ 'psr-0' => array(
+ 'Main' => 'src/',
+ ),
+ ));
+ $package->setDevAutoload(array(
+ 'files' => array('devfiles/foo.php'),
+ ));
+
+ $this->repository->expects($this->once())
+ ->method('getCanonicalPackages')
+ ->will($this->returnValue(array()));
+
+ $this->fs->ensureDirectoryExists($this->workingDir.'/composer');
+ $this->fs->ensureDirectoryExists($this->workingDir.'/src/Main');
+ file_put_contents($this->workingDir.'/src/Main/ClassMain.php', 'fs->ensureDirectoryExists($this->workingDir.'/devfiles');
+ file_put_contents($this->workingDir.'/devfiles/foo.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1');
+
+ // check standard autoload
+ $this->assertAutoloadFiles('main4', $this->vendorDir.'/composer');
+ $this->assertAutoloadFiles('classmap7', $this->vendorDir.'/composer', 'classmap');
+
+ // make sure dev autoload is disabled when dev mode is set to false
+ $this->assertFalse(is_file($this->vendorDir.'/composer/autoload_files.php'));
+ }
+
public function testVendorDirSameAsWorkingDir()
{
$this->vendorDir = $this->workingDir;
diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php
new file mode 100644
index 000000000..5768726d1
--- /dev/null
+++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php
@@ -0,0 +1,10 @@
+ $baseDir . '/src/Main/ClassMain.php',
+);
diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_files2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_files2.php
new file mode 100644
index 000000000..13cb9ecb3
--- /dev/null
+++ b/tests/Composer/Test/Autoload/Fixtures/autoload_files2.php
@@ -0,0 +1,10 @@
+ array($baseDir . '/src'),
+);
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index c5e16d625..12caaffac 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -13,6 +13,8 @@
error_reporting(E_ALL);
$loader = require __DIR__.'/../src/bootstrap.php';
+
+// to be removed
$loader->add('Composer\Test', __DIR__);
require __DIR__.'/Composer/TestCase.php';