950953f12123252c8a294d6d9fef210094907b6d
[yaffs-website] / vendor / drush / drush / includes / output.inc
1 <?php
2
3 use Drupal\Core\Render\Markup;
4 use Drush\Log\LogLevel;
5
6 /**
7  * @defgroup outputfunctions Process output text.
8  * @{
9  */
10
11 /**
12  * Prints a message with optional indentation. In general,
13  * drush_log($message, LogLevel::OK) is often a better choice than this function.
14  * That gets your confirmation message (for example) into the logs for this
15  * drush request. Consider that drush requests may be executed remotely and
16  * non interactively.
17  *
18  * @param $message
19  *   The message to print.
20  * @param $indent
21  *    The indentation (space chars)
22  * @param $handle
23  *    File handle to write to.  NULL will write
24  *    to standard output, STDERR will write to the standard
25  *    error.  See http://php.net/manual/en/features.commandline.io-streams.php
26  * @param $newline
27  *    Add a "\n" to the end of the output.  Defaults to TRUE.
28  */
29 function drush_print($message = '', $indent = 0, $handle = NULL, $newline = TRUE) {
30   $msg = str_repeat(' ', $indent) . (string)$message;
31   if ($newline) {
32     $msg .= "\n";
33   }
34   if (($charset = drush_get_option('output_charset')) && function_exists('iconv')) {
35     $msg = iconv('UTF-8', $charset, $msg);
36   }
37   if (!isset($handle)) {
38     $handle = STDOUT;
39     // In the past, Drush would use `print` here; now that we are using
40     // fwrite (to avoid problems with php sending the http headers), we
41     // must explicitly capture the output, because ob_start() / ob_end()
42     // does not capture output written via fwrite to STDOUT.
43     drush_backend_output_collect($msg);
44   }
45   fwrite($handle, $msg);
46 }
47
48 /**
49  * Print a prompt -- that is, a message with no trailing newline.
50  */
51 function drush_print_prompt($message, $indent = 0, $handle = NULL) {
52   drush_print($message, $indent, $handle, FALSE);
53 }
54
55 /**
56  * Stores a message which is printed during drush_shutdown() if in compact mode.
57  * @param $message
58  *   The message to print.  If $message is an array,
59  *   then each element of the array is printed on a
60  *   separate line.
61  */
62 function drush_print_pipe($message = '') {
63   $buffer = &drush_get_context('DRUSH_PIPE_BUFFER' , '');
64   if (is_array($message)) {
65     $message = implode("\n", $message) . "\n";
66   }
67   $buffer .= $message;
68 }
69
70 /**
71  * Prints an array or string.
72  * @param $array
73  *   The array to print.
74  * @param $newline
75  *    Add a "\n" to the end of the output.  Defaults to TRUE.
76  */
77 function drush_print_r($array, $handle = NULL, $newline = TRUE) {
78   drush_print(print_r($array, TRUE), 0, $handle, $newline);
79 }
80
81 /**
82  * Format some data and print it out.  Respect --format option.
83  */
84 function drush_print_format($input, $default_format, $metadata = NULL) {
85   drush_print(drush_format($input, $metadata, drush_get_option('format', $default_format)));
86 }
87
88 /**
89  * Prepares a variable for printing.  Loads the requested output
90  * formatter and uses it to process the provided input.
91  *
92  * @param mixed $input
93  *   A variable.
94  * @param string $metadata
95  *   Optional formatting metadata. Not used by all formats.
96  *     'label' - text to label data with in some formats (e.g. export, config)
97  * @param string $format
98  *   Optional format; defaults to print_r.
99  * @return string
100  *   The variable formatted according to specified format.
101  *   Ready for drush_print().
102  */
103 function drush_format($input, $metadata = NULL, $format = NULL) {
104   $output = '';
105   // Look up the format and label, and fill in default values for metadata
106   if (is_string($metadata)) {
107     $metadata = array('label' => $metadata);
108   }
109   if (!is_array($metadata)) {
110     $metadata = array();
111   }
112   $metadata += array(
113     'metameta' => array(),
114   );
115   if (isset($metadata['format'])) {
116     // If the format is set in metadata, then it will
117     // override whatever is passed in via the $format parameter.
118     $format = $metadata['format'];
119   }
120   if (!isset($format)) {
121     // TODO: we shouldn't ever call drush_get_option here.
122     // Get rid of this once we confirm that there are no
123     // callers that still need it.
124     $format = drush_get_option('format', 'print-r');
125   }
126
127   $formatter = drush_load_engine('outputformat', $format);
128   if ($formatter) {
129     if ($formatter === TRUE) {
130       return drush_set_error(dt('No outputformat class defined for !format', array('!format' => $format)));
131     }
132     $output = $formatter->process($input, $metadata);
133   }
134
135   return $output;
136 }
137
138 /**
139  * Rudimentary replacement for Drupal API t() function.
140  *
141  * @param string
142  *   String to process, possibly with replacement item.
143  * @param array
144  *  An associative array of replacement items.
145  *
146  * @return
147  *   The processed string.
148  *
149  * @see t()
150  */
151 function dt($string, $args = array()) {
152   $output = NULL;
153   if (function_exists('t') && drush_drupal_major_version() == 7) {
154     $output = t($string, $args);
155   }
156   // The language system requires a working container which has the string
157   // translation service.
158   else if (drush_drupal_major_version() >= 8 && \Drupal::hasService('string_translation')) {
159     // Drupal 8 removes !var replacements, creating a user-level error when
160     // these are used, so we'll pre-replace these before calling translate().
161     list($string, $args) = replace_legacy_dt_args($string, $args);
162     $output = (string) \Drupal::translation()->translate($string, $args);
163   }
164   else if (function_exists('t') && drush_drupal_major_version() <= 7 && function_exists('theme')) {
165     $output = t($string, $args);
166   }
167
168   // If Drupal's t() function unavailable.
169   if (!isset($output)) {
170     if (!empty($args)) {
171       $output = strtr($string, $args);
172     }
173     else {
174       $output = $string;
175     }
176   }
177   return $output;
178 }
179
180 /**
181  * Replace placeholders that begin with a '!' with '@'.
182  */
183 function replace_legacy_dt_args(&$string, &$legacy_args) {
184   $args = array();
185   $replace = array();
186   foreach ($legacy_args as $name => $argument) {
187     if ($name[0] == '!') {
188       $new_arg = '@' . substr($name, 1);
189       $replace[$name] = $new_arg;
190       $args[$new_arg] = Markup::create($argument);
191     }
192     else {
193       $args[$name] = $argument;
194     }
195   }
196   return [
197     strtr($string, $replace),
198     $args
199   ];
200 }
201
202 /**
203  * Convert html to readable text.  Compatible API to
204  * drupal_html_to_text, but less functional.  Caller
205  * might prefer to call drupal_html_to_text if there
206  * is a bootstrapped Drupal site available.
207  *
208  * @param string $html
209  *   The html text to convert.
210  *
211  * @return string
212  *   The plain-text representation of the input.
213  */
214 function drush_html_to_text($html, $allowed_tags = NULL) {
215   $replacements = array(
216     '<hr>' => '------------------------------------------------------------------------------',
217     '<li>' => '  * ',
218     '<h1>' => '===== ',
219     '</h1>' => ' =====',
220     '<h2>' => '---- ',
221     '</h2>' => ' ----',
222     '<h3>' => '::: ',
223     '</h3>' => ' :::',
224     '<br/>' => "\n",
225   );
226   $text = str_replace(array_keys($replacements), array_values($replacements), $html);
227   return html_entity_decode(preg_replace('/ *<[^>]*> */', ' ', $text));
228 }
229
230
231 /**
232  * Print a formatted table.
233  *
234  * @param $rows
235  *   The rows to print.
236  * @param $header
237  *   If TRUE, the first line will be treated as table header.
238  * @param $widths
239  *   An associative array whose keys are column IDs and values are widths of each column (in characters).
240  *   If not specified this will be determined automatically, based on a "best fit" algorithm.
241  * @param $handle
242  *    File handle to write to.  NULL will write
243  *    to standard output, STDERR will write to the standard
244  *    error.  See http://php.net/manual/en/features.commandline.io-streams.php
245  * @return $tbl
246  *   Use $tbl->getTable() to get the output from the return value.
247  */
248 function drush_print_table($rows, $header = FALSE, $widths = array(), $handle = NULL) {
249   $tbl = _drush_format_table($rows, $header, $widths);
250   $output = $tbl->getTable();
251   if (!stristr(PHP_OS, 'WIN')) {
252     $output = str_replace("\r\n", PHP_EOL, $output);
253   }
254
255   drush_print(rtrim($output), 0, $handle);
256   return $tbl;
257 }
258
259 /**
260  * Format a table of data.
261  *
262  * @param $rows
263  *   The rows to print.
264  * @param $header
265  *   If TRUE, the first line will be treated as table header.
266  * @param $widths
267  *   An associative array whose keys are column IDs and values are widths of each column (in characters).
268  *   If not specified this will be determined automatically, based on a "best fit" algorithm.
269  * @param array $console_table_options
270  *   An array that is passed along when constructing a Console_Table instance.
271  * @return $output
272  *   The formatted output.
273  */
274 function drush_format_table($rows, $header = FALSE, $widths = array(), $console_table_options = array()) {
275   $tbl = _drush_format_table($rows, $header, $widths, $console_table_options);
276   $output = $tbl->getTable();
277   if (!drush_is_windows()) {
278     $output = str_replace("\r\n", PHP_EOL, $output);
279   }
280   return $output;
281 }
282
283 function _drush_format_table($rows, $header = FALSE, $widths = array(), $console_table_options = array()) {
284   // Add defaults.
285   $tbl = new ReflectionClass('Console_Table');
286   $console_table_options += array(CONSOLE_TABLE_ALIGN_LEFT , '');
287   $tbl = $tbl->newInstanceArgs($console_table_options);
288
289   $auto_widths = drush_table_column_autowidth($rows, $widths);
290
291   // Do wordwrap on all cells.
292   $newrows = array();
293   foreach ($rows as $rowkey => $row) {
294     foreach ($row as $col_num => $cell) {
295       $newrows[$rowkey][$col_num] = wordwrap($cell, $auto_widths[$col_num], "\n", TRUE);
296       if (isset($widths[$col_num])) {
297         $newrows[$rowkey][$col_num] = str_pad($newrows[$rowkey][$col_num], $widths[$col_num]);
298       }
299     }
300   }
301   if ($header) {
302     $headers = array_shift($newrows);
303     $tbl->setHeaders($headers);
304   }
305
306   $tbl->addData($newrows);
307   return $tbl;
308 }
309
310 /**
311  * Convert an associative array of key : value pairs into
312  * a table suitable for processing by drush_print_table.
313  *
314  * @param $keyvalue_table
315  *    An associative array of key : value pairs.
316  * @param $metadata
317  *    'key-value-item':  If the value is an array, then
318  *    the item key determines which item from the value
319  *    will appear in the output.
320  * @return
321  *    An array of arrays, where the keys from the input
322  *    array are stored in the first column, and the values
323  *    are stored in the third.  A second colum is created
324  *    specifically to hold the ':' separator.
325  */
326 function drush_key_value_to_array_table($keyvalue_table, $metadata = array()) {
327   if (!is_array($keyvalue_table)) {
328     return drush_set_error('DRUSH_INVALID_FORMAT', dt("Data not compatible with selected key-value output format."));
329   }
330   if (!is_array($metadata)) {
331     $metadata = array('key-value-item' => $metadata);
332   }
333   $item_key = array_key_exists('key-value-item', $metadata) ? $metadata['key-value-item'] : NULL;
334   $metadata += array(
335     'format' => 'list',
336     'separator' => ' ',
337   );
338   $table = array();
339   foreach ($keyvalue_table as $key => $value) {
340     if (isset($value)) {
341       if (is_array($value) && isset($item_key)) {
342         $value = $value[$item_key];
343       }
344       // We should only have simple values here, but if
345       // we don't, use drush_format() to flatten as a fallback.
346       if (is_array($value)) {
347         $value = drush_format($value, $metadata, 'list');
348       }
349     }
350     if (isset($metadata['include-field-labels']) && !$metadata['include-field-labels']) {
351       $table[] = array(isset($value) ? $value : '');
352     }
353     elseif (isset($value)) {
354       $table[] = array($key, ' :', $value);
355     }
356     else {
357       $table[] = array($key . ':', '', '');
358     }
359   }
360   return $table;
361 }
362
363 /**
364  * Select the fields that should be used.
365  */
366 function drush_select_fields($all_field_labels, $fields, $strict = TRUE) {
367   $field_labels = array();
368   foreach ($fields as $field) {
369     if (array_key_exists($field, $all_field_labels)) {
370       $field_labels[$field] = $all_field_labels[$field];
371     }
372     else {
373       // Allow the user to select fields via their human-readable names.
374       // This is less convenient than the field name (since the human-readable
375       // names may contain spaces, and must therefore be quoted), but these are
376       // the values that the user sees in the command output. n.b. the help
377       // text lists fields by their more convenient machine names.
378       $key = array_search(strtolower($field), array_map('strtolower', $all_field_labels));
379       if ($key !== FALSE) {
380         $field_labels[$key] = $all_field_labels[$key];
381       }
382       elseif (!$strict) {
383         $field_labels[$field] = $field;
384       }
385     }
386   }
387   return $field_labels;
388 }
389
390 /**
391  * Select the fields from the input array that should be output.
392  *
393  * @param $input
394  *   An associative array of key:value pairs to be output
395  * @param $fields
396  *   An associative array that maps FROM a field in $input
397  *   TO the corresponding field name in $output.
398  * @param $mapping
399  *   An associative array that maps FROM a field in $fields
400  *   TO the actual field in $input to use in the preceeding
401  *   translation described above.
402  * @return
403  *   The input array, re-ordered and re-keyed per $fields
404  */
405 function drush_select_output_fields($input, $fields, $mapping = array(), $default_value = NULL) {
406   $result = array();
407   if (empty($fields)) {
408     $result = $input;
409   }
410   else {
411     foreach ($fields as $key => $label) {
412       $value = drush_lookup_field_by_path($input, $key, $mapping, $default_value);
413       if (isset($value)) {
414         $result[$label] = $value;
415       }
416     }
417   }
418   return $result;
419 }
420
421 /**
422  * Return a specific item inside an array, as identified
423  * by the provided path.
424  *
425  * @param $input:
426  *   An array of items, potentially multiple layers deep.
427  * @param $path:
428  *   A specifier of array keys, either in an array or separated by
429  *   a '/', that list the elements of the array to access.  This
430  *   works much like a very simple version of xpath for arrays, with
431  *   all items being treated identically (like elements).
432  * @param $mapping:
433  *   (optional) An array whose keys may correspond to the $path parameter and
434  *   whose values are the corresponding paths to be used in $input.
435  *
436  * Example 1:
437  *
438  *   $input = array('#name' => 'site.dev', '#id' => '222');
439  *   $path = '#name';
440  *   result: 'site.dev';
441  *
442  * Example 2:
443  *
444  *   $input = array('ca' => array('sf' => array('mission'=>array('1700'=>'woodward'))));
445  *   $path = 'ca/sf/mission/1701';
446  *   result: 'woodward'
447  *
448  * Example 3:
449  *
450  *   $input = array('#name' => 'site.dev', '#id' => '222');
451  *   $path = 'name';
452  *   $mapping = array('name' => '#name');
453  *   result: 'site.dev';
454  */
455 function drush_lookup_field_by_path($input, $path, $mapping = array(), $default_value = NULL) {
456   $result = '';
457   if (isset($mapping[$path])) {
458     $path = $mapping[$path];
459   }
460   if (!is_array($path)) {
461     $parts = explode('/', $path);
462   }
463   if (!empty($parts)) {
464     $result = $input;
465     foreach ($parts as $key) {
466       if ((is_array($result)) && (isset($result[$key]))) {
467         $result = $result[$key];
468       }
469       else {
470         return $default_value;
471       }
472     }
473   }
474   return $result;
475 }
476
477 /**
478  * Given a table array (an associative array of associative arrays),
479  * return an array of all of the values with the specified field name.
480  */
481 function drush_output_get_selected_field($input, $field_name, $default_value = '') {
482   $result = array();
483   foreach ($input as $key => $data) {
484     if (is_array($data) && isset($data[$field_name])) {
485       $result[] = $data[$field_name];
486     }
487     else {
488       $result[] = $default_value;
489     }
490   }
491   return $result;
492 }
493
494 /**
495  * Hide any fields that are empty
496  */
497 function drush_hide_empty_fields($input, $fields) {
498   $has_data = array();
499   foreach ($input as $key => $data) {
500     foreach ($fields as $field => $label) {
501       if (isset($data[$field]) && !empty($data[$field])) {
502         $has_data[$field] = TRUE;
503       }
504     }
505   }
506   foreach ($fields as $field => $label) {
507     if (!isset($has_data[$field])) {
508       unset($fields[$field]);
509     }
510   }
511   return $fields;
512 }
513
514 /**
515  * Convert an array of data rows, where each row contains an
516  * associative array of key : value pairs, into
517  * a table suitable for processing by drush_print_table.
518  * The provided $header determines the order that the items
519  * will appear in the output.  Only data items listed in the
520  * header will be placed in the output.
521  *
522  * @param $rows_of_keyvalue_table
523  *    array(
524  *      'row1' => array('col1' => 'data', 'col2' => 'data'),
525  *      'row2' => array('col1' => 'data', 'col2' => 'data'),
526  *    )
527  * @param $header
528  *    array('col1' => 'Column 1 Label', 'col2' => 'Column 2 Label')
529  * @param $metadata
530  *    (optional) An array of special options, all optional:
531  *    - strip-tags: call the strip_tags function on the data
532  *         before placing it in the table
533  *    - concatenate-columns: an array of:
534  *         - dest-col: array('src-col1', 'src-col2')
535  *         Appends all of the src columns with whatever is
536  *         in the destination column.  Appended columns are
537  *         separated by newlines.
538  *    - transform-columns: an array of:
539  *         - dest-col: array('from' => 'to'),
540  *         Transforms any occurance of 'from' in 'dest-col' to 'to'.
541  *    - format-cell: Drush output format name to use to format
542  *         any cell that is an array.
543  *    - process-cell: php function name to use to process
544  *         any cell that is an array.
545  *    - field-mappings: an array whose keys are some or all of the keys in
546  *         $header and whose values are the corresponding keys to use when
547  *         indexing the values of $rows_of_keyvalue_table.
548  * @return
549  *    An array of arrays
550  */
551 function drush_rows_of_key_value_to_array_table($rows_of_keyvalue_table, $header, $metadata) {
552   if (isset($metadata['hide-empty-fields'])) {
553     $header = drush_hide_empty_fields($rows_of_keyvalue_table, $header);
554   }
555   if (empty($header)) {
556     $first = (array)reset($rows_of_keyvalue_table);
557     $keys = array_keys($first);
558     $header = array_combine($keys, $keys);
559   }
560   $table = array(array_values($header));
561   if (isset($rows_of_keyvalue_table) && is_array($rows_of_keyvalue_table) && !empty($rows_of_keyvalue_table)) {
562     foreach ($rows_of_keyvalue_table as $row_index => $row_data) {
563       $row_data = (array)$row_data;
564       $row = array();
565       foreach ($header as $column_key => $column_label) {
566         $data = drush_lookup_field_by_path($row_data, $column_key, $metadata['field-mappings']);
567         if (array_key_exists('transform-columns', $metadata)) {
568           foreach ($metadata['transform-columns'] as $dest_col => $transformations) {
569             if ($dest_col == $column_key) {
570               $data = str_replace(array_keys($transformations), array_values($transformations), $data);
571             }
572           }
573         }
574         if (array_key_exists('concatenate-columns', $metadata)) {
575           foreach ($metadata['concatenate-columns'] as $dest_col => $src_cols) {
576             if ($dest_col == $column_key) {
577               $data = '';
578               if (!is_array($src_cols)) {
579                 $src_cols = array($src_cols);
580               }
581               foreach($src_cols as $src) {
582                 if (array_key_exists($src, $row_data) && !empty($row_data[$src])) {
583                   if (!empty($data)) {
584                     $data .= "\n";
585                   }
586                   $data .= $row_data[$src];
587                 }
588               }
589             }
590           }
591         }
592         if (array_key_exists('format-cell', $metadata) && is_array($data)) {
593           $data = drush_format($data, array(), $metadata['format-cell']);
594         }
595         if (array_key_exists('process-cell', $metadata) && is_array($data)) {
596           $data = $metadata['process-cell']($data, $metadata);
597         }
598         if (array_key_exists('strip-tags', $metadata)) {
599           $data = strip_tags($data);
600         }
601         $row[] = $data;
602       }
603       $table[] = $row;
604     }
605   }
606   return $table;
607 }
608
609 /**
610  * Determine the best fit for column widths.
611  *
612  * @param $rows
613  *   The rows to use for calculations.
614  * @param $widths
615  *   Manually specified widths of each column (in characters) - these will be
616  *   left as is.
617  */
618 function drush_table_column_autowidth($rows, $widths) {
619   $auto_widths = $widths;
620
621   // First we determine the distribution of row lengths in each column.
622   // This is an array of descending character length keys (i.e. starting at
623   // the rightmost character column), with the value indicating the number
624   // of rows where that character column is present.
625   $col_dist = array();
626   foreach ($rows as $rowkey => $row) {
627     foreach ($row as $col_id => $cell) {
628       if (empty($widths[$col_id])) {
629         $length = strlen($cell);
630         if ($length == 0) {
631           $col_dist[$col_id][0] = 0;
632         }
633         while ($length > 0) {
634           if (!isset($col_dist[$col_id][$length])) {
635             $col_dist[$col_id][$length] = 0;
636           }
637           $col_dist[$col_id][$length]++;
638           $length--;
639         }
640       }
641     }
642   }
643   foreach ($col_dist as $col_id => $count) {
644     // Sort the distribution in decending key order.
645     krsort($col_dist[$col_id]);
646     // Initially we set all columns to their "ideal" longest width
647     // - i.e. the width of their longest column.
648     $auto_widths[$col_id] = max(array_keys($col_dist[$col_id]));
649   }
650
651   // We determine what width we have available to use, and what width the
652   // above "ideal" columns take up.
653   $available_width = drush_get_context('DRUSH_COLUMNS', 80) - (count($auto_widths) * 2);
654   $auto_width_current = array_sum($auto_widths);
655
656   // If we need to reduce a column so that we can fit the space we use this
657   // loop to figure out which column will cause the "least wrapping",
658   // (relative to the other columns) and reduce the width of that column.
659   while ($auto_width_current > $available_width) {
660     $count = 0;
661     $width = 0;
662     foreach ($col_dist as $col_id => $counts) {
663       // If we are just starting out, select the first column.
664       if ($count == 0 ||
665          // OR: if this column would cause less wrapping than the currently
666          // selected column, then select it.
667          (current($counts) < $count) ||
668          // OR: if this column would cause the same amount of wrapping, but is
669          // longer, then we choose to wrap the longer column (proportionally
670          // less wrapping, and helps avoid triple line wraps).
671          (current($counts) == $count && key($counts) > $width)) {
672         // Select the column number, and record the count and current width
673         // for later comparisons.
674         $column = $col_id;
675         $count = current($counts);
676         $width = key($counts);
677       }
678     }
679     if ($width <= 1) {
680       // If we have reached a width of 1 then give up, so wordwrap can still progress.
681       break;
682     }
683     // Reduce the width of the selected column.
684     $auto_widths[$column]--;
685     // Reduce our overall table width counter.
686     $auto_width_current--;
687     // Remove the corresponding data from the disctribution, so next time
688     // around we use the data for the row to the left.
689     unset($col_dist[$column][$width]);
690   }
691   return $auto_widths;
692 }
693
694 /**
695  * Print the contents of a file.
696  *
697  * @param string $file
698  *   Full path to a file.
699  */
700 function drush_print_file($file) {
701   // Don't even bother to print the file in --no mode
702   if (drush_get_context('DRUSH_NEGATIVE')) {
703     return;
704   }
705   if ((substr($file,-4) == ".htm") || (substr($file,-5) == ".html")) {
706     $tmp_file = drush_tempnam(basename($file));
707     file_put_contents($tmp_file, drush_html_to_text(file_get_contents($file)));
708     $file = $tmp_file;
709   }
710   // Do not wait for user input in --yes or --pipe modes
711   if (drush_get_context('DRUSH_PIPE')) {
712     drush_print_pipe(file_get_contents($file));
713   }
714   elseif (drush_get_context('DRUSH_AFFIRMATIVE')) {
715     drush_print(file_get_contents($file));
716   }
717   elseif (drush_shell_exec_interactive("less %s", $file)) {
718     return;
719   }
720   elseif (drush_shell_exec_interactive("more %s", $file)) {
721     return;
722   }
723   else {
724     drush_print(file_get_contents($file));
725   }
726 }
727
728
729 /**
730  * Converts a PHP variable into its Javascript equivalent.
731  *
732  * We provide a copy of D7's drupal_json_encode since this function is
733  * unavailable on earlier versions of Drupal.
734  *
735  * @see drupal_json_decode()
736  * @ingroup php_wrappers
737  */
738 function drush_json_encode($var) {
739   if (version_compare(phpversion(), '5.4.0', '>=')) {
740     $json = json_encode($var, JSON_PRETTY_PRINT);
741   }
742   else {
743     $json = json_encode($var);
744   }
745   // json_encode() does not escape <, > and &, so we do it with str_replace().
746   return str_replace(array('<', '>', '&'), array('\u003c', '\u003e', '\u0026'), $json);
747 }
748
749 /**
750  * Converts an HTML-safe JSON string into its PHP equivalent.
751  *
752  * We provide a copy of D7's drupal_json_decode since this function is
753  * unavailable on earlier versions of Drupal.
754  *
755  * @see drupal_json_encode()
756  * @ingroup php_wrappers
757  */
758 function drush_json_decode($var) {
759   return json_decode($var, TRUE);
760 }
761
762 /**
763  * Drupal-friendly var_export().  Taken from utility.inc in Drupal 8.
764  *
765  * @param $var
766  *   The variable to export.
767  * @param $prefix
768  *   A prefix that will be added at the beginning of every lines of the output.
769  *
770  * @return
771  *   The variable exported in a way compatible to Drupal's coding standards.
772  */
773 function drush_var_export($var, $prefix = '') {
774   if (is_array($var)) {
775     if (empty($var)) {
776       $output = 'array()';
777     }
778     else {
779       $output = "array(\n";
780       // Don't export keys if the array is non associative.
781       $export_keys = array_values($var) != $var;
782       foreach ($var as $key => $value) {
783         $output .= '  ' . ($export_keys ? drush_var_export($key) . ' => ' : '') . drush_var_export($value, '  ', FALSE) . ",\n";
784       }
785       $output .= ')';
786     }
787   }
788   elseif (is_bool($var)) {
789     $output = $var ? 'TRUE' : 'FALSE';
790   }
791   elseif (is_string($var)) {
792     $line_safe_var = str_replace("\n", '\n', $var);
793     if (strpos($var, "\n") !== FALSE || strpos($var, "'") !== FALSE) {
794       // If the string contains a line break or a single quote, use the
795       // double quote export mode. Encode backslash and double quotes and
796       // transform some common control characters.
797       $var = str_replace(array('\\', '"', "\n", "\r", "\t"), array('\\\\', '\"', '\n', '\r', '\t'), $var);
798       $output = '"' . $var . '"';
799     }
800     else {
801       $output = "'" . $var . "'";
802     }
803   }
804   elseif (is_object($var) && get_class($var) === 'stdClass') {
805     // var_export() will export stdClass objects using an undefined
806     // magic method __set_state() leaving the export broken. This
807     // workaround avoids this by casting the object as an array for
808     // export and casting it back to an object when evaluated.
809     $output = '(object) ' . drush_var_export((array) $var, $prefix);
810   }
811   else {
812     $output = var_export($var, TRUE);
813   }
814
815   if ($prefix) {
816     $output = str_replace("\n", "\n$prefix", $output);
817   }
818
819   return $output;
820 }
821
822 /**
823  * @} End of "defgroup outputfunctions".
824  */