56a2b41f34dc5cc37c1c53af5494d2f2f931dc18
[yaffs-website] / vendor / drush / drush / includes / engines.inc
1 <?php
2
3 /**
4  * @file
5  * The drush engines API implementation and helpers.
6  */
7
8 use Drush\Log\LogLevel;
9
10 /**
11  * Obtain all engine types info and normalize with defaults.
12  *
13  * @see hook_drush_engine_type_info().
14  */
15 function drush_get_engine_types_info() {
16   $info = drush_command_invoke_all('drush_engine_type_info');
17   foreach ($info as $type => $data) {
18     $info[$type] += array(
19       'description' => '',
20       'option' => FALSE,
21       'default' => NULL,
22       'options' => array(),
23       'sub-options' => array(),
24       'config-aliases' => array(),
25       'add-options-to-command' => FALSE,
26       'combine-help' => FALSE,
27     );
28   }
29
30   return $info;
31 }
32
33 /**
34  * Return a structured array of engines of a specific type.
35  *
36  * Engines are pluggable subsystems. Each engine of a specific type will
37  * implement the same set of API functions and perform the same high-level
38  * task using a different backend or approach.
39  *
40  * This function/hook is useful when you have a selection of several mutually
41  * exclusive options to present to a user to select from.
42  *
43  * Other commands are able to extend this list and provide their own engines.
44  * The hook can return useful information to help users decide which engine
45  * they need, such as description or list of available engine options.
46  *
47  * The engine path element will automatically default to a subdirectory (within
48  * the directory of the commandfile that implemented the hook) with the name of
49  * the type of engine - e.g. an engine "wget" of type "handler" provided by
50  * the "pm" commandfile would automatically be found if the file
51  * "pm/handler/wget.inc" exists and a specific path is not provided.
52  *
53  * @param $engine_type
54  *   The type of engine.
55  *
56  * @return
57  *   A structured array of engines.
58  */
59 function drush_get_engines($engine_type) {
60   $info = drush_get_engine_types_info();
61   if (!isset($info[$engine_type])) {
62     return drush_set_error('DRUSH_UNKNOWN_ENGINE_TYPE', dt('Unknown engine type !engine_type', array('!engine_type' => $engine_type)));
63   }
64
65   $engines = array(
66     'info' => $info[$engine_type],
67     'engines' => array(),
68   );
69   $list = drush_commandfile_list();
70   $hook = 'drush_engine_' . str_replace('-', '_', $engine_type);
71   foreach ($list as $commandfile => $path) {
72     if (drush_command_hook($commandfile, $hook)) {
73       $function = $commandfile . '_' . $hook;
74       $result = $function();
75       foreach ($result as $engine_name => $engine) {
76         // Add some defaults.
77         $engine += array(
78           'commandfile' => $commandfile,
79           'options' => array(),
80           'sub-options' => array(),
81           'drupal dependencies' => array(),
82         );
83
84         // Legacy engines live in a subdirectory
85         // of the commandfile that declared them.
86         $engine_path = sprintf("%s/%s", dirname($path), $engine_type);
87         if (file_exists($engine_path)) {
88           $engine['path'] = $engine_path;
89         }
90         // Build engine class name, in case the engine doesn't provide it.
91         // The class name is based on the engine type and name, converted
92         // from snake_case to CamelCase.
93         // For example for type 'package_handler' and engine 'git_drupalorg'
94         // the class is \Drush\PackageHandler\GitDrupalorg
95         elseif (!isset($engine['class'])) {
96           $parts = array();
97           $parts[] = '\Drush';
98           $parts[] = str_replace(' ', '', ucwords(strtr($engine_type, '_', ' ')));
99           $parts[] = str_replace(' ', '', ucwords(strtr($engine_name, '_', ' ')));
100           $engine['class'] = implode('\\', $parts);
101         }
102
103         $engines['engines'][$engine_name] = $engine;
104       }
105     }
106   }
107
108   return $engines;
109 }
110
111 /**
112  * Take a look at all of the available engines,
113  * and create topic commands for each one that
114  * declares a topic.
115  */
116 function drush_get_engine_topics() {
117   $items = array();
118   $info = drush_get_engine_types_info();
119   foreach ($info as $engine => $data) {
120     if (array_key_exists('topic', $data)) {
121       $items[$data['topic']] = array(
122         'description' => $data['description'],
123         'hidden' => TRUE,
124         'topic' => TRUE,
125         'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
126         'callback' => 'drush_engine_topic_command',
127         'callback arguments' => array($engine),
128       );
129     }
130   }
131   return $items;
132 }
133
134 /**
135  * Include, instantiate and validate command engines.
136  *
137  * @return FALSE if a engine doesn't validate.
138  */
139 function drush_load_command_engines($command) {
140   $result = TRUE;
141   foreach ($command['engines'] as $engine_type => $config) {
142     $result = drush_load_command_engine($command, $engine_type);
143     // Stop loading engines if any of them fails.
144     if ($result === FALSE) {
145       break;
146     }
147   }
148   return $result;
149 }
150
151 /**
152  * Returns engine config supplied in the command definition.
153  */
154 function drush_get_command_engine_config($command, $engine_type, $metadata = array()) {
155   if (isset($command['engines'][$engine_type])) {
156     $metadata = array_merge($metadata, $command['engines'][$engine_type]);
157   }
158   return $metadata;
159 }
160
161 /**
162  * Selects and loads an engine implementing the given type.
163  *
164  * Loaded engines are stored as a context.
165  */
166 function drush_load_command_engine($command, $engine_type, $metadata = array()) {
167   drush_log(dt("Loading !engine engine.", array('!engine' => $engine_type), LogLevel::BOOTSTRAP));
168
169   $config = drush_get_command_engine_config($command, $engine_type, $metadata);
170   $engine_info = drush_get_engines($engine_type);
171   $engine = drush_select_engine($config, $engine_info);
172   $version = drush_drupal_major_version();
173
174   $context = $engine_type . '_engine_' . $engine . '_' . $version;
175   $instance = drush_get_context($context, FALSE);
176   if ($instance != FALSE) {
177     drush_set_engine($engine_type, $instance);
178   }
179   else {
180     $instance = drush_load_engine($engine_type, $engine, $config);
181     if ($instance == FALSE) {
182       return FALSE;
183     }
184     drush_set_context($context, $instance);
185   }
186   return $instance;
187 }
188
189 /**
190  * Add command structure info from each engine type back into the command.
191  */
192 function drush_merge_engine_data(&$command) {
193   // First remap engine data from the shortcut location
194   // ($command['engine_type']) to the standard location
195   // ($command['engines']['engine_type'])
196   $info = drush_get_engine_types_info();
197   foreach ($info as $engine_type => $info) {
198     if (isset($command[$engine_type])) {
199       $config = $command[$engine_type];
200       foreach ($info['config-aliases'] as $engine_option_alias_name => $engine_option_standard_name) {
201         if (array_key_exists($engine_option_alias_name, $config)) {
202           $config[$engine_option_standard_name] = $config[$engine_option_alias_name];
203           unset($config[$engine_option_alias_name]);
204         }
205       }
206       // Convert single string values of 'require-engine-capability' to an array.
207       if (isset($config['require-engine-capability']) && is_string($config['require-engine-capability'])) {
208         $config['require-engine-capability'] = array($config['require-engine-capability']);
209       }
210       $command['engines'][$engine_type] = $config;
211     }
212   }
213
214   foreach ($command['engines'] as $engine_type => $config) {
215     // Normalize engines structure.
216     if (!is_array($config)) {
217       unset($command['engines'][$engine_type]);
218       $command['engines'][$config] = array();
219       $engine_type = $config;
220     }
221
222     // Get all implementations for this engine type.
223     $engine_info = drush_get_engines($engine_type);
224     if ($engine_info === FALSE) {
225       return FALSE;
226     }
227
228     // Complete command-declared engine type with default info.
229     $command['engines'][$engine_type] += $engine_info['info'];
230     $config = $command['engines'][$engine_type];
231
232     $engine_data = array();
233
234     // If there's a single implementation for this engine type, it will be
235     // loaded by default, and makes no sense to provide a command line option
236     // to select the only flavor (ie. --release_info=updatexml), so we won't
237     // add an option in this case.
238     // Additionally, depending on the command, it may be convenient to extend
239     // the command with the engine options.
240     if (count($engine_info['engines']) == 1) {
241       if ($config['add-options-to-command'] !== FALSE) {
242         // Add options and suboptions of the engine type and
243         // the sole implementation.
244         $engine = key($engine_info['engines']);
245         $data = $engine_info['engines'][$engine];
246         $engine_data += array(
247           'options' => $config['options'] + $data['options'],
248           'sub-options' => $config['sub-options'] + $data['sub-options'],
249         );
250       }
251     }
252     // Otherwise, provide a command option to choose between engines and add
253     // the engine options and sub-options.
254     else {
255       // Add engine type global options and suboptions.
256       $engine_data += array(
257         'options' => $config['options'],
258         'sub-options' => $config['sub-options'],
259       );
260
261       // If the 'combine-help' flag is set in the engine config,
262       // then we will combine all of the help items into the help
263       // text for $config['option'].
264       $combine_help = $config['combine-help'];
265       $combine_help_data = array();
266
267       // Process engines in order. First the default engine, the rest alphabetically.
268       $default = drush_select_engine($config, $engine_info);
269       $engines = array_keys($engine_info['engines']);
270       asort($engines);
271       array_unshift($engines, $default);
272       $engines = array_unique($engines);
273       foreach ($engines as $engine) {
274         $data = $engine_info['engines'][$engine];
275         // Check to see if the command requires any particular
276         // capabilities.  If no capabilities are required, then
277         // all engines are acceptable.
278         $engine_is_usable = TRUE;
279         if (array_key_exists('require-engine-capability', $config)) {
280           // See if the engine declares that it provides any
281           // capabilities.  If no capabilities are listed, then
282           // it is assumed that the engine can satisfy all requirements.
283           if (array_key_exists('engine-capabilities', $data)) {
284             $engine_is_usable = FALSE;
285             // If 'require-engine-capability' is TRUE instead of an array,
286             // then only engines that are universal (do not declare any
287             // particular capabilities) are usable.
288             if (is_array($config['require-engine-capability'])) {
289               foreach ($config['require-engine-capability'] as $required) {
290                 // We need an engine that provides any one of the requirements.
291                 if (in_array($required, $data['engine-capabilities'])) {
292                   $engine_is_usable = TRUE;
293                 }
294               }
295             }
296           }
297         }
298         if ($engine_is_usable) {
299           $command['engines'][$engine_type]['usable'][] = $engine;
300           if (!isset($data['hidden'])) {
301             $option = $config['option'] . '=' . $engine;
302             $engine_data['options'][$option]['description'] = array_key_exists('description', $data) ? $data['description'] : NULL;
303             if ($combine_help) {
304               $engine_data['options'][$option]['hidden'] = TRUE;
305               if (drush_get_context('DRUSH_VERBOSE') || ($default == $engine) || !isset($data['verbose-only'])) {
306                 $combine_help_data[$engine] = $engine . ': ' . $data['description'];
307               }
308             }
309             if (isset($data['options'])) {
310               $engine_data['sub-options'][$option] = $data['options'];
311             }
312             if (isset($data['sub-options'])) {
313               $engine_data['sub-options'] += $data['sub-options'];
314             }
315           }
316         }
317       }
318       if (!empty($combine_help_data)) {
319         $engine_selection_option = $config['option'];
320         if (!is_array($engine_data['options'][$engine_selection_option])) {
321           $engine_data['options'][$engine_selection_option] = array('description' => $config['options'][$engine_selection_option]);
322         }
323         if (drush_get_context('DRUSH_VERBOSE')) {
324           $engine_data['options'][$engine_selection_option]['description'] .= "\n" . dt("All available values are:") . "\n - " . implode("\n - ", $combine_help_data) . "\n";
325         }
326         else {
327           $engine_data['options'][$engine_selection_option]['description'] .= " " . dt("Available: ") . implode(', ', array_keys($combine_help_data)) . ". ";
328         }
329         $engine_data['options'][$engine_selection_option]['description'] .= dt("Default is !default.", array('!default' => $default));
330       }
331       else {
332         // If the help options are not combined, then extend the
333         // default engine description.
334         $desc = $engine_info['engines'][$default]['description'];
335         $engine_info['engines'][$default]['description'] = dt('Default !type engine.', array('!type' => $engine_type)) . ' ' . $desc;
336       }
337     }
338     // This was simply array_merge_recursive($command, $engine_data), but this
339     // function has an undesirable behavior when merging primative types.
340     // If there is a 'key' => 'value' in $command, and the same 'key' => 'value'
341     // exists in $engine data, then the result will be 'key' => array('value', 'value');.
342     // This is NOT what we want, so we provide our own 'overlay' function instead.
343     $command = _drush_array_overlay_recursive($command, $engine_data);
344   }
345 }
346
347 // Works like array_merge_recursive(), but does not convert primative
348 // types into arrays.  Ever.
349 function _drush_array_overlay_recursive($a, $b) {
350   foreach ($b as $key => $value) {
351     if (!isset($a[$key]) || !is_array($a[$key])) {
352       $a[$key] = $b[$key];
353     }
354     else {
355       $a[$key] = _drush_array_overlay_recursive($a[$key], $b[$key]);
356     }
357   }
358   return $a;
359 }
360
361 /**
362  * Implementation of command hook for docs-output-formats
363  */
364 function drush_engine_topic_command($engine) {
365   $engine_instances = drush_get_engines($engine);
366   $option = $engine_instances['info']['option'];
367
368   if (isset($engine_instances['info']['topic-file'])) {
369     // To do: put this file next to the commandfile that defines the
370     // engine type, not in the core docs directory.
371     $docs_dir = drush_get_context('DOC_PREFIX', DRUSH_BASE_PATH);
372     $path = $engine_instances['info']['topic-file'];
373     $docs_file = (drush_is_absolute_path($path) ? '' : $docs_dir . '/') . $path;
374     $doc_text = drush_html_to_text(file_get_contents($docs_file));
375   }
376   elseif (isset($engine_instances['info']['topic-text'])) {
377     $doc_text = $engine_instances['info']['topic-text'];
378   }
379   else {
380     return drush_set_error('DRUSH_BAD_ENGINE_TOPIC', dt("The engine !engine did not define its topic command correctly.", array('!engine' => $engine)));
381   }
382
383   // Look at each instance of the engine; if it has an html
384   // file in the the 'topics' folder named after itself, then
385   // include the file contents in the engine topic text.
386   $instances = $engine_instances['engines'];
387   ksort($instances);
388   foreach ($instances as $instance => $config) {
389     if (isset($config['description'])) {
390       $doc_text .= "\n\n::: --$option=$instance :::\n" . $config['description'];
391       $path = $config['path'] . '/topics/' . $instance . '.html';
392       if (file_exists($path)) {
393         $doc_text .= "\n" . drush_html_to_text(file_get_contents($path));
394       }
395       $additional_topic_text = drush_command_invoke_all('drush_engine_topic_additional_text', $engine, $instance, $config);
396       if (!empty($additional_topic_text)) {
397         $doc_text .= "\n\n" . implode("\n\n", $additional_topic_text);
398       }
399     }
400   }
401
402   // Write the topic text to a file so that is can be paged
403   $file = drush_save_data_to_temp_file($doc_text);
404   drush_print_file($file);
405 }
406
407 /**
408  * Selects an engine between the available ones.
409  *
410  * Precedence:
411  *
412  *  - preferred engine, if available.
413  *  - user supplied engine via cli.
414  *  - default engine from engine type / command declaration.
415  *  - the first engine available.
416  *
417  * @param array $config
418  *   Engine type configuration. My be overridden in command declaration.
419  * @param array $engine_info
420  *   Engine type declaration.
421  * @param string $default
422  *   Preferred engine.
423  *
424  * @return string
425  *   Selected engine.
426  */
427 function drush_select_engine($config, $engine_info, $preferred = NULL) {
428   $engines = array_keys($engine_info['engines']);
429
430   if (in_array($preferred, $engines)) {
431     return $preferred;
432   }
433
434   if (!empty($config['option'])) {
435     $engine = drush_get_option($config['option'], FALSE);
436     if ($engine && in_array($engine, $engines)) {
437       return $engine;
438     }
439   }
440
441   if (isset($config['default']) && in_array($config['default'], $engines)) {
442     return $config['default'];
443   }
444
445   return current($engines);
446 }
447
448 /**
449  * Loads and validate an engine of the given type.
450  *
451  * @param string $type
452  *   Engine type.
453  * @param string $engine
454  *   Engine name.
455  * @param array $config
456  *   Engine configuration. Tipically it comes from a command declaration.
457  *
458  * @return
459  *   TRUE or instanced object of available class on success. FALSE on fail.
460  */
461 function drush_load_engine($type, $engine, $config = array()) {
462   $engine_info = drush_get_engines($type);
463   $engine = drush_select_engine($config, $engine_info, $engine);
464   $config['engine-info'] = $engine_info['engines'][$engine];
465
466   // Check engine dependency on drupal modules before include.
467   $dependencies = $config['engine-info']['drupal dependencies'];
468   foreach ($dependencies as $dependency) {
469     if (!drush_module_exists($dependency)) {
470       return drush_set_error('DRUSH_ENGINE_DEPENDENCY_ERROR', dt('!engine_type: !engine engine needs the following modules installed/enabled to run: !dependencies.', array('!engine_type' => $type, '!engine' => $engine, '!dependencies' => implode(', ', $dependencies))));
471     }
472   }
473
474   $result = drush_include_engine($type, $engine, $config);
475   if (is_object($result)) {
476     $valid = method_exists($result, 'validate') ? $result->validate() : TRUE;
477     if ($valid) {
478       drush_set_engine($type, $result);
479     }
480   }
481   else {
482     $function = strtr($type, '-', '_') . '_validate';
483     $valid = function_exists($function) ? call_user_func($function) : TRUE;
484   }
485   if (!$valid) {
486     return FALSE;
487   }
488   return $result;
489 }
490
491 /**
492  * Include the engine code for a specific named engine of a certain type.
493  *
494  * If the engine type has implemented hook_drush_engine_$type the path to the
495  * engine specified in the array will be used.
496  *
497  * If a class named in the form drush_$type_$engine exists, it will return an
498  * instance of the class.
499  *
500  * @param string $type
501  *   The type of engine.
502  * @param string $engine
503  *   The name for the engine to include.
504  * @param array $config
505  *   Parameters for the engine class constructor.
506  *
507  * @return
508  *   TRUE or instanced object of available class on success. FALSE on fail.
509  */
510 function drush_include_engine($type, $engine, $config = NULL) {
511   $engine_info = drush_get_engines($type);
512
513   // Pick the engine name that actually implements the requested engine.
514   $engine = isset($engine_info['engines'][$engine]['implemented-by']) ? $engine_info['engines'][$engine]['implemented-by'] : $engine;
515
516   // Legacy engines live in a subdirectory of the commandfile
517   // that declares them. We need to explicitly include the file.
518   if (isset($engine_info['engines'][$engine]['path'])) {
519     $path = $engine_info['engines'][$engine]['path'];
520     if (!drush_include($path, $engine)) {
521       return drush_set_error('DRUSH_ENGINE_INCLUDE_FAILED', dt('Unable to include the !type engine !engine from !path.' , array('!path' => $path, '!type' => $type, '!engine' => $engine)));
522     }
523     // Legacy engines may be implemented in a magic class name.
524     $class = 'drush_' . $type . '_' . str_replace('-', '_', $engine);
525     if (class_exists($class)) {
526       $instance = new $class($config);
527       $instance->engine_type = $type;
528       $instance->engine = $engine;
529       return $instance;
530     }
531
532     return TRUE;
533   }
534
535   return drush_get_class($engine_info['engines'][$engine]['class'], array($type, $engine, $config));
536 }
537
538 /**
539  * Return the engine of the specified type that was loaded by the Drush command.
540  */
541 function drush_get_engine($type) {
542   return drush_get_context($type . '_engine', FALSE);
543 }
544
545 /**
546  * Called by the Drush command (@see _drush_load_command_engines())
547  * to cache the active engine instance.
548  */
549 function drush_set_engine($type, $instance) {
550   drush_set_context($type . '_engine', $instance);
551 }
552
553 /**
554  * Add engine topics to the command topics, if any.
555  */
556 function drush_engine_add_help_topics(&$command) {
557   $engine_types = drush_get_engine_types_info();
558   foreach ($command['engines'] as $engine_type => $config) {
559     $info = $engine_types[$engine_type];
560     if (isset($info['topics'])) {
561       $command['topics'] = array_merge($command['topics'], $info['topics']);
562     }
563     if (isset($info['topic'])) {
564       $command['topics'][] = $info['topic'];
565     }
566   }
567 }