PageRenderTime 35ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php

https://gitlab.com/ineszribi/SmartBookStoreWeb
PHP | 382 lines | 349 code | 15 blank | 18 comment | 9 complexity | e3cd5869542d7c536d31f5d2b30af776 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sensio\Bundle\GeneratorBundle\Command;
  11. use Symfony\Component\Console\Input\InputOption;
  12. use Symfony\Component\Console\Input\InputInterface;
  13. use Symfony\Component\Console\Output\OutputInterface;
  14. use Symfony\Component\Console\Output\Output;
  15. use Symfony\Component\Console\Question\Question;
  16. use Symfony\Component\Console\Question\ConfirmationQuestion;
  17. use Symfony\Component\HttpKernel\KernelInterface;
  18. use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator;
  19. use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
  20. use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
  21. use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper;
  22. /**
  23. * Generates bundles.
  24. *
  25. * @author Fabien Potencier <fabien@symfony.com>
  26. */
  27. class GenerateBundleCommand extends GeneratorCommand
  28. {
  29. /**
  30. * @see Command
  31. */
  32. protected function configure()
  33. {
  34. $this
  35. ->setDefinition(array(
  36. new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'),
  37. new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'),
  38. new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'),
  39. new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)'),
  40. new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'),
  41. ))
  42. ->setDescription('Generates a bundle')
  43. ->setHelp(<<<EOT
  44. The <info>generate:bundle</info> command helps you generates new bundles.
  45. By default, the command interacts with the developer to tweak the generation.
  46. Any passed option will be used as a default value for the interaction
  47. (<comment>--namespace</comment> is the only one needed if you follow the
  48. conventions):
  49. <info>php app/console generate:bundle --namespace=Acme/BlogBundle</info>
  50. Note that you can use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any
  51. problem.
  52. If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
  53. <info>php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
  54. Note that the bundle namespace must end with "Bundle".
  55. EOT
  56. )
  57. ->setName('generate:bundle')
  58. ;
  59. }
  60. /**
  61. * @see Command
  62. *
  63. * @throws \InvalidArgumentException When namespace doesn't end with Bundle
  64. * @throws \RuntimeException When bundle can't be executed
  65. */
  66. protected function execute(InputInterface $input, OutputInterface $output)
  67. {
  68. $questionHelper = $this->getQuestionHelper();
  69. if ($input->isInteractive()) {
  70. if (!$questionHelper->ask($input, $output, new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true))) {
  71. $output->writeln('<error>Command aborted</error>');
  72. return 1;
  73. }
  74. }
  75. foreach (array('namespace', 'dir') as $option) {
  76. if (null === $input->getOption($option)) {
  77. throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option));
  78. }
  79. }
  80. // validate the namespace, but don't require a vendor namespace
  81. $namespace = Validators::validateBundleNamespace($input->getOption('namespace'), false);
  82. if (!$bundle = $input->getOption('bundle-name')) {
  83. $bundle = strtr($namespace, array('\\' => ''));
  84. }
  85. $bundle = Validators::validateBundleName($bundle);
  86. $dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace);
  87. if (null === $input->getOption('format')) {
  88. $input->setOption('format', 'annotation');
  89. }
  90. $format = Validators::validateFormat($input->getOption('format'));
  91. $structure = $input->getOption('structure');
  92. $questionHelper->writeSection($output, 'Bundle generation');
  93. if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
  94. $dir = getcwd().'/'.$dir;
  95. }
  96. $generator = $this->getGenerator();
  97. $generator->generate($namespace, $bundle, $dir, $format, $structure);
  98. $output->writeln('Generating the bundle code: <info>OK</info>');
  99. $errors = array();
  100. $runner = $questionHelper->getRunner($output, $errors);
  101. // check that the namespace is already autoloaded
  102. $runner($this->checkAutoloader($output, $namespace, $bundle, $dir));
  103. // register the bundle in the Kernel class
  104. $runner($this->updateKernel($questionHelper, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle));
  105. // routing
  106. $runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format));
  107. $questionHelper->writeGeneratorSummary($output, $errors);
  108. }
  109. protected function interact(InputInterface $input, OutputInterface $output)
  110. {
  111. $questionHelper = $this->getQuestionHelper();
  112. $questionHelper->writeSection($output, 'Welcome to the Symfony2 bundle generator');
  113. // namespace
  114. $namespace = null;
  115. try {
  116. // validate the namespace option (if any) but don't require the vendor namespace
  117. $namespace = $input->getOption('namespace') ? Validators::validateBundleNamespace($input->getOption('namespace'), false) : null;
  118. } catch (\Exception $error) {
  119. $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
  120. }
  121. if (null === $namespace) {
  122. $output->writeln(array(
  123. '',
  124. 'Your application code must be written in <comment>bundles</comment>. This command helps',
  125. 'you generate them easily.',
  126. '',
  127. 'Each bundle is hosted under a namespace (like <comment>Acme/Bundle/BlogBundle</comment>).',
  128. 'The namespace should begin with a "vendor" name like your company name, your',
  129. 'project name, or your client name, followed by one or more optional category',
  130. 'sub-namespaces, and it should end with the bundle name itself',
  131. '(which must have <comment>Bundle</comment> as a suffix).',
  132. '',
  133. 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more',
  134. 'details on bundle naming conventions.',
  135. '',
  136. 'Use <comment>/</comment> instead of <comment>\\ </comment> for the namespace delimiter to avoid any problem.',
  137. '',
  138. ));
  139. $acceptedNamespace = false;
  140. while (!$acceptedNamespace) {
  141. $question = new Question($questionHelper->getQuestion('Bundle namespace', $input->getOption('namespace')), $input->getOption('namespace'));
  142. $question->setValidator(function ($answer) {
  143. return Validators::validateBundleNamespace($answer, false);
  144. });
  145. $namespace = $questionHelper->ask($input, $output, $question);
  146. // mark as accepted, unless they want to try again below
  147. $acceptedNamespace = true;
  148. // see if there is a vendor namespace. If not, this could be accidental
  149. if (false === strpos($namespace, '\\')) {
  150. // language is (almost) duplicated in Validators
  151. $msg = array();
  152. $msg[] = '';
  153. $msg[] = sprintf('The namespace sometimes contain a vendor namespace (e.g. <info>VendorName/BlogBundle</info> instead of simply <info>%s</info>).', $namespace, $namespace);
  154. $msg[] = 'If you\'ve *did* type a vendor namespace, try using a forward slash <info>/</info> (<info>Acme/BlogBundle</info>)?';
  155. $msg[] = '';
  156. $output->writeln($msg);
  157. $question = new ConfirmationQuestion($questionHelper->getQuestion(
  158. sprintf('Keep <comment>%s</comment> as the bundle namespace (choose no to try again)?', $namespace),
  159. 'yes'
  160. ), true);
  161. $acceptedNamespace = $questionHelper->ask($input, $output, $question);
  162. }
  163. }
  164. $input->setOption('namespace', $namespace);
  165. }
  166. // bundle name
  167. $bundle = null;
  168. try {
  169. $bundle = $input->getOption('bundle-name') ? Validators::validateBundleName($input->getOption('bundle-name')) : null;
  170. } catch (\Exception $error) {
  171. $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
  172. }
  173. if (null === $bundle) {
  174. $bundle = strtr($namespace, array('\\Bundle\\' => '', '\\' => ''));
  175. $output->writeln(array(
  176. '',
  177. 'In your code, a bundle is often referenced by its name. It can be the',
  178. 'concatenation of all namespace parts but it\'s really up to you to come',
  179. 'up with a unique name (a good practice is to start with the vendor name).',
  180. 'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
  181. '',
  182. ));
  183. $question = new Question($questionHelper->getQuestion('Bundle name', $bundle), $bundle);
  184. $question->setValidator(
  185. array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName')
  186. );
  187. $bundle = $questionHelper->ask($input, $output, $question);
  188. $input->setOption('bundle-name', $bundle);
  189. }
  190. // target dir
  191. $dir = null;
  192. try {
  193. $dir = $input->getOption('dir') ? Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace) : null;
  194. } catch (\Exception $error) {
  195. $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
  196. }
  197. if (null === $dir) {
  198. $dir = dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src';
  199. $output->writeln(array(
  200. '',
  201. 'The bundle can be generated anywhere. The suggested default directory uses',
  202. 'the standard conventions.',
  203. '',
  204. ));
  205. $question = new Question($questionHelper->getQuestion('Target directory', $dir), $dir);
  206. $question->setValidator(function ($dir) use ($bundle, $namespace) {
  207. return Validators::validateTargetDir($dir, $bundle, $namespace);
  208. });
  209. $dir = $questionHelper->ask($input, $output, $question);
  210. $input->setOption('dir', $dir);
  211. }
  212. // format
  213. $format = null;
  214. try {
  215. $format = $input->getOption('format') ? Validators::validateFormat($input->getOption('format')) : null;
  216. } catch (\Exception $error) {
  217. $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
  218. }
  219. if (null === $format) {
  220. $output->writeln(array(
  221. '',
  222. 'Determine the format to use for the generated configuration.',
  223. '',
  224. ));
  225. $question = new Question($questionHelper->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), $input->getOption('format'));
  226. $question->setValidator(
  227. array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat')
  228. );
  229. $format = $questionHelper->ask($input, $output, $question);
  230. $input->setOption('format', $format);
  231. }
  232. // optional files to generate
  233. $output->writeln(array(
  234. '',
  235. 'To help you get started faster, the command can generate some',
  236. 'code snippets for you.',
  237. '',
  238. ));
  239. $structure = $input->getOption('structure');
  240. $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false);
  241. if (!$structure && $questionHelper->ask($input, $output, $question)) {
  242. $structure = true;
  243. }
  244. $input->setOption('structure', $structure);
  245. // summary
  246. $output->writeln(array(
  247. '',
  248. $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true),
  249. '',
  250. sprintf("You are going to generate a \"<info>%s\\%s</info>\" bundle\nin \"<info>%s</info>\" using the \"<info>%s</info>\" format.", $namespace, $bundle, $dir, $format),
  251. '',
  252. ));
  253. }
  254. protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir)
  255. {
  256. $output->write('Checking that the bundle is autoloaded: ');
  257. if (!class_exists($namespace.'\\'.$bundle)) {
  258. return array(
  259. '- Edit the <comment>composer.json</comment> file and register the bundle',
  260. ' namespace in the "autoload" section:',
  261. '',
  262. );
  263. }
  264. }
  265. protected function updateKernel(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle)
  266. {
  267. $auto = true;
  268. if ($input->isInteractive()) {
  269. $question = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true);
  270. $auto = $questionHelper->ask($input, $output, $question);
  271. }
  272. $output->write('Enabling the bundle inside the Kernel: ');
  273. $manip = new KernelManipulator($kernel);
  274. try {
  275. $ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false;
  276. if (!$ret) {
  277. $reflected = new \ReflectionObject($kernel);
  278. return array(
  279. sprintf('- Edit <comment>%s</comment>', $reflected->getFilename()),
  280. ' and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
  281. '',
  282. sprintf(' <comment>new %s(),</comment>', $namespace.'\\'.$bundle),
  283. '',
  284. );
  285. }
  286. } catch (\RuntimeException $e) {
  287. return array(
  288. sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.', $namespace.'\\'.$bundle),
  289. '',
  290. );
  291. }
  292. }
  293. protected function updateRouting(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, $bundle, $format)
  294. {
  295. $auto = true;
  296. if ($input->isInteractive()) {
  297. $question = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true);
  298. $auto = $questionHelper->ask($input, $output, $question);
  299. }
  300. $output->write('Importing the bundle routing resource: ');
  301. $routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml');
  302. try {
  303. $ret = $auto ? $routing->addResource($bundle, $format) : true;
  304. if (!$ret) {
  305. if ('annotation' === $format) {
  306. $help = sprintf(" <comment>resource: \"@%s/Controller/\"</comment>\n <comment>type: annotation</comment>\n", $bundle);
  307. } else {
  308. $help = sprintf(" <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n", $bundle, $format);
  309. }
  310. $help .= " <comment>prefix: /</comment>\n";
  311. return array(
  312. '- Import the bundle\'s routing resource in the app main routing file:',
  313. '',
  314. sprintf(' <comment>%s:</comment>', $bundle),
  315. $help,
  316. '',
  317. );
  318. }
  319. } catch (\RuntimeException $e) {
  320. return array(
  321. sprintf('Bundle <comment>%s</comment> is already imported.', $bundle),
  322. '',
  323. );
  324. }
  325. }
  326. protected function createGenerator()
  327. {
  328. return new BundleGenerator($this->getContainer()->get('filesystem'));
  329. }
  330. }