SimilarCommandName::filterDuplicates()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 18
ccs 8
cts 8
cp 1
rs 9.2
cc 4
eloc 8
nc 4
nop 2
crap 4
1
<?php
2
3
/*
4
 * This file is part of the webmozart/console package.
5
 *
6
 * (c) Bernhard Schussek <[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 Webmozart\Console\Util;
13
14
use Webmozart\Console\Api\Command\CommandCollection;
15
16
/**
17
 * Utility to find similar command names.
18
 *
19
 * @since  1.0
20
 *
21
 * @author Bernhard Schussek <[email protected]>
22
 */
23
class SimilarCommandName
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
24
{
25
    /**
26
     * Searches a command collection for similar names.
27
     *
28
     * @param string            $commandName The command name that was not found.
29
     * @param CommandCollection $commands    The available commands.
30
     *
31
     * @return string[] The names of similar commands.
32
     */
33 18
    public static function find($commandName, CommandCollection $commands)
34
    {
35 18
        $threshold = 1e3;
36 18
        $distancesByName = array();
37
38
        // Include aliases in the search
39 18
        $actualNames = $commands->getNames(true);
40
41 18
        foreach ($actualNames as $actualName) {
42
            // Get Levenshtein distance between the input and each command name
43 18
            $distance = levenshtein($commandName, $actualName);
44
45 18
            $isSimilar = $distance <= (strlen($commandName) / 3);
46 18
            $isSubString = false !== strpos($actualName, $commandName);
47
48 18
            if ($isSimilar || $isSubString) {
49 18
                $distancesByName[$actualName] = $distance;
50
            }
51
        }
52
53
        // Only keep results with a distance below the threshold
54 18
        $distancesByName = array_filter($distancesByName, function ($distance) use ($threshold) {
55 18
            return $distance < 2 * $threshold;
56 18
        });
57
58
        // Display results with shortest distance first
59 18
        asort($distancesByName);
60
61 18
        $suggestedNames = array_keys($distancesByName);
62
63 18
        return self::filterDuplicates($suggestedNames, $commands);
64
    }
65
66 18
    private static function filterDuplicates(array $names, CommandCollection $commands)
67
    {
68 18
        $filteredNames = array();
69
70 18
        foreach ($names as $nameToFilter) {
71
            // Check all existing names for duplicates
72 18
            foreach ($filteredNames as $filteredName) {
73
                // $nameToFilter is a duplicate - skip
74 14
                if ($commands->get($nameToFilter) === $commands->get($filteredName)) {
75 14
                    continue 2;
76
                }
77
            }
78
79 18
            $filteredNames[] = $nameToFilter;
80
        }
81
82 18
        return $filteredNames;
83
    }
84
85
    private function __construct()
86
    {
87
    }
88
}
89