Passed
Push — master ( 0d899f...766cf7 )
by Alexander
21:42
created

Controller::bindActionParams()   F

Complexity

Conditions 18
Paths 228

Size

Total Lines 60
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 18.0603

Importance

Changes 0
Metric Value
cc 18
eloc 42
c 0
b 0
f 0
nc 228
nop 2
dl 0
loc 60
ccs 33
cts 35
cp 0.9429
crap 18.0603
rs 3.6833

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\console;
9
10
use Yii;
11
use yii\base\Action;
12
use yii\base\InlineAction;
13
use yii\base\InvalidRouteException;
14
use yii\helpers\Console;
15
use yii\helpers\Inflector;
16
17
/**
18
 * Controller is the base class of console command classes.
19
 *
20
 * A console controller consists of one or several actions known as sub-commands.
21
 * Users call a console command by specifying the corresponding route which identifies a controller action.
22
 * The `yii` program is used when calling a console command, like the following:
23
 *
24
 * ```
25
 * yii <route> [--param1=value1 --param2 ...]
26
 * ```
27
 *
28
 * where `<route>` is a route to a controller action and the params will be populated as properties of a command.
29
 * See [[options()]] for details.
30
 *
31
 * @property-read string $helpSummary The one-line short summary describing this controller.
32
 * @property-read array $passedOptionValues The properties corresponding to the passed options.
33
 * @property-read array $passedOptions The names of the options passed during execution.
34
 * @property Request $request
35
 * @property Response $response
36
 *
37
 * @author Qiang Xue <[email protected]>
38
 * @since 2.0
39
 */
40
class Controller extends \yii\base\Controller
41
{
42
    /**
43
     * @deprecated since 2.0.13. Use [[ExitCode::OK]] instead.
44
     */
45
    const EXIT_CODE_NORMAL = 0;
46
    /**
47
     * @deprecated since 2.0.13. Use [[ExitCode::UNSPECIFIED_ERROR]] instead.
48
     */
49
    const EXIT_CODE_ERROR = 1;
50
51
    /**
52
     * @var bool whether to run the command interactively.
53
     */
54
    public $interactive = true;
55
    /**
56
     * @var bool|null whether to enable ANSI color in the output.
57
     * If not set, ANSI color will only be enabled for terminals that support it.
58
     */
59
    public $color;
60
    /**
61
     * @var bool whether to display help information about current command.
62
     * @since 2.0.10
63
     */
64
    public $help = false;
65
    /**
66
     * @var bool|null if true - script finish with `ExitCode::OK` in case of exception.
67
     * false - `ExitCode::UNSPECIFIED_ERROR`.
68
     * Default: `YII_ENV_TEST`
69
     * @since 2.0.36
70
     */
71
    public $silentExitOnException;
72
73
    /**
74
     * @var array the options passed during execution.
75
     */
76
    private $_passedOptions = [];
77
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function beforeAction($action)
83
    {
84
        $silentExit = $this->silentExitOnException !== null ? $this->silentExitOnException : YII_ENV_TEST;
85 240
        Yii::$app->errorHandler->silentExitOnException = $silentExit;
86
87 240
        return parent::beforeAction($action);
88 240
    }
89
90 240
    /**
91
     * Returns a value indicating whether ANSI color is enabled.
92
     *
93
     * ANSI color is enabled only if [[color]] is set true or is not set
94
     * and the terminal supports ANSI color.
95
     *
96
     * @param resource $stream the stream to check.
97
     * @return bool Whether to enable ANSI style in output.
98
     */
99
    public function isColorEnabled($stream = \STDOUT)
100
    {
101
        return $this->color === null ? Console::streamSupportsAnsiColors($stream) : $this->color;
102 7
    }
103
104 7
    /**
105
     * Runs an action with the specified action ID and parameters.
106
     * If the action ID is empty, the method will use [[defaultAction]].
107
     * @param string $id the ID of the action to be executed.
108
     * @param array $params the parameters (name-value pairs) to be passed to the action.
109
     * @return int the status of the action execution. 0 means normal, other values mean abnormal.
110
     * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.
111
     * @throws Exception if there are unknown options or missing arguments
112
     * @see createAction
113
     */
114
    public function runAction($id, $params = [])
115
    {
116
        if (!empty($params)) {
117 240
            // populate options here so that they are available in beforeAction().
118
            $options = $this->options($id === '' ? $this->defaultAction : $id);
119 240
            if (isset($params['_aliases'])) {
120
                $optionAliases = $this->optionAliases();
121 228
                foreach ($params['_aliases'] as $name => $value) {
122 228
                    if (array_key_exists($name, $optionAliases)) {
123 1
                        $params[$optionAliases[$name]] = $value;
124 1
                    } else {
125 1
                        $message = Yii::t('yii', 'Unknown alias: -{name}', ['name' => $name]);
126 1
                        if (!empty($optionAliases)) {
127
                            $aliasesAvailable = [];
128
                            foreach ($optionAliases as $alias => $option) {
129
                                $aliasesAvailable[] = '-' . $alias . ' (--' . $option . ')';
130
                            }
131
132
                            $message .= '. ' . Yii::t('yii', 'Aliases available: {aliases}', [
133
                                'aliases' => implode(', ', $aliasesAvailable)
134
                            ]);
135
                        }
136
                        throw new Exception($message);
137
                    }
138
                }
139 1
                unset($params['_aliases']);
140
            }
141
            foreach ($params as $name => $value) {
142 1
                // Allow camelCase options to be entered in kebab-case
143
                if (!in_array($name, $options, true) && strpos($name, '-') !== false) {
144 228
                    $kebabName = $name;
145
                    $altName = lcfirst(Inflector::id2camel($kebabName));
146 228
                    if (in_array($altName, $options, true)) {
147 1
                        $name = $altName;
148 1
                    }
149 1
                }
150 1
151
                if (in_array($name, $options, true)) {
152
                    $default = $this->$name;
153
                    if (is_array($default) && is_string($value)) {
154 228
                        $this->$name = preg_split('/\s*,\s*(?![^()]*\))/', $value);
155 53
                    } elseif ($default !== null) {
156 53
                        settype($value, gettype($default));
157 52
                        $this->$name = $value;
158 12
                    } else {
159 11
                        $this->$name = $value;
160 11
                    }
161
                    $this->_passedOptions[] = $name;
162 2
                    unset($params[$name]);
163
                    if (isset($kebabName)) {
164 53
                        unset($params[$kebabName]);
165 53
                    }
166 53
                } elseif (!is_int($name)) {
167 53
                    $message = Yii::t('yii', 'Unknown option: --{name}', ['name' => $name]);
168
                    if (!empty($options)) {
169 221
                        $message .= '. ' . Yii::t('yii', 'Options available: {options}', ['options' => '--' . implode(', --', $options)]);
170
                    }
171
172
                    throw new Exception($message);
173
                }
174
            }
175 228
        }
176
        if ($this->help) {
177
            $route = $this->getUniqueId() . '/' . $id;
178
            return Yii::$app->runAction('help', [$route]);
179 240
        }
180 2
181 2
        return parent::runAction($id, $params);
182
    }
183
184 240
    /**
185
     * Binds the parameters to the action.
186
     * This method is invoked by [[Action]] when it begins to run with the given parameters.
187
     * This method will first bind the parameters with the [[options()|options]]
188
     * available to the action. It then validates the given arguments.
189
     * @param Action $action the action to be bound with parameters
190
     * @param array $params the parameters to be bound to the action
191
     * @return array the valid parameters that the action can run with.
192
     * @throws Exception if there are unknown options or missing arguments
193
     */
194
    public function bindActionParams($action, $params)
195
    {
196
        if ($action instanceof InlineAction) {
197 254
            $method = new \ReflectionMethod($this, $action->actionMethod);
198
        } else {
199 254
            $method = new \ReflectionMethod($action, 'run');
200 254
        }
201
202
        $args = [];
203
        $missing = [];
204
        $actionParams = [];
205 254
        $requestedParams = [];
206 254
        foreach ($method->getParameters() as $i => $param) {
207 254
            $name = $param->getName();
208 254
            $key = null;
209 254
            if (array_key_exists($i, $params)) {
210 247
                $key = $i;
211 247
            } elseif (array_key_exists($name, $params)) {
212 247
                $key = $name;
213 221
            }
214 83
215 7
            if ($key !== null) {
216
                if (PHP_VERSION_ID >= 80000) {
217
                    $isArray = ($type = $param->getType()) instanceof \ReflectionNamedType && $type->getName() === 'array';
218 247
                } else {
219 228
                    $isArray = $param->isArray();
220
                }
221
                if ($isArray) {
222 228
                    $params[$key] = $params[$key] === '' ? [] : preg_split('/\s*,\s*/', $params[$key]);
223
                }
224 228
                $args[] = $actionParams[$key] = $params[$key];
225 1
                unset($params[$key]);
226
            } elseif (
227 228
                PHP_VERSION_ID >= 70100
228 228
                && ($type = $param->getType()) !== null
229 79
                && $type instanceof \ReflectionNamedType
230
                && !$type->isBuiltin()
231 5
            ) {
232 2
                try {
233 5
                    $this->bindInjectedParams($type, $name, $args, $requestedParams);
234
                } catch (\yii\base\Exception $e) {
235 74
                    throw new Exception($e->getMessage());
236 74
                }
237
            } elseif ($param->isDefaultValueAvailable()) {
238 247
                $args[] = $actionParams[$i] = $param->getDefaultValue();
239
            } else {
240
                $missing[] = $name;
241
            }
242 252
        }
243 1
244
        if (!empty($missing)) {
245
            throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', ['params' => implode(', ', $missing)]));
246
        }
247 252
248 252
        // We use a different array here, specifically one that doesn't contain service instances but descriptions instead.
249
        if (\Yii::$app->requestedParams === null) {
250
            \Yii::$app->requestedParams = array_merge($actionParams, $requestedParams);
251 252
        }
252
253
        return array_merge($args, $params);
254
    }
255
256
    /**
257
     * Formats a string with ANSI codes.
258
     *
259
     * You may pass additional parameters using the constants defined in [[\yii\helpers\Console]].
260
     *
261
     * Example:
262
     *
263
     * ```
264
     * echo $this->ansiFormat('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
265
     * ```
266
     *
267
     * @param string $string the string to be formatted
268 7
     * @return string
269
     */
270 7
    public function ansiFormat($string)
271 1
    {
272 1
        if ($this->isColorEnabled()) {
273 1
            $args = func_get_args();
274
            array_shift($args);
275
            $string = Console::ansiFormat($string, $args);
276 7
        }
277
278
        return $string;
279
    }
280
281
    /**
282
     * Prints a string to STDOUT.
283
     *
284
     * You may optionally format the string with ANSI codes by
285
     * passing additional parameters using the constants defined in [[\yii\helpers\Console]].
286
     *
287
     * Example:
288
     *
289
     * ```
290
     * $this->stdout('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
291
     * ```
292
     *
293
     * @param string $string the string to print
294
     * @param int ...$args additional parameters to decorate the output
295
     * @return int|bool Number of bytes printed or false on error
296
     */
297
    public function stdout($string)
298
    {
299
        if ($this->isColorEnabled()) {
300
            $args = func_get_args();
301
            array_shift($args);
302
            $string = Console::ansiFormat($string, $args);
303
        }
304
305
        return Console::stdout($string);
306
    }
307
308
    /**
309
     * Prints a string to STDERR.
310
     *
311
     * You may optionally format the string with ANSI codes by
312
     * passing additional parameters using the constants defined in [[\yii\helpers\Console]].
313
     *
314
     * Example:
315
     *
316
     * ```
317
     * $this->stderr('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);
318
     * ```
319
     *
320
     * @param string $string the string to print
321
     * @param int ...$args additional parameters to decorate the output
322
     * @return int|bool Number of bytes printed or false on error
323
     */
324
    public function stderr($string)
325
    {
326
        if ($this->isColorEnabled(\STDERR)) {
327
            $args = func_get_args();
328
            array_shift($args);
329
            $string = Console::ansiFormat($string, $args);
330
        }
331
332
        return fwrite(\STDERR, $string);
333
    }
334
335
    /**
336
     * Prompts the user for input and validates it.
337
     *
338
     * @param string $text prompt string
339
     * @param array $options the options to validate the input:
340
     *
341
     *  - required: whether it is required or not
342
     *  - default: default value if no input is inserted by the user
343
     *  - pattern: regular expression pattern to validate user input
344
     *  - validator: a callable function to validate input. The function must accept two parameters:
345
     *      - $input: the user input to validate
346
     *      - $error: the error value passed by reference if validation failed.
347
     *
348
     * An example of how to use the prompt method with a validator function.
349
     *
350
     * ```php
351
     * $code = $this->prompt('Enter 4-Chars-Pin', ['required' => true, 'validator' => function($input, &$error) {
352
     *     if (strlen($input) !== 4) {
353
     *         $error = 'The Pin must be exactly 4 chars!';
354
     *         return false;
355
     *     }
356
     *     return true;
357
     * }]);
358
     * ```
359
     *
360
     * @return string the user input
361
     */
362
    public function prompt($text, $options = [])
363
    {
364
        if ($this->interactive) {
365
            return Console::prompt($text, $options);
366
        }
367
368
        return isset($options['default']) ? $options['default'] : '';
369
    }
370
371
    /**
372
     * Asks user to confirm by typing y or n.
373
     *
374
     * A typical usage looks like the following:
375
     *
376
     * ```php
377
     * if ($this->confirm("Are you sure?")) {
378
     *     echo "user typed yes\n";
379
     * } else {
380
     *     echo "user typed no\n";
381
     * }
382
     * ```
383
     *
384
     * @param string $message to echo out before waiting for user input
385
     * @param bool $default this value is returned if no selection is made.
386 172
     * @return bool whether user confirmed.
387
     * Will return true if [[interactive]] is false.
388 172
     */
389
    public function confirm($message, $default = false)
390
    {
391
        if ($this->interactive) {
392 172
            return Console::confirm($message, $default);
393
        }
394
395
        return true;
396
    }
397
398
    /**
399
     * Gives the user an option to choose from. Giving '?' as an input will show
400
     * a list of options to choose from and their explanations.
401
     *
402
     * @param string $prompt the prompt message
403
     * @param array $options Key-value array of options to choose from
404
     *
405
     * @return string An option character the user chose
406
     */
407
    public function select($prompt, $options = [])
408
    {
409
        return Console::select($prompt, $options);
410
    }
411
412
    /**
413
     * Returns the names of valid options for the action (id)
414
     * An option requires the existence of a public member variable whose
415
     * name is the option name.
416
     * Child classes may override this method to specify possible options.
417
     *
418
     * Note that the values setting via options are not available
419
     * until [[beforeAction()]] is being called.
420
     *
421 231
     * @param string $actionID the action id of the current request
422
     * @return string[] the names of the options valid for the action
423
     */
424 231
    public function options($actionID)
0 ignored issues
show
Unused Code introduced by
The parameter $actionID is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

424
    public function options(/** @scrutinizer ignore-unused */ $actionID)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
425
    {
426
        // $actionId might be used in subclasses to provide options specific to action id
427
        return ['color', 'interactive', 'help', 'silentExitOnException'];
428
    }
429
430
    /**
431
     * Returns option alias names.
432
     * Child classes may override this method to specify alias options.
433
     *
434
     * @return array the options alias names valid for the action
435
     * where the keys is alias name for option and value is option name.
436
     *
437 2
     * @since 2.0.8
438
     * @see options()
439
     */
440 2
    public function optionAliases()
441
    {
442
        return [
443
            'h' => 'help',
444
        ];
445
    }
446
447
    /**
448
     * Returns properties corresponding to the options for the action id
449
     * Child classes may override this method to specify possible properties.
450
     *
451 65
     * @param string $actionID the action id of the current request
452
     * @return array properties corresponding to the options for the action
453
     */
454 65
    public function getOptionValues($actionID)
0 ignored issues
show
Unused Code introduced by
The parameter $actionID is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

454
    public function getOptionValues(/** @scrutinizer ignore-unused */ $actionID)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
455 65
    {
456 65
        // $actionId might be used in subclasses to provide properties specific to action id
457
        $properties = [];
458
        foreach ($this->options($this->action->id) as $property) {
459 65
            $properties[$property] = $this->$property;
460
        }
461
462
        return $properties;
463
    }
464
465
    /**
466
     * Returns the names of valid options passed during execution.
467
     *
468
     * @return array the names of the options passed during execution
469
     */
470
    public function getPassedOptions()
471
    {
472
        return $this->_passedOptions;
473
    }
474
475
    /**
476
     * Returns the properties corresponding to the passed options.
477 59
     *
478
     * @return array the properties corresponding to the passed options
479 59
     */
480 59
    public function getPassedOptionValues()
481
    {
482
        $properties = [];
483
        foreach ($this->_passedOptions as $property) {
484 59
            $properties[$property] = $this->$property;
485
        }
486
487
        return $properties;
488
    }
489
490
    /**
491
     * Returns one-line short summary describing this controller.
492
     *
493
     * You may override this method to return customized summary.
494
     * The default implementation returns first line from the PHPDoc comment.
495 5
     *
496
     * @return string
497 5
     */
498
    public function getHelpSummary()
499
    {
500
        return $this->parseDocCommentSummary(new \ReflectionClass($this));
501
    }
502
503
    /**
504
     * Returns help information for this controller.
505
     *
506
     * You may override this method to return customized help.
507
     * The default implementation returns help information retrieved from the PHPDoc comment.
508
     * @return string
509
     */
510
    public function getHelp()
511
    {
512
        return $this->parseDocCommentDetail(new \ReflectionClass($this));
513
    }
514
515
    /**
516
     * Returns a one-line short summary describing the specified action.
517 3
     * @param Action $action action to get summary for
518
     * @return string a one-line short summary describing the specified action.
519 3
     */
520 1
    public function getActionHelpSummary($action)
521
    {
522
        if ($action === null) {
523 2
            return $this->ansiFormat(Yii::t('yii', 'Action not found.'), Console::FG_RED);
524
        }
525
526
        return $this->parseDocCommentSummary($this->getActionMethodReflection($action));
527
    }
528
529
    /**
530
     * Returns the detailed help information for the specified action.
531 3
     * @param Action $action action to get help for
532
     * @return string the detailed help information for the specified action.
533 3
     */
534
    public function getActionHelp($action)
535
    {
536
        return $this->parseDocCommentDetail($this->getActionMethodReflection($action));
537
    }
538
539
    /**
540
     * Returns the help information for the anonymous arguments for the action.
541
     *
542
     * The returned value should be an array. The keys are the argument names, and the values are
543
     * the corresponding help information. Each value must be an array of the following structure:
544
     *
545
     * - required: bool, whether this argument is required
546
     * - type: string|null, the PHP type(s) of this argument
547
     * - default: mixed, the default value of this argument
548
     * - comment: string, the description of this argument
549
     *
550
     * The default implementation will return the help information extracted from the Reflection or
551
     * DocBlock of the parameters corresponding to the action method.
552
     *
553 6
     * @param Action $action the action instance
554
     * @return array the help information of the action arguments
555 6
     */
556 6
    public function getActionArgsHelp($action)
557 6
    {
558
        $method = $this->getActionMethodReflection($action);
559 6
560
        $tags = $this->parseDocCommentTags($method);
561
        $tags['param'] = isset($tags['param']) ? (array) $tags['param'] : [];
562 6
        $phpDocParams = [];
563 6
        foreach ($tags['param'] as $i => $tag) {
564
            if (preg_match('/^(?<type>\S+)(\s+\$(?<name>\w+))?(?<comment>.*)/us', $tag, $matches) === 1) {
565
                $key = empty($matches['name']) ? $i : $matches['name'];
566 6
                $phpDocParams[$key] = ['type' => $matches['type'], 'comment' => $matches['comment']];
567
            }
568
        }
569 6
        unset($tags);
570 1
571
        $args = [];
572 6
573 6
        /** @var \ReflectionParameter $parameter */
574 6
        foreach ($method->getParameters() as $i => $parameter) {
575 4
            $type = null;
576 4
            $comment = '';
577
            if (PHP_MAJOR_VERSION > 5 && $parameter->hasType()) {
578 2
                $reflectionType = $parameter->getType();
579 2
                if (PHP_VERSION_ID >= 70100) {
580
                    $types = method_exists($reflectionType, 'getTypes') ? $reflectionType->getTypes() : [$reflectionType];
581 6
                    foreach ($types as $key => $reflectionType) {
582 3
                        $types[$key] = $reflectionType->getName();
583 3
                    }
584 3
                    $type = implode('|', $types);
585 3
                } else {
586 3
                    $type = (string) $reflectionType;
587
                }
588
            }
589 4
            // find PhpDoc tag by property name or position
590 4
            $key = isset($phpDocParams[$parameter->name]) ? $parameter->name : (isset($phpDocParams[$i]) ? $i : null);
591 4
            if ($key !== null) {
592
                $comment = $phpDocParams[$key]['comment'];
593 6
                if ($type === null && !empty($phpDocParams[$key]['type'])) {
594
                    $type = $phpDocParams[$key]['type'];
595
                }
596
            }
597
            // if type still not detected, then using type of default value
598 6
            if ($type === null && $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null) {
599
                $type = gettype($parameter->getDefaultValue());
600
            }
601
602
            $args[$parameter->name] = [
603
                'required' => !$parameter->isOptional(),
604
                'type' => $type,
605
                'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,
606
                'comment' => $comment,
607
            ];
608
        }
609
610
        return $args;
611
    }
612
613
    /**
614
     * Returns the help information for the options for the action.
615
     *
616
     * The returned value should be an array. The keys are the option names, and the values are
617 4
     * the corresponding help information. Each value must be an array of the following structure:
618
     *
619 4
     * - type: string, the PHP type of this argument.
620 4
     * - default: string, the default value of this argument
621
     * - comment: string, the comment of this argument
622
     *
623
     * The default implementation will return the help information extracted from the doc-comment of
624 4
     * the properties corresponding to the action options.
625 4
     *
626 4
     * @param Action $action
627 4
     * @return array the help information of the action options
628 4
     */
629 4
    public function getActionOptionsHelp($action)
630
    {
631 4
        $optionNames = $this->options($action->id);
632 4
        if (empty($optionNames)) {
633
            return [];
634
        }
635 4
636
        $class = new \ReflectionClass($this);
637 4
        $options = [];
638 4
        foreach ($class->getProperties() as $property) {
639 4
            $name = $property->getName();
640
            if (!in_array($name, $optionNames, true)) {
641
                continue;
642 4
            }
643 4
            $defaultValue = $property->getValue($this);
644 4
            $tags = $this->parseDocCommentTags($property);
645
646
            // Display camelCase options in kebab-case
647
            $name = Inflector::camel2id($name, '-', true);
648
649 4
            if (isset($tags['var']) || isset($tags['property'])) {
650 4
                $doc = isset($tags['var']) ? $tags['var'] : $tags['property'];
651 4
                if (is_array($doc)) {
652 4
                    $doc = reset($doc);
653
                }
654
                if (preg_match('/^(\S+)(.*)/s', $doc, $matches)) {
655 1
                    $type = $matches[1];
656 1
                    $comment = $matches[2];
657 1
                } else {
658 4
                    $type = null;
659
                    $comment = $doc;
660
                }
661
                $options[$name] = [
662
                    'type' => $type,
663 4
                    'default' => $defaultValue,
664
                    'comment' => $comment,
665
                ];
666
            } else {
667
                $options[$name] = [
668
                    'type' => null,
669
                    'default' => $defaultValue,
670
                    'comment' => '',
671
                ];
672 8
            }
673
        }
674 8
675 8
        return $options;
676 8
    }
677
678
    private $_reflections = [];
679
680
    /**
681
     * @param Action $action
682 8
     * @return \ReflectionMethod
683
     */
684
    protected function getActionMethodReflection($action)
685
    {
686
        if (!isset($this->_reflections[$action->id])) {
687
            if ($action instanceof InlineAction) {
688
                $this->_reflections[$action->id] = new \ReflectionMethod($this, $action->actionMethod);
689
            } else {
690 6
                $this->_reflections[$action->id] = new \ReflectionMethod($action, 'run');
691
            }
692 6
        }
693 6
694 6
        return $this->_reflections[$action->id];
695 6
    }
696 6
697 6
    /**
698 6
     * Parses the comment block into tags.
699 6
     * @param \Reflector $reflection the comment block
700 6
     * @return array the parsed tags
701
     */
702
    protected function parseDocCommentTags($reflection)
703
    {
704 6
        $comment = $reflection->getDocComment();
0 ignored issues
show
Bug introduced by
The method getDocComment() does not exist on Reflector. It seems like you code against a sub-type of Reflector such as ReflectionProperty or ReflectionFunctionAbstract or ReflectionClassConstant or ReflectionClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

704
        /** @scrutinizer ignore-call */ 
705
        $comment = $reflection->getDocComment();
Loading history...
705
        $comment = "@description \n" . strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($comment, '/'))), "\r", '');
706
        $parts = preg_split('/^\s*@/m', $comment, -1, PREG_SPLIT_NO_EMPTY);
707
        $tags = [];
708
        foreach ($parts as $part) {
709 6
            if (preg_match('/^(\w+)(.*)/ms', trim($part), $matches)) {
710
                $name = $matches[1];
711
                if (!isset($tags[$name])) {
712
                    $tags[$name] = trim($matches[2]);
713
                } elseif (is_array($tags[$name])) {
714
                    $tags[$name][] = trim($matches[2]);
715
                } else {
716
                    $tags[$name] = [$tags[$name], trim($matches[2])];
717
                }
718 5
            }
719
        }
720 5
721 5
        return $tags;
722 5
    }
723
724
    /**
725 2
     * Returns the first line of docblock.
726
     *
727
     * @param \Reflector $reflection
728
     * @return string
729
     */
730
    protected function parseDocCommentSummary($reflection)
731
    {
732
        $docLines = preg_split('~\R~u', $reflection->getDocComment());
733
        if (isset($docLines[1])) {
734 3
            return trim($docLines[1], "\t *");
735
        }
736 3
737 3
        return '';
738 2
    }
739
740 3
    /**
741 2
     * Returns full description from the docblock.
742
     *
743
     * @param \Reflector $reflection
744 1
     * @return string
745
     */
746
    protected function parseDocCommentDetail($reflection)
747
    {
748
        $comment = strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($reflection->getDocComment(), '/'))), "\r", '');
749
        if (preg_match('/^\s*@\w+/m', $comment, $matches, PREG_OFFSET_CAPTURE)) {
750
            $comment = trim(substr($comment, 0, $matches[0][1]));
751
        }
752
        if ($comment !== '') {
753
            return rtrim(Console::renderColoredString(Console::markdownToAnsi($comment)));
754
        }
755
756
        return '';
757
    }
758
}
759