3 // Utilities that do NOT depend on WordPress code.
5 namespace WP_CLI\Utils;
7 use \WP_CLI\Dispatcher;
8 use \WP_CLI\Iterators\Transform;
10 function load_dependencies() {
11 if ( 0 === strpos( WP_CLI_ROOT, 'phar:' ) ) {
12 require WP_CLI_ROOT . '/vendor/autoload.php';
16 $has_autoload = false;
18 foreach ( get_vendor_paths() as $vendor_path ) {
19 if ( file_exists( $vendor_path . '/autoload.php' ) ) {
20 require $vendor_path . '/autoload.php';
26 if ( !$has_autoload ) {
27 fputs( STDERR, "Internal error: Can't find Composer autoloader.\n" );
32 function get_vendor_paths() {
34 WP_CLI_ROOT . '/../../../vendor', // part of a larger project / installed via Composer (preferred)
35 WP_CLI_ROOT . '/vendor', // top-level project / installed as Git clone
39 // Using require() directly inside a class grants access to private methods to the loaded code
40 function load_file( $path ) {
44 function load_command( $name ) {
45 $path = WP_CLI_ROOT . "/php/commands/$name.php";
47 if ( is_readable( $path ) ) {
52 function load_all_commands() {
53 $cmd_dir = WP_CLI_ROOT . '/php/commands';
55 $iterator = new \DirectoryIterator( $cmd_dir );
57 foreach ( $iterator as $filename ) {
58 if ( '.php' != substr( $filename, -4 ) )
61 include_once "$cmd_dir/$filename";
66 * Like array_map(), except it returns a new iterator, instead of a modified array.
70 * $arr = array('Football', 'Socker');
72 * $it = iterator_map($arr, 'strtolower', function($val) {
73 * return str_replace('foo', 'bar', $val);
76 * foreach ( $it as $val ) {
80 * @param array|object Either a plain array or another iterator
81 * @param callback The function to apply to an element
82 * @return object An iterator that applies the given callback(s)
84 function iterator_map( $it, $fn ) {
85 if ( is_array( $it ) ) {
86 $it = new \ArrayIterator( $it );
89 if ( !method_exists( $it, 'add_transform' ) ) {
90 $it = new Transform( $it );
93 foreach ( array_slice( func_get_args(), 1 ) as $fn ) {
94 $it->add_transform( $fn );
101 * Search for file by walking up the directory tree until the first file is found or until $stop_check($dir) returns true
102 * @param string|array The files (or file) to search for
103 * @param string|null The directory to start searching from; defaults to CWD
104 * @param callable Function which is passed the current dir each time a directory level is traversed
105 * @return null|string Null if the file was not found
107 function find_file_upward( $files, $dir = null, $stop_check = null ) {
108 $files = (array) $files;
109 if ( is_null( $dir ) ) {
112 while ( is_readable( $dir ) ) {
113 // Stop walking up when the supplied callable returns true being passed the $dir
114 if ( is_callable( $stop_check ) && call_user_func( $stop_check, $dir ) ) {
118 foreach ( $files as $file ) {
119 $path = $dir . DIRECTORY_SEPARATOR . $file;
120 if ( file_exists( $path ) ) {
125 $parent_dir = dirname( $dir );
126 if ( empty($parent_dir) || $parent_dir === $dir ) {
134 function is_path_absolute( $path ) {
136 if ( isset($path[1]) && ':' === $path[1] )
139 return $path[0] === '/';
143 * Composes positional arguments into a command string.
148 function args_to_str( $args ) {
149 return ' ' . implode( ' ', array_map( 'escapeshellarg', $args ) );
153 * Composes associative arguments into a command string.
158 function assoc_args_to_str( $assoc_args ) {
161 foreach ( $assoc_args as $key => $value ) {
162 if ( true === $value )
165 $str .= " --$key=" . escapeshellarg( $value );
172 * Given a template string and an arbitrary number of arguments,
173 * returns the final command, with the parameters escaped.
175 function esc_cmd( $cmd ) {
176 if ( func_num_args() < 2 )
177 trigger_error( 'esc_cmd() requires at least two arguments.', E_USER_WARNING );
179 $args = func_get_args();
181 $cmd = array_shift( $args );
183 return vsprintf( $cmd, array_map( 'escapeshellarg', $args ) );
186 function locate_wp_config() {
189 if ( null === $path ) {
190 if ( file_exists( ABSPATH . 'wp-config.php' ) )
191 $path = ABSPATH . 'wp-config.php';
192 elseif ( file_exists( ABSPATH . '../wp-config.php' ) && ! file_exists( ABSPATH . '/../wp-settings.php' ) )
193 $path = ABSPATH . '../wp-config.php';
198 $path = realpath( $path );
205 * Output items in a table, JSON, CSV, ids, or the total count
207 * @param string $format Format to use: 'table', 'json', 'csv', 'ids', 'count'
208 * @param array $items Data to output
209 * @param array|string $fields Named fields for each item of data. Can be array or comma-separated list
211 function format_items( $format, $items, $fields ) {
212 $assoc_args = compact( 'format', 'fields' );
213 $formatter = new \WP_CLI\Formatter( $assoc_args );
214 $formatter->display_items( $items );
218 * Write data as CSV to a given file.
220 * @param resource $fd File descriptor
221 * @param array $rows Array of rows to output
222 * @param array $headers List of CSV columns (optional)
224 function write_csv( $fd, $rows, $headers = array() ) {
225 if ( ! empty( $headers ) ) {
226 fputcsv( $fd, $headers );
229 foreach ( $rows as $row ) {
230 if ( ! empty( $headers ) ) {
231 $row = pick_fields( $row, $headers );
234 fputcsv( $fd, array_values( $row ) );
239 * Pick fields from an associative array or object.
241 * @param array|object Associative array or object to pick fields from
242 * @param array List of fields to pick
245 function pick_fields( $item, $fields ) {
246 $item = (object) $item;
250 foreach ( $fields as $field ) {
251 $values[ $field ] = isset( $item->$field ) ? $item->$field : null;
258 * Launch system's $EDITOR to edit text
260 * @param str $content Text to edit (eg post content)
261 * @return str|bool Edited text, if file is saved from editor
262 * False, if no change to file
264 function launch_editor_for_input( $input, $title = 'WP-CLI' ) {
266 $tmpfile = wp_tempnam( $title );
269 \WP_CLI::error( 'Error creating temporary file.' );
272 file_put_contents( $tmpfile, $input );
274 $editor = getenv( 'EDITOR' );
276 if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) )
282 \WP_CLI::launch( "$editor " . escapeshellarg( $tmpfile ) );
284 $output = file_get_contents( $tmpfile );
288 if ( $output === $input )
295 * @param string MySQL host string, as defined in wp-config.php
298 function mysql_host_to_cli_args( $raw_host ) {
299 $assoc_args = array();
301 $host_parts = explode( ':', $raw_host );
302 if ( count( $host_parts ) == 2 ) {
303 list( $assoc_args['host'], $extra ) = $host_parts;
304 $extra = trim( $extra );
305 if ( is_numeric( $extra ) ) {
306 $assoc_args['port'] = intval( $extra );
307 $assoc_args['protocol'] = 'tcp';
308 } else if ( $extra !== '' ) {
309 $assoc_args['socket'] = $extra;
312 $assoc_args['host'] = $raw_host;
318 function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) {
320 $descriptors = array( STDIN, STDOUT, STDERR );
322 if ( isset( $assoc_args['host'] ) ) {
323 $assoc_args = array_merge( $assoc_args, mysql_host_to_cli_args( $assoc_args['host'] ) );
326 $env = (array) $_ENV;
327 if ( isset( $assoc_args['pass'] ) ) {
328 $env['MYSQL_PWD'] = $assoc_args['pass'];
329 unset( $assoc_args['pass'] );
332 $final_cmd = $cmd . assoc_args_to_str( $assoc_args );
334 $proc = proc_open( $final_cmd, $descriptors, $pipes, null, $env );
338 $r = proc_close( $proc );
340 if ( $r ) exit( $r );
344 * Render PHP or other types of files using Mustache templates.
346 * IMPORTANT: Automatic HTML escaping is disabled!
348 function mustache_render( $template_name, $data ) {
349 if ( ! file_exists( $template_name ) )
350 $template_name = WP_CLI_ROOT . "/templates/$template_name";
352 $template = file_get_contents( $template_name );
354 $m = new \Mustache_Engine( array(
355 'escape' => function ( $val ) { return $val; }
358 return $m->render( $template, $data );
361 function make_progress_bar( $message, $count ) {
362 if ( \cli\Shell::isPiped() )
363 return new \WP_CLI\NoOp;
365 return new \cli\progress\Bar( $message, $count );
368 function parse_url( $url ) {
369 $url_parts = \parse_url( $url );
371 if ( !isset( $url_parts['scheme'] ) ) {
372 $url_parts = parse_url( 'http://' . $url );
379 * Check if we're running in a Windows environment (cmd.exe).
381 function is_windows() {
382 return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';