GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 400df7...c8c0ea )
by Robert
16:13
created

Controller::getActionArgsHelp()   F

Complexity

Conditions 20
Paths 1544

Size

Total Lines 55
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 20.0093

Importance

Changes 0
Metric Value
cc 20
eloc 35
nc 1544
nop 1
dl 0
loc 55
ccs 34
cts 35
cp 0.9714
crap 20.0093
rs 0
c 0
b 0
f 0

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

430
    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...
431
    {
432
        // $actionId might be used in subclasses to provide options specific to action id
433 212
        return ['color', 'interactive', 'help', 'silentExitOnException'];
434
    }
435
436
    /**
437
     * Returns option alias names.
438
     * Child classes may override this method to specify alias options.
439
     *
440
     * @return array the options alias names valid for the action
441
     * where the keys is alias name for option and value is option name.
442
     *
443
     * @since 2.0.8
444
     * @see options()
445
     */
446 2
    public function optionAliases()
447
    {
448
        return [
449 2
            'h' => 'help',
450
        ];
451
    }
452
453
    /**
454
     * Returns properties corresponding to the options for the action id
455
     * Child classes may override this method to specify possible properties.
456
     *
457
     * @param string $actionID the action id of the current request
458
     * @return array properties corresponding to the options for the action
459
     */
460 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

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