Completed
Pull Request — master (#601)
by
unknown
14:30
created

DocumentGenerateCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
eloc 5
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\ElasticsearchBundle\Command;
13
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Question\Question;
17
use Symfony\Component\Console\Style\SymfonyStyle;
18
use Symfony\Component\HttpKernel\Kernel;
19
20
class DocumentGenerateCommand extends AbstractManagerAwareCommand
21
{
22
    /**
23
     * {@inheritdoc}
24
     */
25
    protected function configure()
26
    {
27
        parent::configure();
28
29
        $this
30
            ->setName('ongr:es:document:generate')
31
            ->setDescription('Generates a new Elasticsearch document inside a bundle');
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37
    protected function execute(InputInterface $input, OutputInterface $output)
38
    {
39
        if ($input->hasParameterOption(['--no-interaction', '-n'])) {
40
            throw new \InvalidArgumentException('No interaction mode is not allowed!');
41
        }
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    protected function interact(InputInterface $input, OutputInterface $output)
48
    {
49
        $io = new SymfonyStyle($input, $output);
50
        $io->section('Welcome to the ONGRElasticsearchBundle document generator');
51
        $io->writeln(
52
            [
53
                '',
54
                'This command helps you generate ONGRElasticsearchBundle documents.',
55
                '',
56
                'First, you need to give the document name you want to generate.',
57
                'You must use the shortcut notation like <comment>AcmeDemoBundle:Post</comment>.',
58
                '',
59
            ]
60
        );
61
62
        /** @var Kernel $kernel */
63
        $kernel = $this->getContainer()->get('kernel');
64
        $bundleNames = array_keys($kernel->getBundles());
65
66
        while (true) {
67
            $question = new Question('The Document shortcut name');
68
            $question
69
                ->setValidator([$this, 'validateDocumentName'])
70
                ->setAutocompleterValues($bundleNames);
71
72
            $document = $io->askQuestion($question);
73
74
            list($bundle, $document) = $this->parseShortcutNotation($document);
75
76
            if (in_array(strtolower($document), $this->getReservedKeywords())) {
77
                $io->error(sprintf('"%s" is a reserved word.', $document));
78
                continue;
79
            }
80
81
            try {
82
                if (!file_exists(
83
                    $kernel->getBundle($bundle)->getPath() . '/Document/' . str_replace('\\', '/', $document) . '.php'
84
                )) {
85
                    break;
86
                }
87
88
                $io->error(sprintf('Document "%s:%s" already exists.', $bundle, $document));
89
            } catch (\Exception $e) {
90
                $io->error(sprintf('Bundle "%s" does not exist.', $bundle));
91
            }
92
        }
93
94
        $question = new Question('Document type in Elasticsearch', lcfirst($document));
0 ignored issues
show
Bug introduced by
The variable $document does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
95
        $question->setValidator([$this, 'validateFieldName']);
96
        $documentType = $io->askQuestion($question);
97
98
        $properties = [];
99
100
        while (true) {
101
            $question = new Question(
102
                'Enter property name [<comment>No property will be added if empty!</comment>]',
103
                false
104
            );
105
106
            if (!$field = $io->askQuestion($question)) {
107
                break;
108
            }
109
110
            try {
111
                $this->validateFieldName($field);
112
            } catch (\InvalidArgumentException $e) {
113
                $io->error($e->getMessage());
114
                continue;
115
            }
116
117
            $question = new Question('Enter property name in Elasticsearch', $field);
118
            $question->setValidator([$this, 'validateFieldName']);
119
            $name = $io->askQuestion($question);
120
121
            $question = new Question('Enter property type', 'string');
122
            $question
123
                ->setAutocompleterValues($this->getPropertyTypes())
124
                ->setValidator([$this, 'validatePropertyType']);
125
            $type = $io->askQuestion($question);
126
127
            $question = new Question(
128
                'Enter property options [<comment>No options will be added if empty!</comment>]',
129
                false
130
            );
131
132
            $options = $io->askQuestion($question);
133
134
            $properties[] = [
135
                'annotation' => 'Property',
136
                'fieldName' => $field,
137
                'property_name' => $name,
138
                'property_type' => $type,
139
                'property_options' => $options,
140
            ];
141
        }
142
143
        $this->getContainer()->get('es.generate')->generate(
144
            $this->getContainer()->get('kernel')->getBundle($bundle),
0 ignored issues
show
Bug introduced by
The variable $bundle does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
145
            $document,
146
            $documentType,
147
            $properties
148
        );
149
    }
150
151
    /**
152
     * Performs basic checks in document name
153
     *
154
     * @param string $document
155
     *
156
     * @return string
157
     * @throws \InvalidArgumentException
158
     */
159
    public static function validateDocumentName($document)
160
    {
161
        if (!preg_match('{^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*:[a-zA-Z0-9_\x7f-\xff\\\/]+$}', $document)) {
162
            throw new \InvalidArgumentException(
163
                sprintf(
164
                    'The document name isn\'t valid ("%s" given, expecting something like AcmeBlogBundle:Post)',
165
                    $document
166
                )
167
            );
168
        }
169
170
        return $document;
171
    }
172
173
    /**
174
     * Validates field name
175
     *
176
     * @param string $field
177
     *
178
     * @return string
179
     * @throws \InvalidArgumentException
180
     */
181
    public function validateFieldName($field)
182
    {
183
        if (!$field || $field != lcfirst(preg_replace('/[^a-zA-Z]+/', '', $field))) {
184
            throw new \InvalidArgumentException(
185
                sprintf(
186
                    'The parameter isn\'t valid ("%s" given, expecting camelcase separated words)',
187
                    $field
188
                )
189
            );
190
        }
191
192
        if (in_array(strtolower($field), $this->getReservedKeywords())) {
193
            throw new \InvalidArgumentException(sprintf('"%s" is a reserved word.', $field));
194
        }
195
196
        return $field;
197
    }
198
199
    /**
200
     * Validates property type
201
     *
202
     * @param string $type
203
     *
204
     * @return string
205
     * @throws \InvalidArgumentException
206
     */
207
    public function validatePropertyType($type)
208
    {
209
        if (!in_array($type, $this->getPropertyTypes())) {
210
            throw new \InvalidArgumentException(
211
                sprintf(
212
                    'The property type isn\'t valid ("%s" given, expecting one of following: %s)',
213
                    $type,
214
                    implode(', ', $this->getPropertyTypes())
215
                )
216
            );
217
        }
218
219
        return $type;
220
    }
221
222
    /**
223
     * Parses shortcut notation
224
     *
225
     * @param string $shortcut
226
     *
227
     * @return array
228
     * @throws \InvalidArgumentException
229
     */
230
    private function parseShortcutNotation($shortcut)
231
    {
232
        $shortcut = str_replace('/', '\\', $shortcut);
233
234
        if (false === $pos = strpos($shortcut, ':')) {
235
            throw new \InvalidArgumentException(
236
                sprintf(
237
                    'The document name isn\'t valid ("%s" given, expecting something like AcmeBlogBundle:Post)',
238
                    $shortcut
239
                )
240
            );
241
        }
242
243
        return [
244
            substr($shortcut, 0, $pos),
245
            substr($shortcut, $pos + 1),
246
        ];
247
    }
248
249
    /**
250
     * Returns available property types
251
     *
252
     * @return array
253
     */
254
    private function getPropertyTypes()
255
    {
256
        $reflection = new \ReflectionClass('ONGR\ElasticsearchBundle\Annotation\Property');
257
258
        return $this
259
            ->getContainer()
260
            ->get('annotations.cached_reader')
261
            ->getPropertyAnnotation($reflection->getProperty('type'), 'Doctrine\Common\Annotations\Annotation\Enum')
262
            ->value;
263
    }
264
265
    /**
266
     * Returns reserved keywords
267
     *
268
     * @return array
269
     */
270
    private function getReservedKeywords()
271
    {
272
        return [
273
            'abstract',
274
            'and',
275
            'array',
276
            'as',
277
            'break',
278
            'callable',
279
            'case',
280
            'catch',
281
            'class',
282
            'clone',
283
            'const',
284
            'continue',
285
            'declare',
286
            'default',
287
            'do',
288
            'else',
289
            'elseif',
290
            'enddeclare',
291
            'endfor',
292
            'endforeach',
293
            'endif',
294
            'endswitch',
295
            'endwhile',
296
            'extends',
297
            'final',
298
            'finally',
299
            'for',
300
            'foreach',
301
            'function',
302
            'global',
303
            'goto',
304
            'if',
305
            'implements',
306
            'interface',
307
            'instanceof',
308
            'insteadof',
309
            'namespace',
310
            'new',
311
            'or',
312
            'private',
313
            'protected',
314
            'public',
315
            'static',
316
            'switch',
317
            'throw',
318
            'trait',
319
            'try',
320
            'use',
321
            'var',
322
            'while',
323
            'xor',
324
            'yield',
325
            'die',
326
            'echo',
327
            'empty',
328
            'exit',
329
            'eval',
330
            'include',
331
            'include_once',
332
            'isset',
333
            'list',
334
            'require',
335
            'require_once',
336
            'return',
337
            'print',
338
            'unset',
339
        ];
340
    }
341
}
342