3afcc5dbf539e8a72f0c1538bb99b021086ff8d3
[yaffs-website] / vendor / drush / drush / includes / drush.inc
1 <?php
2
3 /**
4  * @file
5  * The drush API implementation and helpers.
6  */
7
8 use Drush\Log\Logger;
9 use Drush\Log\LogLevel;
10 use Psr\Log\LoggerInterface;
11
12 /**
13  * @name Error status definitions
14  * @{
15  * Error code definitions for interpreting the current error status.
16  * @see drush_set_error(), drush_get_error(), drush_get_error_log(), drush_cmp_error()
17  */
18
19 /** The command completed successfully. */
20 define('DRUSH_SUCCESS', 0);
21 /** The command could not be completed because the framework has specified errors that have occured. */
22 define('DRUSH_FRAMEWORK_ERROR', 1);
23 /** The command was aborted because the user chose to cancel it at some prompt.
24 This exit code is arbitrarily the same as EX_TEMPFAIL in sysexits.h, although
25 note that shell error codes are distinct from C exit codes, so this alignment
26 not meaningful. */
27 define('DRUSH_EXITCODE_USER_ABORT', 75);
28 /** The command that was executed resulted in an application error,
29 The most commom causes for this is invalid PHP or a broken SSH
30 pipe when using drush_backend_invoke in a distributed manner. */
31 define('DRUSH_APPLICATION_ERROR', 255);
32
33 /**
34  * @} End of "name Error status defintions".
35  */
36
37 /**
38  * The number of bytes in a kilobyte. Copied from Drupal.
39  */
40 define('DRUSH_KILOBYTE', 1024);
41
42 /**
43  * Default amount of time, in seconds, to cache downloads via
44  * drush_download_file(). One day is 86400 seconds.
45  */
46 define('DRUSH_CACHE_LIFETIME_DEFAULT', 86400);
47
48 /**
49  * Include a file, selecting a version specific file if available.
50  *
51  * For example, if you pass the path "/var/drush" and the name
52  * "update" when bootstrapped on a Drupal 6 site it will first check for
53  * the presence of "/var/drush/update_6.inc" in include it if exists. If this
54  * file does NOT exist it will proceed and check for "/var/drush/update.inc".
55  * If neither file exists, it will return FALSE.
56  *
57  * @param $path
58  *   The path you want to search.
59  * @param $name
60  *   The file base name you want to include (not including a version suffix
61  *   or extension).
62  * @param $version
63  *   The version suffix you want to include (could be specific to the software
64  *   or platform your are connecting to) - defaults to the current Drupal core
65  *   major version.
66  * @param $extension
67  *   The extension - defaults to ".inc".
68  *
69  * @return
70  *   TRUE if the file was found and included.
71  */
72 function drush_include($path, $name, $version = NULL, $extension = 'inc') {
73   $name = str_replace('-', '_', $name);
74   $version = ($version) ? $version : drush_drupal_major_version();
75
76   $file = sprintf("%s/%s_%s.%s", $path, $name, $version, $extension);
77   if (file_exists($file)) {
78     include_once($file);
79     return TRUE;
80   }
81   $file = sprintf("%s/%s.%s", $path, $name, $extension);
82   if (file_exists($file)) {
83     include_once($file);
84     return TRUE;
85   }
86
87   return drush_set_error('DRUSH_INCLUDE_NO_PATH', dt('Unable to include file !name!version!extension or !name!extension from !path.', array('!name' => $name, '!version' => $version, '!extension' => $extension)));
88 }
89
90 /**
91  * Provide a version-specific class instance.
92  *
93  * @param $class_name
94  *   The name of the class to instantiate.  Appends the Drupal
95  *   major version number to the end of the class name before instantiation.
96  * @param $constructor_args
97  *   An array of arguments to pass to the class constructor.
98  *
99  * Example wrapper class to instantiate a widget, called with the
100  * arguments for the WIDGET_CLASS constructor:
101  *
102  *  function drush_WIDGET_CLASS_get_class($widgetName, $widgetStyle) {
103  *    retrun drush_get_class('Widget_Class', func_get_args()));
104  *  }
105  */
106
107 function drush_get_class($class_name, $constructor_args = array(), $variations = array()) {
108   if (empty($variations)) {
109     $variations[] = drush_drupal_major_version();
110   }
111   $class_names = is_array($class_name) ? $class_name : array($class_name);
112   foreach ($class_names as $class_name) {
113     for ($i=count($variations); $i >= 0; $i--) {
114       $variant_class_name = $class_name . implode('', array_slice($variations, 0, $i));
115       if (class_exists($variant_class_name)) {
116         $reflectionClass = new ReflectionClass($variant_class_name);
117         return !empty($constructor_args) ? $reflectionClass->newInstanceArgs($constructor_args) : $reflectionClass->newInstanceArgs();
118       }
119     }
120   }
121   // Something bad happenned. TODO Exception?
122   return drush_set_error('DRUSH_GET_CLASS_ERROR', dt('Unable to load class !class', array('!class' => $class_name)));
123 }
124
125 /**
126  * Generate an .ini file. used by archive-dump."
127  *
128  * @param array $ini
129  *   A two dimensional associative array where top level are sections and
130  *   second level are key => value pairs.
131  *
132  * @return string
133  *   .ini formatted text.
134  */
135 function drush_export_ini($ini) {
136   $output = '';
137   foreach ($ini as $section => $pairs) {
138     if ($section) {
139       $output .= "[$section]\n";
140     }
141
142     foreach ($pairs as $k => $v) {
143       if ($v) {
144         $output .= "$k = \"$v\"\n";
145       }
146     }
147   }
148   return $output;
149 }
150
151 /**
152  * Generate code friendly to the Drupal .info format from a structured array.
153  * Mostly copied from http://drupalcode.org/viewvc/drupal/contributions/modules/features/features.export.inc.
154  *
155  * @param $info
156  *   An array or single value to put in a module's .info file.
157  *
158  * @param boolean $integer_keys
159  *   Use integer in keys.
160  *
161  * @param $parents
162  *   Array of parent keys (internal use only).
163  *
164  * @return
165  *   A code string ready to be written to a module's .info file.
166  */
167 function drush_export_info($info, $integer_keys = FALSE, $parents = array()) {
168   $output = '';
169   if (is_array($info)) {
170     foreach ($info as $k => $v) {
171       $child = $parents;
172       $child[] = $k;
173       $output .= drush_export_info($v, $integer_keys, $child);
174     }
175   }
176   else if (!empty($info) && count($parents)) {
177     $line = array_shift($parents);
178     foreach ($parents as $key) {
179       $line .= (!$integer_keys && is_numeric($key)) ? "[]" : "[{$key}]";
180     }
181     $line .=  " = \"{$info}\"\n";
182     return $line;
183   }
184   return $output;
185 }
186
187 /**
188  * Convert a csv string, or an array of items which
189  * may contain csv strings, into an array of items.
190  *
191  * @param $args
192  *   A simple csv string; e.g. 'a,b,c'
193  *   or a simple list of items; e.g. array('a','b','c')
194  *   or some combination; e.g. array('a,b','c') or array('a,','b,','c,')
195  *
196  * @returns array
197  *   A simple list of items (e.g. array('a','b','c')
198  */
199 function _convert_csv_to_array($args) {
200   //
201   // Step 1: implode(',',$args) converts from, say, array('a,','b,','c,') to 'a,,b,,c,'
202   // Step 2: explode(',', ...) converts to array('a','','b','','c','')
203   // Step 3: array_filter(...) removes the empty items
204   // Step 4: array_map(...) trims extra whitespace from each item
205   // (handles csv strings with extra whitespace, e.g. 'a, b, c')
206   //
207   return array_map('trim', array_filter(explode(',', is_array($args) ? implode(',',$args) : $args)));
208 }
209
210 /**
211  * Convert a nested array into a flat array.  Thows away
212  * the array keys, returning only the values.
213  *
214  * @param $args
215  *   An array that may potentially be nested.
216  *   e.g. array('a', array('b', 'c'))
217  *
218  * @returns array
219  *   A simple list of items (e.g. array('a','b','c')
220  */
221 function drush_flatten_array($a) {
222   $result = array();
223   if (!is_array($a)) {
224     return array($a);
225   }
226   foreach ($a as $value) {
227     $result = array_merge($result, drush_flatten_array($value));
228   }
229   return $result;
230 }
231
232 /**
233  * Get the available global options. Used by help command. Command files may
234  * modify this list using hook_drush_help_alter().
235  *
236  * @param boolean $brief
237  *   Return a reduced set of important options. Used by help command.
238  *
239  * @return
240  *   An associative array containing the option definition as the key,
241  *   and a descriptive array for each of the available options.  The array
242  *   elements for each item are:
243  *
244  *     - short-form: The shortcut form for specifying the key on the commandline.
245  *     - propagate: backend invoke will use drush_get_option to propagate this
246  *       option, when set, to any other Drush command that is called.
247  *     - context: The drush context where the value of this item is cached.  Used
248  *       by backend invoke to propagate values set in code.
249  *     - never-post: If TRUE, backend invoke will never POST this item's value
250  *       on STDIN; it will always be sent as a commandline option.
251  *     - never-propagate: If TRUE, backend invoke will never pass this item on
252  *       to the subcommand being executed.
253  *     - local-context-only: Backend invoke will only pass this value on for local calls.
254  *     - merge: For options such as $options['shell-aliases'] that consist of an array
255  *       of items, make a merged array that contains all of the values specified for
256  *       all of the contexts (config files) where the option is defined.  The value is stored in
257  *       the specified 'context', or in a context named after the option itself if the
258  *       context flag is not specified.
259  *       IMPORTANT: When the merge flag is used, the option value must be obtained via
260  *       drush_get_context('option') rather than drush_get_option('option').
261  *     - merge-pathlist: For options such as --include and --config, make a merged list
262  *       of options from all contexts; works like the 'merge' flag, but also handles string
263  *       values separated by the PATH_SEPARATOR.
264  *     - merge-associative: Like 'merge-pathlist', but key values are preserved.
265  *     - propagate-cli-value: Used to tell backend invoke to include the value for
266  *       this item as specified on the cli.  This can either override 'context'
267  *       (e.g., propagate --include from cli value instead of DRUSH_INCLUDE context),
268  *       or for an independent global setting (e.g. --user)
269  *     - description: The help text for this item. displayed by `drush help`.
270  */
271 function drush_get_global_options($brief = FALSE) {
272   $options['root']               = array('short-form' => 'r', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => "Drupal root directory to use (default: current directory).", 'example-value' => 'path');
273   $options['uri']                = array('short-form' => 'l', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => 'URI of the drupal site to use (only needed in multisite environments or when running on an alternate port).', 'example-value' => 'http://example.com:8888');
274   $options['verbose']            = array('short-form' => 'v', 'context' => 'DRUSH_VERBOSE', 'description' => 'Display extra information about the command.');
275   $options['debug']              = array('short-form' => 'd', 'context' => 'DRUSH_DEBUG', 'description' => 'Display even more information, including internal messages.');
276   $options['yes']                = array('short-form' => 'y', 'context' => 'DRUSH_AFFIRMATIVE', 'description' => "Assume 'yes' as answer to all prompts.");
277   $options['no']                 = array('short-form' => 'n', 'context' => 'DRUSH_NEGATIVE', 'description' => "Assume 'no' as answer to all prompts.");
278   $options['simulate']           = array('short-form' => 's', 'context' => 'DRUSH_SIMULATE', 'never-propagate' => TRUE, 'description' => "Simulate all relevant actions (don't actually change the system).");
279   $options['pipe']               = array('short-form' => 'p', 'hidden' => TRUE, 'description' => "Emit a compact representation of the command for scripting.");
280   $options['help']               = array('short-form' => 'h', 'description' => "This help system.");
281
282   if (!$brief) {
283     $options['version']            = array('description' => "Show drush version.");
284     $options['php']                = array('description' => "The absolute path to your PHP interpreter, if not 'php' in the path.", 'example-value' => '/path/to/file', 'never-propagate' => TRUE);
285     $options['interactive']        = array('short-form' => 'ia', 'description' => "Force interactive mode for commands run on multiple targets (e.g. `drush @site1,@site2 cc --ia`).", 'never-propagate' => TRUE);
286     $options['tty']                = array('hidden' => TRUE, 'description' => "Force allocation of tty for remote commands", 'never-propagate' => TRUE);
287     $options['quiet']               = array('short-form' => 'q', 'description' => 'Suppress non-error messages.');
288     $options['include']             = array('short-form' => 'i', 'short-has-arg' => TRUE, 'context' => 'DRUSH_INCLUDE', 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of additional directory paths to search for drush commands.", 'example-value' => '/path/dir');
289     $options['exclude']             = array('propagate-cli-value' => TRUE, 'never-post' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of files and directory paths to exclude from consideration when searching for drush commandfiles.", 'example-value' => '/path/dir');
290     $options['config']              = array('short-form' => 'c', 'short-has-arg' => TRUE, 'context' => 'DRUSH_CONFIG', 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "Specify an additional config file to load. See example.drushrc.php.", 'example-value' => '/path/file');
291     $options['user']                = array('short-form' => 'u', 'short-has-arg' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specify a Drupal user to login with. May be a name or a number.", 'example-value' => 'name_or_number');
292     $options['backend']             = array('short-form' => 'b', 'never-propagate' => TRUE, 'description' => "Hide all output and return structured data.");
293     $options['choice']              = array('description' => "Provide an answer to a multiple-choice prompt.", 'example-value' => 'number');
294     $options['variables']           = array('description' => "Comma delimited list of name=value pairs. These values take precedence even over settings.php variable overrides.", 'example-value' => 'foo=bar,baz=yaz');
295     $options['search-depth']        = array('description' => "Control the depth that drush will search for alias files.", 'example-value' => 'number');
296     $options['ignored-modules']     = array('description' => "Exclude some modules from consideration when searching for drush command files.", 'example-value' => 'token,views');
297     $options['no-label']            = array('description' => "Remove the site label that drush includes in multi-site command output (e.g. `drush @site1,@site2 status`).");
298     $options['label-separator']     = array('description' => "Specify the separator to use in multi-site command output (e.g. `drush @sites pm-list --label-separator=',' --format=csv`).");
299     $options['nocolor']             = array('context' => 'DRUSH_NOCOLOR', 'propagate-cli-value' => TRUE, 'description' => "Suppress color highlighting on log messages.");
300     $options['show-passwords']      = array('description' => "Show database passwords in commands that display connection information.");
301     $options['show-invoke']         = array('description' => "Show all function names which could have been called for the current command. See drush_invoke().");
302     $options['watchdog']            = array('description' => "Control logging of Drupal's watchdog() to drush log. Recognized values are 'log', 'print', 'disabled'. Defaults to log. 'print' shows calls to admin but does not add them to the log.", 'example-value' => 'print');
303     $options['cache-default-class'] = array('description' => "A cache backend class that implements CacheInterface. Defaults to JSONCache.", 'example-value' => 'JSONCache');
304     $options['cache-class-<bin>']   = array('description' => "A cache backend class that implements CacheInterface to use for a specific cache bin.", 'example-value' => 'className');
305     $options['early']               = array('description' => "Include a file (with relative or full path) and call the drush_early_hook() function (where 'hook' is the filename). The function is called pre-bootstrap and offers an opportunity to alter the drush bootstrap environment or process (returning FALSE from the function will continue the bootstrap), or return output very rapidly (e.g. from caches). See includes/complete.inc for an example.");
306     $options['alias-path']          = array('context' => 'ALIAS_PATH', 'local-context-only' => TRUE, 'merge-pathlist' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specifies the list of paths where drush will search for alias files.", 'example-value' => '/path/alias1:/path/alias2');
307     $options['backup-location']     = array('description' => "Specifies the directory where drush will store backups.", 'example-value' => '/path/to/dir');
308     $options['confirm-rollback']    = array('description' => 'Wait for confirmation before doing a rollback when something goes wrong.');
309     $options['complete-debug']      = array('hidden' => TRUE, 'description' => "Turn on debug mode forf completion code");
310     $options['php-options']         = array('description' => "Options to pass to `php` when running drush.  Only effective when using the drush.launcher script.", 'never-propagate' => TRUE, 'example-value' => '-d error_reporting="E_ALL"');
311     $options['halt-on-error']       = array('propagate-cli-value' => TRUE, 'description' => "Controls error handling of recoverable errors (E_RECOVERABLE_ERROR). --halt-on-error=1: Execution is halted. --halt-on-error=0: Drush execution continues");
312     $options['deferred-sanitization'] = array('hidden' => TRUE, 'description' => "Defer calculating the sanitization operations until after the database has been copied. This is done automatically if the source database is remote.");
313     $options['remote-host']         = array('hidden' => TRUE, 'description' => 'Remote site to execute drush command on. Managed by site alias.');
314     $options['remote-user']         = array('hidden' => TRUE, 'description' => 'User account to use with a remote drush command. Managed by site alias.');
315     $options['remote-os']           = array('hidden' => TRUE, 'description' => 'The operating system used on the remote host. Managed by site alias.');
316     $options['site-list']           = array('hidden' => TRUE, 'description' => 'List of sites to run commands on. Managed by site alias.');
317     $options['reserve-margin']      = array('hidden' => TRUE, 'description' => 'Remove columns from formatted opions. Managed by multi-site command handling.');
318     $options['strict']              = array('propagate' => TRUE, 'description' => 'Return an error on unrecognized options. --strict=0: Allow unrecognized options.  --strict=2: Also return an error on any "warning" log messages.  Optional.  Default is 1.');
319     $options['command-specific']    = array('hidden' => TRUE, 'merge-associative' => TRUE, 'description' => 'Command-specific options.');
320     $options['site-aliases']        = array('hidden' => TRUE, 'merge-associative' => TRUE, 'description' => 'List of site aliases.');
321     $options['shell-aliases']       = array('hidden' => TRUE, 'merge' => TRUE, 'never-propagate' => TRUE, 'description' => 'List of shell aliases.');
322     $options['path-aliases']        = array('hidden' => TRUE, 'never-propagate' => TRUE, 'description' => 'Path aliases from site alias.');
323     $options['ssh-options']         = array('never-propagate' => TRUE, 'description' => 'A string of extra options that will be passed to the ssh command', 'example-value' => '-p 100');
324     $options['editor']              = array('never-propagate' => TRUE, 'description' => 'A string of bash which launches user\'s preferred text editor. Defaults to ${VISUAL-${EDITOR-vi}}.', 'example-value' => 'vi');
325     $options['bg']                  = array('never-propagate' => TRUE, 'description' => 'Run editor in the background. Does not work with editors such as `vi` that run in the terminal. Supresses config-import at the end.');
326     $options['db-url']              = array('hidden' => TRUE, 'description' => 'A Drupal 6 style database URL. Used by various commands.', 'example-value' => 'mysql://root:pass@127.0.0.1/db');
327     $options['drush-coverage']      = array('hidden' => TRUE, 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'description' => 'File to save code coverage data into.');
328     $options['redirect-port']       = array('hidden' => TRUE, 'never-propagate' => TRUE, 'description' => 'Used by the user-login command to specify the redirect port on the local machine; it therefore would not do to pass this to the remote machines.');
329     $options['cache-clear']         = array('propagate' => TRUE, 'description' => 'If 0, Drush skips normal cache clearing; the caller should then clear if needed.', 'example-value' => '0', );
330     $options['local']               = array('propagate' => TRUE, 'description' => 'Don\'t look in global locations for commandfiles, config, and site aliases');
331     $options['ignored-directories'] = array('propagate' => TRUE, 'description' => "Exclude directories when searching for files in drush_scan_directory().", 'example-value' => 'node_modules,bower_components');
332
333     $command = array(
334         'options' => $options,
335         '#brief' => FALSE,
336       ) + drush_command_defaults('global-options', 'global_options', __FILE__);
337     drush_command_invoke_all_ref('drush_help_alter', $command);
338
339     $options = $command['options'];
340   }
341   return $options;
342 }
343
344 /**
345  * Exits with a message. In general, you should use drush_set_error() instead of
346  * this function. That lets drush proceed with other tasks.
347  * TODO: Exit with a correct status code.
348  */
349 function drush_die($msg = NULL, $status = NULL) {
350   die($msg ? "drush: $msg\n" : '');
351 }
352
353 /**
354  * Check to see if the provided line is a "#!/usr/bin/env drush"
355  * "shebang" script line.
356  */
357 function _drush_is_drush_shebang_line($line) {
358   return ((substr($line,0,2) == '#!') && (strstr($line, 'drush') !== FALSE));
359 }
360
361 /**
362  * Check to see if the provided script file is a "#!/usr/bin/env drush"
363  * "shebang" script line.
364  */
365 function _drush_is_drush_shebang_script($script_filename) {
366   $result = FALSE;
367
368   if (file_exists($script_filename)) {
369     $fp = fopen($script_filename, "r");
370     if ($fp !== FALSE) {
371       $line = fgets($fp);
372       $result = _drush_is_drush_shebang_line($line);
373       fclose($fp);
374     }
375   }
376
377   return $result;
378 }
379
380 /**
381  * @defgroup userinput Get input from the user.
382  * @{
383  */
384
385 /**
386  * Asks the user a basic yes/no question.
387  *
388  * @param string $msg
389  *   The question to ask.
390  * @param int $indent
391  *   The number of spaces to indent the message.
392  *
393  * @return bool
394  *   TRUE if the user enters "y" or FALSE if "n".
395  */
396 function drush_confirm($msg, $indent = 0) {
397   drush_print_prompt((string)$msg . " (y/n): ", $indent);
398
399   // Automatically accept confirmations if the --yes argument was supplied.
400   if (drush_get_context('DRUSH_AFFIRMATIVE')) {
401     drush_print("y");
402     return TRUE;
403   }
404   // Automatically cancel confirmations if the --no argument was supplied.
405   elseif (drush_get_context('DRUSH_NEGATIVE')) {
406     drush_print("n");
407     return FALSE;
408   }
409   // See http://drupal.org/node/499758 before changing this.
410   $stdin = fopen("php://stdin","r");
411
412   while ($line = fgets($stdin)) {
413     $line = trim($line);
414     if ($line == 'y') {
415       return TRUE;
416     }
417     if ($line == 'n') {
418       return FALSE;
419     }
420     drush_print_prompt((string)$msg . " (y/n): ", $indent);
421   }
422 }
423
424 /**
425  * Ask the user to select an item from a list.
426  * From a provided associative array, drush_choice will
427  * display all of the questions, numbered from 1 to N,
428  * and return the item the user selected. "0" is always
429  * cancel; entering a blank line is also interpreted
430  * as cancelling.
431  *
432  * @param $options
433  *   A list of questions to display to the user.  The
434  *   KEYS of the array are the result codes to return to the
435  *   caller; the VALUES are the messages to display on
436  *   each line. Special keys of the form '-- something --' can be
437  *   provided as separator between choices groups. Separator keys
438  *    don't alter the numbering.
439  * @param $prompt
440  *   The message to display to the user prompting for input.
441  * @param $label
442  *   Controls the display of each line.  Defaults to
443  *   '!value', which displays the value of each item
444  *   in the $options array to the user.  Use '!key' to
445  *   display the key instead.  In some instances, it may
446  *   be useful to display both the key and the value; for
447  *   example, if the key is a user id and the value is the
448  *   user name, use '!value (uid=!key)'.
449  */
450 function drush_choice($options, $prompt = 'Enter a number.', $label = '!value', $widths = array()) {
451   drush_print(dt($prompt));
452
453   // Preflight so that all rows will be padded out to the same number of columns
454   $array_pad = 0;
455   foreach ($options as $key => $option) {
456     if (is_array($option) && (count($option) > $array_pad)) {
457       $array_pad = count($option);
458     }
459   }
460
461   $rows[] = array_pad(array('[0]', ':', 'Cancel'), $array_pad + 2, '');
462   $selection_number = 0;
463   foreach ($options as $key => $option) {
464     if ((substr($key, 0, 3) == '-- ') && (substr($key, -3) == ' --')) {
465       $rows[] = array_pad(array('', '', $option), $array_pad + 2, '');
466     }
467     else {
468       $selection_number++;
469       $row = array("[$selection_number]", ':');
470       if (is_array($option)) {
471         $row = array_merge($row, $option);
472       }
473       else {
474         $row[] = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
475       }
476       $rows[] = $row;
477       $selection_list[$selection_number] = $key;
478     }
479   }
480   drush_print_table($rows, FALSE, $widths);
481   drush_print_pipe(array_keys($options));
482
483   // If the user specified --choice, then make an
484   // automatic selection.  Cancel if the choice is
485   // not an available option.
486   if (($choice = drush_get_option('choice', FALSE)) !== FALSE) {
487     // First check to see if $choice is one of the symbolic options
488     if (array_key_exists($choice, $options)) {
489       return $choice;
490     }
491     // Next handle numeric selections
492     elseif (array_key_exists($choice, $selection_list)) {
493       return $selection_list[$choice];
494     }
495     return FALSE;
496   }
497
498   // If the user specified --no, then cancel; also avoid
499   // getting hung up waiting for user input in --pipe and
500   // backend modes.  If none of these apply, then wait,
501   // for user input and return the selected result.
502   if (!drush_get_context('DRUSH_NEGATIVE') && !drush_get_context('DRUSH_AFFIRMATIVE') && !drush_get_context('DRUSH_PIPE')) {
503     while ($line = trim(fgets(STDIN))) {
504       if (array_key_exists($line, $selection_list)) {
505         return $selection_list[$line];
506       }
507     }
508   }
509   // We will allow --yes to confirm input if there is only
510   // one choice; otherwise, --yes will cancel to avoid ambiguity
511   if (drush_get_context('DRUSH_AFFIRMATIVE')  && (count($options) == 1)) {
512     return $selection_list[1];
513   }
514   return FALSE;
515 }
516
517 /**
518  * Ask the user to select multiple items from a list.
519  * This is a wrapper around drush_choice, that repeats the selection process,
520  * allowing users to toggle a number of items in a list. The number of values
521  * that can be constrained by both min and max: the user will only be allowed
522  * finalize selection once the minimum number has been selected, and the oldest
523  * selected value will "drop off" the list, if they exceed the maximum number.
524  *
525  * @param $options
526  *   Same as drush_choice() (see above).
527  * @param $defaults
528  *   This can take 3 forms:
529  *   - FALSE: (Default) All options are unselected by default.
530  *   - TRUE: All options are selected by default.
531  *   - Array of $options keys to be selected by default.
532  * @param $prompt
533  *   Same as drush_choice() (see above).
534  * @param $label
535  *   Same as drush_choice() (see above).
536  * @param $mark
537  *   Controls how selected values are marked.  Defaults to '!value (selected)'.
538  * @param $min
539  *   Constraint on minimum number of selections. Defaults to zero. When fewer
540  *   options than this are selected, no final options will be available.
541  * @param $max
542  *   Constraint on minimum number of selections. Defaults to NULL (unlimited).
543  *   If the a new selection causes this value to be exceeded, the oldest
544  *   previously selected value is automatically unselected.
545  * @param $final_options
546  *   An array of additional options in the same format as $options.
547  *   When the minimum number of selections is met, this array is merged into the
548  *   array of options. If the user selects one of these values and the
549  *   selection process will complete (the key for the final option is included
550  *   in the return value). If this is an empty array (default), then a built in
551  *   final option of "Done" will be added to the available options (in this case
552  *   no additional keys are added to the return value).
553  */
554 function drush_choice_multiple($options, $defaults = FALSE, $prompt = 'Select some numbers.', $label = '!value', $mark = '!value (selected)', $min = 0, $max = NULL, $final_options = array()) {
555   $selections = array();
556   // Load default selections.
557   if (is_array($defaults)) {
558     $selections = $defaults;
559   }
560   elseif ($defaults === TRUE) {
561     $selections = array_keys($options);
562   }
563   $complete = FALSE;
564   $final_builtin = array();
565   if (empty($final_options)) {
566     $final_builtin['done'] = dt('Done');
567   }
568   $final_options_keys = array_keys($final_options);
569   while (TRUE) {
570     $current_options = $options;
571     // Mark selections.
572     foreach ($selections as $selection) {
573       $current_options[$selection] = dt($mark, array('!key' => $selection, '!value' => $options[$selection]));
574     }
575     // Add final options, if the minimum number of selections has been reached.
576     if (count($selections) >= $min) {
577       $current_options = array_merge($current_options, $final_options, $final_builtin);
578     }
579     $toggle = drush_choice($current_options, $prompt, $label);
580     if ($toggle === FALSE) {
581       return FALSE;
582     }
583     // Don't include the built in final option in the return value.
584     if (count($selections) >= $min && empty($final_options) && $toggle == 'done') {
585       return $selections;
586     }
587     // Toggle the selected value.
588     $item = array_search($toggle, $selections);
589     if ($item === FALSE) {
590       array_unshift($selections, $toggle);
591     }
592     else {
593       unset($selections[$item]);
594     }
595     // If the user selected one of the final options, return.
596     if (count($selections) >= $min && in_array($toggle, $final_options_keys)) {
597       return $selections;
598     }
599     // If the user selected too many options, drop the oldest selection.
600     if (isset($max) && count($selections) > $max) {
601       array_pop($selections);
602     }
603   }
604 }
605
606 /**
607  * Prompt the user for input
608  *
609  * The input can be anything that fits on a single line (not only y/n),
610  * so we can't use drush_confirm()
611  *
612  * @param $prompt
613  *   The text which is displayed to the user.
614  * @param $default
615  *   The default value of the input.
616  * @param $required
617  *   If TRUE, user may continue even when no value is in the input.
618  * @param $password
619  *   If TRUE, surpress printing of the input.
620  *
621  * @see drush_confirm()
622  */
623 function drush_prompt($prompt, $default = NULL, $required = TRUE, $password = FALSE) {
624   if (isset($default)) {
625     $prompt .= " [" . $default . "]";
626   }
627   $prompt .= ": ";
628
629   drush_print_prompt($prompt);
630
631   if (drush_get_context('DRUSH_AFFIRMATIVE')) {
632     return $default;
633   }
634
635   $stdin = fopen('php://stdin', 'r');
636
637   if ($password) drush_shell_exec("stty -echo");
638
639   stream_set_blocking($stdin, TRUE);
640   while (($line = fgets($stdin)) !== FALSE) {
641     $line = trim($line);
642     if ($line === "") {
643       $line = $default;
644     }
645     if ($line || !$required) {
646       break;
647     }
648     drush_print_prompt($prompt);
649   }
650   fclose($stdin);
651   if ($password) {
652     drush_shell_exec("stty echo");
653     print "\n";
654   }
655   return $line;
656 }
657
658 /**
659  * @} End of "defgroup userinput".
660  */
661
662 /**
663  * Calls a given function, passing through all arguments unchanged.
664  *
665  * This should be used when calling possibly mutative or destructive functions
666  * (e.g. unlink() and other file system functions) so that can be suppressed
667  * if the simulation mode is enabled.
668  *
669  * Important:  Call @see drush_op_system() to execute a shell command,
670  * or @see drush_shell_exec() to execute a shell command and capture the
671  * shell output.
672  *
673  * @param $callable
674  *   The name of the function. Any additional arguments are passed along.
675  * @return
676  *   The return value of the function, or TRUE if simulation mode is enabled.
677  *
678  */
679 function drush_op($callable) {
680   $args_printed = array();
681   $args = func_get_args();
682   array_shift($args); // Skip function name
683   foreach ($args as $arg) {
684     $args_printed[] = is_scalar($arg) ? $arg : (is_array($arg) ? 'Array' : 'Object');
685   }
686
687   if (!is_array($callable)) {
688     $callable_string = $callable;
689   }
690   else {
691     if (is_object($callable[0])) {
692       $callable_string = get_class($callable[0]) . '::' . $callable[1];
693     }
694     else {
695       $callable_string = implode('::', $callable);
696     }
697   }
698
699   // Special checking for drush_op('system')
700   if ($callable == 'system') {
701     drush_log(dt("Do not call drush_op('system'); use drush_op_system instead"), LogLevel::DEBUG);
702   }
703
704   if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
705     drush_log(sprintf("Calling %s(%s)", $callable_string, implode(", ", $args_printed)), LogLevel::DEBUG);
706   }
707
708   if (drush_get_context('DRUSH_SIMULATE')) {
709     return TRUE;
710   }
711
712   return drush_call_user_func_array($callable, $args);
713 }
714
715 /**
716  * Mimic cufa but still call function directly. See http://drupal.org/node/329012#comment-1260752
717  */
718 function drush_call_user_func_array($function, $args = array() ) {
719   if (is_array($function)) {
720     // $callable is a method so always use CUFA.
721     return call_user_func_array($function, $args);
722   }
723
724   switch (count($args)) {
725     case 0: return $function(); break;
726     case 1: return $function($args[0]); break;
727     case 2: return $function($args[0], $args[1]); break;
728     case 3: return $function($args[0], $args[1], $args[2]); break;
729     case 4: return $function($args[0], $args[1], $args[2], $args[3]); break;
730     case 5: return $function($args[0], $args[1], $args[2], $args[3], $args[4]); break;
731     case 6: return $function($args[0], $args[1], $args[2], $args[3], $args[4], $args[5]); break;
732     case 7: return $function($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6]); break;
733     case 8: return $function($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7]); break;
734     case 9: return $function($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7], $args[8]); break;
735     default: return call_user_func_array($function,$args);
736   }
737 }
738
739 /**
740  * Download a file using wget, curl or file_get_contents, or via download cache.
741  *
742  * @param string $url
743  *   The url of the file to download.
744  * @param string $destination
745  *   The name of the file to be saved, which may include the full path.
746  *   Optional, if omitted the filename will be extracted from the url and the
747  *   file downloaded to the current working directory (Drupal root if
748  *   bootstrapped).
749  * @param integer $cache_duration
750  *   The acceptable age of a cached file. If cached file is too old, a fetch
751  *   will occur and cache will be updated. Optional, if ommitted the file will
752  *   be fetched directly.
753  *
754  * @return string
755  *   The path to the downloaded file, or FALSE if the file could not be
756  *   downloaded.
757  */
758 function drush_download_file($url, $destination = FALSE, $cache_duration = 0) {
759   // Generate destination if omitted.
760   if (!$destination) {
761     $file = basename(current(explode('?', $url, 2)));
762     $destination = getcwd() . '/' . basename($file);
763   }
764
765   // Simply copy local files to the destination
766   if (!_drush_is_url($url)) {
767     return copy($url, $destination) ? $destination : FALSE;
768   }
769
770   if ($cache_duration !== 0 && $cache_file = drush_download_file_name($url)) {
771     // Check for cached, unexpired file.
772     if (file_exists($cache_file) && filectime($cache_file) > ($_SERVER['REQUEST_TIME']-$cache_duration)) {
773       drush_log(dt('!name retrieved from cache.', array('!name' => $cache_file)));
774     }
775     else {
776       if (_drush_download_file($url, $cache_file, TRUE)) {
777         // Cache was set just by downloading file to right location.
778       }
779       elseif (file_exists($cache_file)) {
780         drush_log(dt('!name retrieved from an expired cache since refresh failed.', array('!name' => $cache_file)), LogLevel::WARNING);
781       }
782       else {
783         $cache_file = FALSE;
784       }
785     }
786
787     if ($cache_file && copy($cache_file, $destination)) {
788       // Copy cached file to the destination
789       return $destination;
790     }
791   }
792   elseif ($return = _drush_download_file($url, $destination)) {
793     drush_register_file_for_deletion($return);
794     return $return;
795   }
796
797   // Unable to retrieve from cache nor download.
798   return FALSE;
799 }
800
801 /**
802  * Helper function to determine name of cached file.
803  */
804 function drush_download_file_name($url) {
805   if ($cache_dir = drush_directory_cache('download')) {
806     $cache_name = str_replace(array(':', '/', '?', '=', '\\'), '-', $url);
807     return $cache_dir . "/" . $cache_name;
808   }
809   else {
810     return FALSE;
811   }
812 }
813
814 /**
815  * Check whether the given path is just a url or a local path
816  * @param string $url
817  * @return boolean
818  *   TRUE if the path does not contain a schema:// part.
819  */
820 function _drush_is_url($url) {
821   return parse_url($url, PHP_URL_SCHEME) !== NULL;
822 }
823
824 /**
825  * Download a file using wget, curl or file_get_contents. Does not use download
826  * cache.
827  *
828  * @param string $url
829  *   The url of the file to download.
830  * @param string $destination
831  *   The name of the file to be saved, which may include the full path.
832  * @param boolean $overwrite
833  *   Overwrite any file thats already at the destination.
834  * @return string
835  *   The path to the downloaded file, or FALSE if the file could not be
836  *   downloaded.
837  */
838 function _drush_download_file($url, $destination, $overwrite = TRUE) {
839   static $use_wget;
840   if ($use_wget === NULL) {
841     $use_wget = drush_shell_exec('wget --version');
842   }
843
844   $destination_tmp = drush_tempnam('download_file');
845   if ($use_wget) {
846     drush_shell_exec("wget -q --timeout=30 -O %s %s", $destination_tmp, $url);
847   }
848   else {
849     // Force TLS1+ as per https://github.com/drush-ops/drush/issues/894.
850     drush_shell_exec("curl --tlsv1 --fail -s -L --connect-timeout 30 -o %s %s", $destination_tmp, $url);
851   }
852   if (!drush_file_not_empty($destination_tmp) && $file = @file_get_contents($url)) {
853     @file_put_contents($destination_tmp, $file);
854   }
855   if (!drush_file_not_empty($destination_tmp)) {
856     // Download failed.
857     return FALSE;
858   }
859
860   drush_move_dir($destination_tmp, $destination, $overwrite);
861   return $destination;
862 }
863
864 /**
865  * Determines the MIME content type of the specified file.
866  *
867  * The power of this function depends on whether the PHP installation
868  * has either mime_content_type() or finfo installed -- if not, only tar,
869  * gz, zip and bzip2 types can be detected.
870  *
871  * If mime type can't be obtained, an error will be set.
872  *
873  * @return mixed
874  *   The MIME content type of the file or FALSE.
875  */
876 function drush_mime_content_type($filename) {
877   $content_type = drush_attempt_mime_content_type($filename);
878   if ($content_type) {
879     drush_log(dt('Mime type for !file is !mt', array('!file' => $filename, '!mt' => $content_type)), LogLevel::NOTICE);
880     return $content_type;
881   }
882   return drush_set_error('MIME_CONTENT_TYPE_UNKNOWN', dt('Unable to determine mime type for !file.', array('!file' => $filename)));
883 }
884
885 /**
886  * Works like drush_mime_content_type, but does not set an error
887  * if the type is unknown.
888  */
889 function drush_attempt_mime_content_type($filename) {
890   $content_type = FALSE;
891   if (class_exists('finfo')) {
892     $finfo = new finfo(FILEINFO_MIME_TYPE);
893     $content_type = $finfo->file($filename);
894     if ($content_type == 'application/octet-stream') {
895       drush_log(dt('Mime type for !file is application/octet-stream.', array('!file' => $filename)), LogLevel::DEBUG);
896       $content_type = FALSE;
897     }
898   }
899   // If apache is configured in such a way that all files are considered
900   // octet-stream (e.g with mod_mime_magic and an http conf that's serving all
901   // archives as octet-stream for other reasons) we'll detect mime types on our
902   //  own by examing the file's magic header bytes.
903   if (!$content_type) {
904     drush_log(dt('Examining !file headers.', array('!file' => $filename)), LogLevel::DEBUG);
905     if ($file = fopen($filename, 'rb')) {
906       $first = fread($file, 2);
907       fclose($file);
908
909       if ($first !== FALSE) {
910         // Interpret the two bytes as a little endian 16-bit unsigned int.
911         $data = unpack('v', $first);
912         switch ($data[1]) {
913           case 0x8b1f:
914             // First two bytes of gzip files are 0x1f, 0x8b (little-endian).
915             // See http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
916             $content_type = 'application/x-gzip';
917             break;
918
919           case 0x4b50:
920             // First two bytes of zip files are 0x50, 0x4b ('PK') (little-endian).
921             // See http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
922             $content_type = 'application/zip';
923             break;
924
925           case 0x5a42:
926             // First two bytes of bzip2 files are 0x5a, 0x42 ('BZ') (big-endian).
927             // See http://en.wikipedia.org/wiki/Bzip2#File_format
928             $content_type = 'application/x-bzip2';
929             break;
930
931           default:
932             drush_log(dt('Unable to determine mime type from header bytes 0x!hex of !file.', array('!hex' => dechex($data[1]), '!file' => $filename,), LogLevel::DEBUG));
933         }
934       }
935       else {
936         drush_log(dt('Unable to read !file.', array('!file' => $filename)), LogLevel::WARNING);
937       }
938     }
939     else {
940       drush_log(dt('Unable to open !file.', array('!file' => $filename)), LogLevel::WARNING);
941     }
942   }
943
944   // 3. Lastly if above methods didn't work, try to guess the mime type from
945   // the file extension. This is useful if the file has no identificable magic
946   // header bytes (for example tarballs).
947   if (!$content_type) {
948     drush_log(dt('Examining !file extension.', array('!file' => $filename)), LogLevel::DEBUG);
949
950     // Remove querystring from the filename, if present.
951     $filename = basename(current(explode('?', $filename, 2)));
952     $extension_mimetype = array(
953       '.tar'     => 'application/x-tar',
954       '.sql'     => 'application/octet-stream',
955     );
956     foreach ($extension_mimetype as $extension => $ct) {
957       if (substr($filename, -strlen($extension)) === $extension) {
958         $content_type = $ct;
959         break;
960       }
961     }
962   }
963   return $content_type;
964 }
965
966 /**
967  * Check whether a file is a supported tarball.
968  *
969  * @return mixed
970  *   The file content type if it's a tarball. FALSE otherwise.
971  */
972 function drush_file_is_tarball($path) {
973   $content_type = drush_attempt_mime_content_type($path);
974   $supported = array(
975     'application/x-bzip2',
976     'application/x-gzip',
977     'application/x-tar',
978     'application/x-zip',
979     'application/zip',
980   );
981   if (in_array($content_type, $supported)) {
982     return $content_type;
983   }
984   return FALSE;
985 }
986
987 /**
988  * Extract a tarball.
989  *
990  * @param string $path
991  *   Path to the archive to be extracted.
992  * @param string $destination
993  *   The destination directory the tarball should be extracted into.
994  *   Optional, if ommitted the tarball directory will be used as destination.
995  * @param boolean $listing
996  *   If TRUE, a listing of the tar contents will be returned on success.
997  * @param string $tar_extra_options
998  *   Extra options to be passed to the tar command.
999  *
1000  * @return mixed
1001  *   TRUE on success, FALSE on fail. If $listing is TRUE, a file listing of the
1002  *   tarball is returned if the extraction reported success, instead of TRUE.
1003  */
1004 function drush_tarball_extract($path, $destination = FALSE, $listing = FALSE, $tar_extra_options = '') {
1005   // Check if tarball is supported.
1006   if (!($mimetype = drush_file_is_tarball($path))) {
1007     return drush_set_error('TARBALL_EXTRACT_UNKNOWN_FORMAT', dt('Unable to extract !path. Unknown archive format.', array('!path' => $path)));
1008   }
1009
1010   // Check if destination is valid.
1011   if (!$destination) {
1012     $destination = dirname($path);
1013   }
1014   if (!drush_mkdir($destination)) {
1015     // drush_mkdir already set an error.
1016     return FALSE;
1017   }
1018
1019   // Perform the extraction of a zip file.
1020   if (($mimetype == 'application/zip') || ($mimetype == 'application/x-zip')) {
1021     $return = drush_shell_cd_and_exec(dirname($path), "unzip %s -d %s", $path, $destination);
1022     if (!$return) {
1023       return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to unzip !filename.', array('!filename' => $path)));
1024     }
1025     if ($listing) {
1026       // unzip prefixes the file listing output with a header line,
1027       // and prefixes each line with a verb representing the compression type.
1028       $output = drush_shell_exec_output();
1029       // Remove the header line.
1030       array_shift($output);
1031       // Remove the prefix verb from each line.
1032       $output = array_map(
1033         function ($str) use ($destination) {
1034           return substr($str, strpos($str, ":") + 3 + strlen($destination));
1035         },
1036         $output
1037       );
1038       // Remove any remaining blank lines.
1039       $return = array_filter(
1040         $output,
1041         function ($str) {
1042           return $str != "";
1043         }
1044       );
1045     }
1046   }
1047   // Otherwise we have a possibly-compressed Tar file.
1048   // If we are not on Windows, then try to do "tar" in a single operation.
1049   elseif (!drush_is_windows()) {
1050     $tar = drush_get_tar_executable();
1051     $tar_compression_flag = '';
1052     if ($mimetype == 'application/x-gzip') {
1053       $tar_compression_flag = 'z';
1054     }
1055     elseif ($mimetype == 'application/x-bzip2') {
1056       $tar_compression_flag = 'j';
1057     }
1058
1059     $return = drush_shell_cd_and_exec(dirname($path), "$tar {$tar_extra_options} -C %s -x%sf %s", $destination, $tar_compression_flag, basename($path));
1060     if (!$return) {
1061       return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to untar !filename.', array('!filename' => $path)));
1062     }
1063     if ($listing) {
1064       // We use a separate tar -tf instead of -xvf above because
1065       // the output is not the same in Mac.
1066       drush_shell_cd_and_exec(dirname($path), "$tar -t%sf %s", $tar_compression_flag, basename($path));
1067       $return = drush_shell_exec_output();
1068     }
1069   }
1070   // In windows, do the extraction by its primitive steps.
1071   else {
1072     // 1. copy the source tarball to the destination directory. Rename to a
1073     // temp name in case the destination directory == dirname($path)
1074     $tmpfile = drush_tempnam(basename($path), $destination);
1075     drush_copy_dir($path, $tmpfile, FILE_EXISTS_OVERWRITE);
1076
1077     // 2. uncompress the tarball, if compressed.
1078     if (($mimetype == 'application/x-gzip') || ($mimetype == 'application/x-bzip2')) {
1079       if ($mimetype == 'application/x-gzip') {
1080         $compressed = $tmpfile . '.gz';
1081         // We used to use gzip --decompress in --stdout > out, but the output
1082         // redirection sometimes failed on Windows for some binary output.
1083         $command = 'gzip --decompress %s';
1084       }
1085       elseif ($mimetype == 'application/x-bzip2') {
1086         $compressed = $tmpfile . '.bz2';
1087         $command = 'bzip2 --decompress %s';
1088       }
1089       drush_op('rename', $tmpfile, $compressed);
1090       $return = drush_shell_cd_and_exec(dirname($compressed), $command, $compressed);
1091       if (!$return || !file_exists($tmpfile)) {
1092         return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to decompress !filename.', array('!filename' => $compressed)));
1093       }
1094     }
1095
1096     // 3. Untar.
1097     $tar = drush_get_tar_executable();
1098     $return = drush_shell_cd_and_exec(dirname($tmpfile), "$tar {$tar_extra_options} -xvf %s", basename($tmpfile));
1099     if (!$return) {
1100       return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to untar !filename.', array('!filename' => $tmpfile)));
1101     }
1102     if ($listing) {
1103       $return = drush_shell_exec_output();
1104       // Cut off the 'x ' prefix for the each line of the tar output
1105       // See http://drupal.org/node/1775520
1106       foreach($return as &$line) {
1107         if(strpos($line, "x ") === 0)
1108           $line = substr($line, 2);
1109       }
1110     }
1111
1112     // Remove the temporary file so the md5 hash is accurate.
1113     unlink($tmpfile);
1114   }
1115
1116   return $return;
1117 }
1118
1119 /**
1120  * @defgroup commandprocessing Command processing functions.
1121  * @{
1122  *
1123  * These functions manage command processing by the
1124  * main function in drush.php.
1125  */
1126
1127 /**
1128  * Determine whether or not an argument should be removed from the
1129  * DRUSH_COMMAND_ARGS context.  This method is used when a Drush
1130  * command has set the 'strict-option-handling' flag indicating
1131  * that it will pass through all commandline arguments and any
1132  * additional options (not known to Drush) to some shell command.
1133  *
1134  * Take as an example the following call to core-rsync:
1135  *
1136  *   drush --yes core-rsync -v -az --exclude-paths='.git:.svn' local-files/ @site:%files
1137  *
1138  * In this instance:
1139  *
1140  *   --yes is a global Drush option
1141  *
1142  *   -v is an rsync option.  It will make rsync run in verbose mode,
1143  *     but will not make Drush run in verbose mode due to the fact that
1144  *     core-rsync sets the 'strict-option-handling' flag.
1145  *
1146  *   --exclude-paths is a local Drush option.  It will be converted by
1147  *     Drush into --exclude='.git' and --exclude='.svn', and then passed
1148  *     on to the rsync command.
1149  *
1150  * The parameter $arg passed to this function is one of the elements
1151  * of DRUSH_COMMAND_ARGS.  It will have values such as:
1152  *   -v
1153  *   -az
1154  *   --exclude-paths='.git:.svn'
1155  *   local-files/
1156  *   @site:%files
1157  *
1158  * Our job in this function is to determine if $arg should be removed
1159  * by virtue of appearing in $removal_list.  $removal_list is an array
1160  * that will contain values such as 'exclude-paths'.  Both the key and
1161  * the value of $removal_list is the same.
1162  */
1163 function _drush_should_remove_command_arg($arg, $removal_list) {
1164   foreach ($removal_list as $candidate) {
1165     if (($arg == "-$candidate") ||
1166       ($arg == "--$candidate") ||
1167       (substr($arg,0,strlen($candidate)+3) == "--$candidate=") ) {
1168       return TRUE;
1169     }
1170   }
1171   return FALSE;
1172 }
1173
1174 /**
1175  * Redispatch the specified command using the same
1176  * options that were passed to this invocation of drush.
1177  */
1178 function drush_do_command_redispatch($command, $args = array(), $remote_host = NULL, $remote_user = NULL, $drush_path = NULL, $user_interactive = FALSE, $aditional_options = array()) {
1179   $additional_global_options = array();
1180   $command_options = drush_redispatch_get_options();
1181   $command_options = $aditional_options + $command_options;
1182   if (is_array($command)) {
1183     $command_name = $command['command'];
1184     // If we are executing a remote command that uses strict option handling,
1185     // then mark all of the options in the alias context as global, so that they
1186     // will appear before the command name.
1187     if (!empty($command['strict-option-handling'])) {
1188       foreach(drush_get_context('alias') as $alias_key => $alias_value) {
1189         if (array_key_exists($alias_key, $command_options) && !array_key_exists($alias_key, $command['options'])) {
1190           $additional_global_options[$alias_key] = $alias_value;
1191         }
1192       }
1193     }
1194   }
1195   else {
1196     $command_name = $command;
1197   }
1198   // If the path to drush was supplied, then use it to invoke the new command.
1199   if ($drush_path == NULL) {
1200     $drush_path = drush_get_option('drush-script');
1201     if (!isset($drush_path)) {
1202       $drush_folder = drush_get_option('drush');
1203       if (isset($drush)) {
1204         $drush_path = $drush_folder . '/drush';
1205       }
1206     }
1207   }
1208   $backend_options = array('drush-script' => $drush_path, 'remote-host' => $remote_host, 'remote-user' => $remote_user, 'integrate' => TRUE, 'additional-global-options' => $additional_global_options);
1209   // Set the tty if requested, if the command necessitates it,
1210   // or if the user explicitly asks for interactive mode, but
1211   // not if interactive mode is forced.  tty implies interactive
1212   if (drush_get_option('tty') || $user_interactive || !empty($command['remote-tty'])) {
1213     $backend_options['#tty'] = TRUE;
1214     $backend_options['interactive'] = TRUE;
1215   }
1216   elseif (drush_get_option('interactive')) {
1217     $backend_options['interactive'] = TRUE;
1218   }
1219
1220   // Run the command in a new process.
1221   drush_log(dt('Begin redispatch via drush_invoke_process().'));
1222   $values = drush_invoke_process('@self', $command_name, $args, $command_options, $backend_options);
1223   drush_log(dt('End redispatch via drush_invoke_process().'));
1224
1225   return $values;
1226 }
1227
1228
1229 /**
1230  * @} End of "defgroup commandprocessing".
1231  */
1232
1233 /**
1234  * @defgroup logging Logging information to be provided as output.
1235  * @{
1236  *
1237  * These functions are primarily for diagnostic purposes, but also provide an overview of tasks that were taken
1238  * by drush.
1239  */
1240
1241 /**
1242  * Add a log message to the log history.
1243  *
1244  * This function calls the callback stored in the 'DRUSH_LOG_CALLBACK' context with
1245  * the resulting entry at the end of execution.
1246  *
1247  * This allows you to replace it with custom logging implementations if needed,
1248  * such as logging to a file or logging to a database (drupal or otherwise).
1249  *
1250  * The default callback is the Drush\Log\Logger class with prints the messages
1251  * to the shell.
1252  *
1253  * @param message
1254  *   String containing the message to be logged.
1255  * @param type
1256  *   The type of message to be logged. Common types are 'warning', 'error', 'success' and 'notice'.
1257  *   A type of 'ok' or 'success' can also be supplied to flag something that worked.
1258  *   If you want your log messages to print to screen without the user entering
1259  *   a -v or --verbose flag, use type 'ok', this prints log messages out to
1260  *   STDERR, which prints to screen (unless you have redirected it). All other
1261  *   types of messages will be assumed to be notices.
1262  */
1263 function drush_log($message, $type = LogLevel::NOTICE, $error = null) {
1264   $entry = array(
1265     'type' => $type,
1266     'message' => $message,
1267     'timestamp' => microtime(TRUE),
1268     'memory' => memory_get_usage(),
1269   );
1270   $entry['error'] = $error;
1271
1272   return _drush_log($entry);
1273 }
1274
1275 /**
1276  * Future: add some sort of dependency injection to Drush.
1277  */
1278 function _drush_create_default_logger() {
1279   $drush_logger = new Logger();
1280   drush_set_context('DRUSH_LOGGER', $drush_logger);
1281   drush_set_context('DRUSH_LOG_CALLBACK', $drush_logger);
1282 }
1283
1284 /**
1285  * Call the default logger, or the user's log callback, as
1286  * appropriate.
1287  */
1288 function _drush_log($entry) {
1289   $callback = drush_get_context('DRUSH_LOG_CALLBACK');
1290
1291   if ($callback instanceof LoggerInterface) {
1292     _drush_log_to_logger($callback, $entry);
1293   }
1294   elseif ($callback) {
1295     $log =& drush_get_context('DRUSH_LOG', array());
1296     $log[] = $entry;
1297     drush_backend_packet('log', $entry);
1298     return $callback($entry);
1299   }
1300 }
1301
1302 // Maintain compatibility with extensions that hook into
1303 // DRUSH_LOG_CALLBACK (e.g. drush_ctex_bonus)
1304 function _drush_print_log($entry) {
1305   $drush_logger = drush_get_context('DRUSH_LOGGER');
1306   if ($drush_logger) {
1307     _drush_log_to_logger($drush_logger, $entry);
1308   }
1309 }
1310
1311 function _drush_log_to_logger($logger, $entry) {
1312     $context = $entry;
1313     $log_level = $entry['type'];
1314     $message = $entry['message'];
1315     unset($entry['type']);
1316     unset($entry['message']);
1317
1318     $logger->log($log_level, $message, $context);
1319 }
1320
1321 function drush_log_has_errors($types = array(LogLevel::WARNING, LogLevel::ERROR, LogLevel::FAILED)) {
1322   $log =& drush_get_context('DRUSH_LOG', array());
1323   foreach ($log as $entry) {
1324     if (in_array($entry['type'], $types)) {
1325       return TRUE;
1326     }
1327   }
1328   return FALSE;
1329 }
1330
1331 /**
1332  * Backend command callback. Add a log message to the log history.
1333  *
1334  * @param entry
1335  *   The log entry.
1336  */
1337 function drush_backend_packet_log($entry, $backend_options) {
1338   if (!$backend_options['log']) {
1339     return;
1340   }
1341   if (!is_string($entry['message'])) {
1342     $entry['message'] = implode("\n", (array)$entry['message']);
1343   }
1344   $entry['message'] = $entry['message'];
1345   if (array_key_exists('#output-label', $backend_options)) {
1346     $entry['message'] = $backend_options['#output-label'] . $entry['message'];
1347   }
1348
1349   // If integrate is FALSE, then log messages are stored in DRUSH_LOG,
1350   // but are -not- printed to the console.
1351   if ($backend_options['integrate']) {
1352     _drush_log($entry);
1353   }
1354   else {
1355     $log =& drush_get_context('DRUSH_LOG', array());
1356     $log[] = $entry;
1357     // Yes, this looks odd, but we might in fact be a backend command
1358     // that ran another backend command.
1359     drush_backend_packet('log', $entry);
1360   }
1361 }
1362
1363 /**
1364  * Retrieve the log messages from the log history
1365  *
1366  * @return
1367  *   Entire log history
1368  */
1369 function drush_get_log() {
1370   return drush_get_context('DRUSH_LOG', array());
1371 }
1372
1373 /**
1374  * Run print_r on a variable and log the output.
1375  */
1376 function dlm($object) {
1377   drush_log(print_r($object, TRUE));
1378 }
1379
1380 /**
1381  * Display the pipe output for the current request.
1382  */
1383 function drush_pipe_output() {
1384   $pipe = drush_get_context('DRUSH_PIPE_BUFFER');
1385   if (!empty($pipe)) {
1386     drush_print_r($pipe, NULL, FALSE);
1387   }
1388 }
1389
1390 // Print all timers for the request.
1391 function drush_print_timers() {
1392   global $timers;
1393   $temparray = array();
1394   foreach ((array)$timers as $name => $timerec) {
1395     // We have to use timer_read() for active timers, and check the record for others
1396     if (isset($timerec['start'])) {
1397       $temparray[$name] = timer_read($name);
1398     }
1399     else {
1400       $temparray[$name] = $timerec['time'];
1401     }
1402   }
1403   // Go no farther if there were no timers
1404   if (count($temparray) > 0) {
1405     // Put the highest cumulative times first
1406     arsort($temparray);
1407     $table = array();
1408     $table[] = array('Timer', 'Cum (sec)', 'Count', 'Avg (msec)');
1409     foreach ($temparray as $name => $time) {
1410       $cum = round($time/1000, 3);
1411       $count = $timers[$name]['count'];
1412       if ($count > 0) {
1413         $avg = round($time/$count, 3);
1414       }
1415       else {
1416         $avg = 'N/A';
1417       }
1418       $table[] = array($name, $cum, $count, $avg);
1419     }
1420     drush_print_table($table, TRUE, array(), STDERR);
1421   }
1422 }
1423
1424 /**
1425  * Turn drupal_set_message errors into drush_log errors
1426  */
1427 function _drush_log_drupal_messages() {
1428   if (function_exists('drupal_get_messages')) {
1429
1430     $messages = drupal_get_messages(NULL, TRUE);
1431
1432     if (array_key_exists('error', $messages)) {
1433       //Drupal message errors.
1434       foreach ((array) $messages['error'] as $error) {
1435         $error = strip_tags($error);
1436         $header = preg_match('/^warning: Cannot modify header information - headers already sent by /i', $error);
1437         $session = preg_match('/^warning: session_start\(\): Cannot send session /i', $error);
1438         if ($header || $session) {
1439           //These are special cases for an unavoidable warnings
1440           //that are generated by generating output before Drupal is bootstrapped.
1441           //or sending a session cookie (seems to affect d7 only?)
1442           //Simply ignore them.
1443           continue;
1444         }
1445         elseif (preg_match('/^warning:/i', $error)) {
1446           drush_log(preg_replace('/^warning: /i', '', $error), LogLevel::WARNING);
1447         }
1448         elseif (preg_match('/^notice:/i', $error)) {
1449           drush_log(preg_replace('/^notice: /i', '', $error), LogLevel::NOTICE);
1450         }
1451         elseif (preg_match('/^user warning:/i', $error)) {
1452           // This is a special case. PHP logs sql errors as 'User Warnings', not errors.
1453           drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', preg_replace('/^user warning: /i', '', $error));
1454         }
1455         else {
1456           drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', $error);
1457         }
1458       }
1459     }
1460     unset($messages['error']);
1461
1462     // Log non-error messages.
1463     foreach ($messages as $type => $items) {
1464       foreach ($items as $item) {
1465         drush_log(strip_tags($item), $type);
1466       }
1467     }
1468   }
1469 }
1470
1471 // Copy of format_size() in Drupal.
1472 function drush_format_size($size) {
1473   if ($size < DRUSH_KILOBYTE) {
1474     // format_plural() not always available.
1475     return dt('@count bytes', array('@count' => $size));
1476   }
1477   else {
1478     $size = $size / DRUSH_KILOBYTE; // Convert bytes to kilobytes.
1479     $units = array(
1480       dt('@size KB', array()),
1481       dt('@size MB', array()),
1482       dt('@size GB', array()),
1483       dt('@size TB', array()),
1484       dt('@size PB', array()),
1485       dt('@size EB', array()),
1486       dt('@size ZB', array()),
1487       dt('@size YB', array()),
1488     );
1489     foreach ($units as $unit) {
1490       if (round($size, 2) >= DRUSH_KILOBYTE) {
1491         $size = $size / DRUSH_KILOBYTE;
1492       }
1493       else {
1494         break;
1495       }
1496     }
1497     return str_replace('@size', round($size, 2), $unit);
1498   }
1499 }
1500
1501 /**
1502  * @} End of "defgroup logging".
1503  */
1504
1505 /**
1506  * @defgroup errorhandling Managing errors that occur in the Drush framework.
1507  * @{
1508  * Functions that manage the current error status of the Drush framework.
1509  *
1510  * These functions operate by maintaining a static variable that is a equal to the constant DRUSH_FRAMEWORK_ERROR if an
1511  * error has occurred.
1512  * This error code is returned at the end of program execution, and provide the shell or calling application with
1513  * more information on how to diagnose any problems that may have occurred.
1514  */
1515
1516 /**
1517  * Set an error code for the error handling system.
1518  *
1519  * @param \Drupal\Component\Render\MarkupInterface|string $error
1520  *   A text string identifying the type of error.
1521  * @param null|string $message
1522  *   Optional. Error message to be logged. If no message is specified,
1523  *   hook_drush_help will be consulted, using a key of 'error:MY_ERROR_STRING'.
1524  * @param null|string $output_label
1525  *   Optional. Label to prepend to the error message.
1526  *
1527  * @return bool
1528  *   Always returns FALSE, to allow returning false in the calling functions,
1529  *   such as <code>return drush_set_error('DRUSH_FRAMEWORK_ERROR')</code>.
1530  */
1531 function drush_set_error($error, $message = null, $output_label = "") {
1532   $error_code =& drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
1533   $error_code = DRUSH_FRAMEWORK_ERROR;
1534
1535   $error_log =& drush_get_context('DRUSH_ERROR_LOG', array());
1536
1537   if (is_numeric($error)) {
1538     $error = 'DRUSH_FRAMEWORK_ERROR';
1539   }
1540   elseif (!is_string($error)) {
1541     // Typical case: D8 TranslatableMarkup, implementing MarkupInterface.
1542     $error = "$error";
1543   }
1544
1545   $message = ($message) ? $message : drush_command_invoke_all('drush_help', 'error:' . $error);
1546
1547   if (is_array($message)) {
1548     $message = implode("\n", $message);
1549   }
1550
1551   $error_log[$error][] = $message;
1552   if (!drush_backend_packet('set_error', array('error' => $error, 'message' => $message))) {
1553     drush_log(($message) ? $output_label . $message : $output_label . $error, LogLevel::ERROR, $error);
1554   }
1555
1556   return FALSE;
1557 }
1558
1559 /**
1560  * Return the current error handling status
1561  *
1562  * @return
1563  *   The current aggregate error status
1564  */
1565 function drush_get_error() {
1566   return drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
1567 }
1568
1569 /**
1570  * Return the current list of errors that have occurred.
1571  *
1572  * @return
1573  *   An associative array of error messages indexed by the type of message.
1574  */
1575 function drush_get_error_log() {
1576   return drush_get_context('DRUSH_ERROR_LOG', array());
1577 }
1578
1579 /**
1580  * Check if a specific error status has been set.
1581  *
1582  * @param error
1583  *   A text string identifying the error that has occurred.
1584  * @return
1585  *   TRUE if the specified error has been set, FALSE if not
1586  */
1587 function drush_cmp_error($error) {
1588   $error_log = drush_get_error_log();
1589
1590   if (is_numeric($error)) {
1591     $error = 'DRUSH_FRAMEWORK_ERROR';
1592   }
1593
1594   return array_key_exists($error, $error_log);
1595 }
1596
1597 /**
1598  * Clear error context.
1599  */
1600 function drush_clear_error() {
1601   drush_set_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
1602 }
1603
1604 /**
1605  * Exit due to user declining a confirmation prompt.
1606  *
1607  * Usage:  return drush_user_abort();
1608  */
1609 function drush_user_abort($msg = NULL) {
1610   drush_set_context('DRUSH_USER_ABORT', TRUE);
1611   drush_set_context('DRUSH_EXIT_CODE', DRUSH_EXITCODE_USER_ABORT);
1612   drush_log($msg ? $msg : dt('Cancelled.'), LogLevel::CANCEL);
1613   return FALSE;
1614 }
1615
1616 /**
1617  * Turn PHP error handling off.
1618  *
1619  * This is commonly used while bootstrapping Drupal for install
1620  * or updates.
1621  *
1622  * This also records the previous error_reporting setting, in
1623  * case it wasn't recorded previously.
1624  *
1625  * @see drush_errors_off()
1626  */
1627 function drush_errors_off() {
1628   drush_get_context('DRUSH_ERROR_REPORTING', error_reporting(0));
1629   ini_set('display_errors', FALSE);
1630 }
1631
1632 /**
1633  * Turn PHP error handling on.
1634  *
1635  * We default to error_reporting() here just in
1636  * case drush_errors_on() is called before drush_errors_off() and
1637  * the context is not yet set.
1638  *
1639  * @arg $errors string
1640  *   The default error level to set in drush. This error level will be
1641  *   carried through further drush_errors_on()/off() calls even if not
1642  *   provided in later calls.
1643  *
1644  * @see error_reporting()
1645  * @see drush_errors_off()
1646  */
1647 function drush_errors_on($errors = null) {
1648   if (!isset($errors)) {
1649     $errors = error_reporting();
1650   }
1651   else {
1652     drush_set_context('DRUSH_ERROR_REPORTING', $errors);
1653   }
1654   error_reporting(drush_get_context('DRUSH_ERROR_REPORTING', $errors));
1655   ini_set('display_errors', TRUE);
1656 }
1657
1658 /**
1659  * @} End of "defgroup errorhandling".
1660  */
1661
1662 /**
1663  * Get the PHP memory_limit value in bytes.
1664  */
1665 function drush_memory_limit() {
1666   $value = trim(ini_get('memory_limit'));
1667   $last = strtolower($value[strlen($value)-1]);
1668   switch ($last) {
1669     case 'g':
1670       $value *= DRUSH_KILOBYTE;
1671     case 'm':
1672       $value *= DRUSH_KILOBYTE;
1673     case 'k':
1674       $value *= DRUSH_KILOBYTE;
1675   }
1676
1677   return $value;
1678 }
1679
1680 /**
1681  * Unset the named key anywhere in the provided
1682  * data structure.
1683  */
1684 function drush_unset_recursive(&$data, $unset_key) {
1685   if (!empty($data) && is_array($data)) {
1686     unset($data[$unset_key]);
1687     foreach ($data as $key => $value) {
1688       if (is_array($value)) {
1689         drush_unset_recursive($data[$key], $unset_key);
1690       }
1691     }
1692   }
1693 }
1694
1695 /**
1696  * Return a list of VCSs reserved files and directories.
1697  */
1698 function drush_version_control_reserved_files() {
1699   static $files = FALSE;
1700
1701   if (!$files) {
1702     // Also support VCSs that are not drush vc engines.
1703     $files = array('.git', '.gitignore', '.hg', '.hgignore', '.hgrags');
1704     $engine_info = drush_get_engines('version_control');
1705     $vcs = array_keys($engine_info['engines']);
1706     foreach ($vcs as $name) {
1707       $version_control = drush_include_engine('version_control', $name);
1708       $files = array_merge($files, $version_control->reserved_files());
1709     }
1710   }
1711
1712   return $files;
1713 }
1714
1715 /**
1716  * Generate a random alphanumeric password.  Copied from user.module.
1717  */
1718 function drush_generate_password($length = 10) {
1719   // This variable contains the list of allowable characters for the
1720   // password. Note that the number 0 and the letter 'O' have been
1721   // removed to avoid confusion between the two. The same is true
1722   // of 'I', 1, and 'l'.
1723   $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
1724
1725   // Zero-based count of characters in the allowable list:
1726   $len = strlen($allowable_characters) - 1;
1727
1728   // Declare the password as a blank string.
1729   $pass = '';
1730
1731   // Loop the number of times specified by $length.
1732   for ($i = 0; $i < $length; $i++) {
1733
1734     // Each iteration, pick a random character from the
1735     // allowable string and append it to the password:
1736     $pass .= $allowable_characters[mt_rand(0, $len)];
1737   }
1738
1739   return $pass;
1740 }
1741
1742 /**
1743  * Form an associative array from a linear array.
1744  *
1745  * This function walks through the provided array and constructs an associative
1746  * array out of it. The keys of the resulting array will be the values of the
1747  * input array. The values will be the same as the keys unless a function is
1748  * specified, in which case the output of the function is used for the values
1749  * instead.
1750  *
1751  * @param $array
1752  *   A linear array.
1753  * @param $function
1754  *   A name of a function to apply to all values before output.
1755  *
1756  * @return
1757  *   An associative array.
1758  */
1759 function drush_map_assoc($array, $function = NULL) {
1760   // array_combine() fails with empty arrays:
1761   // http://bugs.php.net/bug.php?id=34857.
1762   $array = !empty($array) ? array_combine($array, $array) : array();
1763   if (is_callable($function)) {
1764     $array = array_map($function, $array);
1765   }
1766   return $array;
1767 }
1768 /**
1769  * Clears completion caches.
1770  *
1771  * If called with no parameters the entire complete cache will be cleared.
1772  * If called with just the $type parameter the global cache for that type will
1773  * be cleared (in the site context, if any). If called with both $type and
1774  * $command parameters the command cache of that type will be cleared  (in the
1775  * site context, if any).
1776  *
1777  * This is included in drush.inc as complete.inc is only loaded conditionally.
1778  *
1779  * @param $type
1780  *   The completion type (optional).
1781  * @param $command
1782  *   The command name (optional), if command specific cache is to be cleared.
1783  *   If specifying a command, $type is not optional.
1784  */
1785 function drush_complete_cache_clear($type = NULL, $command = NULL) {
1786   require_once DRUSH_BASE_PATH . '/includes/complete.inc';
1787   if ($type) {
1788     drush_cache_clear_all(drush_complete_cache_cid($type, $command), 'complete');
1789     return;
1790   }
1791   // No type or command, so clear the entire complete cache.
1792   drush_cache_clear_all('*', 'complete', TRUE);
1793 }
1794
1795 /*
1796  * Cast a value according to the provided format
1797  *
1798  * @param mixed $value.
1799  * @param string $format
1800  *   Allowed values: auto, integer, float, bool, boolean, json, yaml.
1801  *
1802  * @return $value
1803  *  The value, re-typed as needed.
1804  */
1805 function drush_value_format($value, $format) {
1806   if ($format == 'auto') {
1807     if (is_numeric($value)) {
1808       $value = $value + 0; // http://php.net/manual/en/function.is-numeric.php#107326
1809       $format = gettype($value);
1810     }
1811     elseif (($value == 'TRUE') || ($value == 'FALSE')) {
1812       $format = 'bool';
1813     }
1814   }
1815
1816   // Now, we parse the object.
1817   switch ($format) {
1818     case 'integer':
1819       $value = (integer)$value;
1820       break;
1821     // from: http://php.net/gettype
1822     // for historical reasons "double" is returned in case of a float, and not simply "float"
1823     case 'double':
1824     case 'float':
1825       $value = (float)$value;
1826       break;
1827     case 'bool':
1828     case 'boolean':
1829       if ($value == 'TRUE') {
1830         $value = TRUE;
1831       }
1832       elseif ($value == 'FALSE') {
1833         $value = FALSE;
1834       }
1835       else {
1836         $value = (bool)$value;
1837       }
1838       break;
1839
1840     case 'json':
1841       $value = drush_json_decode($value);
1842       break;
1843
1844     case 'yaml':
1845       $value = \Symfony\Component\Yaml\Yaml::parse($value, FALSE, TRUE);
1846       break;
1847   }
1848   return $value;
1849 }