"composer init --autoload" - Interactive generates PSR-4 autoloader in composer.json (#9829)
- Generates PSR-4 autoload entry in composer.json. - Run dump-autoload, if no dependencies are setpull/9848/head
parent
e291aa1d12
commit
bb1bb022e4
|
@ -65,6 +65,7 @@ php composer.phar init
|
||||||
the list of requires. Every repository can be either an HTTP URL pointing
|
the list of requires. Every repository can be either an HTTP URL pointing
|
||||||
to a `composer` repository or a JSON string which similar to what the
|
to a `composer` repository or a JSON string which similar to what the
|
||||||
[repositories](04-schema.md#repositories) key accepts.
|
[repositories](04-schema.md#repositories) key accepts.
|
||||||
|
* **--autoload (-a):** Add a PSR-4 autoload mapping to the composer.json. Automatically maps your package's namespace to the provided directory. (Expects a relative path, e.g. src/) See also [PSR-4 autoload](04-schema.md#psr-4).
|
||||||
|
|
||||||
## install / i
|
## install / i
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ use Composer\Repository\CompositeRepository;
|
||||||
use Composer\Repository\PlatformRepository;
|
use Composer\Repository\PlatformRepository;
|
||||||
use Composer\Repository\RepositoryFactory;
|
use Composer\Repository\RepositoryFactory;
|
||||||
use Composer\Repository\RepositorySet;
|
use Composer\Repository\RepositorySet;
|
||||||
|
use Composer\Util\Filesystem;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Semver\Constraint\Constraint;
|
use Composer\Semver\Constraint\Constraint;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
@ -69,6 +70,7 @@ class InitCommand extends BaseCommand
|
||||||
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'),
|
new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum stability (empty or one of: '.implode(', ', array_keys(BasePackage::$stabilities)).')'),
|
||||||
new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'),
|
new InputOption('license', 'l', InputOption::VALUE_REQUIRED, 'License of package'),
|
||||||
new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'),
|
new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories, either by URL or using JSON arrays'),
|
||||||
|
new InputOption('autoload', 'a', InputOption::VALUE_REQUIRED, 'Add PSR-4 autoload mapping. Maps your package\'s namespace to the provided directory. (Expects a relative path, e.g. src/)'),
|
||||||
))
|
))
|
||||||
->setHelp(
|
->setHelp(
|
||||||
<<<EOT
|
<<<EOT
|
||||||
|
@ -90,7 +92,7 @@ EOT
|
||||||
{
|
{
|
||||||
$io = $this->getIO();
|
$io = $this->getIO();
|
||||||
|
|
||||||
$allowlist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license');
|
$allowlist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license', 'autoload');
|
||||||
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($allowlist)));
|
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($allowlist)));
|
||||||
|
|
||||||
if (isset($options['author'])) {
|
if (isset($options['author'])) {
|
||||||
|
@ -123,6 +125,18 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --autoload - create autoload object
|
||||||
|
$autoloadPath = null;
|
||||||
|
if (isset($options['autoload'])) {
|
||||||
|
$autoloadPath = $options['autoload'];
|
||||||
|
$namespace = $this->namespaceFromPackageName($input->getOption('name'));
|
||||||
|
$options['autoload'] = (object) array(
|
||||||
|
'psr-4' => array(
|
||||||
|
$namespace . '\\' => $autoloadPath,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$file = new JsonFile(Factory::getComposerFile());
|
$file = new JsonFile(Factory::getComposerFile());
|
||||||
$json = JsonFile::encode($options);
|
$json = JsonFile::encode($options);
|
||||||
|
|
||||||
|
@ -143,6 +157,17 @@ EOT
|
||||||
|
|
||||||
$file->write($options);
|
$file->write($options);
|
||||||
|
|
||||||
|
// --autoload - Create src folder
|
||||||
|
if ($autoloadPath) {
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
$filesystem->ensureDirectoryExists($autoloadPath);
|
||||||
|
|
||||||
|
// dump-autoload only for projects without added dependencies.
|
||||||
|
if (!$this->hasDependencies($options)) {
|
||||||
|
$this->runDumpAutoloadCommand($output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($input->isInteractive() && is_dir('.git')) {
|
if ($input->isInteractive() && is_dir('.git')) {
|
||||||
$ignoreFile = realpath('.gitignore');
|
$ignoreFile = realpath('.gitignore');
|
||||||
|
|
||||||
|
@ -164,6 +189,14 @@ EOT
|
||||||
$this->installDependencies($output);
|
$this->installDependencies($output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --autoload - Show post-install configuration info
|
||||||
|
if ($autoloadPath) {
|
||||||
|
$namespace = $this->namespaceFromPackageName($input->getOption('name'));
|
||||||
|
|
||||||
|
$io->writeError('PSR-4 autoloading configured. Use "<comment>namespace '.$namespace.';</comment>" in '.$autoloadPath);
|
||||||
|
$io->writeError('Include the Composer autoloader with: <comment>require \'vendor/autoload.php\';</comment>');
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,6 +414,37 @@ EOT
|
||||||
$devRequirements = $this->determineRequirements($input, $output, $requireDev, $platformRepo, $preferredStability);
|
$devRequirements = $this->determineRequirements($input, $output, $requireDev, $platformRepo, $preferredStability);
|
||||||
}
|
}
|
||||||
$input->setOption('require-dev', $devRequirements);
|
$input->setOption('require-dev', $devRequirements);
|
||||||
|
|
||||||
|
// --autoload - input and validation
|
||||||
|
$autoload = $input->getOption('autoload') ?: 'src/';
|
||||||
|
$namespace = $this->namespaceFromPackageName($input->getOption('name'));
|
||||||
|
$autoload = $io->askAndValidate(
|
||||||
|
'Add PSR-4 autoload mapping? Maps namespace "'.$namespace.'" to the entered relative path. [<comment>'.$autoload.'</comment>, n to skip]: ',
|
||||||
|
function ($value) use ($autoload) {
|
||||||
|
if (null === $value) {
|
||||||
|
return $autoload;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === 'n' || $value === 'no') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $value ?: $autoload;
|
||||||
|
|
||||||
|
if (!preg_match('{^[^/][A-Za-z0-9\-_/]+/$}', $value)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'The src folder name "%s" is invalid. Please add a relative path with tailing forward slash. [A-Za-z0-9_-/]+/',
|
||||||
|
$value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
$autoload
|
||||||
|
);
|
||||||
|
$input->setOption('autoload', $autoload);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -593,6 +657,33 @@ EOT
|
||||||
return array($this->parseAuthorString($author));
|
return array($this->parseAuthorString($author));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract namespace from package's vendor name.
|
||||||
|
*
|
||||||
|
* new_projects.acme-extra/package-name becomes "NewProjectsAcmeExtra\PackageName"
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function namespaceFromPackageName($packageName)
|
||||||
|
{
|
||||||
|
if (!$packageName || strpos($packageName, '/') === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$namespace = array_map(
|
||||||
|
function($part) {
|
||||||
|
$part = preg_replace('/[^a-z0-9]/i', ' ', $part);
|
||||||
|
$part = ucwords($part);
|
||||||
|
return str_replace(' ', '', $part);
|
||||||
|
},
|
||||||
|
explode('/', $packageName)
|
||||||
|
);
|
||||||
|
|
||||||
|
return join('\\', $namespace);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getGitConfig()
|
protected function getGitConfig()
|
||||||
{
|
{
|
||||||
if (null !== $this->gitConfig) {
|
if (null !== $this->gitConfig) {
|
||||||
|
@ -882,6 +973,16 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function runDumpAutoloadCommand($output)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$command = $this->getApplication()->find('dump-autoload');
|
||||||
|
$command->run(new ArrayInput(array()), $output);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->getIO()->writeError('Could not run dump-autoload.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function hasDependencies($options)
|
private function hasDependencies($options)
|
||||||
{
|
{
|
||||||
$requires = (array) $options['require'];
|
$requires = (array) $options['require'];
|
||||||
|
|
|
@ -92,4 +92,26 @@ class InitCommandTest extends TestCase
|
||||||
$this->setExpectedException('InvalidArgumentException');
|
$this->setExpectedException('InvalidArgumentException');
|
||||||
$command->parseAuthorString('John Smith <john>');
|
$command->parseAuthorString('John Smith <john>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNamespaceFromValidPackageName()
|
||||||
|
{
|
||||||
|
$command = new InitCommand;
|
||||||
|
$namespace = $command->namespaceFromPackageName('new_projects.acme-extra/package-name');
|
||||||
|
$this->assertEquals('NewProjectsAcmeExtra\PackageName', $namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNamespaceFromInvalidPackageName()
|
||||||
|
{
|
||||||
|
$command = new InitCommand;
|
||||||
|
$namespace = $command->namespaceFromPackageName('invalid-package-name');
|
||||||
|
$this->assertNull($namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNamespaceFromMissingPackageName()
|
||||||
|
{
|
||||||
|
$command = new InitCommand;
|
||||||
|
$namespace = $command->namespaceFromPackageName(null);
|
||||||
|
$this->assertNull($namespace);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue