Passed
Pull Request — master (#166)
by
unknown
12:06
created

CommandLoader::validateAliases()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Console;
6
7
use Psr\Container\ContainerInterface;
8
use RuntimeException;
9
use Symfony\Component\Console\Command\Command;
10
use Symfony\Component\Console\Command\LazyCommand;
11
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
12
use Symfony\Component\Console\Exception\CommandNotFoundException;
13
14
use function array_shift;
15
use function explode;
16
17
final class CommandLoader implements CommandLoaderInterface
18
{
19
    /**
20
     * @psalm-var array<string, array{
21
     *     name: non-empty-string,
22
     *     aliases: non-empty-string[],
23
     *     class: class-string<Command>,
24
     *     hidden: bool,
25
     * }>
26
     */
27
    private array $commandMap;
28
29
    /**
30
     * @var string[]
31
     */
32
    private array $commandNames;
33
34
    /**
35
     * @param array $commandMap An array with command names as keys and service ids as values.
36
     *
37
     * @psalm-param array<string, class-string> $commandMap
38
     */
39 46
    public function __construct(private ContainerInterface $container, array $commandMap)
40
    {
41 46
        $this->setCommandMap($commandMap);
42
    }
43
44 13
    public function get(string $name): Command
45
    {
46 13
        if (!$this->has($name)) {
47 1
            throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
48
        }
49
50 12
        $commandName = $this->commandMap[$name]['name'];
51 12
        $commandAliases = $this->commandMap[$name]['aliases'];
52 12
        $commandClass = $this->commandMap[$name]['class'];
53 12
        $commandHidden = $this->commandMap[$name]['hidden'];
54
55 12
        $description = $commandClass::getDefaultDescription();
56
57 12
        if ($description === null) {
58
            return $this->getCommandInstance($name);
59
        }
60
61 12
        return new LazyCommand(
62 12
            $commandName,
63 12
            $commandAliases,
64 12
            $description,
65 12
            $commandHidden,
66 12
            fn () => $this->getCommandInstance($name),
67 12
        );
68
    }
69
70 13
    public function has(string $name): bool
71
    {
72 13
        return isset($this->commandMap[$name]);
73
    }
74
75 1
    public function getNames(): array
76
    {
77 1
        return $this->commandNames;
78
    }
79
80 10
    private function getCommandInstance(string $name): Command
81
    {
82 10
        $commandName = $this->commandMap[$name]['name'];
83 10
        $commandClass = $this->commandMap[$name]['class'];
84 10
        $commandAliases = $this->commandMap[$name]['aliases'];
85
86
        /** @var Command $command */
87 10
        $command = $this->container->get($commandClass);
88
89 9
        if ($command->getName() !== $commandName) {
90 1
            $command->setName($commandName);
91
        }
92
93 9
        if ($command->getAliases() !== $commandAliases) {
94 2
            $command->setAliases($commandAliases);
95
        }
96
97 9
        return $command;
98
    }
99
100
    /**
101
     * @psalm-param array<string, class-string> $commandMap
102
     */
103 46
    private function setCommandMap(array $commandMap): void
104
    {
105 46
        $this->commandMap = [];
106 46
        $this->commandNames = [];
107
108 46
        foreach ($commandMap as $name => $class) {
109 46
            $aliases = explode('|', $name);
110
111 46
            $hidden = false;
112 46
            if ($aliases[0] === '') {
113 1
                $hidden = true;
114 1
                array_shift($aliases);
115
            }
116
117 46
            $this->validateAliases($aliases);
118
119 46
            $primaryName = array_shift($aliases);
120
121 46
            $item = [
122 46
                'name' => $primaryName,
123 46
                'aliases' => $aliases,
124 46
                'class' => $class,
125 46
                'hidden' => $hidden,
126 46
            ];
127
128 46
            $this->commandMap[$primaryName] = $item;
129 46
            $this->commandNames[] = $primaryName;
130
131 46
            foreach ($aliases as $alias) {
132 46
                $this->commandMap[$alias] = $item;
133
            }
134
        }
135
    }
136
137
    /**
138
     * @psalm-param string[] $aliases
139
     *
140
     * @psalm-assert non-empty-string[] $aliases
141
     */
142 46
    private function validateAliases(array $aliases): void
143
    {
144 46
        foreach ($aliases as $alias) {
145 46
            if ($alias === '') {
146 2
                throw new RuntimeException('Do not allow empty command name or alias.');
147
            }
148
        }
149
    }
150
}
151