Update
This commit is contained in:
parent
a37785b391
commit
33458b2ca3
9915 changed files with 1247019 additions and 0 deletions
|
|
@ -0,0 +1,419 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Sensio\Bundle\GeneratorBundle\Command;
|
||||
|
||||
use Sensio\Bundle\GeneratorBundle\Manipulator\ConfigurationManipulator;
|
||||
use Sensio\Bundle\GeneratorBundle\Model\Bundle;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator;
|
||||
use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
|
||||
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
|
||||
|
||||
/**
|
||||
* Generates bundles.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class GenerateBundleCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* @see Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('generate:bundle')
|
||||
->setDescription('Generates a bundle')
|
||||
->setDefinition(array(
|
||||
new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'),
|
||||
new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle', 'src/'),
|
||||
new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'),
|
||||
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)'),
|
||||
new InputOption('shared', '', InputOption::VALUE_NONE, 'Are you planning on sharing this bundle across multiple applications?'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
The <info>%command.name%</info> command helps you generates new bundles.
|
||||
|
||||
By default, the command interacts with the developer to tweak the generation.
|
||||
Any passed option will be used as a default value for the interaction
|
||||
(<comment>--namespace</comment> is the only one needed if you follow the
|
||||
conventions):
|
||||
|
||||
<info>php %command.full_name% --namespace=Acme/BlogBundle</info>
|
||||
|
||||
Note that you can use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any
|
||||
problems.
|
||||
|
||||
If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
|
||||
|
||||
<info>php %command.full_name% --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
|
||||
|
||||
Note that the bundle namespace must end with "Bundle".
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Command
|
||||
*
|
||||
* @throws \InvalidArgumentException When namespace doesn't end with Bundle
|
||||
* @throws \RuntimeException When bundle can't be executed
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$questionHelper = $this->getQuestionHelper();
|
||||
|
||||
$bundle = $this->createBundleObject($input);
|
||||
$questionHelper->writeSection($output, 'Bundle generation');
|
||||
|
||||
/** @var BundleGenerator $generator */
|
||||
$generator = $this->getGenerator();
|
||||
|
||||
$output->writeln(sprintf(
|
||||
'> Generating a sample bundle skeleton into <info>%s</info>',
|
||||
$this->makePathRelative($bundle->getTargetDirectory())
|
||||
));
|
||||
$generator->generateBundle($bundle);
|
||||
|
||||
$errors = array();
|
||||
$runner = $questionHelper->getRunner($output, $errors);
|
||||
|
||||
// check that the namespace is already autoloaded
|
||||
$runner($this->checkAutoloader($output, $bundle));
|
||||
|
||||
// register the bundle in the Kernel class
|
||||
$runner($this->updateKernel($output, $this->getContainer()->get('kernel'), $bundle));
|
||||
|
||||
// routing importing
|
||||
$runner($this->updateRouting($output, $bundle));
|
||||
|
||||
if (!$bundle->shouldGenerateDependencyInjectionDirectory()) {
|
||||
// we need to import their services.yml manually!
|
||||
$runner($this->updateConfiguration($output, $bundle));
|
||||
}
|
||||
|
||||
$questionHelper->writeGeneratorSummary($output, $errors);
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$questionHelper = $this->getQuestionHelper();
|
||||
$questionHelper->writeSection($output, 'Welcome to the Symfony bundle generator!');
|
||||
|
||||
/*
|
||||
* shared option
|
||||
*/
|
||||
$shared = $input->getOption('shared');
|
||||
// ask, but use $shared as the default
|
||||
$question = new ConfirmationQuestion($questionHelper->getQuestion(
|
||||
'Are you planning on sharing this bundle across multiple applications?',
|
||||
$shared ? 'yes' : 'no'
|
||||
), $shared);
|
||||
$shared = $questionHelper->ask($input, $output, $question);
|
||||
$input->setOption('shared', $shared);
|
||||
|
||||
/*
|
||||
* namespace option
|
||||
*/
|
||||
$namespace = $input->getOption('namespace');
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'Your application code must be written in <comment>bundles</comment>. This command helps',
|
||||
'you generate them easily.',
|
||||
'',
|
||||
));
|
||||
|
||||
$askForBundleName = true;
|
||||
if ($shared) {
|
||||
// a shared bundle, so it should probably have a vendor namespace
|
||||
$output->writeln(array(
|
||||
'Each bundle is hosted under a namespace (like <comment>Acme/BlogBundle</comment>).',
|
||||
'The namespace should begin with a "vendor" name like your company name, your',
|
||||
'project name, or your client name, followed by one or more optional category',
|
||||
'sub-namespaces, and it should end with the bundle name itself',
|
||||
'(which must have <comment>Bundle</comment> as a suffix).',
|
||||
'',
|
||||
'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#bundle-name for more',
|
||||
'details on bundle naming conventions.',
|
||||
'',
|
||||
'Use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any problems.',
|
||||
'',
|
||||
));
|
||||
|
||||
$question = new Question($questionHelper->getQuestion(
|
||||
'Bundle namespace',
|
||||
$namespace
|
||||
), $namespace);
|
||||
$question->setValidator(function ($answer) {
|
||||
return Validators::validateBundleNamespace($answer, true);
|
||||
});
|
||||
$namespace = $questionHelper->ask($input, $output, $question);
|
||||
} else {
|
||||
// a simple application bundle
|
||||
$output->writeln(array(
|
||||
'Give your bundle a descriptive name, like <comment>BlogBundle</comment>.',
|
||||
));
|
||||
|
||||
$question = new Question($questionHelper->getQuestion(
|
||||
'Bundle name',
|
||||
$namespace
|
||||
), $namespace);
|
||||
$question->setValidator(function ($inputNamespace) {
|
||||
return Validators::validateBundleNamespace($inputNamespace, false);
|
||||
});
|
||||
$namespace = $questionHelper->ask($input, $output, $question);
|
||||
|
||||
if (strpos($namespace, '\\') === false) {
|
||||
// this is a bundle name (FooBundle) not a namespace (Acme\FooBundle)
|
||||
// so this is the bundle name (and it is also the namespace)
|
||||
$input->setOption('bundle-name', $namespace);
|
||||
$askForBundleName = false;
|
||||
}
|
||||
}
|
||||
$input->setOption('namespace', $namespace);
|
||||
|
||||
/*
|
||||
* bundle-name option
|
||||
*/
|
||||
if ($askForBundleName) {
|
||||
$bundle = $input->getOption('bundle-name');
|
||||
// no bundle yet? Get a default from the namespace
|
||||
if (!$bundle) {
|
||||
$bundle = strtr($namespace, array('\\Bundle\\' => '', '\\' => ''));
|
||||
}
|
||||
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'In your code, a bundle is often referenced by its name. It can be the',
|
||||
'concatenation of all namespace parts but it\'s really up to you to come',
|
||||
'up with a unique name (a good practice is to start with the vendor name).',
|
||||
'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
|
||||
'',
|
||||
));
|
||||
$question = new Question($questionHelper->getQuestion(
|
||||
'Bundle name',
|
||||
$bundle
|
||||
), $bundle);
|
||||
$question->setValidator(
|
||||
array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName')
|
||||
);
|
||||
$bundle = $questionHelper->ask($input, $output, $question);
|
||||
$input->setOption('bundle-name', $bundle);
|
||||
}
|
||||
|
||||
/*
|
||||
* dir option
|
||||
*/
|
||||
// defaults to src/ in the option
|
||||
$dir = $input->getOption('dir');
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'Bundles are usually generated into the <info>src/</info> directory. Unless you\'re',
|
||||
'doing something custom, hit enter to keep this default!',
|
||||
'',
|
||||
));
|
||||
|
||||
$question = new Question($questionHelper->getQuestion(
|
||||
'Target Directory',
|
||||
$dir
|
||||
), $dir);
|
||||
$dir = $questionHelper->ask($input, $output, $question);
|
||||
$input->setOption('dir', $dir);
|
||||
|
||||
/*
|
||||
* format option
|
||||
*/
|
||||
$format = $input->getOption('format');
|
||||
if (!$format) {
|
||||
$format = $shared ? 'xml' : 'annotation';
|
||||
}
|
||||
$output->writeln(array(
|
||||
'',
|
||||
'What format do you want to use for your generated configuration?',
|
||||
'',
|
||||
));
|
||||
|
||||
$question = new Question($questionHelper->getQuestion(
|
||||
'Configuration format (annotation, yml, xml, php)',
|
||||
$format
|
||||
), $format);
|
||||
$question->setValidator(function ($format) {
|
||||
return Validators::validateFormat($format);
|
||||
});
|
||||
$question->setAutocompleterValues(array('annotation', 'yml', 'xml', 'php'));
|
||||
$format = $questionHelper->ask($input, $output, $question);
|
||||
$input->setOption('format', $format);
|
||||
}
|
||||
|
||||
protected function checkAutoloader(OutputInterface $output, Bundle $bundle)
|
||||
{
|
||||
$output->writeln('> Checking that the bundle is autoloaded');
|
||||
if (!class_exists($bundle->getBundleClassName())) {
|
||||
return array(
|
||||
'- Edit the <comment>composer.json</comment> file and register the bundle',
|
||||
' namespace in the "autoload" section:',
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateKernel(OutputInterface $output, KernelInterface $kernel, Bundle $bundle)
|
||||
{
|
||||
$kernelManipulator = new KernelManipulator($kernel);
|
||||
|
||||
$output->writeln(sprintf(
|
||||
'> Enabling the bundle inside <info>%s</info>',
|
||||
$this->makePathRelative($kernelManipulator->getFilename())
|
||||
));
|
||||
|
||||
try {
|
||||
$ret = $kernelManipulator->addBundle($bundle->getBundleClassName());
|
||||
|
||||
if (!$ret) {
|
||||
$reflected = new \ReflectionObject($kernel);
|
||||
|
||||
return array(
|
||||
sprintf('- Edit <comment>%s</comment>', $reflected->getFilename()),
|
||||
' and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
|
||||
'',
|
||||
sprintf(' <comment>new %s(),</comment>', $bundle->getBundleClassName()),
|
||||
'',
|
||||
);
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
return array(
|
||||
sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.', $bundle->getBundleClassName()),
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateRouting(OutputInterface $output, Bundle $bundle)
|
||||
{
|
||||
$targetRoutingPath = $this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml';
|
||||
$output->writeln(sprintf(
|
||||
'> Importing the bundle\'s routes from the <info>%s</info> file',
|
||||
$this->makePathRelative($targetRoutingPath)
|
||||
));
|
||||
$routing = new RoutingManipulator($targetRoutingPath);
|
||||
try {
|
||||
$ret = $routing->addResource($bundle->getName(), $bundle->getConfigurationFormat());
|
||||
if (!$ret) {
|
||||
if ('annotation' === $bundle->getConfigurationFormat()) {
|
||||
$help = sprintf(" <comment>resource: \"@%s/Controller/\"</comment>\n <comment>type: annotation</comment>\n", $bundle->getName());
|
||||
} else {
|
||||
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n", $bundle->getName(), $bundle->getConfigurationFormat());
|
||||
}
|
||||
$help .= " <comment>prefix: /</comment>\n";
|
||||
|
||||
return array(
|
||||
'- Import the bundle\'s routing resource in the app\'s main routing file:',
|
||||
'',
|
||||
sprintf(' <comment>%s:</comment>', $bundle->getName()),
|
||||
$help,
|
||||
'',
|
||||
);
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
return array(
|
||||
sprintf('Bundle <comment>%s</comment> is already imported.', $bundle->getName()),
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateConfiguration(OutputInterface $output, Bundle $bundle)
|
||||
{
|
||||
$targetConfigurationPath = $this->getContainer()->getParameter('kernel.root_dir').'/config/config.yml';
|
||||
$output->writeln(sprintf(
|
||||
'> Importing the bundle\'s %s from the <info>%s</info> file',
|
||||
$bundle->getServicesConfigurationFilename(),
|
||||
$this->makePathRelative($targetConfigurationPath)
|
||||
));
|
||||
$manipulator = new ConfigurationManipulator($targetConfigurationPath);
|
||||
try {
|
||||
$manipulator->addResource($bundle);
|
||||
} catch (\RuntimeException $e) {
|
||||
return array(
|
||||
sprintf('- Import the bundle\'s "%s" resource in the app\'s main configuration file:', $bundle->getServicesConfigurationFilename()),
|
||||
'',
|
||||
$manipulator->getImportCode($bundle),
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Bundle object based on the user's (non-interactive) input.
|
||||
*
|
||||
* @param InputInterface $input
|
||||
*
|
||||
* @return Bundle
|
||||
*/
|
||||
protected function createBundleObject(InputInterface $input)
|
||||
{
|
||||
foreach (array('namespace', 'dir') as $option) {
|
||||
if (null === $input->getOption($option)) {
|
||||
throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option));
|
||||
}
|
||||
}
|
||||
|
||||
$shared = $input->getOption('shared');
|
||||
|
||||
$namespace = Validators::validateBundleNamespace($input->getOption('namespace'), $shared);
|
||||
if (!$bundleName = $input->getOption('bundle-name')) {
|
||||
$bundleName = strtr($namespace, array('\\' => ''));
|
||||
}
|
||||
$bundleName = Validators::validateBundleName($bundleName);
|
||||
$dir = $input->getOption('dir');
|
||||
if (null === $input->getOption('format')) {
|
||||
$input->setOption('format', 'annotation');
|
||||
}
|
||||
$format = Validators::validateFormat($input->getOption('format'));
|
||||
|
||||
// an assumption that the kernel root dir is in a directory (like app/)
|
||||
$projectRootDirectory = $this->getContainer()->getParameter('kernel.root_dir').'/..';
|
||||
|
||||
if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
|
||||
$dir = $projectRootDirectory.'/'.$dir;
|
||||
}
|
||||
// add trailing / if necessary
|
||||
$dir = '/' === substr($dir, -1, 1) ? $dir : $dir.'/';
|
||||
|
||||
$bundle = new Bundle(
|
||||
$namespace,
|
||||
$bundleName,
|
||||
$dir,
|
||||
$format,
|
||||
$shared
|
||||
);
|
||||
|
||||
// not shared - put the tests in the root
|
||||
if (!$shared) {
|
||||
$testsDir = $projectRootDirectory.'/tests/'.$bundleName;
|
||||
$bundle->setTestsDirectory($testsDir);
|
||||
}
|
||||
|
||||
return $bundle;
|
||||
}
|
||||
|
||||
protected function createGenerator()
|
||||
{
|
||||
return new BundleGenerator($this->getContainer()->get('filesystem'));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue