Completed
Push — master ( ae5e03...0447ee )
by Jeroen
10:35 queued 04:37
created

GeneratorBundle/Command/GenerateBundleCommand.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\GeneratorBundle\Command;
4
5
use Kunstmaan\GeneratorBundle\Generator\BundleGenerator;
6
use Kunstmaan\GeneratorBundle\Helper\GeneratorUtils;
7
use Sensio\Bundle\GeneratorBundle\Command\GeneratorCommand;
8
use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper;
9
use Sensio\Bundle\GeneratorBundle\Command\Validators;
10
use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
11
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\Console\Question\ConfirmationQuestion;
16
use Symfony\Component\Console\Question\Question;
17
use Symfony\Component\HttpKernel\KernelInterface;
18
19
/**
20
 * Generates bundles.
21
 */
22
class GenerateBundleCommand extends GeneratorCommand
23
{
24
    /**
25
     * @see Command
26
     */
27 View Code Duplication
    protected function configure()
28
    {
29
        $this
30
            ->setDefinition(
31
                array(new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'),
32
                    new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'),
33
                    new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'), ))
34
            ->setHelp(
35
                <<<'EOT'
36
            The <info>generate:bundle</info> command helps you generates new bundles.
37
38
By default, the command interacts with the developer to tweak the generation.
39
Any passed option will be used as a default value for the interaction
40
(<comment>--namespace</comment> is the only one needed if you follow the
41
conventions):
42
43
<info>php bin/console kuma:generate:bundle --namespace=Acme/BlogBundle</info>
44
45
Note that you can use <comment>/</comment> instead of <comment>\</comment> for the namespace delimiter to avoid any
46
problem.
47
48
If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
49
50
<info>php bin/console kuma:generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
51
52
Note that the bundle namespace must end with "Bundle".
53
EOT
54
            )
55
            ->setName('kuma:generate:bundle');
56
    }
57
58
    /**
59
     * Executes the command.
60
     *
61
     * @param InputInterface  $input  An InputInterface instance
62
     * @param OutputInterface $output An OutputInterface instance
63
     *
64
     * @throws \RuntimeException
65
     */
66
    protected function execute(InputInterface $input, OutputInterface $output)
67
    {
68
        $questionHelper = $this->getQuestionHelper();
69
70 View Code Duplication
        if ($input->isInteractive()) {
71
            $confirmationQuestion = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true);
72
            if (!$questionHelper->ask($input, $output, $confirmationQuestion)) {
73
                $output->writeln('<error>Command aborted</error>');
74
75
                return 1;
76
            }
77
        }
78
79
        GeneratorUtils::ensureOptionsProvided($input, array('namespace', 'dir'));
80
81
        $namespace = Validators::validateBundleNamespace($input->getOption('namespace'));
82
        if (!$bundle = $input->getOption('bundle-name')) {
83
            $bundle = strtr($namespace, array('\\' => ''));
84
        }
85
        $bundle = Validators::validateBundleName($bundle);
86
        $dir = $this::validateTargetDir($input->getOption('dir'), $bundle, $namespace);
87
        $format = 'yml';
88
89
        $questionHelper->writeSection($output, 'Bundle generation');
90
91
        if (!$this
92
            ->getContainer()
93
            ->get('filesystem')
94
            ->isAbsolutePath($dir)
95
        ) {
96
            $dir = getcwd() . '/' . $dir;
97
        }
98
99
        $generator = $this->getGenerator($this->getApplication()->getKernel()->getBundle('KunstmaanGeneratorBundle'));
100
        $generator->generate($namespace, $bundle, $dir, $format);
101
102
        $output->writeln('Generating the bundle code: <info>OK</info>');
103
104
        $errors = array();
105
        $runner = $questionHelper->getRunner($output, $errors);
106
107
        // check that the namespace is already autoloaded
108
        $runner($this->checkAutoloader($output, $namespace, $bundle));
109
110
        // register the bundle in the Kernel class
111
        $runner($this->updateKernel($questionHelper, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle));
0 ignored issues
show
$this->getContainer()->get('kernel') is of type object|null, but the function expects a object<Symfony\Component...Kernel\KernelInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
112
113
        // routing
114
        $runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format));
115
116
        $questionHelper->writeGeneratorSummary($output, $errors);
117
118
        return 0;
119
    }
120
121
    /**
122
     * Executes the command.
123
     *
124
     * @param InputInterface  $input  An InputInterface instance
125
     * @param OutputInterface $output An OutputInterface instance
126
     */
127
    protected function interact(InputInterface $input, OutputInterface $output)
128
    {
129
        $questionHelper = $this->getQuestionHelper();
130
        $questionHelper->writeSection($output, 'Welcome to the Kunstmaan bundle generator');
131
132
        // namespace
133
        $output
134
            ->writeln(
135
                array('', 'Your application code must be written in <comment>bundles</comment>. This command helps', 'you generate them easily.', '',
136
                    'Each bundle is hosted under a namespace (like <comment>Acme/Bundle/BlogBundle</comment>).',
137
                    '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',
138
                    'sub-namespaces, and it should end with the bundle name itself', '(which must have <comment>Bundle</comment> as a suffix).', '',
139
                    'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more', 'details on bundle naming conventions.', '',
140
                    'Use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any problems.', '', ));
141
142
        $question = new Question($questionHelper->getQuestion('Bundle namespace', $input->getOption('namespace')), $input->getOption('namespace'));
143
        $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'));
144
        $namespace = $questionHelper->ask($input, $output, $question);
145
        $input->setOption('namespace', $namespace);
146
147
        // bundle name
148
        if ($input->getOption('bundle-name')) {
149
            $bundle = $input->getOption('bundle-name');
150
        } else {
151
            $bundle = strtr($namespace, array('\\Bundle\\' => '', '\\' => ''));
152
        }
153
        $output
154
            ->writeln(
155
                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',
156
                    'up with a unique name (a good practice is to start with the vendor name).', 'Based on the namespace, we suggest <comment>' . $bundle . '</comment>.', '', ));
157
        $question = new Question($questionHelper->getQuestion('Bundle name', $bundle), $bundle);
158
        $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'));
159
        $bundle = $questionHelper->ask($input, $output, $question);
160
        $input->setOption('bundle-name', $bundle);
161
162
        // target dir
163
        $dir = $input->getOption('dir') ?: dirname($this
164
                ->getContainer()
165
                ->getParameter('kernel.root_dir')) . '/src';
166
        $output->writeln(array('', 'The bundle can be generated anywhere. The suggested default directory uses', 'the standard conventions.', ''));
167
        $question = new Question($questionHelper->getQuestion('Target directory', $dir), $dir);
168
        $question->setValidator(function ($dir) use ($bundle, $namespace) {
169
            return $this::validateTargetDir($dir, $bundle, $namespace);
170
        });
171
        $dir = $questionHelper->ask($input, $output, $question);
172
        $input->setOption('dir', $dir);
173
174
        // format
175
        $output->writeln(array('', 'Determine the format to use for the generated configuration.', ''));
176
        $output->writeln(array('', 'Determined \'yml\' to be used as the format for the generated configuration', ''));
177
        $format = 'yml';
178
179
        // summary
180
        $output
181
            ->writeln(
182
                array('', $this
183
                    ->getHelper('formatter')
184
                    ->formatBlock('Summary before generation', 'bg=blue;fg=white', true), '',
185
                    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),
186
                    '', ));
187
    }
188
189
    /**
190
     * @param OutputInterface $output    The output
191
     * @param string          $namespace The namespace
192
     * @param string          $bundle    The bundle name
193
     *
194
     * @return array
195
     */
196
    protected function checkAutoloader(OutputInterface $output, $namespace, $bundle)
197
    {
198
        $output->write('Checking that the bundle is autoloaded: ');
199
        if (!class_exists($namespace . '\\' . $bundle)) {
200
            return array('- Edit the <comment>composer.json</comment> file and register the bundle', '  namespace in the "autoload" section:', '');
201
        }
202
    }
203
204
    /**
205
     * @param QuestionHelper  $questionHelper The question helper
206
     * @param InputInterface  $input          The command input
207
     * @param OutputInterface $output         The command output
208
     * @param KernelInterface $kernel         The kernel
209
     * @param string          $namespace      The namespace
210
     * @param string          $bundle         The bundle
211
     *
212
     * @return array
213
     */
214
    protected function updateKernel(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle)
215
    {
216
        $auto = true;
217 View Code Duplication
        if ($input->isInteractive()) {
218
            $confirmationQuestion = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true);
219
            $auto = $questionHelper->ask($input, $output, $confirmationQuestion);
220
        }
221
222
        $output->write('Enabling the bundle inside the Kernel: ');
223
        $manip = new KernelManipulator($kernel);
224
225
        try {
226
            $ret = $auto ? $manip->addBundle($namespace . '\\' . $bundle) : false;
227
228
            if (!$ret) {
229
                $reflected = new \ReflectionObject($kernel);
230
231
                return array(sprintf('- Edit <comment>%s</comment>', $reflected->getFilename()), '  and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:', '',
232
                    sprintf('    <comment>new %s(),</comment>', $namespace . '\\' . $bundle), '', );
233
            }
234
        } catch (\RuntimeException $e) {
235
            return array(sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.', $namespace . '\\' . $bundle), '');
236
        }
237
    }
238
239
    /**
240
     * @param QuestionHelper  $questionHelper The question helper
241
     * @param InputInterface  $input          The command input
242
     * @param OutputInterface $output         The command output
243
     * @param string          $bundle         The bundle name
244
     * @param string          $format         The format
245
     *
246
     * @return array
247
     */
248
    protected function updateRouting(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, $bundle, $format)
249
    {
250
        $auto = true;
251 View Code Duplication
        if ($input->isInteractive()) {
252
            $confirmationQuestion = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true);
253
            $auto = $questionHelper->ask($input, $output, $confirmationQuestion);
254
        }
255
256
        $output->write('Importing the bundle routing resource: ');
257
        $routing = new RoutingManipulator($this
258
                ->getContainer()
259
                ->getParameter('kernel.root_dir') . '/config/routing.yml');
260
261
        try {
262
            $ret = $auto ? $routing->addResource($bundle, $format) : false;
263
            if (!$ret) {
264
                $help = sprintf("        <comment>resource: \"@%s/Resources/config/routing.yml\"</comment>\n", $bundle);
265
                $help .= "        <comment>prefix:   /</comment>\n";
266
267
                return array('- Import the bundle\'s routing resource in the app main routing file:', '', sprintf('    <comment>%s:</comment>', $bundle), $help, '');
268
            }
269
        } catch (\RuntimeException $e) {
270
            return array(sprintf('Bundle <comment>%s</comment> is already imported.', $bundle), '');
271
        }
272
    }
273
274
    protected function createGenerator()
275
    {
276
        return new BundleGenerator();
277
    }
278
279
    /**
280
     * Validation function taken from <3.0 release of Sensio Generator bundle
281
     *
282
     * @param string $dir       The target directory
283
     * @param string $bundle    The bundle name
284
     * @param string $namespace The namespace
285
     *
286
     * @return string
287
     */
288
    public static function validateTargetDir($dir, $bundle, $namespace)
289
    {
290
        // add trailing / if necessary
291
        return '/' === substr($dir, -1, 1) ? $dir : $dir.'/';
292
    }
293
}
294