Passed
Push — master ( daf0b2...38121e )
by
unknown
17:34
created

CommandRegistry::extractNamespace()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Core\Console;
19
20
use Psr\Container\ContainerInterface;
21
use Symfony\Component\Console\Command\Command;
22
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
23
use Symfony\Component\Console\Descriptor\ApplicationDescription;
24
use Symfony\Component\Console\Exception\CommandNotFoundException;
25
use TYPO3\CMS\Core\SingletonInterface;
26
27
/**
28
 * Registry for Symfony commands, populated via dependency injection tags
29
 */
30
class CommandRegistry implements CommandLoaderInterface, SingletonInterface
31
{
32
    /**
33
     * @var ContainerInterface
34
     */
35
    protected $container;
36
37
    /**
38
     * Map of command configurations with the command name as key
39
     *
40
     * @var array[]
41
     */
42
    protected $commandConfigurations = [];
43
44
    /**
45
     * Map of command aliases
46
     *
47
     * @var array[]
48
     */
49
    protected $aliases = [];
50
51
    /**
52
     * @param ContainerInterface $container
53
     */
54
    public function __construct(ContainerInterface $container)
55
    {
56
        $this->container = $container;
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function has($name)
63
    {
64
        return array_key_exists($name, $this->commandConfigurations);
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function get($name)
71
    {
72
        try {
73
            return $this->getCommandByIdentifier($name);
74
        } catch (UnknownCommandException $e) {
75
            throw new CommandNotFoundException($e->getMessage(), [], 1567969355, $e);
76
        }
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function getNames()
83
    {
84
        return array_keys($this->commandConfigurations);
85
    }
86
87
    /**
88
     * Get all commands which are allowed for scheduling recurring commands.
89
     *
90
     * @return \Generator
91
     */
92
    public function getSchedulableCommands(): \Generator
93
    {
94
        foreach ($this->commandConfigurations as $commandName => $configuration) {
95
            if ($configuration['schedulable'] ?? true) {
96
                yield $commandName => $this->getInstance($configuration['serviceName']);
97
            }
98
        }
99
    }
100
101
    /**
102
     * @param string $identifier
103
     * @throws UnknownCommandException
104
     * @return Command
105
     */
106
    public function getCommandByIdentifier(string $identifier): Command
107
    {
108
        if (!isset($this->commandConfigurations[$identifier])) {
109
            throw new UnknownCommandException(
110
                sprintf('Command "%s" has not been registered.', $identifier),
111
                1510906768
112
            );
113
        }
114
115
        return $this->getInstance($this->commandConfigurations[$identifier]['serviceName']);
116
    }
117
118
    protected function getInstance(string $service): Command
119
    {
120
        return $this->container->get($service);
121
    }
122
123
    /**
124
     * @internal
125
     */
126
    public function getNamespaces(): array
127
    {
128
        $namespaces = [];
129
        foreach ($this->commandConfigurations as $commandName => $configuration) {
130
            if ($configuration['hidden']) {
131
                continue;
132
            }
133
            if ($configuration['aliasFor'] !== null) {
134
                continue;
135
            }
136
            $namespace = $configuration['namespace'];
137
            $namespaces[$namespace]['id'] = $namespace;
138
            $namespaces[$namespace]['commands'][] = $commandName;
139
        }
140
141
        ksort($namespaces);
142
        foreach ($namespaces as &$commands) {
143
            ksort($commands);
144
        }
145
146
        return $namespaces;
147
    }
148
149
    /**
150
     * Gets the commands (registered in the given namespace if provided).
151
     *
152
     * The array keys are the full names and the values the command instances.
153
     *
154
     * @return array An array of Command descriptors
155
     * @internal
156
     */
157
    public function filter(string $namespace = null): array
158
    {
159
        $commands = [];
160
        foreach ($this->commandConfigurations as $commandName => $configuration) {
161
            if ($configuration['hidden']) {
162
                continue;
163
            }
164
            if ($namespace !== null && $namespace !== $this->extractNamespace($commandName, substr_count($namespace, ':') + 1)) {
165
                continue;
166
            }
167
            if ($configuration['aliasFor'] !== null) {
168
                continue;
169
            }
170
171
            $commands[$commandName] = $configuration;
172
            $commands[$commandName]['aliases'] = $this->aliases[$commandName] ?? [];
173
        }
174
175
        return $commands;
176
    }
177
178
    /**
179
     * @internal
180
     */
181
    public function addLazyCommand(
182
        string $commandName,
183
        string $serviceName,
184
        string $description = null,
185
        bool $hidden = false,
186
        bool $schedulable = false,
187
        string $aliasFor = null
188
    ): void {
189
        $this->commandConfigurations[$commandName] = [
190
            'name' => $aliasFor ?? $commandName,
191
            'serviceName' => $serviceName,
192
            'description' => $description,
193
            'hidden' => $hidden,
194
            'schedulable' => $schedulable,
195
            'aliasFor' => $aliasFor,
196
            'namespace' => $this->extractNamespace($commandName, 1),
197
        ];
198
199
        if ($aliasFor !== null) {
200
            $this->aliases[$aliasFor][] = $commandName;
201
        }
202
    }
203
204
    /**
205
     * Returns the namespace part of the command name.
206
     *
207
     * This method is not part of public API and should not be used directly.
208
     *
209
     * @return string The namespace of the command
210
     */
211
    private function extractNamespace(string $name, int $limit = null): string
212
    {
213
        $parts = explode(':', $name, -1);
214
        if (count($parts) === 0) {
215
            return ApplicationDescription::GLOBAL_NAMESPACE;
216
        }
217
218
        return implode(':', $limit === null ? $parts : array_slice($parts, 0, $limit));
219
    }
220
}
221