Chris@0
|
1 <?php
|
Chris@0
|
2 namespace Consolidation\AnnotatedCommand;
|
Chris@0
|
3
|
Chris@0
|
4 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ReplaceCommandHookDispatcher;
|
Chris@0
|
5 use Psr\Log\LoggerAwareInterface;
|
Chris@0
|
6 use Psr\Log\LoggerAwareTrait;
|
Chris@0
|
7 use Symfony\Component\Console\Input\InputInterface;
|
Chris@0
|
8 use Symfony\Component\Console\Output\OutputInterface;
|
Chris@0
|
9 use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
Chris@0
|
10
|
Chris@0
|
11 use Consolidation\OutputFormatters\FormatterManager;
|
Chris@0
|
12 use Consolidation\OutputFormatters\Options\FormatterOptions;
|
Chris@0
|
13 use Consolidation\AnnotatedCommand\Hooks\HookManager;
|
Chris@0
|
14 use Consolidation\AnnotatedCommand\Options\PrepareFormatter;
|
Chris@0
|
15
|
Chris@0
|
16 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher;
|
Chris@0
|
17 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\OptionsHookDispatcher;
|
Chris@0
|
18 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InteractHookDispatcher;
|
Chris@0
|
19 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ValidateHookDispatcher;
|
Chris@0
|
20 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ProcessResultHookDispatcher;
|
Chris@0
|
21 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\StatusDeterminerHookDispatcher;
|
Chris@0
|
22 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ExtracterHookDispatcher;
|
Chris@0
|
23
|
Chris@0
|
24 /**
|
Chris@0
|
25 * Process a command, including hooks and other callbacks.
|
Chris@0
|
26 * There should only be one command processor per application.
|
Chris@0
|
27 * Provide your command processor to the AnnotatedCommandFactory
|
Chris@0
|
28 * via AnnotatedCommandFactory::setCommandProcessor().
|
Chris@0
|
29 */
|
Chris@0
|
30 class CommandProcessor implements LoggerAwareInterface
|
Chris@0
|
31 {
|
Chris@0
|
32 use LoggerAwareTrait;
|
Chris@0
|
33
|
Chris@17
|
34 /** @var HookManager */
|
Chris@0
|
35 protected $hookManager;
|
Chris@17
|
36 /** @var FormatterManager */
|
Chris@0
|
37 protected $formatterManager;
|
Chris@17
|
38 /** @var PrepareFormatterOptions[] */
|
Chris@0
|
39 protected $prepareOptionsList = [];
|
Chris@17
|
40 /** @var boolean */
|
Chris@0
|
41 protected $passExceptions;
|
Chris@17
|
42 /** @var ResultWriter */
|
Chris@17
|
43 protected $resultWriter;
|
Chris@17
|
44 /** @var ParameterInjection */
|
Chris@17
|
45 protected $parameterInjection;
|
Chris@0
|
46
|
Chris@0
|
47 public function __construct(HookManager $hookManager)
|
Chris@0
|
48 {
|
Chris@0
|
49 $this->hookManager = $hookManager;
|
Chris@0
|
50 }
|
Chris@0
|
51
|
Chris@0
|
52 /**
|
Chris@0
|
53 * Return the hook manager
|
Chris@0
|
54 * @return HookManager
|
Chris@0
|
55 */
|
Chris@0
|
56 public function hookManager()
|
Chris@0
|
57 {
|
Chris@0
|
58 return $this->hookManager;
|
Chris@0
|
59 }
|
Chris@0
|
60
|
Chris@17
|
61 public function resultWriter()
|
Chris@17
|
62 {
|
Chris@17
|
63 if (!$this->resultWriter) {
|
Chris@17
|
64 $this->setResultWriter(new ResultWriter());
|
Chris@17
|
65 }
|
Chris@17
|
66 return $this->resultWriter;
|
Chris@17
|
67 }
|
Chris@17
|
68
|
Chris@17
|
69 public function setResultWriter($resultWriter)
|
Chris@17
|
70 {
|
Chris@17
|
71 $this->resultWriter = $resultWriter;
|
Chris@17
|
72 }
|
Chris@17
|
73
|
Chris@17
|
74 public function parameterInjection()
|
Chris@17
|
75 {
|
Chris@17
|
76 if (!$this->parameterInjection) {
|
Chris@17
|
77 $this->setParameterInjection(new ParameterInjection());
|
Chris@17
|
78 }
|
Chris@17
|
79 return $this->parameterInjection;
|
Chris@17
|
80 }
|
Chris@17
|
81
|
Chris@17
|
82 public function setParameterInjection($parameterInjection)
|
Chris@17
|
83 {
|
Chris@17
|
84 $this->parameterInjection = $parameterInjection;
|
Chris@17
|
85 }
|
Chris@17
|
86
|
Chris@0
|
87 public function addPrepareFormatter(PrepareFormatter $preparer)
|
Chris@0
|
88 {
|
Chris@0
|
89 $this->prepareOptionsList[] = $preparer;
|
Chris@0
|
90 }
|
Chris@0
|
91
|
Chris@0
|
92 public function setFormatterManager(FormatterManager $formatterManager)
|
Chris@0
|
93 {
|
Chris@0
|
94 $this->formatterManager = $formatterManager;
|
Chris@17
|
95 $this->resultWriter()->setFormatterManager($formatterManager);
|
Chris@0
|
96 return $this;
|
Chris@0
|
97 }
|
Chris@0
|
98
|
Chris@0
|
99 public function setDisplayErrorFunction(callable $fn)
|
Chris@0
|
100 {
|
Chris@17
|
101 $this->resultWriter()->setDisplayErrorFunction($fn);
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 /**
|
Chris@0
|
105 * Set a mode to make the annotated command library re-throw
|
Chris@0
|
106 * any exception that it catches while processing a command.
|
Chris@0
|
107 *
|
Chris@0
|
108 * The default behavior in the current (2.x) branch is to catch
|
Chris@0
|
109 * the exception and replace it with a CommandError object that
|
Chris@0
|
110 * may be processed by the normal output processing passthrough.
|
Chris@0
|
111 *
|
Chris@0
|
112 * In the 3.x branch, exceptions will never be caught; they will
|
Chris@0
|
113 * be passed through, as if setPassExceptions(true) were called.
|
Chris@0
|
114 * This is the recommended behavior.
|
Chris@0
|
115 */
|
Chris@0
|
116 public function setPassExceptions($passExceptions)
|
Chris@0
|
117 {
|
Chris@0
|
118 $this->passExceptions = $passExceptions;
|
Chris@0
|
119 return $this;
|
Chris@0
|
120 }
|
Chris@0
|
121
|
Chris@0
|
122 public function commandErrorForException(\Exception $e)
|
Chris@0
|
123 {
|
Chris@0
|
124 if ($this->passExceptions) {
|
Chris@0
|
125 throw $e;
|
Chris@0
|
126 }
|
Chris@0
|
127 return new CommandError($e->getMessage(), $e->getCode());
|
Chris@0
|
128 }
|
Chris@0
|
129
|
Chris@0
|
130 /**
|
Chris@0
|
131 * Return the formatter manager
|
Chris@0
|
132 * @return FormatterManager
|
Chris@0
|
133 */
|
Chris@0
|
134 public function formatterManager()
|
Chris@0
|
135 {
|
Chris@0
|
136 return $this->formatterManager;
|
Chris@0
|
137 }
|
Chris@0
|
138
|
Chris@0
|
139 public function initializeHook(
|
Chris@0
|
140 InputInterface $input,
|
Chris@0
|
141 $names,
|
Chris@0
|
142 AnnotationData $annotationData
|
Chris@0
|
143 ) {
|
Chris@0
|
144 $initializeDispatcher = new InitializeHookDispatcher($this->hookManager(), $names);
|
Chris@0
|
145 return $initializeDispatcher->initialize($input, $annotationData);
|
Chris@0
|
146 }
|
Chris@0
|
147
|
Chris@0
|
148 public function optionsHook(
|
Chris@0
|
149 AnnotatedCommand $command,
|
Chris@0
|
150 $names,
|
Chris@0
|
151 AnnotationData $annotationData
|
Chris@0
|
152 ) {
|
Chris@0
|
153 $optionsDispatcher = new OptionsHookDispatcher($this->hookManager(), $names);
|
Chris@0
|
154 $optionsDispatcher->getOptions($command, $annotationData);
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 public function interact(
|
Chris@0
|
158 InputInterface $input,
|
Chris@0
|
159 OutputInterface $output,
|
Chris@0
|
160 $names,
|
Chris@0
|
161 AnnotationData $annotationData
|
Chris@0
|
162 ) {
|
Chris@0
|
163 $interactDispatcher = new InteractHookDispatcher($this->hookManager(), $names);
|
Chris@0
|
164 return $interactDispatcher->interact($input, $output, $annotationData);
|
Chris@0
|
165 }
|
Chris@0
|
166
|
Chris@0
|
167 public function process(
|
Chris@0
|
168 OutputInterface $output,
|
Chris@0
|
169 $names,
|
Chris@0
|
170 $commandCallback,
|
Chris@0
|
171 CommandData $commandData
|
Chris@0
|
172 ) {
|
Chris@0
|
173 $result = [];
|
Chris@0
|
174 try {
|
Chris@0
|
175 $result = $this->validateRunAndAlter(
|
Chris@0
|
176 $names,
|
Chris@0
|
177 $commandCallback,
|
Chris@0
|
178 $commandData
|
Chris@0
|
179 );
|
Chris@0
|
180 return $this->handleResults($output, $names, $result, $commandData);
|
Chris@0
|
181 } catch (\Exception $e) {
|
Chris@0
|
182 $result = $this->commandErrorForException($e);
|
Chris@0
|
183 return $this->handleResults($output, $names, $result, $commandData);
|
Chris@0
|
184 }
|
Chris@0
|
185 }
|
Chris@0
|
186
|
Chris@0
|
187 public function validateRunAndAlter(
|
Chris@0
|
188 $names,
|
Chris@0
|
189 $commandCallback,
|
Chris@0
|
190 CommandData $commandData
|
Chris@0
|
191 ) {
|
Chris@0
|
192 // Validators return any object to signal a validation error;
|
Chris@0
|
193 // if the return an array, it replaces the arguments.
|
Chris@0
|
194 $validateDispatcher = new ValidateHookDispatcher($this->hookManager(), $names);
|
Chris@0
|
195 $validated = $validateDispatcher->validate($commandData);
|
Chris@0
|
196 if (is_object($validated)) {
|
Chris@0
|
197 return $validated;
|
Chris@0
|
198 }
|
Chris@0
|
199
|
Chris@17
|
200 // Once we have validated the optins, create the formatter options.
|
Chris@17
|
201 $this->createFormatterOptions($commandData);
|
Chris@17
|
202
|
Chris@0
|
203 $replaceDispatcher = new ReplaceCommandHookDispatcher($this->hookManager(), $names);
|
Chris@0
|
204 if ($this->logger) {
|
Chris@0
|
205 $replaceDispatcher->setLogger($this->logger);
|
Chris@0
|
206 }
|
Chris@0
|
207 if ($replaceDispatcher->hasReplaceCommandHook()) {
|
Chris@0
|
208 $commandCallback = $replaceDispatcher->getReplacementCommand($commandData);
|
Chris@0
|
209 }
|
Chris@0
|
210
|
Chris@0
|
211 // Run the command, alter the results, and then handle output and status
|
Chris@0
|
212 $result = $this->runCommandCallback($commandCallback, $commandData);
|
Chris@0
|
213 return $this->processResults($names, $result, $commandData);
|
Chris@0
|
214 }
|
Chris@0
|
215
|
Chris@0
|
216 public function processResults($names, $result, CommandData $commandData)
|
Chris@0
|
217 {
|
Chris@0
|
218 $processDispatcher = new ProcessResultHookDispatcher($this->hookManager(), $names);
|
Chris@0
|
219 return $processDispatcher->process($result, $commandData);
|
Chris@0
|
220 }
|
Chris@0
|
221
|
Chris@0
|
222 /**
|
Chris@0
|
223 * Create a FormatterOptions object for use in writing the formatted output.
|
Chris@0
|
224 * @param CommandData $commandData
|
Chris@0
|
225 * @return FormatterOptions
|
Chris@0
|
226 */
|
Chris@0
|
227 protected function createFormatterOptions($commandData)
|
Chris@0
|
228 {
|
Chris@0
|
229 $options = $commandData->input()->getOptions();
|
Chris@0
|
230 $formatterOptions = new FormatterOptions($commandData->annotationData()->getArrayCopy(), $options);
|
Chris@0
|
231 foreach ($this->prepareOptionsList as $preparer) {
|
Chris@0
|
232 $preparer->prepare($commandData, $formatterOptions);
|
Chris@0
|
233 }
|
Chris@17
|
234 $commandData->setFormatterOptions($formatterOptions);
|
Chris@0
|
235 return $formatterOptions;
|
Chris@0
|
236 }
|
Chris@0
|
237
|
Chris@0
|
238 /**
|
Chris@17
|
239 * Handle the result output and status code calculation.
|
Chris@0
|
240 */
|
Chris@17
|
241 public function handleResults(OutputInterface $output, $names, $result, CommandData $commandData)
|
Chris@0
|
242 {
|
Chris@17
|
243 $statusCodeDispatcher = new StatusDeterminerHookDispatcher($this->hookManager(), $names);
|
Chris@17
|
244 $extractDispatcher = new ExtracterHookDispatcher($this->hookManager(), $names);
|
Chris@17
|
245
|
Chris@17
|
246 return $this->resultWriter()->handle($output, $result, $commandData, $statusCodeDispatcher, $extractDispatcher);
|
Chris@0
|
247 }
|
Chris@0
|
248
|
Chris@0
|
249 /**
|
Chris@17
|
250 * Run the main command callback
|
Chris@0
|
251 */
|
Chris@17
|
252 protected function runCommandCallback($commandCallback, CommandData $commandData)
|
Chris@17
|
253 {
|
Chris@17
|
254 $result = false;
|
Chris@17
|
255 try {
|
Chris@17
|
256 $args = $this->parameterInjection()->args($commandData);
|
Chris@17
|
257 $result = call_user_func_array($commandCallback, $args);
|
Chris@17
|
258 } catch (\Exception $e) {
|
Chris@17
|
259 $result = $this->commandErrorForException($e);
|
Chris@0
|
260 }
|
Chris@17
|
261 return $result;
|
Chris@0
|
262 }
|
Chris@0
|
263
|
Chris@17
|
264 public function injectIntoCommandData($commandData, $injectedClasses)
|
Chris@0
|
265 {
|
Chris@17
|
266 $this->parameterInjection()->injectIntoCommandData($commandData, $injectedClasses);
|
Chris@0
|
267 }
|
Chris@0
|
268 }
|