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 PEL
  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.         /*
  105.          * shared option
  106.          */
  107.         $shared $input->getOption('shared');
  108.         // ask, but use $shared as the default
  109.         $question = new ConfirmationQuestion($questionHelper->getQuestion(
  110.             'Are you planning on sharing this bundle across multiple applications?',
  111.             $shared 'yes' 'no'
  112.         ), $shared);
  113.         $shared $questionHelper->ask($input$output$question);
  114.         $input->setOption('shared'$shared);
  115.         /*
  116.          * namespace option
  117.          */
  118.         $namespace $input->getOption('namespace');
  119.         $output->writeln([
  120.             '',
  121.             'Your application code must be written in <comment>bundles</comment>. This command helps',
  122.             'you generate them easily.',
  123.             '',
  124.         ]);
  125.         $askForBundleName true;
  126.         if ($shared) {
  127.             // a shared bundle, so it should probably have a vendor namespace
  128.             $output->writeln([
  129.                 'Each bundle is hosted under a namespace (like <comment>Acme/BlogBundle</comment>).',
  130.                 'The namespace should begin with a "vendor" name like your company name, your',
  131.                 'project name, or your client name, followed by one or more optional category',
  132.                 'sub-namespaces, and it should end with the bundle name itself',
  133.                 '(which must have <comment>Bundle</comment> as a suffix).',
  134.                 '',
  135.                 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#bundle-name for more',
  136.                 'details on bundle naming conventions.',
  137.                 '',
  138.                 'Use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any problems.',
  139.                 '',
  140.             ]);
  141.             $question = new Question($questionHelper->getQuestion(
  142.                 'Bundle namespace',
  143.                 $namespace
  144.             ), $namespace);
  145.             $question->setValidator(function ($answer) {
  146.                 return Validators::validateBundleNamespace($answertrue);
  147.             });
  148.             $namespace $questionHelper->ask($input$output$question);
  149.         } else {
  150.             // a simple application bundle
  151.             $output->writeln([
  152.                 'Give your bundle a descriptive name, like <comment>BlogBundle</comment>.',
  153.             ]);
  154.             $question = new Question($questionHelper->getQuestion(
  155.                 'Bundle name',
  156.                 $namespace
  157.             ), $namespace);
  158.             $question->setValidator(function ($inputNamespace) {
  159.                 return Validators::validateBundleNamespace($inputNamespacefalse);
  160.             });
  161.             $namespace $questionHelper->ask($input$output$question);
  162.             if (strpos($namespace'\\') === false) {
  163.                 // this is a bundle name (FooBundle) not a namespace (Acme\FooBundle)
  164.                 // so this is the bundle name (and it is also the namespace)
  165.                 $input->setOption('bundle-name'$namespace);
  166.                 $askForBundleName false;
  167.             }
  168.         }
  169.         $input->setOption('namespace'$namespace);
  170.         /*
  171.          * bundle-name option
  172.          */
  173.         if ($askForBundleName) {
  174.             $bundle $input->getOption('bundle-name');
  175.             // no bundle yet? Get a default from the namespace
  176.             if (!$bundle) {
  177.                 $bundle strtr($namespace, ['\\Bundle\\' => '''\\' => '']);
  178.             }
  179.             $output->writeln([
  180.                 '',
  181.                 'In your code, a bundle is often referenced by its name. It can be the',
  182.                 'concatenation of all namespace parts but it\'s really up to you to come',
  183.                 'up with a unique name (a good practice is to start with the vendor name).',
  184.                 'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
  185.                 '',
  186.             ]);
  187.             $question = new Question($questionHelper->getQuestion(
  188.                 'Bundle name',
  189.                 $bundle
  190.             ), $bundle);
  191.             $question->setValidator(
  192.                 ['Pimcore\Bundle\GeneratorBundle\Command\Validators''validateBundleName']
  193.             );
  194.             $bundle $questionHelper->ask($input$output$question);
  195.             $input->setOption('bundle-name'$bundle);
  196.         }
  197.         /*
  198.          * dir option
  199.          */
  200.         // defaults to src/ in the option
  201.         $dir $input->getOption('dir');
  202.         $output->writeln([
  203.             '',
  204.             'Bundles are usually generated into the <info>src/</info> directory. Unless you\'re',
  205.             'doing something custom, hit enter to keep this default!',
  206.             '',
  207.         ]);
  208.         $question = new Question($questionHelper->getQuestion(
  209.             'Target Directory',
  210.             $dir
  211.         ), $dir);
  212.         $dir $questionHelper->ask($input$output$question);
  213.         $input->setOption('dir'$dir);
  214.         /*
  215.          * format option
  216.          */
  217.         $format $input->getOption('format');
  218.         if (!$format) {
  219.             $format $shared 'xml' 'annotation';
  220.         }
  221.         $output->writeln([
  222.             '',
  223.             'What format do you want to use for your generated configuration?',
  224.             '',
  225.         ]);
  226.         $question = new Question($questionHelper->getQuestion(
  227.             'Configuration format (annotation, yml, xml, php)',
  228.             $format
  229.         ), $format);
  230.         $question->setValidator(function ($format) {
  231.             return Validators::validateFormat($format);
  232.         });
  233.         $question->setAutocompleterValues(['annotation''yml''xml''php']);
  234.         $format $questionHelper->ask($input$output$question);
  235.         $input->setOption('format'$format);
  236.     }
  237.     protected function checkAutoloader(OutputInterface $outputBundle $bundle)
  238.     {
  239.         $output->writeln('> Checking that the bundle is autoloaded');
  240.         if (!class_exists($bundle->getBundleClassName())) {
  241.             return [
  242.                 '- Edit the <comment>composer.json</comment> file and register the bundle',
  243.                 '  namespace in the "autoload" section:',
  244.                 '',
  245.             ];
  246.         }
  247.     }
  248.     protected function updateKernel(OutputInterface $outputKernelInterface $kernelBundle $bundle)
  249.     {
  250.         $kernelManipulator = new KernelManipulator($kernel);
  251.         $output->writeln(sprintf(
  252.             '> Enabling the bundle inside <info>%s</info>',
  253.             $this->makePathRelative($kernelManipulator->getFilename())
  254.         ));
  255.         try {
  256.             $ret $kernelManipulator->addBundle($bundle->getBundleClassName());
  257.             if (!$ret) {
  258.                 $reflected = new \ReflectionObject($kernel);
  259.                 return [
  260.                     sprintf('- Edit <comment>%s</comment>'$reflected->getFilename()),
  261.                     '  and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
  262.                     '',
  263.                     sprintf('    <comment>new %s(),</comment>'$bundle->getBundleClassName()),
  264.                     '',
  265.                 ];
  266.             }
  267.         } catch (\RuntimeException $e) {
  268.             return [
  269.                 sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.'$bundle->getBundleClassName()),
  270.                 '',
  271.             ];
  272.         }
  273.     }
  274.     protected function updateRouting(OutputInterface $outputBundle $bundle)
  275.     {
  276.         $targetRoutingPath $this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml';
  277.         $output->writeln(sprintf(
  278.             '> Importing the bundle\'s routes from the <info>%s</info> file',
  279.             $this->makePathRelative($targetRoutingPath)
  280.         ));
  281.         $routing = new RoutingManipulator($targetRoutingPath);
  282.         try {
  283.             $ret $routing->addResource($bundle->getName(), $bundle->getConfigurationFormat());
  284.             if (!$ret) {
  285.                 if ('annotation' === $bundle->getConfigurationFormat()) {
  286.                     $help sprintf("        <comment>resource: \"@%s/Controller/\"</comment>\n        <comment>type:     annotation</comment>\n"$bundle->getName());
  287.                 } else {
  288.                     $help sprintf("        <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n"$bundle->getName(), $bundle->getConfigurationFormat());
  289.                 }
  290.                 $help .= "        <comment>prefix:   /</comment>\n";
  291.                 return [
  292.                     '- Import the bundle\'s routing resource in the app\'s main routing file:',
  293.                     '',
  294.                     sprintf('    <comment>%s:</comment>'$bundle->getName()),
  295.                     $help,
  296.                     '',
  297.                 ];
  298.             }
  299.         } catch (\RuntimeException $e) {
  300.             return [
  301.                 sprintf('Bundle <comment>%s</comment> is already imported.'$bundle->getName()),
  302.                 '',
  303.             ];
  304.         }
  305.     }
  306.     protected function updateConfiguration(OutputInterface $outputBundle $bundle)
  307.     {
  308.         $targetConfigurationPath $this->getContainer()->getParameter('kernel.root_dir').'/config/config.yml';
  309.         $output->writeln(sprintf(
  310.             '> Importing the bundle\'s %s from the <info>%s</info> file',
  311.             $bundle->getServicesConfigurationFilename(),
  312.             $this->makePathRelative($targetConfigurationPath)
  313.         ));
  314.         $manipulator = new ConfigurationManipulator($targetConfigurationPath);
  315.         try {
  316.             $manipulator->addResource($bundle);
  317.         } catch (\RuntimeException $e) {
  318.             return [
  319.                 sprintf('- Import the bundle\'s "%s" resource in the app\'s main configuration file:'$bundle->getServicesConfigurationFilename()),
  320.                 '',
  321.                 $manipulator->getImportCode($bundle),
  322.                 '',
  323.             ];
  324.         }
  325.     }
  326.     /**
  327.      * Creates the Bundle object based on the user's (non-interactive) input.
  328.      *
  329.      * @param InputInterface $input
  330.      *
  331.      * @return Bundle
  332.      */
  333.     protected function createBundleObject(InputInterface $input)
  334.     {
  335.         foreach (['namespace''dir'] as $option) {
  336.             if (null === $input->getOption($option)) {
  337.                 throw new \RuntimeException(sprintf('The "%s" option must be provided.'$option));
  338.             }
  339.         }
  340.         $shared $input->getOption('shared');
  341.         $namespace Validators::validateBundleNamespace($input->getOption('namespace'), $shared);
  342.         if (!$bundleName $input->getOption('bundle-name')) {
  343.             $bundleName strtr($namespace, ['\\' => '']);
  344.         }
  345.         $bundleName Validators::validateBundleName($bundleName);
  346.         $dir $input->getOption('dir');
  347.         if (null === $input->getOption('format')) {
  348.             $input->setOption('format''annotation');
  349.         }
  350.         $format Validators::validateFormat($input->getOption('format'));
  351.         // an assumption that the kernel root dir is in a directory (like app/)
  352.         $projectRootDirectory $this->getContainer()->getParameter('kernel.root_dir').'/..';
  353.         if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
  354.             $dir $projectRootDirectory.'/'.$dir;
  355.         }
  356.         // add trailing / if necessary
  357.         $dir '/' === substr($dir, -11) ? $dir $dir.'/';
  358.         $bundle = new Bundle(
  359.             $namespace,
  360.             $bundleName,
  361.             $dir,
  362.             $format,
  363.             $shared
  364.         );
  365.         // not shared - put the tests in the root
  366.         if (!$shared) {
  367.             $testsDir $projectRootDirectory.'/tests/'.$bundleName;
  368.             $bundle->setTestsDirectory($testsDir);
  369.         }
  370.         return $bundle;
  371.     }
  372.     protected function createGenerator()
  373.     {
  374.         return new BundleGenerator();
  375.     }
  376. }