Completed
Pull Request — master (#869)
by
unknown
03:07 queued 01:21
created

DocumentGenerateCommand::validateFieldName()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
cc 4
nc 3
nop 1
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\Helper\FormatterHelper;
15
use Symfony\Component\Console\Helper\QuestionHelper;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Output\OutputInterface;
18
use Symfony\Component\Console\Question\ConfirmationQuestion;
19
use Symfony\Component\Console\Question\Question;
20
use Symfony\Component\HttpKernel\Kernel;
21
22
class DocumentGenerateCommand extends AbstractIndexServiceAwareCommand
23
{
24
    const NAME = 'ongr:es:document:generate';
25
26
    /**
27
     * @var QuestionHelper
28
     */
29
    private $questionHelper;
30
31
    /**
32
     * @var string[]
33
     */
34
    private $propertyAnnotations;
35
36
    /**
37
     * @var string[]
38
     */
39
    private $propertyVisibilities;
40
41
    /**
42
     * {@inheritdoc}
43
     */
44
    protected function configure()
45
    {
46
        parent::configure();
47
48
        $this
49
            ->setName(self::NAME)
50
            ->setDescription('Generates a new Elasticsearch document inside a bundle');
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    protected function execute(InputInterface $input, OutputInterface $output)
57
    {
58
        if ($input->hasParameterOption(['--no-interaction', '-n'])) {
59
            throw $this->getException('No interaction mode is not allowed!');
60
        }
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    protected function interact(InputInterface $input, OutputInterface $output)
67
    {
68
        /** @var FormatterHelper $formatter */
69
        $formatter = $this->getHelperSet()->get('formatter');
70
        $this->questionHelper = new QuestionHelper();
71
72
        $output->writeln(
73
            [
0 ignored issues
show
Documentation introduced by
array('', $formatter->fo...cument</comment>.', '') is of type array<integer,string,{"0..."string","7":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
74
                '',
75
                $formatter->formatBlock('Welcome to the Elasticsearch Bundle document generator', 'bg=blue', true),
76
                '',
77
                'This command helps you generate Elasticsearch documents.',
78
                '',
79
                'First, you need to give the document class namespace you want to generate.',
80
                'You must use the shortcut notation like <comment>App\YourProjectPath\PostDocument</comment>.',
81
                '',
82
            ]
83
        );
84
85
        /** @var Kernel $kernel */
86
        $kernel = $this->getContainer()->get('kernel');
87
        $bundleNames = array_keys($kernel->getBundles());
88
89
        while (true) {
90
            $document = $this->questionHelper->ask(
91
                $input,
92
                $output,
93
                $this->getQuestion('The Document shortcut name', null, [$this, 'validateDocumentName'], $bundleNames)
94
            );
95
96
            list($bundle, $document) = $this->parseShortcutNotation($document);
97
98
            if (in_array(strtolower($document), $this->getReservedKeywords())) {
99
                $output->writeln($this->getException('"%s" is a reserved word.', [$document])->getMessage());
100
                continue;
101
            }
102
103
            try {
104
                if (!file_exists(
105
                    $kernel->getBundle($bundle)->getPath() . '/Document/' . str_replace('\\', '/', $document) . '.php'
106
                )) {
107
                    break;
108
                }
109
110
                $output->writeln(
111
                    $this->getException('Document "%s:%s" already exists.', [$bundle, $document])->getMessage()
112
                );
113
            } catch (\Exception $e) {
114
                $output->writeln($this->getException('Bundle "%s" does not exist.', [$bundle])->getMessage());
115
            }
116
        }
117
118
        $output->writeln($this->getOptionsLabel($this->getDocumentAnnotations(), 'Available types'));
0 ignored issues
show
Documentation introduced by
$this->getOptionsLabel($...s(), 'Available types') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
119
        $annotation = $this->questionHelper->ask(
120
            $input,
121
            $output,
122
            $this->getQuestion(
123
                'Document type',
124
                'document',
125
                [$this, 'validateDocumentAnnotation'],
126
                $this->getDocumentAnnotations()
127
            )
128
        );
129
130
        $this->propertyAnnotations = ['embedded', 'property'];
131
        $documentType = 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...
132
133
        if ($annotation == 'document') {
134
            $this->propertyAnnotations = ['embedded', 'id', 'parentDocument', 'property', 'ttl'];
135
            $documentType = $this->questionHelper->ask(
136
                $input,
137
                $output,
138
                $this->getQuestion(
139
                    "\n" . 'Elasticsearch Document name',
140
                    lcfirst($document),
141
                    [$this, 'validateFieldName']
142
                )
143
            );
144
        }
145
146
        $properties = [];
147
        $output->writeln(['', $formatter->formatBlock('New Document Property?', 'bg=blue;fg=white', true)]);
0 ignored issues
show
Documentation introduced by
array('', $formatter->fo...=blue;fg=white', true)) is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
148
149
        while (true) {
150
            $property = [];
151
            $question = $this->getQuestion(
152
                'Property name [<comment>press <info><return></info> to stop</comment>]',
153
                false
154
            );
155
156
            if (!$field = $this->questionHelper->ask($input, $output, $question)) {
157
                break;
158
            }
159
160
            foreach ($properties as $previousProperty) {
161
                if ($previousProperty['field_name'] == $field) {
162
                    $output->writeln($this->getException('Duplicate field name "%s"', [$field])->getMessage());
163
                    continue(2);
164
                }
165
            }
166
167
            try {
168
                $this->validateFieldName($field);
169
            } catch (\InvalidArgumentException $e) {
170
                $output->writeln($e->getMessage());
171
                continue;
172
            }
173
174
            $this->propertyVisibilities  = ['private', 'protected', 'public'];
175
            $output->writeln($this->getOptionsLabel($this->propertyVisibilities, 'Available visibilities'));
0 ignored issues
show
Documentation introduced by
$this->getOptionsLabel($...vailable visibilities') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
176
            $property['visibility'] = $this->questionHelper->ask(
177
                $input,
178
                $output,
179
                $this->getQuestion(
180
                    'Property visibility',
181
                    'private',
182
                    [$this, 'validatePropertyVisibility'],
183
                    $this->propertyVisibilities
184
                )
185
            );
186
187
            $output->writeln($this->getOptionsLabel($this->propertyAnnotations, 'Available annotations'));
0 ignored issues
show
Documentation introduced by
$this->getOptionsLabel($...Available annotations') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
188
            $property['annotation'] = $this->questionHelper->ask(
189
                $input,
190
                $output,
191
                $this->getQuestion(
192
                    'Property meta field',
193
                    'property',
194
                    [$this, 'validatePropertyAnnotation'],
195
                    $this->propertyAnnotations
196
                )
197
            );
198
199
            $property['field_name'] = $property['property_name'] = $field;
200
201
            switch ($property['annotation']) {
202
                case 'embedded':
203
                    $property['property_name'] = $this->askForPropertyName($input, $output, $property['field_name']);
204
                    $property['property_class'] = $this->askForPropertyClass($input, $output);
205
206
                    $question = new ConfirmationQuestion("\n<info>Multiple</info> [<comment>no</comment>]: ", false);
207
                    $question->setAutocompleterValues(['yes', 'no']);
0 ignored issues
show
Documentation introduced by
array('yes', 'no') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a object<Symfony\Component...Question\iterable>|null.

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...
208
                    $property['property_multiple'] = $this->questionHelper->ask($input, $output, $question);
209
210
                    $property['property_options'] = $this->askForPropertyOptions($input, $output);
211
                    break;
212 View Code Duplication
                case 'parentDocument':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
213
                    if (!$this->isUniqueAnnotation($properties, $property['annotation'])) {
214
                        $output->writeln(
215
                            $this
216
                                ->getException('Only one "%s" field can be added', [$property['annotation']])
217
                                ->getMessage()
218
                        );
219
                        continue(2);
220
                    }
221
                    $property['property_class'] = $this->askForPropertyClass($input, $output);
222
                    break;
223
                case 'property':
224
                    $property['property_name'] = $this->askForPropertyName($input, $output, $property['field_name']);
225
226
                    $output->writeln($this->getOptionsLabel($this->getPropertyTypes(), 'Available types'));
0 ignored issues
show
Documentation introduced by
$this->getOptionsLabel($...s(), 'Available types') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
227
                    $property['property_type'] = $this->questionHelper->ask(
228
                        $input,
229
                        $output,
230
                        $this->getQuestion(
231
                            'Property type',
232
                            'text',
233
                            [$this, 'validatePropertyType'],
234
                            $this->getPropertyTypes()
235
                        )
236
                    );
237
238
                    $property['property_options'] = $this->askForPropertyOptions($input, $output);
239
                    break;
240 View Code Duplication
                case 'ttl':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
                    if (!$this->isUniqueAnnotation($properties, $property['annotation'])) {
242
                        $output->writeln(
243
                            $this
244
                                ->getException('Only one "%s" field can be added', [$property['annotation']])
245
                                ->getMessage()
246
                        );
247
                        continue(2);
248
                    }
249
                    $property['property_default'] = $this->questionHelper->ask(
250
                        $input,
251
                        $output,
252
                        $this->getQuestion("\n" . 'Default time to live')
253
                    );
254
                    break;
255
                case 'id':
256
                    if (!$this->isUniqueAnnotation($properties, $property['annotation'])) {
257
                        $output->writeln(
258
                            $this
259
                                ->getException('Only one "%s" field can be added', [$property['annotation']])
260
                                ->getMessage()
261
                        );
262
                        continue(2);
263
                    }
264
                    break;
265
            }
266
267
            $properties[] = $property;
268
            $output->writeln(['', $formatter->formatBlock('New Document Property', 'bg=blue;fg=white', true)]);
0 ignored issues
show
Documentation introduced by
array('', $formatter->fo...=blue;fg=white', true)) is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

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...
269
        }
270
271
        $this->getContainer()->get('es.generate')->generate(
272
            $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...
273
            $document,
274
            $annotation,
275
            $documentType,
276
            $properties
277
        );
278
    }
279
280
    /**
281
     * @param array  $properties
282
     * @param string $annotation
283
     *
284
     * @return string
285
     */
286
    private function isUniqueAnnotation($properties, $annotation)
287
    {
288
        foreach ($properties as $property) {
289
            if ($property['annotation'] == $annotation) {
290
                return false;
291
            }
292
        }
293
294
        return true;
295
    }
296
297
    /**
298
     * Asks for property name
299
     *
300
     * @param InputInterface  $input
301
     * @param OutputInterface $output
302
     * @param string          $default
303
     *
304
     * @return string
305
     */
306
    private function askForPropertyName(InputInterface $input, OutputInterface $output, $default = null)
307
    {
308
        return $this->questionHelper->ask(
309
            $input,
310
            $output,
311
            $this->getQuestion("\n" . 'Property name in Elasticsearch', $default, [$this, 'validateFieldName'])
312
        );
313
    }
314
315
    /**
316
     * Asks for property options
317
     *
318
     * @param InputInterface  $input
319
     * @param OutputInterface $output
320
     *
321
     * @return string
322
     */
323
    private function askForPropertyOptions(InputInterface $input, OutputInterface $output)
324
    {
325
        $output->writeln(
326
            "\n"
327
                . '<info>Enter property options, for example <comment>"index"="not_analyzed"</comment>'
328
                . ' allows mapper to index this field, so it is searchable, but value will be not analyzed.</info>'
329
        );
330
331
        return $this->questionHelper->ask(
332
            $input,
333
            $output,
334
            $this->getQuestion(
335
                'Property options [<comment>press <info><return></info> to stop</comment>]',
336
                false,
337
                null,
338
                ['"index"="not_analyzed"', '"analyzer"="standard"']
339
            )
340
        );
341
    }
342
343
    /**
344
     * Asks for property class
345
     *
346
     * @param InputInterface  $input
347
     * @param OutputInterface $output
348
     *
349
     * @return string
350
     */
351
    private function askForPropertyClass(InputInterface $input, OutputInterface $output)
352
    {
353
        return $this->questionHelper->ask(
354
            $input,
355
            $output,
356
            $this->getQuestion(
357
                "\n" . 'Property class',
358
                null,
359
                [$this, 'validatePropertyClass'],
360
                array_merge($this->getDocumentClasses(), array_keys($this->getContainer()->get('kernel')->getBundles()))
361
            )
362
        );
363
    }
364
365
    /**
366
     * Returns available document classes
367
     *
368
     * @return array
369
     */
370
    private function getDocumentClasses()
371
    {
372
//        /** @var MetadataCollector $metadataCollector */
373
//        $metadataCollector = $this->getContainer()->get('es.metadata_collector');
374
//        $classes = [];
375
//
376
//        foreach ($this->getContainer()->getParameter('es.managers') as $manager) {
377
//            $documents = $metadataCollector->getMappings($manager['mappings']);
378
//            foreach ($documents as $document) {
379
//                $classes[] = sprintf('%s:%s', $document['bundle'], $document['class']);
380
//            }
381
//        }
382
//        return $classes;
383
        return [];
384
    }
385
386
    /**
387
     * Parses shortcut notation
388
     *
389
     * @param string $shortcut
390
     *
391
     * @return string[]
392
     * @throws \InvalidArgumentException
393
     */
394
    private function parseShortcutNotation($shortcut)
395
    {
396
        $shortcut = str_replace('/', '\\', $shortcut);
397
398
        if (false === $pos = strpos($shortcut, ':')) {
399
            throw $this->getException(
400
                'The document name isn\'t valid ("%s" given, expecting something like AcmeBundle:Post)',
401
                [$shortcut]
402
            );
403
        }
404
405
        return [substr($shortcut, 0, $pos), substr($shortcut, $pos + 1)];
406
    }
407
408
    /**
409
     * Validates property class
410
     *
411
     * @param string $input
412
     *
413
     * @return string
414
     * @throws \InvalidArgumentException
415
     */
416
    public function validatePropertyClass($input)
417
    {
418
        list($bundle, $document) = $this->parseShortcutNotation($input);
419
420
        try {
421
            $bundlePath = $this->getContainer()->get('kernel')->getBundle($bundle)->getPath();
422
        } catch (\Exception $e) {
423
            throw $this->getException('Bundle "%s" does not exist.', [$bundle]);
424
        }
425
426
        if (!file_exists($bundlePath . '/Document/' . str_replace('\\', '/', $document) . '.php')) {
427
            throw $this->getException('Document "%s:%s" does not exist.', [$bundle, $document]);
428
        }
429
430
        return $input;
431
    }
432
433
    /**
434
     * Performs basic checks in document name
435
     *
436
     * @param string $document
437
     *
438
     * @return string
439
     * @throws \InvalidArgumentException
440
     */
441
    public function validateDocumentName($document)
442
    {
443
        if (!preg_match('{^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*:[a-zA-Z0-9_\x7f-\xff\\\/]+$}', $document)) {
444
            throw $this->getException(
445
                'The document name isn\'t valid ("%s" given, expecting something like AcmeBundle:Post)',
446
                [$document]
447
            );
448
        }
449
450
        return $document;
451
    }
452
453
    /**
454
     * Validates field name
455
     *
456
     * @param string $field
457
     *
458
     * @return string
459
     * @throws \InvalidArgumentException
460
     */
461
    public function validateFieldName($field)
462
    {
463
        if (!$field || $field != lcfirst(preg_replace('/[^a-zA-Z]+/', '', $field))) {
464
            throw $this->getException(
465
                'The parameter isn\'t valid ("%s" given, expecting camelcase separated words)',
466
                [$field]
467
            );
468
        }
469
470
        if (in_array(strtolower($field), $this->getReservedKeywords())) {
471
            throw $this->getException('"%s" is a reserved word.', [$field]);
472
        }
473
474
        return $field;
475
    }
476
477
    /**
478
     * Validates property type
479
     *
480
     * @param string $type
481
     *
482
     * @return string
483
     * @throws \InvalidArgumentException
484
     */
485 View Code Duplication
    public function validatePropertyType($type)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
486
    {
487
        if (!in_array($type, $this->getPropertyTypes())) {
488
            throw $this->getException(
489
                'The property type isn\'t valid ("%s" given, expecting one of following: %s)',
490
                [$type, implode(', ', $this->getPropertyTypes())]
491
            );
492
        }
493
494
        return $type;
495
    }
496
497
    /**
498
     * Validates document annotation
499
     *
500
     * @param string $annotation
501
     *
502
     * @return string
503
     * @throws \InvalidArgumentException
504
     */
505 View Code Duplication
    public function validateDocumentAnnotation($annotation)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
506
    {
507
        if (!in_array($annotation, $this->getDocumentAnnotations())) {
508
            throw $this->getException(
509
                'The document annotation isn\'t valid ("%s" given, expecting one of following: %s)',
510
                [$annotation, implode(', ', $this->getDocumentAnnotations())]
511
            );
512
        }
513
514
        return $annotation;
515
    }
516
517
    /**
518
     * Validates property annotation
519
     *
520
     * @param string $annotation
521
     *
522
     * @return string
523
     * @throws \InvalidArgumentException
524
     */
525 View Code Duplication
    public function validatePropertyAnnotation($annotation)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
526
    {
527
        if (!in_array($annotation, $this->propertyAnnotations)) {
528
            throw $this->getException(
529
                'The property annotation isn\'t valid ("%s" given, expecting one of following: %s)',
530
                [$annotation, implode(', ', $this->propertyAnnotations)]
531
            );
532
        }
533
534
        return $annotation;
535
    }
536
537
    /**
538
     * Validates property visibility
539
     *
540
     * @param string $visibility
541
     *
542
     * @return string
543
     * @throws \InvalidArgumentException When the visibility is not found in the list of allowed ones.
544
     */
545 View Code Duplication
    public function validatePropertyVisibility($visibility)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
546
    {
547
        if (!in_array($visibility, $this->propertyVisibilities)) {
548
            throw $this->getException(
549
                'The property visibility isn\'t valid ("%s" given, expecting one of following: %s)',
550
                [$visibility, implode(', ', $this->propertyVisibilities)]
551
            );
552
        }
553
554
        return $visibility;
555
    }
556
557
    /**
558
     * Returns formatted question
559
     *
560
     * @param string        $question
561
     * @param mixed         $default
562
     * @param callable|null $validator
563
     * @param array|null    $values
564
     *
565
     * @return Question
566
     */
567
    private function getQuestion($question, $default = null, callable $validator = null, array $values = null)
568
    {
569
        $question = new Question(
570
            sprintf('<info>%s</info>%s: ', $question, $default ? sprintf(' [<comment>%s</comment>]', $default) : ''),
571
            $default
572
        );
573
574
        $question
575
            ->setValidator($validator)
576
            ->setAutocompleterValues($values);
0 ignored issues
show
Bug introduced by
It seems like $values defined by parameter $values on line 567 can also be of type array; however, Symfony\Component\Consol...etAutocompleterValues() does only seem to accept object<Symfony\Component...Question\iterable>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
577
578
        return $question;
579
    }
580
581
    /**
582
     * Returns options label
583
     *
584
     * @param array  $options
585
     * @param string $suffix
586
     *
587
     * @return string[]
588
     */
589
    private function getOptionsLabel(array $options, $suffix)
590
    {
591
        $label = sprintf('<info>%s:</info> ', $suffix);
592
593
        foreach ($options as &$option) {
594
            $option = sprintf('<comment>%s</comment>', $option);
595
        }
596
597
        return ['', $label . implode(', ', $options) . '.'];
598
    }
599
600
    /**
601
     * Returns formatted exception
602
     *
603
     * @param string $format
604
     * @param array  $args
605
     *
606
     * @return \InvalidArgumentException
607
     */
608
    private function getException($format, $args = [])
609
    {
610
        /** @var FormatterHelper $formatter */
611
        $formatter = $this->getHelperSet()->get('formatter');
612
        return new \InvalidArgumentException($formatter->formatBlock(vsprintf($format, $args), 'bg=red', true));
613
    }
614
615
    /**
616
     * Returns available property types
617
     *
618
     * @return array
619
     */
620
    private function getPropertyTypes()
621
    {
622
        $reflection = new \ReflectionClass('ONGR\ElasticsearchBundle\Annotation\Property');
623
624
        return $this
625
            ->getContainer()
626
            ->get('es.annotations.cached_reader')
627
            ->getPropertyAnnotation($reflection->getProperty('type'), 'Doctrine\Common\Annotations\Annotation\Enum')
628
            ->value;
629
    }
630
631
    /**
632
     * Returns document annotations
633
     *
634
     * @return string[]
635
     */
636
    private function getDocumentAnnotations()
637
    {
638
        return ['document', 'nested', 'object'];
639
    }
640
641
    /**
642
     * Returns reserved keywords
643
     *
644
     * @return string[]
645
     */
646
    private function getReservedKeywords()
647
    {
648
        return [
649
            'abstract',
650
            'and',
651
            'array',
652
            'as',
653
            'break',
654
            'callable',
655
            'case',
656
            'catch',
657
            'class',
658
            'clone',
659
            'const',
660
            'continue',
661
            'declare',
662
            'default',
663
            'do',
664
            'else',
665
            'elseif',
666
            'enddeclare',
667
            'endfor',
668
            'endforeach',
669
            'endif',
670
            'endswitch',
671
            'endwhile',
672
            'extends',
673
            'final',
674
            'finally',
675
            'for',
676
            'foreach',
677
            'function',
678
            'global',
679
            'goto',
680
            'if',
681
            'implements',
682
            'interface',
683
            'instanceof',
684
            'insteadof',
685
            'namespace',
686
            'new',
687
            'or',
688
            'private',
689
            'protected',
690
            'public',
691
            'static',
692
            'switch',
693
            'throw',
694
            'trait',
695
            'try',
696
            'use',
697
            'var',
698
            'while',
699
            'xor',
700
            'yield',
701
            'die',
702
            'echo',
703
            'empty',
704
            'exit',
705
            'eval',
706
            'include',
707
            'include_once',
708
            'isset',
709
            'list',
710
            'require',
711
            'require_once',
712
            'return',
713
            'print',
714
            'unset',
715
        ];
716
    }
717
}
718