diff -r 000000000000 -r 7f95f8617b0b vendor/bundles/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/bundles/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php Sat Sep 24 15:40:41 2011 +0200 @@ -0,0 +1,315 @@ + + * + * 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 Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator; +use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator; +use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; + +/** + * Generates bundles. + * + * @author Fabien Potencier + */ +class GenerateBundleCommand extends ContainerAwareCommand +{ + private $generator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->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'), + 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)', 'annotation'), + new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'), + )) + ->setDescription('Generates a bundle') + ->setHelp(<<generate:bundle 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 +(--namespace is the only one needed if you follow the +conventions): + +php app/console generate:bundle --namespace=Acme/BlogBundle + +Note that you can use / instead of \\ for the namespace delimiter to avoid any +problem. + +If you want to disable any user interaction, use --no-interaction but don't forget to pass all needed options: + +php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction + +Note that the bundle namespace must end with "Bundle". +EOT + ) + ->setName('generate:bundle') + ; + } + + /** + * @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) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + foreach (array('namespace', 'dir') as $option) { + if (null === $input->getOption($option)) { + throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); + } + } + + $namespace = Validators::validateBundleNamespace($input->getOption('namespace')); + if (!$bundle = $input->getOption('bundle-name')) { + $bundle = strtr($namespace, array('\\' => '')); + } + $bundle = Validators::validateBundleName($bundle); + $dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace); + $format = Validators::validateFormat($input->getOption('format')); + $structure = $input->getOption('structure'); + + $dialog->writeSection($output, 'Bundle generation'); + + if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { + $dir = getcwd().'/'.$dir; + } + + $generator = $this->getGenerator(); + $generator->generate($namespace, $bundle, $dir, $format, $structure); + + $output->writeln('Generating the bundle code: OK'); + + $errors = array(); + $runner = $dialog->getRunner($output, $errors); + + // check that the namespace is already autoloaded + $runner($this->checkAutoloader($output, $namespace, $bundle, $dir)); + + // register the bundle in the Kernel class + $runner($this->updateKernel($dialog, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle)); + + // routing + $runner($this->updateRouting($dialog, $input, $output, $bundle, $format)); + + $dialog->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Symfony2 bundle generator'); + + // namespace + $output->writeln(array( + '', + 'Your application code must be written in bundles. This command helps', + 'you generate them easily.', + '', + 'Each bundle is hosted under a namespace (like Acme/Bundle/BlogBundle).', + '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 Bundle as a suffix).', + '', + 'Use / instead of \\ for the namespace delimiter to avoid any problem.', + '', + )); + + $namespace = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace')); + $input->setOption('namespace', $namespace); + + // bundle name + $bundle = $input->getOption('bundle-name') ?: 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 '.$bundle.'.', + '', + )); + $bundle = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle name', $bundle), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'), false, $bundle); + $input->setOption('bundle-name', $bundle); + + // target dir + $dir = $input->getOption('dir') ?: dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src'; + $output->writeln(array( + '', + 'The bundle can be generated anywhere. The suggested default directory uses', + 'the standard conventions.', + '', + )); + $dir = $dialog->askAndValidate($output, $dialog->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir); + $input->setOption('dir', $dir); + + // format + $output->writeln(array( + '', + 'Determine the format to use for the generated configuration.', + '', + )); + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format')); + $input->setOption('format', $format); + + // optional files to generate + $output->writeln(array( + '', + 'To help you getting started faster, the command can generate some', + 'code snippets for you.', + '', + )); + + $structure = $input->getOption('structure'); + if (!$structure && $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) { + $structure = true; + } + $input->setOption('structure', $structure); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf("You are going to generate a \"%s\\%s\" bundle\nin \"%s\" using the \"%s\" format.", $namespace, $bundle, $dir, $format), + '', + )); + } + + protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir) + { + $output->write('Checking that the bundle is autoloaded: '); + if (!class_exists($namespace.'\\'.$bundle)) { + return array( + '- Edit the app/autoloader.php file and register the bundle', + ' namespace at the top of the registerNamespaces() call:', + '', + sprintf(' \'%s\' => \'%s\',', $namespace, realpath($dir)), + '', + ); + } + } + + protected function updateKernel($dialog, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true); + } + + $output->write('Enabling the bundle inside the Kernel: '); + $manip = new KernelManipulator($kernel); + try { + $ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false; + + if (!$ret) { + $reflected = new \ReflectionObject($kernel); + + return array( + sprintf('- Edit %s', $reflected->getFilename()), + ' and add the following bundle in the AppKernel::registerBundles() method:', + '', + sprintf(' new %s(),', $namespace.'\\'.$bundle), + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already defined in AppKernel::registerBundles().', $namespace.'\\'.$bundle), + '', + ); + } + } + + protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + } + + $output->write('Importing the bundle routing resource: '); + $routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml'); + try { + $ret = $auto ? $routing->addResource($bundle, $format) : false; + if (!$ret) { + if ('annotation' === $format) { + $help = sprintf(" resource: \"@%s/Resources/Controller/\"\n type: annotation", $bundle); + } else { + $help = sprintf(" resource: \"@%s/Resources/config/routing.%s\"\n", $bundle, $format); + } + $help .= " prefix: /\n"; + + return array( + '- Import the bundle\'s routing resource in the app main routing file:', + '', + sprintf(' %s:', $bundle), + $help, + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already imported.', $bundle), + '', + ); + } + } + + protected function getGenerator() + { + if (null === $this->generator) { + $this->generator = new BundleGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/bundle'); + } + + return $this->generator; + } + + public function setGenerator(BundleGenerator $generator) + { + $this->generator = $generator; + } + + protected function getDialogHelper() + { + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') { + $this->getHelperSet()->set($dialog = new DialogHelper()); + } + + return $dialog; + } +}