1
0
Fork 0

"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 set
pull/9848/head
Andreas Scheibel 2021-04-20 21:58:38 +02:00 committed by GitHub
parent e291aa1d12
commit bb1bb022e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 1 deletions

View File

@ -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

View File

@ -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'];

View File

@ -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);
}
} }