Chris@0: hookManager = $hookManager; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return the hook manager Chris@0: * @return HookManager Chris@0: */ Chris@0: public function hookManager() Chris@0: { Chris@0: return $this->hookManager; Chris@0: } Chris@0: Chris@0: public function addPrepareFormatter(PrepareFormatter $preparer) Chris@0: { Chris@0: $this->prepareOptionsList[] = $preparer; Chris@0: } Chris@0: Chris@0: public function setFormatterManager(FormatterManager $formatterManager) Chris@0: { Chris@0: $this->formatterManager = $formatterManager; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: public function setDisplayErrorFunction(callable $fn) Chris@0: { Chris@0: $this->displayErrorFunction = $fn; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set a mode to make the annotated command library re-throw Chris@0: * any exception that it catches while processing a command. Chris@0: * Chris@0: * The default behavior in the current (2.x) branch is to catch Chris@0: * the exception and replace it with a CommandError object that Chris@0: * may be processed by the normal output processing passthrough. Chris@0: * Chris@0: * In the 3.x branch, exceptions will never be caught; they will Chris@0: * be passed through, as if setPassExceptions(true) were called. Chris@0: * This is the recommended behavior. Chris@0: */ Chris@0: public function setPassExceptions($passExceptions) Chris@0: { Chris@0: $this->passExceptions = $passExceptions; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: public function commandErrorForException(\Exception $e) Chris@0: { Chris@0: if ($this->passExceptions) { Chris@0: throw $e; Chris@0: } Chris@0: return new CommandError($e->getMessage(), $e->getCode()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Return the formatter manager Chris@0: * @return FormatterManager Chris@0: */ Chris@0: public function formatterManager() Chris@0: { Chris@0: return $this->formatterManager; Chris@0: } Chris@0: Chris@0: public function initializeHook( Chris@0: InputInterface $input, Chris@0: $names, Chris@0: AnnotationData $annotationData Chris@0: ) { Chris@0: $initializeDispatcher = new InitializeHookDispatcher($this->hookManager(), $names); Chris@0: return $initializeDispatcher->initialize($input, $annotationData); Chris@0: } Chris@0: Chris@0: public function optionsHook( Chris@0: AnnotatedCommand $command, Chris@0: $names, Chris@0: AnnotationData $annotationData Chris@0: ) { Chris@0: $optionsDispatcher = new OptionsHookDispatcher($this->hookManager(), $names); Chris@0: $optionsDispatcher->getOptions($command, $annotationData); Chris@0: } Chris@0: Chris@0: public function interact( Chris@0: InputInterface $input, Chris@0: OutputInterface $output, Chris@0: $names, Chris@0: AnnotationData $annotationData Chris@0: ) { Chris@0: $interactDispatcher = new InteractHookDispatcher($this->hookManager(), $names); Chris@0: return $interactDispatcher->interact($input, $output, $annotationData); Chris@0: } Chris@0: Chris@0: public function process( Chris@0: OutputInterface $output, Chris@0: $names, Chris@0: $commandCallback, Chris@0: CommandData $commandData Chris@0: ) { Chris@0: $result = []; Chris@0: try { Chris@0: $result = $this->validateRunAndAlter( Chris@0: $names, Chris@0: $commandCallback, Chris@0: $commandData Chris@0: ); Chris@0: return $this->handleResults($output, $names, $result, $commandData); Chris@0: } catch (\Exception $e) { Chris@0: $result = $this->commandErrorForException($e); Chris@0: return $this->handleResults($output, $names, $result, $commandData); Chris@0: } Chris@0: } Chris@0: Chris@0: public function validateRunAndAlter( Chris@0: $names, Chris@0: $commandCallback, Chris@0: CommandData $commandData Chris@0: ) { Chris@0: // Validators return any object to signal a validation error; Chris@0: // if the return an array, it replaces the arguments. Chris@0: $validateDispatcher = new ValidateHookDispatcher($this->hookManager(), $names); Chris@0: $validated = $validateDispatcher->validate($commandData); Chris@0: if (is_object($validated)) { Chris@0: return $validated; Chris@0: } Chris@0: Chris@0: $replaceDispatcher = new ReplaceCommandHookDispatcher($this->hookManager(), $names); Chris@0: if ($this->logger) { Chris@0: $replaceDispatcher->setLogger($this->logger); Chris@0: } Chris@0: if ($replaceDispatcher->hasReplaceCommandHook()) { Chris@0: $commandCallback = $replaceDispatcher->getReplacementCommand($commandData); Chris@0: } Chris@0: Chris@0: // Run the command, alter the results, and then handle output and status Chris@0: $result = $this->runCommandCallback($commandCallback, $commandData); Chris@0: return $this->processResults($names, $result, $commandData); Chris@0: } Chris@0: Chris@0: public function processResults($names, $result, CommandData $commandData) Chris@0: { Chris@0: $processDispatcher = new ProcessResultHookDispatcher($this->hookManager(), $names); Chris@0: return $processDispatcher->process($result, $commandData); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Handle the result output and status code calculation. Chris@0: */ Chris@0: public function handleResults(OutputInterface $output, $names, $result, CommandData $commandData) Chris@0: { Chris@0: $statusCodeDispatcher = new StatusDeterminerHookDispatcher($this->hookManager(), $names); Chris@0: $status = $statusCodeDispatcher->determineStatusCode($result); Chris@0: // If the result is an integer and no separate status code was provided, then use the result as the status and do no output. Chris@0: if (is_integer($result) && !isset($status)) { Chris@0: return $result; Chris@0: } Chris@0: $status = $this->interpretStatusCode($status); Chris@0: Chris@0: // Get the structured output, the output stream and the formatter Chris@0: $extractDispatcher = new ExtracterHookDispatcher($this->hookManager(), $names); Chris@0: $structuredOutput = $extractDispatcher->extractOutput($result); Chris@0: $output = $this->chooseOutputStream($output, $status); Chris@0: if ($status != 0) { Chris@0: return $this->writeErrorMessage($output, $status, $structuredOutput, $result); Chris@0: } Chris@0: if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) { Chris@0: return $this->writeUsingFormatter($output, $structuredOutput, $commandData); Chris@0: } Chris@0: return $this->writeCommandOutput($output, $structuredOutput); Chris@0: } Chris@0: Chris@0: protected function dataCanBeFormatted($structuredOutput) Chris@0: { Chris@0: if (!isset($this->formatterManager)) { Chris@0: return false; Chris@0: } Chris@0: return Chris@0: is_object($structuredOutput) || Chris@0: is_array($structuredOutput); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Run the main command callback Chris@0: */ Chris@0: protected function runCommandCallback($commandCallback, CommandData $commandData) Chris@0: { Chris@0: $result = false; Chris@0: try { Chris@0: $args = $commandData->getArgsAndOptions(); Chris@0: $result = call_user_func_array($commandCallback, $args); Chris@0: } catch (\Exception $e) { Chris@0: $result = $this->commandErrorForException($e); Chris@0: } Chris@0: return $result; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determine the formatter that should be used to render Chris@0: * output. Chris@0: * Chris@0: * If the user specified a format via the --format option, Chris@0: * then always return that. Otherwise, return the default Chris@0: * format, unless --pipe was specified, in which case Chris@0: * return the default pipe format, format-pipe. Chris@0: * Chris@0: * n.b. --pipe is a handy option introduced in Drush 2 Chris@0: * (or perhaps even Drush 1) that indicates that the command Chris@0: * should select the output format that is most appropriate Chris@0: * for use in scripts (e.g. to pipe to another command). Chris@0: * Chris@0: * @return string Chris@0: */ Chris@0: protected function getFormat(FormatterOptions $options) Chris@0: { Chris@0: // In Symfony Console, there is no way for us to differentiate Chris@0: // between the user specifying '--format=table', and the user Chris@0: // not specifying --format when the default value is 'table'. Chris@0: // Therefore, we must make --field always override --format; it Chris@0: // cannot become the default value for --format. Chris@0: if ($options->get('field')) { Chris@0: return 'string'; Chris@0: } Chris@0: $defaults = []; Chris@0: if ($options->get('pipe')) { Chris@0: return $options->get('pipe-format', [], 'tsv'); Chris@0: } Chris@0: return $options->getFormat($defaults); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determine whether we should use stdout or stderr. Chris@0: */ Chris@0: protected function chooseOutputStream(OutputInterface $output, $status) Chris@0: { Chris@0: // If the status code indicates an error, then print the Chris@0: // result to stderr rather than stdout Chris@0: if ($status && ($output instanceof ConsoleOutputInterface)) { Chris@0: return $output->getErrorOutput(); Chris@0: } Chris@0: return $output; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Call the formatter to output the provided data. Chris@0: */ Chris@0: protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, CommandData $commandData) Chris@0: { Chris@0: $formatterOptions = $this->createFormatterOptions($commandData); Chris@0: $format = $this->getFormat($formatterOptions); Chris@0: $this->formatterManager->write( Chris@0: $output, Chris@0: $format, Chris@0: $structuredOutput, Chris@0: $formatterOptions Chris@0: ); Chris@0: return 0; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Create a FormatterOptions object for use in writing the formatted output. Chris@0: * @param CommandData $commandData Chris@0: * @return FormatterOptions Chris@0: */ Chris@0: protected function createFormatterOptions($commandData) Chris@0: { Chris@0: $options = $commandData->input()->getOptions(); Chris@0: $formatterOptions = new FormatterOptions($commandData->annotationData()->getArrayCopy(), $options); Chris@0: foreach ($this->prepareOptionsList as $preparer) { Chris@0: $preparer->prepare($commandData, $formatterOptions); Chris@0: } Chris@0: return $formatterOptions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Description Chris@0: * @param OutputInterface $output Chris@0: * @param int $status Chris@0: * @param string $structuredOutput Chris@0: * @param mixed $originalResult Chris@0: * @return type Chris@0: */ Chris@0: protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult) Chris@0: { Chris@0: if (isset($this->displayErrorFunction)) { Chris@0: call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult); Chris@0: } else { Chris@0: $this->writeCommandOutput($output, $structuredOutput); Chris@0: } Chris@0: return $status; Chris@0: } Chris@0: Chris@0: /** Chris@0: * If the result object is a string, then print it. Chris@0: */ Chris@0: protected function writeCommandOutput( Chris@0: OutputInterface $output, Chris@0: $structuredOutput Chris@0: ) { Chris@0: // If there is no formatter, we will print strings, Chris@0: // but can do no more than that. Chris@0: if (is_string($structuredOutput)) { Chris@0: $output->writeln($structuredOutput); Chris@0: } Chris@0: return 0; Chris@0: } Chris@0: Chris@0: /** Chris@0: * If a status code was set, then return it; otherwise, Chris@0: * presume success. Chris@0: */ Chris@0: protected function interpretStatusCode($status) Chris@0: { Chris@0: if (isset($status)) { Chris@0: return $status; Chris@0: } Chris@0: return 0; Chris@0: } Chris@0: }