Completed
Pull Request — 1.1 (#646)
by
unknown
18:44
created

DocumentGenerateCommand::execute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 2
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 ONGR\ElasticsearchBundle\Mapping\MetadataCollector;
15
use Symfony\Component\Console\Helper\FormatterHelper;
16
use Symfony\Component\Console\Helper\QuestionHelper;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Question\ConfirmationQuestion;
20
use Symfony\Component\Console\Question\Question;
21
use Symfony\Component\HttpKernel\Kernel;
22
23
class DocumentGenerateCommand extends AbstractManagerAwareCommand
24
{
25
    /**
26
     * @var QuestionHelper
27
     */
28
    private $questionHelper;
29
30
    /**
31
     * @var string[]
32
     */
33
    private $propertyAnnotations;
34
35
    /**
36
     * {@inheritdoc}
37
     */
38
    protected function configure()
39
    {
40
        parent::configure();
41
42
        $this
43
            ->setName('ongr:es:document:generate')
44
            ->setDescription('Generates a new Elasticsearch document inside a bundle');
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    protected function execute(InputInterface $input, OutputInterface $output)
51
    {
52
        if ($input->hasParameterOption(['--no-interaction', '-n'])) {
53
            throw $this->getException('No interaction mode is not allowed!');
54
        }
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    protected function interact(InputInterface $input, OutputInterface $output)
61
    {
62
        /** @var FormatterHelper $formatter */
63
        $formatter = $this->getHelperSet()->get('formatter');
64
        $this->questionHelper = new QuestionHelper();
65
66
        $output->writeln(
67
            [
68
                '',
69
                $formatter->formatBlock('Welcome to the Elasticsearch Bundle document generator', 'bg=blue', true),
70
                '',
71
                'This command helps you generate ONGRElasticsearchBundle documents.',
72
                '',
73
                'First, you need to give the document name you want to generate.',
74
                'You must use the shortcut notation like <comment>AcmeDemoBundle:Post</comment>.',
75
                '',
76
            ]
77
        );
78
79
        /** @var Kernel $kernel */
80
        $kernel = $this->getContainer()->get('kernel');
81
        $bundleNames = array_keys($kernel->getBundles());
82
83
        while (true) {
84
            $document = $this->questionHelper->ask(
85
                $input,
86
                $output,
87
                $this->getQuestion('The Document shortcut name', null, [$this, 'validateDocumentName'], $bundleNames)
88
            );
89
90
            list($bundle, $document) = $this->parseShortcutNotation($document);
91
92
            if (in_array(strtolower($document), $this->getReservedKeywords())) {
93
                $output->writeln($this->getException('"%s" is a reserved word.', [$document])->getMessage());
94
                continue;
95
            }
96
97
            try {
98
                if (!file_exists(
99
                    $kernel->getBundle($bundle)->getPath() . '/Document/' . str_replace('\\', '/', $document) . '.php'
100
                )) {
101
                    break;
102
                }
103
104
                $output->writeln(
105
                    $this->getException('Document "%s:%s" already exists.', [$bundle, $document])->getMessage()
106
                );
107
            } catch (\Exception $e) {
108
                $output->writeln($this->getException('Bundle "%s" does not exist.', [$bundle])->getMessage());
109
            }
110
        }
111
112
        $output->writeln($this->getOptionsLabel($this->getDocumentAnnotations(), 'Available types'));
113
        $annotation = $this->questionHelper->ask(
114
            $input,
115
            $output,
116
            $this->getQuestion(
117
                'Document type',
118
                'document',
119
                [$this, 'validateDocumentAnnotation'],
120
                $this->getDocumentAnnotations()
121
            )
122
        );
123
124
        $this->propertyAnnotations = ['embedded', 'property'];
125
        $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...
126
127
        if ($annotation == 'document') {
128
            $this->propertyAnnotations = ['embedded', 'id', 'parentDocument', 'property', 'ttl'];
129
            $documentType = $this->questionHelper->ask(
130
                $input,
131
                $output,
132
                $this->getQuestion(
133
                    "\n" . 'Elasticsearch Document name',
134
                    lcfirst($document),
135
                    [$this, 'validateFieldName']
136
                )
137
            );
138
        }
139
140
        $properties = [];
141
        $output->writeln(['', $formatter->formatBlock('New Document Property?', 'bg=blue;fg=white', true)]);
142
143
        while (true) {
144
            $property = [];
145
            $question = $this->getQuestion(
146
                'Property name [<comment>press <info><return></info> to stop</comment>]',
147
                false
148
            );
149
150
            if (!$field = $this->questionHelper->ask($input, $output, $question)) {
151
                break;
152
            }
153
154
            foreach ($properties as $previousProperty) {
155
                if ($previousProperty['field_name'] == $field) {
156
                    $output->writeln($this->getException('Duplicate field name "%s"', [$field])->getMessage());
157
                    continue(2);
158
                }
159
            }
160
161
            try {
162
                $this->validateFieldName($field);
163
            } catch (\InvalidArgumentException $e) {
164
                $output->writeln($e->getMessage());
165
                continue;
166
            }
167
168
            $output->writeln($this->getOptionsLabel($this->propertyAnnotations, 'Available annotations'));
169
            $property['annotation'] = $this->questionHelper->ask(
170
                $input,
171
                $output,
172
                $this->getQuestion(
173
                    'Property meta field',
174
                    'property',
175
                    [$this, 'validatePropertyAnnotation'],
176
                    $this->propertyAnnotations
177
                )
178
            );
179
180
            $property['field_name'] = $property['property_name'] = $field;
181
182
            switch ($property['annotation']) {
183
                case 'embedded':
184
                    $property['property_name'] = $this->askForPropertyName($input, $output, $property['field_name']);
185
                    $property['property_class'] = $this->askForPropertyClass($input, $output);
186
187
                    $question = new ConfirmationQuestion("\n<info>Multiple</info> [<comment>no</comment>]: ", false);
188
                    $question->setAutocompleterValues(['yes', 'no']);
189
                    $property['property_multiple'] = $this->questionHelper->ask($input, $output, $question);
190
191
                    $property['property_options'] = $this->askForPropertyOptions($input, $output);
192
                    break;
193 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...
194
                    if (!$this->isUniqueAnnotation($properties, $property['annotation'])) {
195
                        $output->writeln(
196
                            $this
197
                                ->getException('Only one "%s" field can be added', [$property['annotation']])
198
                                ->getMessage()
199
                        );
200
                        continue(2);
201
                    }
202
                    $property['property_class'] = $this->askForPropertyClass($input, $output);
203
                    break;
204
                case 'property':
205
                    $property['property_name'] = $this->askForPropertyName($input, $output, $property['field_name']);
206
207
                    $output->writeln($this->getOptionsLabel($this->getPropertyTypes(), 'Available types'));
208
                    $property['property_type'] = $this->questionHelper->ask(
209
                        $input,
210
                        $output,
211
                        $this->getQuestion(
212
                            'Property type',
213
                            'string',
214
                            [$this, 'validatePropertyType'],
215
                            $this->getPropertyTypes()
216
                        )
217
                    );
218
219
                    $property['property_options'] = $this->askForPropertyOptions($input, $output);
220
                    break;
221 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...
222
                    if (!$this->isUniqueAnnotation($properties, $property['annotation'])) {
223
                        $output->writeln(
224
                            $this
225
                                ->getException('Only one "%s" field can be added', [$property['annotation']])
226
                                ->getMessage()
227
                        );
228
                        continue(2);
229
                    }
230
                    $property['property_default'] = $this->questionHelper->ask(
231
                        $input,
232
                        $output,
233
                        $this->getQuestion("\n" . 'Default time to live')
234
                    );
235
                    break;
236
                case 'id':
237
                    if (!$this->isUniqueAnnotation($properties, $property['annotation'])) {
238
                        $output->writeln(
239
                            $this
240
                                ->getException('Only one "%s" field can be added', [$property['annotation']])
241
                                ->getMessage()
242
                        );
243
                        continue(2);
244
                    }
245
                    break;
246
            }
247
248
            $properties[] = $property;
249
            $output->writeln(['', $formatter->formatBlock('New Document Property', 'bg=blue;fg=white', true)]);
250
        }
251
252
        $this->getContainer()->get('es.generate')->generate(
253
            $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...
254
            $document,
255
            $annotation,
256
            $documentType,
257
            $properties
258
        );
259
    }
260
261
    /**
262
     * @param array  $properties
263
     * @param string $annotation
264
     *
265
     * @return string
266
     */
267
    private function isUniqueAnnotation($properties, $annotation)
268
    {
269
        foreach ($properties as $property) {
270
            if ($property['annotation'] == $annotation) {
271
                return false;
272
            }
273
        }
274
275
        return true;
276
    }
277
278
    /**
279
     * Asks for property name
280
     *
281
     * @param InputInterface  $input
282
     * @param OutputInterface $output
283
     * @param string          $default
284
     *
285
     * @return string
286
     */
287
    private function askForPropertyName(InputInterface $input, OutputInterface $output, $default = null)
288
    {
289
        return $this->questionHelper->ask(
290
            $input,
291
            $output,
292
            $this->getQuestion("\n" . 'Property name in Elasticsearch', $default, [$this, 'validateFieldName'])
293
        );
294
    }
295
296
    /**
297
     * Asks for property options
298
     *
299
     * @param InputInterface  $input
300
     * @param OutputInterface $output
301
     *
302
     * @return string
303
     */
304
    private function askForPropertyOptions(InputInterface $input, OutputInterface $output)
305
    {
306
        $output->writeln(
307
            "\n"
308
                . '<info>Enter property options, for example <comment>"index"="not_analyzed"</comment>'
309
                . ' allows mapper to index this field, so it is searchable, but value will be not analyzed.</info>'
310
        );
311
312
        return $this->questionHelper->ask(
313
            $input,
314
            $output,
315
            $this->getQuestion(
316
                'Property options [<comment>press <info><return></info> to stop</comment>]',
317
                false,
318
                null,
319
                ['"index"="not_analyzed"', '"analyzer"="standard"']
320
            )
321
        );
322
    }
323
324
    /**
325
     * Asks for property class
326
     *
327
     * @param InputInterface  $input
328
     * @param OutputInterface $output
329
     *
330
     * @return string
331
     */
332
    private function askForPropertyClass(InputInterface $input, OutputInterface $output)
333
    {
334
        return $this->questionHelper->ask(
335
            $input,
336
            $output,
337
            $this->getQuestion(
338
                "\n" . 'Property class',
339
                null,
340
                [$this, 'validatePropertyClass'],
341
                array_merge($this->getDocumentClasses(), array_keys($this->getContainer()->get('kernel')->getBundles()))
342
            )
343
        );
344
    }
345
346
    /**
347
     * Returns available document classes
348
     *
349
     * @return array
350
     */
351
    private function getDocumentClasses()
352
    {
353
        /** @var MetadataCollector $metadataCollector */
354
        $metadataCollector = $this->getContainer()->get('es.metadata_collector');
355
        $classes = [];
356
357
        foreach ($this->getContainer()->getParameter('es.managers') as $manager) {
358
            $documents = $metadataCollector->getMappings($manager['mappings']);
359
            foreach ($documents as $document) {
360
                $classes[] = sprintf('%s:%s', $document['bundle'], $document['class']);
361
            }
362
        }
363
364
        return $classes;
365
    }
366
367
    /**
368
     * Parses shortcut notation
369
     *
370
     * @param string $shortcut
371
     *
372
     * @return string[]
373
     * @throws \InvalidArgumentException
374
     */
375
    private function parseShortcutNotation($shortcut)
376
    {
377
        $shortcut = str_replace('/', '\\', $shortcut);
378
379
        if (false === $pos = strpos($shortcut, ':')) {
380
            throw $this->getException(
381
                'The document name isn\'t valid ("%s" given, expecting something like AcmeBundle:Post)',
382
                [$shortcut]
383
            );
384
        }
385
386
        return [substr($shortcut, 0, $pos), substr($shortcut, $pos + 1)];
387
    }
388
389
    /**
390
     * Validates property class
391
     *
392
     * @param string $input
393
     *
394
     * @return string
395
     * @throws \InvalidArgumentException
396
     */
397
    public function validatePropertyClass($input)
398
    {
399
        list($bundle, $document) = $this->parseShortcutNotation($input);
400
401
        try {
402
            $bundlePath = $this->getContainer()->get('kernel')->getBundle($bundle)->getPath();
403
        } catch (\Exception $e) {
404
            throw $this->getException('Bundle "%s" does not exist.', [$bundle]);
405
        }
406
407
        if (!file_exists($bundlePath . '/Document/' . str_replace('\\', '/', $document) . '.php')) {
408
            throw $this->getException('Document "%s:%s" does not exist.', [$bundle, $document]);
409
        }
410
411
        return $input;
412
    }
413
414
    /**
415
     * Performs basic checks in document name
416
     *
417
     * @param string $document
418
     *
419
     * @return string
420
     * @throws \InvalidArgumentException
421
     */
422
    public function validateDocumentName($document)
423
    {
424
        if (!preg_match('{^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*:[a-zA-Z0-9_\x7f-\xff\\\/]+$}', $document)) {
425
            throw $this->getException(
426
                'The document name isn\'t valid ("%s" given, expecting something like AcmeBundle:Post)',
427
                [$document]
428
            );
429
        }
430
431
        return $document;
432
    }
433
434
    /**
435
     * Validates field name
436
     *
437
     * @param string $field
438
     *
439
     * @return string
440
     * @throws \InvalidArgumentException
441
     */
442
    public function validateFieldName($field)
443
    {
444
        if (!$field || $field != lcfirst(preg_replace('/[^a-zA-Z]+/', '', $field))) {
445
            throw $this->getException(
446
                'The parameter isn\'t valid ("%s" given, expecting camelcase separated words)',
447
                [$field]
448
            );
449
        }
450
451
        if (in_array(strtolower($field), $this->getReservedKeywords())) {
452
            throw $this->getException('"%s" is a reserved word.', [$field]);
453
        }
454
455
        return $field;
456
    }
457
458
    /**
459
     * Validates property type
460
     *
461
     * @param string $type
462
     *
463
     * @return string
464
     * @throws \InvalidArgumentException
465
     */
466 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...
467
    {
468
        if (!in_array($type, $this->getPropertyTypes())) {
469
            throw $this->getException(
470
                'The property type isn\'t valid ("%s" given, expecting one of following: %s)',
471
                [$type, implode(', ', $this->getPropertyTypes())]
472
            );
473
        }
474
475
        return $type;
476
    }
477
478
    /**
479
     * Validates document annotation
480
     *
481
     * @param string $annotation
482
     *
483
     * @return string
484
     * @throws \InvalidArgumentException
485
     */
486 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...
487
    {
488
        if (!in_array($annotation, $this->getDocumentAnnotations())) {
489
            throw $this->getException(
490
                'The document annotation isn\'t valid ("%s" given, expecting one of following: %s)',
491
                [$annotation, implode(', ', $this->getDocumentAnnotations())]
492
            );
493
        }
494
495
        return $annotation;
496
    }
497
498
    /**
499
     * Validates property annotation
500
     *
501
     * @param string $annotation
502
     *
503
     * @return string
504
     * @throws \InvalidArgumentException
505
     */
506 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...
507
    {
508
        if (!in_array($annotation, $this->propertyAnnotations)) {
509
            throw $this->getException(
510
                'The property annotation isn\'t valid ("%s" given, expecting one of following: %s)',
511
                [$annotation, implode(', ', $this->propertyAnnotations)]
512
            );
513
        }
514
515
        return $annotation;
516
    }
517
518
    /**
519
     * Returns formatted question
520
     *
521
     * @param string        $question
522
     * @param mixed         $default
523
     * @param callable|null $validator
524
     * @param array|null    $values
525
     *
526
     * @return Question
527
     */
528
    private function getQuestion($question, $default = null, callable $validator = null, array $values = null)
529
    {
530
        $question = new Question(
531
            sprintf('<info>%s</info>%s: ', $question, $default ? sprintf(' [<comment>%s</comment>]', $default) : ''),
532
            $default
533
        );
534
535
        $question
536
            ->setValidator($validator)
537
            ->setAutocompleterValues($values);
538
539
        return $question;
540
    }
541
542
    /**
543
     * Returns options label
544
     *
545
     * @param array  $options
546
     * @param string $suffix
547
     *
548
     * @return string[]
549
     */
550
    private function getOptionsLabel(array $options, $suffix)
551
    {
552
        $label = sprintf('<info>%s:</info> ', $suffix);
553
554
        foreach ($options as &$option) {
555
            $option = sprintf('<comment>%s</comment>', $option);
556
        }
557
558
        return ['', $label . implode(', ', $options) . '.'];
559
    }
560
561
    /**
562
     * Returns formatted exception
563
     *
564
     * @param string $format
565
     * @param array  $args
566
     *
567
     * @return \InvalidArgumentException
568
     */
569
    private function getException($format, $args = [])
570
    {
571
        /** @var FormatterHelper $formatter */
572
        $formatter = $this->getHelperSet()->get('formatter');
573
        return new \InvalidArgumentException($formatter->formatBlock(vsprintf($format, $args), 'bg=red', true));
574
    }
575
576
    /**
577
     * Returns available property types
578
     *
579
     * @return array
580
     */
581
    private function getPropertyTypes()
582
    {
583
        $reflection = new \ReflectionClass('ONGR\ElasticsearchBundle\Annotation\Property');
584
585
        return $this
586
            ->getContainer()
587
            ->get('annotations.cached_reader')
588
            ->getPropertyAnnotation($reflection->getProperty('type'), 'Doctrine\Common\Annotations\Annotation\Enum')
589
            ->value;
590
    }
591
592
    /**
593
     * Returns document annotations
594
     *
595
     * @return string[]
596
     */
597
    private function getDocumentAnnotations()
598
    {
599
        return ['document', 'nested', 'object'];
600
    }
601
602
    /**
603
     * Returns reserved keywords
604
     *
605
     * @return string[]
606
     */
607
    private function getReservedKeywords()
608
    {
609
        return [
610
            'abstract',
611
            'and',
612
            'array',
613
            'as',
614
            'break',
615
            'callable',
616
            'case',
617
            'catch',
618
            'class',
619
            'clone',
620
            'const',
621
            'continue',
622
            'declare',
623
            'default',
624
            'do',
625
            'else',
626
            'elseif',
627
            'enddeclare',
628
            'endfor',
629
            'endforeach',
630
            'endif',
631
            'endswitch',
632
            'endwhile',
633
            'extends',
634
            'final',
635
            'finally',
636
            'for',
637
            'foreach',
638
            'function',
639
            'global',
640
            'goto',
641
            'if',
642
            'implements',
643
            'interface',
644
            'instanceof',
645
            'insteadof',
646
            'namespace',
647
            'new',
648
            'or',
649
            'private',
650
            'protected',
651
            'public',
652
            'static',
653
            'switch',
654
            'throw',
655
            'trait',
656
            'try',
657
            'use',
658
            'var',
659
            'while',
660
            'xor',
661
            'yield',
662
            'die',
663
            'echo',
664
            'empty',
665
            'exit',
666
            'eval',
667
            'include',
668
            'include_once',
669
            'isset',
670
            'list',
671
            'require',
672
            'require_once',
673
            'return',
674
            'print',
675
            'unset',
676
        ];
677
    }
678
}
679