vendor/pimcore/pimcore/bundles/GeneratorBundle/Command/BaseGenerateBundleCommand.php line 36

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Bundle\GeneratorBundle\Command;
  15. use Pimcore\Bundle\GeneratorBundle\Generator\BundleGenerator;
  16. use Pimcore\Bundle\GeneratorBundle\Manipulator\ConfigurationManipulator;
  17. use Pimcore\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
  18. use Pimcore\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
  19. use Pimcore\Bundle\GeneratorBundle\Model\Bundle;
  20. use Symfony\Component\Console\Input\InputInterface;
  21. use Symfony\Component\Console\Input\InputOption;
  22. use Symfony\Component\Console\Output\OutputInterface;
  23. use Symfony\Component\Console\Question\ConfirmationQuestion;
  24. use Symfony\Component\Console\Question\Question;
  25. use Symfony\Component\HttpKernel\KernelInterface;
  26. /**
  27.  * @deprecated
  28.  * Generates bundles.
  29.  *
  30.  * The following class is copied from \Sensio\Bundle\GeneratorBundle\Command\GenerateBundleCommand
  31.  */
  32. class BaseGenerateBundleCommand extends BaseGeneratorCommand
  33. {
  34.     /**
  35.      * @see Command
  36.      */
  37.     protected function configure()
  38.     {
  39.         $this
  40.             ->setName('generate:bundle')
  41.             ->setDescription('Generates a bundle')
  42.             ->setDefinition([
  43.                 new InputOption('namespace'''InputOption::VALUE_REQUIRED'The namespace of the bundle to create'),
  44.                 new InputOption('dir'''InputOption::VALUE_REQUIRED'The directory where to create the bundle''src/'),
  45.                 new InputOption('bundle-name'''InputOption::VALUE_REQUIRED'The optional bundle name'),
  46.                 new InputOption('format'''InputOption::VALUE_REQUIRED'Use the format for configuration files (php, xml, yml, or annotation)'),
  47.                 new InputOption('shared'''InputOption::VALUE_NONE'Are you planning on sharing this bundle across multiple applications?'),
  48.             ])
  49.             ->setHelp(<<<EOT
  50. The <info>%command.name%</info> command helps you generates new bundles.
  51. By default, the command interacts with the developer to tweak the generation.
  52. Any passed option will be used as a default value for the interaction
  53. (<comment>--namespace</comment> is the only one needed if you follow the
  54. conventions):
  55. <info>php %command.full_name% --namespace=Acme/BlogBundle</info>
  56. Note that you can use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any
  57. problems.
  58. If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
  59. <info>php %command.full_name% --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
  60. Note that the bundle namespace must end with "Bundle".
  61. EOT
  62.             )
  63.         ;
  64.     }
  65.     /**
  66.      * @see Command
  67.      *
  68.      * @throws \InvalidArgumentException When namespace doesn't end with Bundle
  69.      * @throws \RuntimeException         When bundle can't be executed
  70.      *
  71.      * @return int
  72.      */
  73.     protected function execute(InputInterface $inputOutputInterface $output)
  74.     {
  75.         $questionHelper $this->getQuestionHelper();
  76.         $bundle $this->createBundleObject($input);
  77.         $questionHelper->writeSection($output'Bundle generation');
  78.         /** @var BundleGenerator $generator */
  79.         $generator $this->getGenerator();
  80.         $output->writeln(sprintf(
  81.             '> Generating a sample bundle skeleton into <info>%s</info>',
  82.             $this->makePathRelative($bundle->getTargetDirectory())
  83.         ));
  84.         $generator->generateBundle($bundle);
  85.         $errors = [];
  86.         $runner $questionHelper->getRunner($output$errors);
  87.         // check that the namespace is already autoloaded
  88.         $runner($this->checkAutoloader($output$bundle));
  89.         // register the bundle in the Kernel class
  90.         $runner($this->updateKernel($output$this->getContainer()->get('kernel'), $bundle));
  91.         // routing importing
  92.         $runner($this->updateRouting($output$bundle));
  93.         if (!$bundle->shouldGenerateDependencyInjectionDirectory()) {
  94.             // we need to import their services.yml manually!
  95.             $runner($this->updateConfiguration($output$bundle));
  96.         }
  97.         $questionHelper->writeGeneratorSummary($output$errors);
  98.         return 0;
  99.     }
  100.     protected function interact(InputInterface $inputOutputInterface $output)
  101.     {
  102.         $questionHelper $this->getQuestionHelper();
  103.         $questionHelper->writeSection($output'Welcome to the Symfony bundle generator!');
  104.         // shared option
  105.         $shared $input->getOption('shared');
  106.         // ask, but use $shared as the default
  107.         $question = new ConfirmationQuestion($questionHelper->getQuestion(
  108.             'Are you planning on sharing this bundle across multiple applications?',
  109.             $shared 'yes' 'no'
  110.         ), $shared);
  111.         $shared $questionHelper->ask($input$output$question);
  112.         $input->setOption('shared'$shared);
  113.         // namespace option
  114.         $namespace $input->getOption('namespace');
  115.         $output->writeln([
  116.             '',
  117.             'Your application code must be written in <comment>bundles</comment>. This command helps',
  118.             'you generate them easily.',
  119.             '',
  120.         ]);
  121.         $askForBundleName true;
  122.         if ($shared) {
  123.             // a shared bundle, so it should probably have a vendor namespace
  124.             $output->writeln([
  125.                 'Each bundle is hosted under a namespace (like <comment>Acme/BlogBundle</comment>).',
  126.                 'The namespace should begin with a "vendor" name like your company name, your',
  127.                 'project name, or your client name, followed by one or more optional category',
  128.                 'sub-namespaces, and it should end with the bundle name itself',
  129.                 '(which must have <comment>Bundle</comment> as a suffix).',
  130.                 '',
  131.                 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#bundle-name for more',
  132.                 'details on bundle naming conventions.',
  133.                 '',
  134.                 'Use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any problems.',
  135.                 '',
  136.             ]);
  137.             $question = new Question($questionHelper->getQuestion(
  138.                 'Bundle namespace',
  139.                 $namespace
  140.             ), $namespace);
  141.             $question->setValidator(function ($answer) {
  142.                 return Validators::validateBundleNamespace($answertrue);
  143.             });
  144.             $namespace $questionHelper->ask($input$output$question);
  145.         } else {
  146.             // a simple application bundle
  147.             $output->writeln([
  148.                 'Give your bundle a descriptive name, like <comment>BlogBundle</comment>.',
  149.             ]);
  150.             $question = new Question($questionHelper->getQuestion(
  151.                 'Bundle name',
  152.                 $namespace
  153.             ), $namespace);
  154.             $question->setValidator(function ($inputNamespace) {
  155.                 return Validators::validateBundleNamespace($inputNamespacefalse);
  156.             });
  157.             $namespace $questionHelper->ask($input$output$question);
  158.             if (strpos($namespace'\\') === false) {
  159.                 // this is a bundle name (FooBundle) not a namespace (Acme\FooBundle)
  160.                 // so this is the bundle name (and it is also the namespace)
  161.                 $input->setOption('bundle-name'$namespace);
  162.                 $askForBundleName false;
  163.             }
  164.         }
  165.         $input->setOption('namespace'$namespace);
  166.         // bundle-name option
  167.         if ($askForBundleName) {
  168.             $bundle $input->getOption('bundle-name');
  169.             // no bundle yet? Get a default from the namespace
  170.             if (!$bundle) {
  171.                 $bundle strtr($namespace, ['\\Bundle\\' => '''\\' => '']);
  172.             }
  173.             $output->writeln([
  174.                 '',
  175.                 'In your code, a bundle is often referenced by its name. It can be the',
  176.                 'concatenation of all namespace parts but it\'s really up to you to come',
  177.                 'up with a unique name (a good practice is to start with the vendor name).',
  178.                 'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
  179.                 '',
  180.             ]);
  181.             $question = new Question($questionHelper->getQuestion(
  182.                 'Bundle name',
  183.                 $bundle
  184.             ), $bundle);
  185.             $question->setValidator(
  186.                 ['Pimcore\Bundle\GeneratorBundle\Command\Validators''validateBundleName']
  187.             );
  188.             $bundle $questionHelper->ask($input$output$question);
  189.             $input->setOption('bundle-name'$bundle);
  190.         }
  191.         // dir option
  192.         // defaults to src/ in the option
  193.         $dir $input->getOption('dir');
  194.         $output->writeln([
  195.             '',
  196.             'Bundles are usually generated into the <info>src/</info> directory. Unless you\'re',
  197.             'doing something custom, hit enter to keep this default!',
  198.             '',
  199.         ]);
  200.         $question = new Question($questionHelper->getQuestion(
  201.             'Target Directory',
  202.             $dir
  203.         ), $dir);
  204.         $dir $questionHelper->ask($input$output$question);
  205.         $input->setOption('dir'$dir);
  206.         // format option
  207.         $format $input->getOption('format');
  208.         if (!$format) {
  209.             $format $shared 'xml' 'annotation';
  210.         }
  211.         $output->writeln([
  212.             '',
  213.             'What format do you want to use for your generated configuration?',
  214.             '',
  215.         ]);
  216.         $question = new Question($questionHelper->getQuestion(
  217.             'Configuration format (annotation, yml, xml, php)',
  218.             $format
  219.         ), $format);
  220.         $question->setValidator(function ($format) {
  221.             return Validators::validateFormat($format);
  222.         });
  223.         $question->setAutocompleterValues(['annotation''yml''xml''php']);
  224.         $format $questionHelper->ask($input$output$question);
  225.         $input->setOption('format'$format);
  226.     }
  227.     protected function checkAutoloader(OutputInterface $outputBundle $bundle)
  228.     {
  229.         $output->writeln('> Checking that the bundle is autoloaded');
  230.         if (!class_exists($bundle->getBundleClassName())) {
  231.             return [
  232.                 '- Edit the <comment>composer.json</comment> file and register the bundle',
  233.                 '  namespace in the "autoload" section:',
  234.                 '',
  235.             ];
  236.         }
  237.     }
  238.     protected function updateKernel(OutputInterface $outputKernelInterface $kernelBundle $bundle)
  239.     {
  240.         $kernelManipulator = new KernelManipulator($kernel);
  241.         $output->writeln(sprintf(
  242.             '> Enabling the bundle inside <info>%s</info>',
  243.             $this->makePathRelative($kernelManipulator->getFilename())
  244.         ));
  245.         try {
  246.             $ret $kernelManipulator->addBundle($bundle->getBundleClassName());
  247.             if (!$ret) {
  248.                 $reflected = new \ReflectionObject($kernel);
  249.                 return [
  250.                     sprintf('- Edit <comment>%s</comment>'$reflected->getFilename()),
  251.                     '  and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
  252.                     '',
  253.                     sprintf('    <comment>new %s(),</comment>'$bundle->getBundleClassName()),
  254.                     '',
  255.                 ];
  256.             }
  257.         } catch (\RuntimeException $e) {
  258.             return [
  259.                 sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.'$bundle->getBundleClassName()),
  260.                 '',
  261.             ];
  262.         }
  263.     }
  264.     protected function updateRouting(OutputInterface $outputBundle $bundle)
  265.     {
  266.         $targetRoutingPath $this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml';
  267.         $output->writeln(sprintf(
  268.             '> Importing the bundle\'s routes from the <info>%s</info> file',
  269.             $this->makePathRelative($targetRoutingPath)
  270.         ));
  271.         $routing = new RoutingManipulator($targetRoutingPath);
  272.         try {
  273.             $ret $routing->addResource($bundle->getName(), $bundle->getConfigurationFormat());
  274.             if (!$ret) {
  275.                 if ('annotation' === $bundle->getConfigurationFormat()) {
  276.                     $help sprintf("        <comment>resource: \"@%s/Controller/\"</comment>\n        <comment>type:     annotation</comment>\n"$bundle->getName());
  277.                 } else {
  278.                     $help sprintf("        <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n"$bundle->getName(), $bundle->getConfigurationFormat());
  279.                 }
  280.                 $help .= "        <comment>prefix:   /</comment>\n";
  281.                 return [
  282.                     '- Import the bundle\'s routing resource in the app\'s main routing file:',
  283.                     '',
  284.                     sprintf('    <comment>%s:</comment>'$bundle->getName()),
  285.                     $help,
  286.                     '',
  287.                 ];
  288.             }
  289.         } catch (\RuntimeException $e) {
  290.             return [
  291.                 sprintf('Bundle <comment>%s</comment> is already imported.'$bundle->getName()),
  292.                 '',
  293.             ];
  294.         }
  295.     }
  296.     protected function updateConfiguration(OutputInterface $outputBundle $bundle)
  297.     {
  298.         $targetConfigurationPath $this->getContainer()->getParameter('kernel.root_dir').'/config/config.yml';
  299.         $output->writeln(sprintf(
  300.             '> Importing the bundle\'s %s from the <info>%s</info> file',
  301.             $bundle->getServicesConfigurationFilename(),
  302.             $this->makePathRelative($targetConfigurationPath)
  303.         ));
  304.         $manipulator = new ConfigurationManipulator($targetConfigurationPath);
  305.         try {
  306.             $manipulator->addResource($bundle);
  307.         } catch (\RuntimeException $e) {
  308.             return [
  309.                 sprintf('- Import the bundle\'s "%s" resource in the app\'s main configuration file:'$bundle->getServicesConfigurationFilename()),
  310.                 '',
  311.                 $manipulator->getImportCode($bundle),
  312.                 '',
  313.             ];
  314.         }
  315.     }
  316.     /**
  317.      * Creates the Bundle object based on the user's (non-interactive) input.
  318.      *
  319.      * @param InputInterface $input
  320.      *
  321.      * @return Bundle
  322.      */
  323.     protected function createBundleObject(InputInterface $input)
  324.     {
  325.         foreach (['namespace''dir'] as $option) {
  326.             if (null === $input->getOption($option)) {
  327.                 throw new \RuntimeException(sprintf('The "%s" option must be provided.'$option));
  328.             }
  329.         }
  330.         $shared $input->getOption('shared');
  331.         $namespace Validators::validateBundleNamespace($input->getOption('namespace'), $shared);
  332.         if (!$bundleName $input->getOption('bundle-name')) {
  333.             $bundleName strtr($namespace, ['\\' => '']);
  334.         }
  335.         $bundleName Validators::validateBundleName($bundleName);
  336.         $dir $input->getOption('dir');
  337.         if (null === $input->getOption('format')) {
  338.             $input->setOption('format''annotation');
  339.         }
  340.         $format Validators::validateFormat($input->getOption('format'));
  341.         // an assumption that the kernel root dir is in a directory (like app/)
  342.         $projectRootDirectory $this->getContainer()->getParameter('kernel.root_dir').'/..';
  343.         if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
  344.             $dir $projectRootDirectory.'/'.$dir;
  345.         }
  346.         // add trailing / if necessary
  347.         $dir '/' === substr($dir, -11) ? $dir $dir.'/';
  348.         $bundle = new Bundle(
  349.             $namespace,
  350.             $bundleName,
  351.             $dir,
  352.             $format,
  353.             $shared
  354.         );
  355.         // not shared - put the tests in the root
  356.         if (!$shared) {
  357.             $testsDir $projectRootDirectory.'/tests/'.$bundleName;
  358.             $bundle->setTestsDirectory($testsDir);
  359.         }
  360.         return $bundle;
  361.     }
  362.     protected function createGenerator()
  363.     {
  364.         return new BundleGenerator();
  365.     }
  366. }