app = $app; $this->root = $root; $this->aliases = [ '--version' => 'version', '--help' => 'help', '-h' => 'help', ]; } /** * Replace the entire alias map for a runner. * * Aliases allow you to define alternate names for commands * in the collection. This can be useful to add top level switches * like `--version` or `-h` * * ### Usage * * ``` * $runner->setAliases(['--version' => 'version']); * ``` * * @param array $aliases The map of aliases to replace. * @return $this */ public function setAliases(array $aliases) { $this->aliases = $aliases; return $this; } /** * Run the command contained in $argv. * * Use the application to do the following: * * - Bootstrap the application * - Create the CommandCollection using the console() hook on the application. * - Trigger the `Console.buildCommands` event of auto-wiring plugins. * - Run the requested command. * * @param array $argv The arguments from the CLI environment. * @param \Cake\Console\ConsoleIo $io The ConsoleIo instance. Used primarily for testing. * @return int The exit code of the command. * @throws \RuntimeException */ public function run(array $argv, ConsoleIo $io = null) { $this->bootstrap(); $commands = new CommandCollection([ 'version' => VersionCommand::class, 'help' => HelpCommand::class, ]); $commands = $this->app->console($commands); if ($this->app instanceof PluginApplicationInterface) { $commands = $this->app->pluginConsole($commands); } if (!($commands instanceof CommandCollection)) { $type = getTypeName($commands); throw new RuntimeException( "The application's `console` method did not return a CommandCollection." . " Got '{$type}' instead." ); } $this->dispatchEvent('Console.buildCommands', ['commands' => $commands]); if (empty($argv)) { throw new RuntimeException("Cannot run any commands. No arguments received."); } // Remove the root executable segment array_shift($argv); $io = $io ?: new ConsoleIo(); $name = $this->resolveName($commands, $io, array_shift($argv)); $result = Shell::CODE_ERROR; $shell = $this->getShell($io, $commands, $name); if ($shell instanceof Shell) { $result = $this->runShell($shell, $argv); } if ($shell instanceof Command) { $result = $shell->run($argv, $io); } if ($result === null || $result === true) { return Shell::CODE_SUCCESS; } if (is_int($result)) { return $result; } return Shell::CODE_ERROR; } /** * Application bootstrap wrapper. * * Calls `bootstrap()` and `events()` if application implements `EventApplicationInterface`. * After the application is bootstrapped and events are attached, plugins are bootstrapped * and have their events attached. * * @return void */ protected function bootstrap() { $this->app->bootstrap(); if ($this->app instanceof PluginApplicationInterface) { $this->app->pluginBootstrap(); $events = $this->app->getEventManager(); $events = $this->app->events($events); $events = $this->app->pluginEvents($events); $this->app->setEventManager($events); } } /** * Get the application's event manager or the global one. * * @return \Cake\Event\EventManagerInterface */ public function getEventManager() { if ($this->app instanceof PluginApplicationInterface) { return $this->app->getEventManager(); } return EventManager::instance(); } /** * Get/set the application's event manager. * * If the application does not support events and this method is used as * a setter, an exception will be raised. * * @param \Cake\Event\EventManagerInterface $events The event manager to set. * @return \Cake\Event\EventManagerInterface|$this * @deprecated Will be removed in 4.0 */ public function eventManager(EventManager $events = null) { if ($eventManager === null) { return $this->getEventManager(); } return $this->setEventManager($events); } /** * Get/set the application's event manager. * * If the application does not support events and this method is used as * a setter, an exception will be raised. * * @param \Cake\Event\EventManagerInterface $events The event manager to set. * @return $this */ public function setEventManager(EventManager $events) { if ($this->app instanceof PluginApplicationInterface) { return $this->app->setEventManager($events); } throw new InvalidArgumentException('Cannot set the event manager, the application does not support events.'); } /** * Get the shell instance for a given command name * * @param \Cake\Console\ConsoleIo $io The IO wrapper for the created shell class. * @param \Cake\Console\CommandCollection $commands The command collection to find the shell in. * @param string $name The command name to find * @return \Cake\Console\Shell|\Cake\Console\Command */ protected function getShell(ConsoleIo $io, CommandCollection $commands, $name) { $instance = $commands->get($name); if (is_string($instance)) { $instance = $this->createShell($instance, $io); } if ($instance instanceof Shell) { $instance->setRootName($this->root); } if ($instance instanceof Command) { $instance->setName("{$this->root} {$name}"); } if ($instance instanceof CommandCollectionAwareInterface) { $instance->setCommandCollection($commands); } return $instance; } /** * Resolve the command name into a name that exists in the collection. * * Apply backwards compatibile inflections and aliases. * * @param \Cake\Console\CommandCollection $commands The command collection to check. * @param \Cake\Console\ConsoleIo $io ConsoleIo object for errors. * @param string $name The name from the CLI args. * @return string The resolved name. */ protected function resolveName($commands, $io, $name) { if (!$name) { $io->err('No command provided. Choose one of the available commands.', 2); $name = 'help'; } if (isset($this->aliases[$name])) { $name = $this->aliases[$name]; } if (!$commands->has($name)) { $name = Inflector::underscore($name); } if (!$commands->has($name)) { throw new RuntimeException( "Unknown command `{$this->root} {$name}`." . " Run `{$this->root} --help` to get the list of valid commands." ); } return $name; } /** * Execute a Shell class. * * @param \Cake\Console\Shell $shell The shell to run. * @param array $argv The CLI arguments to invoke. * @return int Exit code */ protected function runShell(Shell $shell, array $argv) { try { $shell->initialize(); return $shell->runCommand($argv, true); } catch (StopException $e) { return $e->getCode(); } } /** * The wrapper for creating shell instances. * * @param string $className Shell class name. * @param \Cake\Console\ConsoleIo $io The IO wrapper for the created shell class. * @return \Cake\Console\Shell|\Cake\Console\Command */ protected function createShell($className, ConsoleIo $io) { if (is_subclass_of($className, Shell::class)) { return new $className($io); } // Command class return new $className(); } }