Completed
Push — fix-numbervalidator-comma-deci... ( 08054b...a7f0a3 )
by Alexander
40:41 queued 37:41
created

UnknownCommandException::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 4
crap 1
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\console\controllers\HelpController;
11
12
/**
13
 * UnknownCommandException represents an exception caused by incorrect usage of a console command.
14
 *
15
 * @author Carsten Brandt <[email protected]>
16
 * @since 2.0.11
17
 */
18
class UnknownCommandException extends Exception
19
{
20
    /**
21
     * @var string the name of the command that could not be recognized.
22
     */
23
    public $command;
24
    /**
25
     * @var Application
26
     */
27
    protected $application;
28
29
30
    /**
31
     * Construct the exception.
32
     *
33
     * @param string $route the route of the command that could not be found.
34
     * @param Application $application the console application instance involved.
35
     * @param int $code the Exception code.
36
     * @param \Exception $previous the previous exception used for the exception chaining.
37
     */
38 16
    public function __construct($route, $application, $code = 0, \Exception $previous = null)
39
    {
40 16
        $this->command = $route;
41 16
        $this->application = $application;
42 16
        parent::__construct("Unknown command \"$route\".", $code, $previous);
43 16
    }
44
45
    /**
46
     * @return string the user-friendly name of this exception
47
     */
48 1
    public function getName()
49
    {
50 1
        return 'Unknown command';
51
    }
52
53
    /**
54
     * Suggest alternative commands for [[$command]] based on string similarity.
55
     *
56
     * Alternatives are searched using the following steps:
57
     *
58
     * - suggest alternatives that begin with `$command`
59
     * - find typos by calculating the Levenshtein distance between the unknown command and all
60
     *   available commands. The Levenshtein distance is defined as the minimal number of
61
     *   characters you have to replace, insert or delete to transform str1 into str2.
62
     *
63
     * @see http://php.net/manual/en/function.levenshtein.php
64
     * @return array a list of suggested alternatives sorted by similarity.
65
     */
66 15
    public function getSuggestedAlternatives()
67
    {
68 15
        $help = $this->application->createController('help');
69 15
        if ($help === false) {
70
            return [];
71
        }
72
        /** @var $helpController HelpController */
73 15
        list($helpController, $actionID) = $help;
0 ignored issues
show
Unused Code introduced by
The assignment to $actionID is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
74
75 15
        $availableActions = [];
76 15
        $commands = $helpController->getCommands();
77 15
        foreach ($commands as $command) {
78 15
            $result = $this->application->createController($command);
79 15
            if ($result === false) {
80
                continue;
81
            }
82
            // add the command itself (default action)
83 15
            $availableActions[] = $command;
84
85
            // add all actions of this controller
86
            /** @var $controller Controller */
87 15
            list($controller, $actionID) = $result;
0 ignored issues
show
Unused Code introduced by
The assignment to $actionID is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
88 15
            $actions = $helpController->getActions($controller);
89 15
            if (!empty($actions)) {
90 15
                $prefix = $controller->getUniqueId();
91 15
                foreach ($actions as $action) {
92 15
                    $availableActions[] = $prefix . '/' . $action;
93 15
                }
94 15
            }
95 15
        }
96 15
        return $this->filterBySimilarity($availableActions, $this->command);
97
    }
98
99
    /**
100
     * Find suggest alternative commands based on string similarity.
101
     *
102
     * Alternatives are searched using the following steps:
103
     *
104
     * - suggest alternatives that begin with `$command`
105
     * - find typos by calculating the Levenshtein distance between the unknown command and all
106
     *   available commands. The Levenshtein distance is defined as the minimal number of
107
     *   characters you have to replace, insert or delete to transform str1 into str2.
108
     *
109
     * @see http://php.net/manual/en/function.levenshtein.php
110
     * @param array $actions available command names.
111
     * @param string $command the command to compare to.
112
     * @return array a list of suggested alternatives sorted by similarity.
113
     */
114 15
    private function filterBySimilarity($actions, $command)
115
    {
116 15
        $alternatives = [];
117
118
        // suggest alternatives that begin with $command first
119 15
        foreach ($actions as $action) {
120 15
            if (strpos($action, $command) === 0) {
121 2
                $alternatives[] = $action;
122 2
            }
123 15
        }
124
125
        // calculate the Levenshtein distance between the unknown command and all available commands.
126
        $distances = array_map(function($action) use ($command) {
127 15
            $action = strlen($action) > 255 ? substr($action, 0, 255) : $action;
128 15
            $command = strlen($command) > 255 ? substr($command, 0, 255) : $command;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $command, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
129 15
            return levenshtein($action, $command);
130 15
        }, array_combine($actions, $actions));
131
132
        // we assume a typo if the levensthein distance is no more than 3, i.e. 3 replacements needed
133 15
        $relevantTypos = array_filter($distances, function($distance) {
134 15
            return $distance <= 3;
135 15
        });
136 15
        asort($relevantTypos);
137 15
        $alternatives = array_merge($alternatives, array_flip($relevantTypos));
138
139 15
        return array_unique($alternatives);
140
    }
141
}
142