| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- <?php
- declare(strict_types=1);
- /**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link https://cakephp.org CakePHP(tm) Project
- * @since 4.0.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Console;
- use Cake\Console\Exception\ConsoleException;
- use Cake\Console\Exception\StopException;
- use Cake\Event\EventDispatcherInterface;
- use Cake\Event\EventDispatcherTrait;
- use Cake\Utility\Inflector;
- /**
- * Base class for console commands.
- *
- * Provides hooks for common command features:
- *
- * - `initialize` Acts as a post-construct hook.
- * - `buildOptionParser` Build/Configure the option parser for your command.
- * - `execute` Execute your command with parsed Arguments and ConsoleIo
- *
- * @implements \Cake\Event\EventDispatcherInterface<\Cake\Command\Command>
- */
- abstract class BaseCommand implements CommandInterface, EventDispatcherInterface
- {
- /**
- * @use \Cake\Event\EventDispatcherTrait<\Cake\Command\Command>
- */
- use EventDispatcherTrait;
- /**
- * The name of this command.
- *
- * @var string
- */
- protected string $name = 'cake unknown';
- protected ?CommandFactoryInterface $factory = null;
- /**
- * Constructor
- *
- * @param \Cake\Console\CommandFactoryInterface $factory Command factory instance.
- */
- public function __construct(?CommandFactoryInterface $factory = null)
- {
- $this->factory = $factory;
- }
- /**
- * @inheritDoc
- */
- public function setName(string $name)
- {
- assert(
- str_contains($name, ' ') && !str_starts_with($name, ' '),
- "The name '{$name}' is missing a space. Names should look like `cake routes`"
- );
- $this->name = $name;
- return $this;
- }
- /**
- * Get the command name.
- *
- * @return string
- */
- public function getName(): string
- {
- return $this->name;
- }
- /**
- * Get the command description.
- *
- * @return string
- */
- public static function getDescription(): string
- {
- return '';
- }
- /**
- * Get the root command name.
- *
- * @return string
- */
- public function getRootName(): string
- {
- [$root] = explode(' ', $this->name);
- return $root;
- }
- /**
- * Get the command name.
- *
- * Returns the command name based on class name.
- * For e.g. for a command with class name `UpdateTableCommand` the default
- * name returned would be `'update_table'`.
- *
- * @return string
- */
- public static function defaultName(): string
- {
- $pos = strrpos(static::class, '\\');
- /** @psalm-suppress PossiblyFalseOperand */
- $name = substr(static::class, $pos + 1, -7);
- return Inflector::underscore($name);
- }
- /**
- * Get the option parser.
- *
- * You can override buildOptionParser() to define your options & arguments.
- *
- * @return \Cake\Console\ConsoleOptionParser
- * @throws \Cake\Core\Exception\CakeException When the parser is invalid
- */
- public function getOptionParser(): ConsoleOptionParser
- {
- [$root, $name] = explode(' ', $this->name, 2);
- $parser = new ConsoleOptionParser($name);
- $parser->setRootName($root);
- $parser->setDescription(static::getDescription());
- $parser = $this->buildOptionParser($parser);
- return $parser;
- }
- /**
- * Hook method for defining this command's option parser.
- *
- * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
- * @return \Cake\Console\ConsoleOptionParser The built parser.
- */
- protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
- {
- return $parser;
- }
- /**
- * Hook method invoked by CakePHP when a command is about to be executed.
- *
- * Override this method and implement expensive/important setup steps that
- * should not run on every command run. This method will be called *before*
- * the options and arguments are validated and processed.
- *
- * @return void
- */
- public function initialize(): void
- {
- }
- /**
- * @inheritDoc
- */
- public function run(array $argv, ConsoleIo $io): ?int
- {
- $this->initialize();
- $parser = $this->getOptionParser();
- try {
- [$options, $arguments] = $parser->parse($argv, $io);
- $args = new Arguments(
- $arguments,
- $options,
- $parser->argumentNames()
- );
- } catch (ConsoleException $e) {
- $io->err('Error: ' . $e->getMessage());
- return static::CODE_ERROR;
- }
- $this->setOutputLevel($args, $io);
- if ($args->getOption('help')) {
- $this->displayHelp($parser, $args, $io);
- return static::CODE_SUCCESS;
- }
- if ($args->getOption('quiet')) {
- $io->setInteractive(false);
- }
- $this->dispatchEvent('Command.beforeExecute', ['args' => $args]);
- /** @var int|null $result */
- $result = $this->execute($args, $io);
- $this->dispatchEvent('Command.afterExecute', ['args' => $args, 'result' => $result]);
- return $result;
- }
- /**
- * Output help content
- *
- * @param \Cake\Console\ConsoleOptionParser $parser The option parser.
- * @param \Cake\Console\Arguments $args The command arguments.
- * @param \Cake\Console\ConsoleIo $io The console io
- * @return void
- */
- protected function displayHelp(ConsoleOptionParser $parser, Arguments $args, ConsoleIo $io): void
- {
- $format = 'text';
- if ($args->getArgumentAt(0) === 'xml') {
- $format = 'xml';
- $io->setOutputAs(ConsoleOutput::RAW);
- }
- $io->out($parser->help($format));
- }
- /**
- * Set the output level based on the Arguments.
- *
- * @param \Cake\Console\Arguments $args The command arguments.
- * @param \Cake\Console\ConsoleIo $io The console io
- * @return void
- */
- protected function setOutputLevel(Arguments $args, ConsoleIo $io): void
- {
- $io->setLoggers(ConsoleIo::NORMAL);
- if ($args->getOption('quiet')) {
- $io->level(ConsoleIo::QUIET);
- $io->setLoggers(ConsoleIo::QUIET);
- }
- if ($args->getOption('verbose')) {
- $io->level(ConsoleIo::VERBOSE);
- $io->setLoggers(ConsoleIo::VERBOSE);
- }
- }
- /**
- * Implement this method with your command's logic.
- *
- * @param \Cake\Console\Arguments $args The command arguments.
- * @param \Cake\Console\ConsoleIo $io The console io
- * @return int|null|void The exit code or null for success
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint
- */
- abstract public function execute(Arguments $args, ConsoleIo $io);
- /**
- * Halt the current process with a StopException.
- *
- * @param int $code The exit code to use.
- * @throws \Cake\Console\Exception\StopException
- * @return never
- */
- public function abort(int $code = self::CODE_ERROR): never
- {
- throw new StopException('Command aborted', $code);
- }
- /**
- * Execute another command with the provided set of arguments.
- *
- * If you are using a string command name, that command's dependencies
- * will not be resolved with the application container. Instead you will
- * need to pass the command as an object with all of its dependencies.
- *
- * @param \Cake\Console\CommandInterface|string $command The command class name or command instance.
- * @param array $args The arguments to invoke the command with.
- * @param \Cake\Console\ConsoleIo|null $io The ConsoleIo instance to use for the executed command.
- * @return int|null The exit code or null for success of the command.
- */
- public function executeCommand(CommandInterface|string $command, array $args = [], ?ConsoleIo $io = null): ?int
- {
- if (is_string($command)) {
- assert(
- is_subclass_of($command, CommandInterface::class),
- sprintf('Command `%s` is not a subclass of `%s`.', $command, CommandInterface::class)
- );
- $command = $this->factory?->create($command) ?? new $command();
- }
- $io = $io ?: new ConsoleIo();
- try {
- return $command->run($args, $io);
- } catch (StopException $e) {
- return $e->getCode();
- }
- }
- }
|