Skip to content

Commit 1baff84

Browse files
committed
v1.1.5 prerelease: refresh test suite, require wp-cli ^1.5.
1 parent b447337 commit 1baff84

File tree

5 files changed

+221
-43
lines changed

5 files changed

+221
-43
lines changed

composer.json

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,20 @@
2020
"psr-4": {
2121
"": "src/"
2222
},
23-
"files": [
24-
"search-replace-command.php"
25-
]
26-
},
27-
"require": {
28-
"wp-cli/wp-cli": "*"
23+
"files": [ "search-replace-command.php" ]
2924
},
25+
"require": {},
3026
"require-dev": {
31-
"behat/behat": "~2.5"
27+
"behat/behat": "~2.5",
28+
"wp-cli/wp-cli": "^1.5"
3229
},
3330
"extra": {
3431
"branch-alias": {
3532
"dev-master": "1.x-dev"
3633
},
34+
"bundled": true,
3735
"commands": [
3836
"search-replace"
39-
],
40-
"bundled": true
37+
]
4138
}
4239
}

features/bootstrap/FeatureContext.php

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
8787
private $running_procs = array();
8888

8989
/**
90-
* Array of variables available as {VARIABLE_NAME}. Some are always set: CORE_CONFIG_SETTINGS, SRC_DIR, CACHE_DIR, WP_VERSION-version-latest. Some are step-dependent:
91-
* RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
90+
* Array of variables available as {VARIABLE_NAME}. Some are always set: CORE_CONFIG_SETTINGS, SRC_DIR, CACHE_DIR, WP_VERSION-version-latest.
91+
* Some are step-dependent: RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. One is set on use: INVOKE_WP_CLI_WITH_PHP_ARGS-args.
92+
* Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
9293
*/
9394
public $variables = array();
9495

@@ -117,8 +118,9 @@ private static function get_process_env_variables() {
117118
// Ensure we're using the expected `wp` binary
118119
$bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ?: realpath( __DIR__ . '/../../bin' );
119120
$vendor_dir = realpath( __DIR__ . '/../../vendor/bin' );
121+
$path_separator = Utils\is_windows() ? ';' : ':';
120122
$env = array(
121-
'PATH' => $bin_dir . ':' . $vendor_dir . ':' . getenv( 'PATH' ),
123+
'PATH' => $bin_dir . $path_separator . $vendor_dir . $path_separator . getenv( 'PATH' ),
122124
'BEHAT_RUN' => 1,
123125
'HOME' => sys_get_temp_dir() . '/wp-cli-home',
124126
);
@@ -328,20 +330,57 @@ public function getHookDefinitionResources() {
328330
}
329331

330332
/**
331-
* Replace {VARIABLE_NAME}. Note that variable names can only contain uppercase letters and underscores (no numbers).
333+
* Replace standard {VARIABLE_NAME} variables and the special {INVOKE_WP_CLI_WITH_PHP_ARGS-args} and {WP_VERSION-version-latest} variables.
334+
* Note that standard variable names can only contain uppercase letters, digits and underscores and cannot begin with a digit.
332335
*/
333336
public function replace_variables( $str ) {
334-
$ret = preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str );
337+
if ( false !== strpos( $str, '{INVOKE_WP_CLI_WITH_PHP_ARGS-' ) ) {
338+
$str = $this->replace_invoke_wp_cli_with_php_args( $str );
339+
}
340+
$str = preg_replace_callback( '/\{([A-Z_][A-Z_0-9]*)\}/', array( $this, 'replace_var' ), $str );
335341
if ( false !== strpos( $str, '{WP_VERSION-' ) ) {
336-
$ret = $this->_replace_wp_versions( $ret );
342+
$str = $this->replace_wp_versions( $str );
343+
}
344+
return $str;
345+
}
346+
347+
/**
348+
* Substitute {INVOKE_WP_CLI_WITH_PHP_ARGS-args} variables.
349+
*/
350+
private function replace_invoke_wp_cli_with_php_args( $str ) {
351+
static $phar_path = null, $shell_path = null;
352+
353+
if ( null === $phar_path ) {
354+
$phar_path = false;
355+
$phar_begin = '#!/usr/bin/env php';
356+
$phar_begin_len = strlen( $phar_begin );
357+
if ( ( $bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ) && file_exists( $bin_dir . '/wp' ) && $phar_begin === file_get_contents( $bin_dir . '/wp', false, null, 0, $phar_begin_len ) ) {
358+
$phar_path = $bin_dir . '/wp';
359+
} else {
360+
$src_dir = dirname( dirname( __DIR__ ) );
361+
$bin_path = $src_dir . '/bin/wp';
362+
$vendor_bin_path = $src_dir . '/vendor/bin/wp';
363+
if ( file_exists( $bin_path ) && is_executable( $bin_path ) ) {
364+
$shell_path = $bin_path;
365+
} elseif ( file_exists( $vendor_bin_path ) && is_executable( $vendor_bin_path ) ) {
366+
$shell_path = $vendor_bin_path;
367+
} else {
368+
$shell_path = 'wp';
369+
}
370+
}
337371
}
338-
return $ret;
372+
373+
$str = preg_replace_callback( '/{INVOKE_WP_CLI_WITH_PHP_ARGS-([^}]*)}/', function ( $matches ) use ( $phar_path, $shell_path ) {
374+
return $phar_path ? "php {$matches[1]} {$phar_path}" : ( 'WP_CLI_PHP_ARGS=' . escapeshellarg( $matches[1] ) . ' ' . $shell_path );
375+
}, $str );
376+
377+
return $str;
339378
}
340379

341380
/**
342381
* Replace variables callback.
343382
*/
344-
private function _replace_var( $matches ) {
383+
private function replace_var( $matches ) {
345384
$cmd = $matches[0];
346385

347386
foreach ( array_slice( $matches, 1 ) as $key ) {
@@ -352,9 +391,9 @@ private function _replace_var( $matches ) {
352391
}
353392

354393
/**
355-
* Substitute "{WP_VERSION-version-latest}" variables.
394+
* Substitute {WP_VERSION-version-latest} variables.
356395
*/
357-
private function _replace_wp_versions( $str ) {
396+
private function replace_wp_versions( $str ) {
358397
static $wp_versions = null;
359398
if ( null === $wp_versions ) {
360399
$wp_versions = array();

features/bootstrap/Process.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace WP_CLI;
44

5+
use WP_CLI\Utils;
6+
57
/**
68
* Run a system process, and learn what happened.
79
*/
@@ -67,7 +69,7 @@ private function __construct() {}
6769
public function run() {
6870
$start_time = microtime( true );
6971

70-
$proc = proc_open( $this->command, self::$descriptors, $pipes, $this->cwd, $this->env );
72+
$proc = Utils\proc_open_compat( $this->command, self::$descriptors, $pipes, $this->cwd, $this->env );
7173

7274
$stdout = stream_get_contents( $pipes[1] );
7375
fclose( $pipes[1] );

features/bootstrap/utils.php

Lines changed: 151 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,11 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {
383383

384384
$editor = getenv( 'EDITOR' );
385385
if ( ! $editor ) {
386-
$editor = 'vi';
387-
388-
if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) {
389-
$editor = 'notepad';
390-
}
386+
$editor = is_windows() ? 'notepad' : 'vi';
391387
}
392388

393389
$descriptorspec = array( STDIN, STDOUT, STDERR );
394-
$process = proc_open( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes );
390+
$process = proc_open_compat( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes );
395391
$r = proc_close( $process );
396392
if ( $r ) {
397393
exit( $r );
@@ -453,7 +449,7 @@ function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) {
453449

454450
$final_cmd = force_env_on_nix_systems( $cmd ) . assoc_args_to_str( $assoc_args );
455451

456-
$proc = proc_open( $final_cmd, $descriptors, $pipes );
452+
$proc = proc_open_compat( $final_cmd, $descriptors, $pipes );
457453
if ( ! $proc ) {
458454
exit( 1 );
459455
}
@@ -515,14 +511,15 @@ function mustache_render( $template_name, $data = array() ) {
515511
*
516512
* @param string $message Text to display before the progress bar.
517513
* @param integer $count Total number of ticks to be performed.
514+
* @param int $interval Optional. The interval in milliseconds between updates. Default 100.
518515
* @return cli\progress\Bar|WP_CLI\NoOp
519516
*/
520-
function make_progress_bar( $message, $count ) {
517+
function make_progress_bar( $message, $count, $interval = 100 ) {
521518
if ( \cli\Shell::isPiped() ) {
522519
return new \WP_CLI\NoOp;
523520
}
524521

525-
return new \cli\progress\Bar( $message, $count );
522+
return new \cli\progress\Bar( $message, $count, $interval );
526523
}
527524

528525
function parse_url( $url ) {
@@ -775,6 +772,16 @@ function trailingslashit( $string ) {
775772
return rtrim( $string, '/\\' ) . '/';
776773
}
777774

775+
/**
776+
* Convert Windows EOLs to *nix.
777+
*
778+
* @param string $str String to convert.
779+
* @return string String with carriage return / newline pairs reduced to newlines.
780+
*/
781+
function normalize_eols( $str ) {
782+
return str_replace( "\r\n", "\n", $str );
783+
}
784+
778785
/**
779786
* Get the system's temp directory. Warns user if it isn't writable.
780787
*
@@ -790,14 +797,8 @@ function get_temp_dir() {
790797
return $temp;
791798
}
792799

793-
$temp = '/tmp/';
794-
795-
// `sys_get_temp_dir()` introduced PHP 5.2.1.
796-
if ( $try = sys_get_temp_dir() ) {
797-
$temp = trailingslashit( $try );
798-
} elseif ( $try = ini_get( 'upload_tmp_dir' ) ) {
799-
$temp = trailingslashit( $try );
800-
}
800+
// `sys_get_temp_dir()` introduced PHP 5.2.1. Will always return something.
801+
$temp = trailingslashit( sys_get_temp_dir() );
801802

802803
if ( ! is_writable( $temp ) ) {
803804
\WP_CLI::warning( "Temp directory isn't writable: {$temp}" );
@@ -1110,7 +1111,9 @@ function glob_brace( $pattern, $dummy_flags = null ) {
11101111
function get_suggestion( $target, array $options, $threshold = 2 ) {
11111112

11121113
$suggestion_map = array(
1114+
'add' => 'create',
11131115
'check' => 'check-update',
1116+
'capability' => 'cap',
11141117
'clear' => 'flush',
11151118
'decrement' => 'decr',
11161119
'del' => 'delete',
@@ -1126,10 +1129,11 @@ function get_suggestion( $target, array $options, $threshold = 2 ) {
11261129
'regen' => 'regenerate',
11271130
'rep' => 'replace',
11281131
'repl' => 'replace',
1132+
'trash' => 'delete',
11291133
'v' => 'version',
11301134
);
11311135

1132-
if ( array_key_exists( $target, $suggestion_map ) ) {
1136+
if ( array_key_exists( $target, $suggestion_map ) && in_array( $suggestion_map[ $target ], $options, true ) ) {
11331137
return $suggestion_map[ $target ];
11341138
}
11351139

@@ -1318,3 +1322,132 @@ function get_php_binary() {
13181322

13191323
return 'php';
13201324
}
1325+
1326+
/**
1327+
* Windows compatible `proc_open()`.
1328+
* Works around bug in PHP, and also deals with *nix-like `ENV_VAR=blah cmd` environment variable prefixes.
1329+
*
1330+
* @access public
1331+
*
1332+
* @param string $command Command to execute.
1333+
* @param array $descriptorspec Indexed array of descriptor numbers and their values.
1334+
* @param array &$pipes Indexed array of file pointers that correspond to PHP's end of any pipes that are created.
1335+
* @param string $cwd Initial working directory for the command.
1336+
* @param array $env Array of environment variables.
1337+
* @param array $other_options Array of additional options (Windows only).
1338+
*
1339+
* @return string Command stripped of any environment variable settings.
1340+
*/
1341+
function proc_open_compat( $cmd, $descriptorspec, &$pipes, $cwd = null, $env = null, $other_options = null ) {
1342+
if ( is_windows() ) {
1343+
// Need to encompass the whole command in double quotes - PHP bug https://bugs.php.net/bug.php?id=49139
1344+
$cmd = '"' . _proc_open_compat_win_env( $cmd, $env ) . '"';
1345+
}
1346+
return proc_open( $cmd, $descriptorspec, $pipes, $cwd, $env, $other_options );
1347+
}
1348+
1349+
/**
1350+
* For use by `proc_open_compat()` only. Separated out for ease of testing. Windows only.
1351+
* Turns *nix-like `ENV_VAR=blah command` environment variable prefixes into stripped `cmd` with prefixed environment variables added to passed in environment array.
1352+
*
1353+
* @access private
1354+
*
1355+
* @param string $command Command to execute.
1356+
* @param array &$env Array of existing environment variables. Will be modified if any settings in command.
1357+
*
1358+
* @return string Command stripped of any environment variable settings.
1359+
*/
1360+
function _proc_open_compat_win_env( $cmd, &$env ) {
1361+
if ( false !== strpos( $cmd, '=' ) ) {
1362+
while ( preg_match( '/^([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|[^ ]*) /', $cmd, $matches ) ) {
1363+
$cmd = substr( $cmd, strlen( $matches[0] ) );
1364+
if ( null === $env ) {
1365+
$env = array();
1366+
}
1367+
$env[ $matches[1] ] = isset( $matches[2][0] ) && '"' === $matches[2][0] ? substr( $matches[2], 1, -1 ) : $matches[2];
1368+
}
1369+
}
1370+
return $cmd;
1371+
}
1372+
1373+
/**
1374+
* First half of escaping for LIKE special characters % and _ before preparing for MySQL.
1375+
*
1376+
* Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
1377+
*
1378+
* Copied from core "wp-includes/wp-db.php". Avoids dependency on WP 4.4 wpdb.
1379+
*
1380+
* @access public
1381+
*
1382+
* @param string $text The raw text to be escaped. The input typed by the user should have no
1383+
* extra or deleted slashes.
1384+
* @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare()
1385+
* or real_escape next.
1386+
*/
1387+
function esc_like( $text ) {
1388+
return addcslashes( $text, '_%\\' );
1389+
}
1390+
1391+
/**
1392+
* Escapes (backticks) MySQL identifiers (aka schema object names) - i.e. column names, table names, and database/index/alias/view etc names.
1393+
* See https://dev.mysql.com/doc/refman/5.5/en/identifiers.html
1394+
*
1395+
* @param string|array $idents A single identifier or an array of identifiers.
1396+
* @return string|array An escaped string if given a string, or an array of escaped strings if given an array of strings.
1397+
*/
1398+
function esc_sql_ident( $idents ) {
1399+
$backtick = function ( $v ) {
1400+
// Escape any backticks in the identifier by doubling.
1401+
return '`' . str_replace( '`', '``', $v ) . '`';
1402+
};
1403+
if ( is_string( $idents ) ) {
1404+
return $backtick( $idents );
1405+
}
1406+
return array_map( $backtick, $idents );
1407+
}
1408+
1409+
/**
1410+
* Check whether a given string is a valid JSON representation.
1411+
*
1412+
* @param string $argument String to evaluate.
1413+
* @param bool $ignore_scalars Optional. Whether to ignore scalar values.
1414+
* Defaults to true.
1415+
*
1416+
* @return bool Whether the provided string is a valid JSON representation.
1417+
*/
1418+
function is_json( $argument, $ignore_scalars = true ) {
1419+
if ( ! is_string( $argument ) || '' === $argument ) {
1420+
return false;
1421+
}
1422+
1423+
if ( $ignore_scalars && ! in_array( $argument[0], array( '{', '[' ), true ) ) {
1424+
return false;
1425+
}
1426+
1427+
json_decode( $argument, $assoc = true );
1428+
1429+
return json_last_error() === JSON_ERROR_NONE;
1430+
}
1431+
1432+
/**
1433+
* Parse known shell arrays included in the $assoc_args array.
1434+
*
1435+
* @param array $assoc_args Associative array of arguments.
1436+
* @param array $array_arguments Array of argument keys that should receive an
1437+
* array through the shell.
1438+
*
1439+
* @return array
1440+
*/
1441+
function parse_shell_arrays( $assoc_args, $array_arguments ) {
1442+
if ( empty( $assoc_args ) || empty( $array_arguments ) ) {
1443+
return $assoc_args;
1444+
}
1445+
1446+
foreach ( $array_arguments as $key ) {
1447+
if ( array_key_exists( $key, $assoc_args ) && is_json( $assoc_args[ $key ] ) ) {
1448+
$assoc_args[ $key ] = json_decode( $assoc_args[ $key ], $assoc = true );
1449+
}
1450+
}
1451+
1452+
return $assoc_args;
1453+
}

0 commit comments

Comments
 (0)