Passed
Push — master ( 2fcde2...4a2108 )
by Alexander
02:29
created

CommandLoader::validateAliases()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

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