hooks; } /** * Add a hook * * @param mixed $callback The callback function to call * @param string $hook The name of the hook to add * @param string $name The name of the command to hook * ('*' for all) */ public function add(callable $callback, $hook, $name = '*') { if (empty($name)) { $name = static::getClassNameFromCallback($callback); } $this->hooks[$name][$hook][] = $callback; return $this; } public function recordHookOptions($commandInfo, $name) { $this->hookOptions[$name][] = $commandInfo; return $this; } public static function getNames($command, $callback) { return array_filter( array_merge( static::getNamesUsingCommands($command), [static::getClassNameFromCallback($callback)] ) ); } protected static function getNamesUsingCommands($command) { return array_merge( [$command->getName()], $command->getAliases() ); } /** * If a command hook does not specify any particular command * name that it should be attached to, then it will be applied * to every command that is defined in the same class as the hook. * This is controlled by using the namespace + class name of * the implementing class of the callback hook. */ protected static function getClassNameFromCallback($callback) { if (!is_array($callback)) { return ''; } $reflectionClass = new \ReflectionClass($callback[0]); return $reflectionClass->getName(); } /** * Add a replace command hook * * @param type ReplaceCommandHookInterface $provider * @param type string $command_name The name of the command to replace */ public function addReplaceCommandHook(ReplaceCommandHookInterface $replaceCommandHook, $name) { $this->hooks[$name][self::REPLACE_COMMAND_HOOK][] = $replaceCommandHook; return $this; } /** * Add an configuration provider hook * * @param type InitializeHookInterface $provider * @param type $name The name of the command to hook * ('*' for all) */ public function addInitializeHook(InitializeHookInterface $initializeHook, $name = '*') { $this->hooks[$name][self::INITIALIZE][] = $initializeHook; return $this; } /** * Add an option hook * * @param type ValidatorInterface $validator * @param type $name The name of the command to hook * ('*' for all) */ public function addOptionHook(OptionHookInterface $interactor, $name = '*') { $this->hooks[$name][self::INTERACT][] = $interactor; return $this; } /** * Add an interact hook * * @param type ValidatorInterface $validator * @param type $name The name of the command to hook * ('*' for all) */ public function addInteractor(InteractorInterface $interactor, $name = '*') { $this->hooks[$name][self::INTERACT][] = $interactor; return $this; } /** * Add a pre-validator hook * * @param type ValidatorInterface $validator * @param type $name The name of the command to hook * ('*' for all) */ public function addPreValidator(ValidatorInterface $validator, $name = '*') { $this->hooks[$name][self::PRE_ARGUMENT_VALIDATOR][] = $validator; return $this; } /** * Add a validator hook * * @param type ValidatorInterface $validator * @param type $name The name of the command to hook * ('*' for all) */ public function addValidator(ValidatorInterface $validator, $name = '*') { $this->hooks[$name][self::ARGUMENT_VALIDATOR][] = $validator; return $this; } /** * Add a pre-command hook. This is the same as a validator hook, except * that it will run after all of the post-validator hooks. * * @param type ValidatorInterface $preCommand * @param type $name The name of the command to hook * ('*' for all) */ public function addPreCommandHook(ValidatorInterface $preCommand, $name = '*') { $this->hooks[$name][self::PRE_COMMAND_HOOK][] = $preCommand; return $this; } /** * Add a post-command hook. This is the same as a pre-process hook, * except that it will run before the first pre-process hook. * * @param type ProcessResultInterface $postCommand * @param type $name The name of the command to hook * ('*' for all) */ public function addPostCommandHook(ProcessResultInterface $postCommand, $name = '*') { $this->hooks[$name][self::POST_COMMAND_HOOK][] = $postCommand; return $this; } /** * Add a result processor. * * @param type ProcessResultInterface $resultProcessor * @param type $name The name of the command to hook * ('*' for all) */ public function addResultProcessor(ProcessResultInterface $resultProcessor, $name = '*') { $this->hooks[$name][self::PROCESS_RESULT][] = $resultProcessor; return $this; } /** * Add a result alterer. After a result is processed * by a result processor, an alter hook may be used * to convert the result from one form to another. * * @param type AlterResultInterface $resultAlterer * @param type $name The name of the command to hook * ('*' for all) */ public function addAlterResult(AlterResultInterface $resultAlterer, $name = '*') { $this->hooks[$name][self::ALTER_RESULT][] = $resultAlterer; return $this; } /** * Add a status determiner. Usually, a command should return * an integer on error, or a result object on success (which * implies a status code of zero). If a result contains the * status code in some other field, then a status determiner * can be used to call the appropriate accessor method to * determine the status code. This is usually not necessary, * though; a command that fails may return a CommandError * object, which contains a status code and a result message * to display. * @see CommandError::getExitCode() * * @param type StatusDeterminerInterface $statusDeterminer * @param type $name The name of the command to hook * ('*' for all) */ public function addStatusDeterminer(StatusDeterminerInterface $statusDeterminer, $name = '*') { $this->hooks[$name][self::STATUS_DETERMINER][] = $statusDeterminer; return $this; } /** * Add an output extractor. If a command returns an object * object, by default it is passed directly to the output * formatter (if in use) for rendering. If the result object * contains more information than just the data to render, though, * then an output extractor can be used to call the appopriate * accessor method of the result object to get the data to * rendered. This is usually not necessary, though; it is preferable * to have complex result objects implement the OutputDataInterface. * @see OutputDataInterface::getOutputData() * * @param type ExtractOutputInterface $outputExtractor * @param type $name The name of the command to hook * ('*' for all) */ public function addOutputExtractor(ExtractOutputInterface $outputExtractor, $name = '*') { $this->hooks[$name][self::EXTRACT_OUTPUT][] = $outputExtractor; return $this; } public function getHookOptionsForCommand($command) { $names = $this->addWildcardHooksToNames($command->getNames(), $command->getAnnotationData()); return $this->getHookOptions($names); } /** * @return CommandInfo[] */ public function getHookOptions($names) { $result = []; foreach ($names as $name) { if (isset($this->hookOptions[$name])) { $result = array_merge($result, $this->hookOptions[$name]); } } return $result; } /** * Get a set of hooks with the provided name(s). Include the * pre- and post- hooks, and also include the global hooks ('*') * in addition to the named hooks provided. * * @param string|array $names The name of the function being hooked. * @param string[] $hooks A list of hooks (e.g. [HookManager::ALTER_RESULT]) * * @return callable[] */ public function getHooks($names, $hooks, $annotationData = null) { return $this->get($this->addWildcardHooksToNames($names, $annotationData), $hooks); } protected function addWildcardHooksToNames($names, $annotationData = null) { $names = array_merge( (array)$names, ($annotationData == null) ? [] : array_map(function ($item) { return "@$item"; }, $annotationData->keys()) ); $names[] = '*'; return array_unique($names); } /** * Get a set of hooks with the provided name(s). * * @param string|array $names The name of the function being hooked. * @param string[] $hooks The list of hook names (e.g. [HookManager::ALTER_RESULT]) * * @return callable[] */ public function get($names, $hooks) { $result = []; foreach ((array)$hooks as $hook) { foreach ((array)$names as $name) { $result = array_merge($result, $this->getHook($name, $hook)); } } return $result; } /** * Get a single named hook. * * @param string $name The name of the hooked method * @param string $hook The specific hook name (e.g. alter) * * @return callable[] */ public function getHook($name, $hook) { if (isset($this->hooks[$name][$hook])) { return $this->hooks[$name][$hook]; } return []; } /** * Call the command event hooks. * * TODO: This should be moved to CommandEventHookDispatcher, which * should become the class that implements EventSubscriberInterface. * This change would break all clients, though, so postpone until next * major release. * * @param ConsoleCommandEvent $event */ public function callCommandEventHooks(ConsoleCommandEvent $event) { /* @var Command $command */ $command = $event->getCommand(); $dispatcher = new CommandEventHookDispatcher($this, [$command->getName()]); $dispatcher->callCommandEventHooks($event); } /** * @{@inheritdoc} */ public static function getSubscribedEvents() { return [ConsoleEvents::COMMAND => 'callCommandEventHooks']; } }