17a450c249657204c95297a0b4fef0755a66a70f
[yaffs-website] / vendor / drush / drush / includes / annotationcommand_adapter.inc
1 <?php
2
3 /**
4  * @file
5  * annotationcommand_adapter.inc
6  */
7
8 use Consolidation\AnnotatedCommand\CommandFileDiscovery;
9 use Consolidation\AnnotatedCommand\AnnotatedCommandFactory;
10 use Consolidation\AnnotatedCommand\CommandProcessor;
11 use Consolidation\AnnotatedCommand\Hooks\HookManager;
12 use Consolidation\OutputFormatters\FormatterManager;
13 use Consolidation\OutputFormatters\Options\FormatterOptions;
14 use Symfony\Component\Console\Input\ArrayInput;
15 use Consolidation\AnnotatedCommand\AnnotationData;
16 use Consolidation\AnnotatedCommand\CommandData;
17 use Drush\Command\DrushInputAdapter;
18 use Drush\Command\DrushOutputAdapter;
19
20 use Symfony\Component\Console\Output\ConsoleOutput;
21
22 /**
23  * Cache the command file discovery object.
24  *
25  * @return CommandFileDiscovery
26  */
27 function annotationcommand_adapter_get_discovery() {
28   static $discovery;
29   if (!isset($discovery)) {
30     $discovery = new CommandFileDiscovery();
31     $discovery
32       ->setIncludeFilesAtBase(false)
33       ->setSearchLocations(['Commands'])
34       ->setSearchPattern('#.*Commands.php$#');
35   }
36   return $discovery;
37 }
38
39 /**
40  * Initialize and cache the command factory. Drush 9 uses dependency injection.
41  *
42  * @return AnnotatedCommandFactory
43  */
44 function annotationcommand_adapter_get_factory() {
45   static $factory;
46   if (!isset($factory)) {
47     $factory = new AnnotatedCommandFactory();
48     $factory->commandProcessor()->hookManager()->add('annotatedcomand_adapter_backend_result', HookManager::EXTRACT_OUTPUT);
49     $formatter = new FormatterManager();
50     $formatter->addDefaultFormatters();
51     $formatter->addDefaultSimplifiers();
52     $factory->commandProcessor()->setFormatterManager($formatter);
53   }
54   return $factory;
55 }
56
57 /**
58  * Fetch the command processor from the factory.
59  *
60  * @return AnnotatedCommandFactory
61  */
62 function annotationcommand_adapter_get_processor() {
63   $factory = annotationcommand_adapter_get_factory();
64   return $factory->commandProcessor();
65 }
66
67 /**
68  * Fetch the formatter manager from the command processor
69  *
70  * @return FormatterManager
71  */
72 function annotatedcomand_adapter_get_formatter() {
73   $commandProcessor = annotationcommand_adapter_get_processor();
74   return $commandProcessor->formatterManager();
75 }
76
77 /**
78  * Callback function called by HookManager::EXTRACT_OUTPUT to set
79  * the backend result.
80  */
81 function annotatedcomand_adapter_backend_result($structured_data) {
82   $return = drush_backend_get_result();
83   if (empty($return)) {
84     drush_backend_set_result($structured_data);
85   }
86 }
87
88 /**
89  * Return the cached commands built by annotationcommand_adapter_discover.
90  * @see drush_get_commands()
91  */
92 function annotationcommand_adapter_commands() {
93   $annotation_commandfiles = drush_get_context('DRUSH_ANNOTATED_COMMANDFILES');
94   // Remove any entry in the commandfiles list from an ignored module.
95   $ignored = implode('|', drush_get_option_list('ignored-modules'));
96   $regex = "#/(modules|themes|profiles)(/|/.*/)($ignored)/#";
97   foreach ($annotation_commandfiles as $key => $path) {
98     if (preg_match($regex, $path)) {
99       unset($annotation_commandfiles[$key]);
100     }
101   }
102   $commands = annotationcommand_adapter_get_commands($annotation_commandfiles);
103   $module_service_commands = drush_get_context('DRUSH_MODULE_SERVICE_COMMANDS');
104   return array_merge($commands, $module_service_commands);
105 }
106
107 /**
108  * Search for annotation commands at the provided search path.
109  * @see _drush_find_commandfiles()
110  */
111 function annotationcommand_adapter_discover($searchpath, $phase = false, $phase_max = false) {
112   if (empty($searchpath)) {
113     return;
114   }
115   if (($phase >= DRUSH_BOOTSTRAP_DRUPAL_SITE) && (drush_drupal_major_version() >= 8)) {
116     return;
117   }
118   $annotation_commandfiles = [];
119   // Assemble a cid specific to the bootstrap phase and searchpaths.
120   // Bump $cf_version when making a change to a dev version of Drush
121   // that invalidates the commandfile cache.
122   $cf_version = 1;
123   $cid = drush_get_cid('annotationfiles-' . $phase, array(), array_merge($searchpath, array($cf_version)));
124   $command_cache = drush_cache_get($cid);
125   if (isset($command_cache->data)) {
126     $annotation_commandfiles = $command_cache->data;
127   }
128   else {
129     // Check to see if this is the Drush searchpath for instances where we are
130     // NOT going to do a full bootstrap (e.g. when running a help command)
131     if (($phase == DRUSH_BOOTSTRAP_DRUPAL_SITE) && ($phase_max < DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
132       $searchpath = annotationcommand_adapter_refine_searchpaths($searchpath);
133     }
134     $discovery = annotationcommand_adapter_get_discovery();
135     $annotation_commandfiles = $discovery->discoverNamespaced($searchpath, '\Drupal');
136     drush_cache_set($cid, $annotation_commandfiles);
137   }
138   drush_set_context(
139     'DRUSH_ANNOTATED_COMMANDFILES',
140     array_merge(
141       drush_get_context('DRUSH_ANNOTATED_COMMANDFILES'),
142       $annotation_commandfiles
143     )
144   );
145 }
146
147 /**
148  * This function is set as the $command['callback'] for Symfony Console commands
149  * e.g. those provided by Drupal 8 modules.  Note that Drush 9 calls these with
150  * Symfony's Application::run() method, but Drush 8 continues to use the
151  * legacy Drush command dispatcher for all commands.
152  *
153  * @return bolean false if command failed (expect drush_set_error was called in this case)
154  */
155 function annotationcommand_adapter_run_console_command() {
156   $args = func_get_args();
157   $command = drush_get_command();
158
159   $console_command = $command['drush-console-command'];
160   // TODO: Build an appropriate input object
161   $input = annotationcommand_adapter_build_input($console_command, $args);
162   $output = new ConsoleOutput();
163   $result = $console_command->run($input, $output);
164
165   return $result;
166 }
167
168 /**
169  * TODO: This could probably just be a DrushInputAdapter now.
170  */
171 function annotationcommand_adapter_build_input($console_command, $userArgs) {
172   $args = [];
173   $defaultOptions = [];
174   $definition = $console_command->getDefinition();
175   $inputArguments = $definition->getArguments();
176   foreach ($inputArguments as $key => $inputOption) {
177     $value = array_shift($userArgs);
178     if (!isset($value)) {
179       $value = $inputOption->getDefault();
180     }
181     $args[$key] = $value;
182   }
183   $inputOptions = $definition->getOptions();
184   foreach ($inputOptions as $option => $inputOption) {
185     $defaultOptions[$option] = $inputOption->getDefault();
186   }
187   foreach ($defaultOptions as $option => $value) {
188     $args["--$option"] = drush_get_option($option, $value);
189   }
190   // TODO: Need to add global options. Note that ArrayInput is validated.
191   $input = new ArrayInput($args, $definition);
192   return $input;
193 }
194
195 /**
196  * Collect all of the options defined in every relevant context, and
197  * merge them together to form the options array.
198  *
199  * @return array
200  */
201 function annotationcommand_adapter_get_options($command) {
202   $default_options = isset($command['consolidation-option-defaults']) ? $command['consolidation-option-defaults'] : [];
203   $options = drush_redispatch_get_options() + $default_options;
204
205   $options += drush_get_merged_options();
206
207   return $options;
208 }
209
210 /**
211  * This function is set as the $command['callback'] for commands that have
212  * been converted to annotated commands.  Note that Drush 9 calls these with
213  * Symfony's Application::run() method, but Drush 8 continues to use the
214  * legacy Drush command dispatcher for all commands.
215  *
216  * @return bolean false if command failed (expect drush_set_error was called in this case)
217  */
218 function annotationcommand_adapter_process_command() {
219   $userArgs = func_get_args();
220   $commandprocessor = annotationcommand_adapter_get_processor();
221   $command = drush_get_command();
222   annotationcommand_adapter_add_hook_options($command);
223   $args = [];
224   foreach ($command['consolidation-arg-defaults'] as $key => $default) {
225     $value = array_shift($userArgs);
226     if (!isset($value)) {
227       $value = $default;
228     }
229     $args[$key] = $value;
230   }
231
232   $input = new DrushInputAdapter($args, annotationcommand_adapter_get_options($command), $command['command']);
233   $output = new DrushOutputAdapter();
234   $annotationData = $command['annotations'];
235   $commandData = new CommandData(
236     $annotationData,
237     $input,
238     $output
239   );
240   $commandData->setIncludeOptionsInArgs($command['add-options-to-arguments']);
241   $names = annotationcommand_adapter_command_names($command);
242
243   // n.b.: backend result is set by a post-alter hook.
244   $result = $commandprocessor->process(
245     $output,
246     $names,
247     $command['annotated-command-callback'],
248     $commandData
249   );
250
251   return $result;
252 }
253
254 /**
255  * Internal function called by annotationcommand_adapter_commands, which
256  * is called by drush_get_commands().
257  *
258  * @param array $annotation_commandfiles path => class mapping
259  *
260  * @return object[]
261  */
262 function annotationcommand_adapter_get_commands($annotation_commandfiles) {
263   $commands = [];
264   // This will give us a list containing something akin to:
265   //   'modules/default_content/src/CliTools/DefaultContentCommands.php' =>
266   //   '\\Drupal\\default_content\\CliTools\\DefaultContentCommands',
267   foreach ($annotation_commandfiles as $commandfile_path => $commandfile_class) {
268     if (file_exists($commandfile_path)) {
269       $commandhandler = annotationcommand_adapter_create_commandfile_instance($commandfile_path, $commandfile_class);
270       $commands_for_this_commandhandler = annotationcommand_adapter_get_commands_for_commandhandler($commandhandler, $commandfile_path);
271       $commands = array_merge($commands, $commands_for_this_commandhandler);
272     }
273   }
274   return $commands;
275 }
276
277 /**
278  * Create and cache a commandfile instance.
279  *
280  * @param string $commandfile_path Path to the commandfile implementation
281  * @param string $commandfile_class Namespace and class of the commandfile object
282  *
283  * @return object
284  */
285 function annotationcommand_adapter_create_commandfile_instance($commandfile_path, $commandfile_class) {
286   $cache =& drush_get_context('DRUSH_ANNOTATION_COMMANDFILE_INSTANCES');
287   if (!isset($cache[$commandfile_path])) {
288     include_once $commandfile_path;
289     $commandhandler = new $commandfile_class;
290     $cache[$commandfile_path] = $commandhandler;
291   }
292   return $cache[$commandfile_path];
293 }
294
295 /**
296  * TODO: document
297  */
298 function annotationcommand_adapter_cache_module_console_commands($console_command, $commandfile_path = null) {
299   if (!isset($commandfile_path)) {
300     $class = new \ReflectionClass($console_command);
301     $commandfile_path = $class->getFileName();
302   }
303   $module_service_commands = drush_get_context('DRUSH_MODULE_SERVICE_COMMANDS');
304   $commands = annotationcommand_adapter_get_command_for_console_command($console_command, $commandfile_path);
305   drush_set_context('DRUSH_MODULE_SERVICE_COMMANDS', array_merge($commands, $module_service_commands));
306 }
307
308 /**
309  * TODO: document
310  */
311 function annotationcommand_adapter_cache_module_service_commands($commandhandler, $commandfile_path = null) {
312   if (!isset($commandfile_path)) {
313     $class = new \ReflectionClass($commandhandler);
314     $commandfile_path = $class->getFileName();
315   }
316   $module_service_commands = drush_get_context('DRUSH_MODULE_SERVICE_COMMANDS');
317   $commands = annotationcommand_adapter_get_commands_for_commandhandler($commandhandler, $commandfile_path, false);
318   drush_set_context('DRUSH_MODULE_SERVICE_COMMANDS', array_merge($commands, $module_service_commands));
319 }
320
321 /**
322  * Convert a Symfony Console command into a Drush $command record
323  *
324  * @param Symfony\Component\Console\Command\Command $console_command The Symfony Console command to convert
325  * @param string $commandfile_path Path to console command file
326  *
327  * @return array Drush $command record
328  */
329 function annotationcommand_adapter_get_command_for_console_command($console_command, $commandfile_path) {
330   $commands = [];
331   $commandfile = basename($commandfile_path, '.php');
332   $factory = annotationcommand_adapter_get_factory();
333   $inputDefinition = $console_command->getDefinition();
334   $inputArguments = $inputDefinition->getArguments();
335   $inputOptions = $inputDefinition->getOptions();
336   $aliases = $console_command->getAliases();
337   $command_name = strtolower($console_command->getName());
338   $standard_alias = str_replace(':', '-', $command_name);
339   if ($command_name != $standard_alias) {
340     $aliases[] = $standard_alias;
341   }
342   $command = [
343     'name' => $command_name,
344     'callback' => 'annotationcommand_adapter_run_console_command',
345     'drush-console-command' => $console_command,
346     'commandfile' => $commandfile,
347     'category' => $commandfile,
348     'options' => [],
349     'arguments' => [],
350     'description' => $console_command->getDescription(),
351     'examples' => $console_command->getUsages(),
352     'aliases' => $aliases,
353   ];
354   foreach ($inputArguments as $arg => $inputArg) {
355     $command['arguments'][$arg] = $inputArg->getDescription();
356   }
357   $command['required-arguments'] = $inputDefinition->getArgumentRequiredCount();
358   foreach ($inputOptions as $option => $inputOption) {
359     $description = $inputOption->getDescription();
360     $default = $inputOption->getDefault();
361     $command['options'][$option] = ['description' => $description];
362     if (!empty($default)) {
363       $command['options'][$option]['example-value'] = $default;
364     }
365   }
366   $command += drush_command_defaults($command_name, $commandfile, $commandfile_path);
367   $commands[$command_name] = $command;
368   return $commands;
369 }
370
371 /**
372  * Convert an annotated command command handler object into a Drush $command record.
373  *
374  * @param object $commandhandler Command handler object
375  * @param string $commandfile_path
376  * @param boolean $includeAllPublicMethods TODO remove this, make it 'false' always
377  *
378  * @return array Drush $command record
379  */
380 function annotationcommand_adapter_get_commands_for_commandhandler($commandhandler, $commandfile_path, $includeAllPublicMethods = true) {
381   $cache =& drush_get_context('DRUSH_ANNOTATION_COMMANDS_FOR_COMMANDFILE');
382   if (isset($cache[$commandfile_path])) {
383     return $cache[$commandfile_path];
384   }
385   $factory = annotationcommand_adapter_get_factory();
386   $commands = [];
387   $commandfile = basename($commandfile_path, '.php');
388
389   $commandinfo_list = $factory->getCommandInfoListFromClass($commandhandler);
390
391   foreach ($commandinfo_list as $commandinfo) {
392     // Hooks are automatically registered when the commandhandler is
393     // created via registerCommandClass(), so we don't need to do it again here.
394     // $factory->registerCommandHook($commandinfo, $commandhandler);
395     $aliases = $commandinfo->getAliases();
396     $command_name = strtolower($commandinfo->getName());
397     $standard_alias = str_replace(':', '-', $command_name);
398     if ($command_name != $standard_alias) {
399       $aliases[] = $standard_alias;
400     }
401     $handle_remote_commands = $commandinfo->getAnnotation('handle-remote-commands') == 'true';
402     // TODO: if there is no 'bootstrap' annotation, maybe we should default to NONE instead of FULL?
403     if ($bootstrap = $commandinfo->getAnnotation('bootstrap')) {
404       $bootstrap = constant($bootstrap);
405     }
406     $command = [
407       'name' => $command_name,
408       //'callback' => [$commandhandler, $commandinfo->getMethodName()],
409       'callback' => 'annotationcommand_adapter_process_command',
410       'annotated-command-callback' => [$commandhandler, $commandinfo->getMethodName()],
411       'commandfile' => $commandfile,
412       'category' => $commandfile,
413       'options' => [],
414       'arguments' => [],
415       'description' => $commandinfo->getDescription(),
416       'examples' => $commandinfo->getExampleUsages(),
417       'bootstrap' => $bootstrap,
418       'handle-remote-commands' => $handle_remote_commands,
419       'aliases' => $aliases,
420       'add-options-to-arguments' => TRUE,
421       'consolidation-output-formatters' => TRUE,
422       'consolidation-option-defaults' => $commandinfo->options()->getValues(),
423       'consolidation-arg-defaults' => $commandinfo->arguments()->getValues(),
424     ];
425     $required_arguments = 0;
426     foreach ($commandinfo->arguments()->getValues() as $arg => $default) {
427       $command['arguments'][$arg] = $commandinfo->arguments()->getDescription($arg);
428       if (!isset($default)) {
429         ++$required_arguments;
430       }
431     }
432     $command['required-arguments'] = $required_arguments;
433     foreach ($commandinfo->options()->getValues() as $option => $default) {
434       $description = $commandinfo->options()->getDescription($option);
435       $command['options'][$option] = ['description' => $description];
436       if (!empty($default)) {
437         $command['options'][$option]['example-value'] = $default;
438       }
439       $fn = 'annotationcommand_adapter_alter_option_description_' . $option;
440       if (function_exists($fn)) {
441         $command['options'][$option] = $fn($command['options'][$option], $commandinfo, $default);
442       }
443     }
444     $command['annotations'] = $commandinfo->getAnnotations();
445     // If the command has a '@return' annotation, then
446     // remember information we will need to use the output formatter.
447     $returnType = $commandinfo->getReturnType();
448     if (isset($returnType)) {
449       $command['return-type'] = $returnType;
450     }
451     $command += drush_command_defaults($command_name, $commandfile, $commandfile_path);
452     $commands[$command_name] = $command;
453   }
454   $cache[$commandfile_path] = $commands;
455   return $commands;
456 }
457
458 /**
459  * Modify a $command record, adding option definitions defined by any
460  * command hook.
461  *
462  * @param array $command Drush command record to modify
463  */
464 function annotationcommand_adapter_add_hook_options(&$command)
465 {
466   // Get options added by hooks.  We postpone doing this until the
467   // last minute rather than doing it when processing commandfiles
468   // so that we do not need to worry about what order we process the
469   // commandfiles in -- we can load extensions late, and still have
470   // the extension hook a core command, or have an early-loaded global
471   // extension hook a late-loaded extension (e.g. attached to a module).
472   $names = annotationcommand_adapter_command_names($command);
473   $names[] = '*'; // we are missing annotations here; maybe we just don't support that? (TODO later, maybe)
474   $factory = annotationcommand_adapter_get_factory();
475   $extraOptions = $factory->hookManager()->getHookOptions($names);
476   foreach ($extraOptions as $commandinfo) {
477     if (!isset($command['consolidation-option-defaults'])) {
478       $command['consolidation-option-defaults'] = array();
479     }
480     $command['consolidation-option-defaults'] += $commandinfo->options()->getValues();
481     foreach ($commandinfo->options()->getValues() as $option => $default) {
482       $description = $commandinfo->options()->getDescription($option);
483       $command['options'][$option] = ['description' => $description];
484       if (!empty($default)) {
485         $command['options'][$option]['example-value'] = $default;
486       }
487       $fn = 'annotationcommand_adapter_alter_option_description_' . $option;
488       if (function_exists($fn)) {
489         $command['options'][$option] = $fn($command['options'][$option], $commandinfo, $default);
490       }
491     }
492   }
493 }
494
495 /**
496  * Build all of the name variants for a Drush $command record
497  *
498  * @param array $command Drush command record
499  *
500  * @return string[]
501  */
502 function annotationcommand_adapter_command_names($command)
503 {
504   $names = array_merge(
505     [$command['command']],
506     $command['aliases']
507   );
508   if (!empty($command['annotated-command-callback'])) {
509     $commandHandler = $command['annotated-command-callback'][0];
510     $reflectionClass = new \ReflectionClass($commandHandler);
511     $commandFileClass = $reflectionClass->getName();
512     $names[] = $commandFileClass;
513   }
514   return $names;
515 }
516
517 /**
518  * Convert from an old-style Drush initialize hook into annotated-command hooks.
519  * @see _drush_invoke_hooks().
520  *
521  * @param string[] $names All of the applicable names for the command being hooked
522  * @param CommandData $commandData All of the parameter data associated with the
523  *   current command invokation, including the InputInterface, OutputInterface
524  *   and AnnotationData
525  */
526 function annotationcommand_adapter_call_initialize($names, CommandData $commandData)
527 {
528   $factory = annotationcommand_adapter_get_factory();
529   $hookManager = $factory->hookManager();
530
531   $hooks = $hookManager->getHooks(
532     $names,
533     [
534       HookManager::PRE_INITIALIZE,
535       HookManager::INITIALIZE,
536       HookManager::POST_INITIALIZE,
537     ],
538     $commandData->annotationData()
539   );
540
541   foreach ((array)$hooks as $hook) {
542     $hook($commandData->input(), $commandData->annotationData());
543   }
544 }
545
546 /**
547  * Convert from an old-style Drush pre-validate hook into annotated-command hooks.
548  * @see _drush_invoke_hooks().
549  *
550  * @param string[] $names All of the applicable names for the command being hooked
551  * @param CommandData $commandData All of the parameter data associated with the
552  *   current command invokation, including the InputInterface, OutputInterface
553  *   and AnnotationData
554  *
555  * @return boolean|object
556  */
557 function annotationcommand_adapter_call_hook_pre_validate($names, CommandData $commandData)
558 {
559   return annotationcommand_adapter_call_validate_interface(
560     $names,
561     [
562       HookManager::PRE_ARGUMENT_VALIDATOR,
563     ],
564     $commandData
565   );
566 }
567
568 /**
569  * Convert from an old-style Drush validate hook into annotated-command hooks.
570  * @see _drush_invoke_hooks().
571  *
572  * @param string[] $names All of the applicable names for the command being hooked
573  * @param CommandData $commandData All of the parameter data associated with the
574  *   current command invokation, including the InputInterface, OutputInterface
575  *   and AnnotationData
576  *
577  * @return boolean|object
578  */
579 function annotationcommand_adapter_call_hook_validate($names, CommandData $commandData)
580 {
581   return annotationcommand_adapter_call_validate_interface(
582     $names,
583     [
584       HookManager::ARGUMENT_VALIDATOR,
585     ],
586     $commandData
587   );
588 }
589
590 /**
591  * Convert from an old-style Drush pre-command hook into annotated-command hooks.
592  * @see _drush_invoke_hooks().
593  *
594  * @param string[] $names All of the applicable names for the command being hooked
595  * @param CommandData $commandData All of the parameter data associated with the
596  *   current command invokation, including the InputInterface, OutputInterface
597  *   and AnnotationData
598  *
599  * @return boolean|object
600  */
601 function annotationcommand_adapter_call_hook_pre_command($names, CommandData $commandData)
602 {
603   return annotationcommand_adapter_call_validate_interface(
604     $names,
605     [
606       HookManager::PRE_COMMAND_HOOK,
607     ],
608     $commandData
609   );
610 }
611
612 /**
613  * Convert from an old-style Drush 'command' hook into annotated-command hooks.
614  * @see _drush_invoke_hooks().
615  *
616  * @param string[] $names All of the applicable names for the command being hooked
617  * @param CommandData $commandData All of the parameter data associated with the
618  *   current command invokation, including the InputInterface, OutputInterface
619  *   and AnnotationData
620  *
621  * @return boolean|object
622  */
623 function annotationcommand_adapter_call_hook_command($names, CommandData $commandData)
624 {
625   return annotationcommand_adapter_call_validate_interface(
626     $names,
627     [
628       HookManager::COMMAND_HOOK,
629     ],
630     $commandData
631   );
632 }
633
634 /**
635  * Convert from an old-style Drush post-command hook into annotated-command hooks.
636  * @see _drush_invoke_hooks().
637  *
638  * @param string[] $names All of the applicable names for the command being hooked
639  * @param CommandData $commandData All of the parameter data associated with the
640  *   current command invokation, including the InputInterface, OutputInterface
641  *   and AnnotationData
642  * @param mixed $return The return value of the command being executed
643  *
644  * @return mixed The altered command return value
645  */
646 function annotationcommand_adapter_call_hook_post_command($names, CommandData $commandData, $return)
647 {
648   return annotationcommand_adapter_call_process_interface(
649     $names,
650     [
651       HookManager::POST_COMMAND_HOOK,
652     ],
653     $commandData,
654     $return
655   );
656 }
657
658 /**
659  * After the primary Drush command hook is called, call all of the annotated-command
660  * process and alter hooks.
661  * @see _drush_invoke_hooks().
662  *
663  * @param string[] $names All of the applicable names for the command being hooked
664  * @param CommandData $commandData All of the parameter data associated with the
665  *   current command invokation, including the InputInterface, OutputInterface
666  *   and AnnotationData
667  * @param mixed $return The return value of the command being executed
668  *
669  * @return mixed The altered command return value
670  */
671 function annotationcommand_adapter_call_hook_process_and_alter($names, $commandData, $return)
672 {
673   return annotationcommand_adapter_call_process_interface(
674     $names,
675     [
676       HookManager::PRE_PROCESS_RESULT,
677       HookManager::PROCESS_RESULT,
678       HookManager::POST_PROCESS_RESULT,
679       HookManager::PRE_ALTER_RESULT,
680       HookManager::ALTER_RESULT,
681       HookManager::POST_ALTER_RESULT,
682     ],
683     $commandData,
684     $return
685   );
686 }
687
688 /**
689  * Given a list of hooks that conform to the interface ProcessResultInterface,
690  * call them and return the result.
691  *
692  * @param string[] $names All of the applicable names for the command being hooked
693  * @param string[] $hooks All of the HookManager hooks that should be called
694  * @param CommandData $commandData All of the parameter data associated with the
695  *   current command invokation, including the InputInterface, OutputInterface
696  *   and AnnotationData
697  * @param mixed $return The return value of the command being executed
698  *
699  * @return mixed The altered command return value
700  */
701 function annotationcommand_adapter_call_process_interface($names, $hooks, CommandData $commandData, $return)
702 {
703   $factory = annotationcommand_adapter_get_factory();
704   $hookManager = $factory->hookManager();
705
706   $hooks = $hookManager->getHooks($names, $hooks, $commandData->annotationData());
707
708   foreach ((array)$hooks as $hook) {
709     $result = $hook($return, $commandData);
710     if (isset($result)) {
711       $return = $result;
712     }
713   }
714   return $return;
715 }
716
717 /**
718  * Given a list of hooks that conform to the interface ValidatorInterface,
719  * call them and return the result.
720  *
721  * @param string[] $names All of the applicable names for the command being hooked
722  * @param string[] $hooks All of the HookManager hooks that should be called
723  * @param CommandData $commandData All of the parameter data associated with the
724  *   current command invokation, including the InputInterface, OutputInterface
725  *   and AnnotationData
726  *
727  * @return boolean|object
728  */
729 function annotationcommand_adapter_call_validate_interface($names, $hooks, CommandData $commandData)
730 {
731   $factory = annotationcommand_adapter_get_factory();
732   $hookManager = $factory->hookManager();
733
734   $annotationData = $commandData->annotationData();
735   $hooks = $hookManager->getHooks($names, $hooks, $annotationData);
736
737   foreach ((array)$hooks as $hook) {
738     $validated = $hook($commandData);
739     // TODO: if $validated is a CommandError, maybe the best thing to do is 'return drush_set_error()'?
740     if (is_object($validated) || ($validated === false)) {
741       return $validated;
742     }
743   }
744   return true;
745 }
746
747 /**
748  * TODO: Document
749  */
750 function annotationcommand_adapter_alter_option_description_format($option_help, $commandinfo, $default) {
751   $formatterManager = annotatedcomand_adapter_get_formatter();
752   $return_type = $commandinfo->getReturnType();
753   if (!empty($return_type)) {
754     $available_formats = $formatterManager->validFormats($return_type);
755     $option_help['description'] = dt('Select output format. Available: !formats.', array('!formats' => implode(', ', $available_formats)));
756     if (!empty($default)) {
757       $option_help['description'] .= dt(' Default is !default.', array('!default' => $default));
758     }
759   }
760   return $option_help;
761 }
762
763 /**
764  * TODO: Document
765  */
766 function annotationcommand_adapter_alter_option_description_fields($option_help, $commandinfo, $default) {
767   $formatOptions = new FormatterOptions($commandinfo->getAnnotations()->getArrayCopy());
768   $field_labels = $formatOptions->get(FormatterOptions::FIELD_LABELS);
769   $default_fields = $formatOptions->get(FormatterOptions::DEFAULT_FIELDS, [], array_keys($field_labels));
770   $available_fields = array_keys($field_labels);
771   $option_help['example-value'] = implode(', ', $default_fields);
772   $option_help['description'] = dt('Fields to output. All available fields are: !available.', array('!available' => implode(', ', $available_fields)));
773   return $option_help;
774 }
775
776 /**
777  * In some circumstances, Drush just does a deep search for any *.drush.inc
778  * file, so that it can find all commands, in enabled and disabled modules alike,
779  * for the purpose of displaying the help text for that command.
780  */
781 function annotationcommand_adapter_refine_searchpaths($searchpath) {
782   $result = [];
783   foreach ($searchpath as $path) {
784     $max_depth = TRUE;
785     $pattern = '/.*\.info$/';
786     if (drush_drupal_major_version() > 7) {
787       $pattern = '/.*\.info.yml$/';
788     }
789     $locations = drush_scan_directory($path, $pattern, ['.', '..'], false, $max_depth);
790
791     // Search for any directory that might be a module or theme (contains
792     // a *.info or a *.info.yml file)
793     foreach ($locations as $key => $info) {
794       $result[dirname($key)] = true;
795     }
796   }
797   return array_keys($result);
798 }