commandConfigurations); } /** * {@inheritdoc} */ public function get(string $name): Command { try { return $this->getCommandByIdentifier($name); } catch (UnknownCommandException $e) { throw new CommandNotFoundException($e->getMessage(), [], 1567969355, $e); } } /** * {@inheritdoc} */ public function getNames(): array { return array_keys($this->commandConfigurations); } /** * Get the configuration form all commands which are schedulable. * By using the #[AsNonSchedulableCommand] attribute, all commands * are filtered out which utilize this attribute, as the Symfony * #[AsCommand] attribute does not allow to set additional meta-data. */ public function getSchedulableCommandsConfiguration(): array { return array_filter( $this->commandConfigurations, static fn(array $configuration): bool => ($configuration['schedulable'] ?? true) ); } /** * Get all commands which are allowed for scheduling recurring commands. * @todo Not used by core. Consider deprecating! */ public function getSchedulableCommands(): \Generator { foreach ($this->commandConfigurations as $commandName => $configuration) { if ($configuration['schedulable'] ?? true) { yield $commandName => $this->getInstance($configuration['serviceName']); } } } /** * @throws UnknownCommandException */ public function getCommandByIdentifier(string $identifier): Command { if (!isset($this->commandConfigurations[$identifier])) { throw new UnknownCommandException( sprintf('Command "%s" has not been registered.', $identifier), 1510906768 ); } return $this->getInstance($this->commandConfigurations[$identifier]['serviceName']); } protected function getInstance(string $service): Command { return $this->container->get($service); } /** * @internal */ public function getNamespaces(): array { $namespaces = []; foreach ($this->commandConfigurations as $commandName => $configuration) { if ($configuration['hidden']) { continue; } if ($configuration['aliasFor'] !== null) { continue; } $namespace = $configuration['namespace']; $namespaces[$namespace]['id'] = $namespace; $namespaces[$namespace]['commands'][] = $commandName; } ksort($namespaces); foreach ($namespaces as &$commands) { ksort($commands); } return $namespaces; } /** * Gets the commands (registered in the given namespace if provided). * * The array keys are the full names and the values the command instances. * * @return array An array of Command descriptors * @internal */ public function filter(?string $namespace = null): array { $commands = []; foreach ($this->commandConfigurations as $commandName => $configuration) { if ($configuration['hidden']) { continue; } if ($namespace !== null && $namespace !== $this->extractNamespace($commandName, substr_count($namespace, ':') + 1)) { continue; } if ($configuration['aliasFor'] !== null) { continue; } $commands[$commandName] = $configuration; $commands[$commandName]['aliases'] = $this->aliases[$commandName] ?? []; } return $commands; } /** * @internal */ public function addLazyCommand( string $commandName, string $serviceName, ?string $description = null, bool $hidden = false, bool $schedulable = false, ?string $aliasFor = null ): void { if ($schedulable) { // Workaround: Symfony #[AsCommand] can not utilize an extra 'schedulable' key. Thus, we // evaluate the additional #[AsUnschedulableCommand] as well. try { $reflection = new \ReflectionClass($serviceName); $attributes = $reflection->getAttributes(AsNonSchedulableCommand::class); if ($attributes !== []) { // The presence of the attribute alone is sufficient, no further reflection // or construction is required at this time. Might be needed if the attribute // ever gets properties. $schedulable = false; } } catch (\ReflectionException $e) { } } $this->commandConfigurations[$commandName] = [ 'name' => $aliasFor ?? $commandName, 'serviceName' => $serviceName, 'description' => $description, 'hidden' => $hidden, 'schedulable' => $schedulable, 'aliasFor' => $aliasFor, 'namespace' => $this->extractNamespace($commandName, 1), ]; if ($aliasFor !== null) { $this->aliases[$aliasFor][] = $commandName; } } /** * Returns the namespace part of the command name. * * This method is not part of public API and should not be used directly. * * @return string The namespace of the command */ private function extractNamespace(string $name, ?int $limit = null): string { $parts = explode(':', $name, -1); if (count($parts) === 0) { return ApplicationDescription::GLOBAL_NAMESPACE; } return implode(':', $limit === null ? $parts : array_slice($parts, 0, $limit)); } }