Skip to content

Commit

Permalink
Merge pull request #64 from wp-cli/prerelease-1.1.5
Browse files Browse the repository at this point in the history
v1.1.5 prerelease: refresh test suite, require wp-cli ^1.5.
  • Loading branch information
miya0001 authored Jan 29, 2018
2 parents 3c4372a + 1baff84 commit c688e51
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 43 deletions.
15 changes: 6 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,20 @@
"psr-4": {
"": "src/"
},
"files": [
"search-replace-command.php"
]
},
"require": {
"wp-cli/wp-cli": "*"
"files": [ "search-replace-command.php" ]
},
"require": {},
"require-dev": {
"behat/behat": "~2.5"
"behat/behat": "~2.5",
"wp-cli/wp-cli": "^1.5"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
},
"bundled": true,
"commands": [
"search-replace"
],
"bundled": true
]
}
}
59 changes: 49 additions & 10 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
private $running_procs = array();

/**
* 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:
* 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.
* 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: RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. One is set on use: INVOKE_WP_CLI_WITH_PHP_ARGS-args.
* Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
*/
public $variables = array();

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

/**
* Replace {VARIABLE_NAME}. Note that variable names can only contain uppercase letters and underscores (no numbers).
* Replace standard {VARIABLE_NAME} variables and the special {INVOKE_WP_CLI_WITH_PHP_ARGS-args} and {WP_VERSION-version-latest} variables.
* Note that standard variable names can only contain uppercase letters, digits and underscores and cannot begin with a digit.
*/
public function replace_variables( $str ) {
$ret = preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str );
if ( false !== strpos( $str, '{INVOKE_WP_CLI_WITH_PHP_ARGS-' ) ) {
$str = $this->replace_invoke_wp_cli_with_php_args( $str );
}
$str = preg_replace_callback( '/\{([A-Z_][A-Z_0-9]*)\}/', array( $this, 'replace_var' ), $str );
if ( false !== strpos( $str, '{WP_VERSION-' ) ) {
$ret = $this->_replace_wp_versions( $ret );
$str = $this->replace_wp_versions( $str );
}
return $str;
}

/**
* Substitute {INVOKE_WP_CLI_WITH_PHP_ARGS-args} variables.
*/
private function replace_invoke_wp_cli_with_php_args( $str ) {
static $phar_path = null, $shell_path = null;

if ( null === $phar_path ) {
$phar_path = false;
$phar_begin = '#!/usr/bin/env php';
$phar_begin_len = strlen( $phar_begin );
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 ) ) {
$phar_path = $bin_dir . '/wp';
} else {
$src_dir = dirname( dirname( __DIR__ ) );
$bin_path = $src_dir . '/bin/wp';
$vendor_bin_path = $src_dir . '/vendor/bin/wp';
if ( file_exists( $bin_path ) && is_executable( $bin_path ) ) {
$shell_path = $bin_path;
} elseif ( file_exists( $vendor_bin_path ) && is_executable( $vendor_bin_path ) ) {
$shell_path = $vendor_bin_path;
} else {
$shell_path = 'wp';
}
}
}
return $ret;

$str = preg_replace_callback( '/{INVOKE_WP_CLI_WITH_PHP_ARGS-([^}]*)}/', function ( $matches ) use ( $phar_path, $shell_path ) {
return $phar_path ? "php {$matches[1]} {$phar_path}" : ( 'WP_CLI_PHP_ARGS=' . escapeshellarg( $matches[1] ) . ' ' . $shell_path );
}, $str );

return $str;
}

/**
* Replace variables callback.
*/
private function _replace_var( $matches ) {
private function replace_var( $matches ) {
$cmd = $matches[0];

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

/**
* Substitute "{WP_VERSION-version-latest}" variables.
* Substitute {WP_VERSION-version-latest} variables.
*/
private function _replace_wp_versions( $str ) {
private function replace_wp_versions( $str ) {
static $wp_versions = null;
if ( null === $wp_versions ) {
$wp_versions = array();
Expand Down
4 changes: 3 additions & 1 deletion features/bootstrap/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace WP_CLI;

use WP_CLI\Utils;

/**
* Run a system process, and learn what happened.
*/
Expand Down Expand Up @@ -67,7 +69,7 @@ private function __construct() {}
public function run() {
$start_time = microtime( true );

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

$stdout = stream_get_contents( $pipes[1] );
fclose( $pipes[1] );
Expand Down
169 changes: 151 additions & 18 deletions features/bootstrap/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -383,15 +383,11 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {

$editor = getenv( 'EDITOR' );
if ( ! $editor ) {
$editor = 'vi';

if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) {
$editor = 'notepad';
}
$editor = is_windows() ? 'notepad' : 'vi';
}

$descriptorspec = array( STDIN, STDOUT, STDERR );
$process = proc_open( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes );
$process = proc_open_compat( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes );
$r = proc_close( $process );
if ( $r ) {
exit( $r );
Expand Down Expand Up @@ -453,7 +449,7 @@ function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) {

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

$proc = proc_open( $final_cmd, $descriptors, $pipes );
$proc = proc_open_compat( $final_cmd, $descriptors, $pipes );
if ( ! $proc ) {
exit( 1 );
}
Expand Down Expand Up @@ -515,14 +511,15 @@ function mustache_render( $template_name, $data = array() ) {
*
* @param string $message Text to display before the progress bar.
* @param integer $count Total number of ticks to be performed.
* @param int $interval Optional. The interval in milliseconds between updates. Default 100.
* @return cli\progress\Bar|WP_CLI\NoOp
*/
function make_progress_bar( $message, $count ) {
function make_progress_bar( $message, $count, $interval = 100 ) {
if ( \cli\Shell::isPiped() ) {
return new \WP_CLI\NoOp;
}

return new \cli\progress\Bar( $message, $count );
return new \cli\progress\Bar( $message, $count, $interval );
}

function parse_url( $url ) {
Expand Down Expand Up @@ -775,6 +772,16 @@ function trailingslashit( $string ) {
return rtrim( $string, '/\\' ) . '/';
}

/**
* Convert Windows EOLs to *nix.
*
* @param string $str String to convert.
* @return string String with carriage return / newline pairs reduced to newlines.
*/
function normalize_eols( $str ) {
return str_replace( "\r\n", "\n", $str );
}

/**
* Get the system's temp directory. Warns user if it isn't writable.
*
Expand All @@ -790,14 +797,8 @@ function get_temp_dir() {
return $temp;
}

$temp = '/tmp/';

// `sys_get_temp_dir()` introduced PHP 5.2.1.
if ( $try = sys_get_temp_dir() ) {
$temp = trailingslashit( $try );
} elseif ( $try = ini_get( 'upload_tmp_dir' ) ) {
$temp = trailingslashit( $try );
}
// `sys_get_temp_dir()` introduced PHP 5.2.1. Will always return something.
$temp = trailingslashit( sys_get_temp_dir() );

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

$suggestion_map = array(
'add' => 'create',
'check' => 'check-update',
'capability' => 'cap',
'clear' => 'flush',
'decrement' => 'decr',
'del' => 'delete',
Expand All @@ -1126,10 +1129,11 @@ function get_suggestion( $target, array $options, $threshold = 2 ) {
'regen' => 'regenerate',
'rep' => 'replace',
'repl' => 'replace',
'trash' => 'delete',
'v' => 'version',
);

if ( array_key_exists( $target, $suggestion_map ) ) {
if ( array_key_exists( $target, $suggestion_map ) && in_array( $suggestion_map[ $target ], $options, true ) ) {
return $suggestion_map[ $target ];
}

Expand Down Expand Up @@ -1318,3 +1322,132 @@ function get_php_binary() {

return 'php';
}

/**
* Windows compatible `proc_open()`.
* Works around bug in PHP, and also deals with *nix-like `ENV_VAR=blah cmd` environment variable prefixes.
*
* @access public
*
* @param string $command Command to execute.
* @param array $descriptorspec Indexed array of descriptor numbers and their values.
* @param array &$pipes Indexed array of file pointers that correspond to PHP's end of any pipes that are created.
* @param string $cwd Initial working directory for the command.
* @param array $env Array of environment variables.
* @param array $other_options Array of additional options (Windows only).
*
* @return string Command stripped of any environment variable settings.
*/
function proc_open_compat( $cmd, $descriptorspec, &$pipes, $cwd = null, $env = null, $other_options = null ) {
if ( is_windows() ) {
// Need to encompass the whole command in double quotes - PHP bug https://bugs.php.net/bug.php?id=49139
$cmd = '"' . _proc_open_compat_win_env( $cmd, $env ) . '"';
}
return proc_open( $cmd, $descriptorspec, $pipes, $cwd, $env, $other_options );
}

/**
* For use by `proc_open_compat()` only. Separated out for ease of testing. Windows only.
* Turns *nix-like `ENV_VAR=blah command` environment variable prefixes into stripped `cmd` with prefixed environment variables added to passed in environment array.
*
* @access private
*
* @param string $command Command to execute.
* @param array &$env Array of existing environment variables. Will be modified if any settings in command.
*
* @return string Command stripped of any environment variable settings.
*/
function _proc_open_compat_win_env( $cmd, &$env ) {
if ( false !== strpos( $cmd, '=' ) ) {
while ( preg_match( '/^([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|[^ ]*) /', $cmd, $matches ) ) {
$cmd = substr( $cmd, strlen( $matches[0] ) );
if ( null === $env ) {
$env = array();
}
$env[ $matches[1] ] = isset( $matches[2][0] ) && '"' === $matches[2][0] ? substr( $matches[2], 1, -1 ) : $matches[2];
}
}
return $cmd;
}

/**
* First half of escaping for LIKE special characters % and _ before preparing for MySQL.
*
* Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
*
* Copied from core "wp-includes/wp-db.php". Avoids dependency on WP 4.4 wpdb.
*
* @access public
*
* @param string $text The raw text to be escaped. The input typed by the user should have no
* extra or deleted slashes.
* @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare()
* or real_escape next.
*/
function esc_like( $text ) {
return addcslashes( $text, '_%\\' );
}

/**
* Escapes (backticks) MySQL identifiers (aka schema object names) - i.e. column names, table names, and database/index/alias/view etc names.
* See https://dev.mysql.com/doc/refman/5.5/en/identifiers.html
*
* @param string|array $idents A single identifier or an array of identifiers.
* @return string|array An escaped string if given a string, or an array of escaped strings if given an array of strings.
*/
function esc_sql_ident( $idents ) {
$backtick = function ( $v ) {
// Escape any backticks in the identifier by doubling.
return '`' . str_replace( '`', '``', $v ) . '`';
};
if ( is_string( $idents ) ) {
return $backtick( $idents );
}
return array_map( $backtick, $idents );
}

/**
* Check whether a given string is a valid JSON representation.
*
* @param string $argument String to evaluate.
* @param bool $ignore_scalars Optional. Whether to ignore scalar values.
* Defaults to true.
*
* @return bool Whether the provided string is a valid JSON representation.
*/
function is_json( $argument, $ignore_scalars = true ) {
if ( ! is_string( $argument ) || '' === $argument ) {
return false;
}

if ( $ignore_scalars && ! in_array( $argument[0], array( '{', '[' ), true ) ) {
return false;
}

json_decode( $argument, $assoc = true );

return json_last_error() === JSON_ERROR_NONE;
}

/**
* Parse known shell arrays included in the $assoc_args array.
*
* @param array $assoc_args Associative array of arguments.
* @param array $array_arguments Array of argument keys that should receive an
* array through the shell.
*
* @return array
*/
function parse_shell_arrays( $assoc_args, $array_arguments ) {
if ( empty( $assoc_args ) || empty( $array_arguments ) ) {
return $assoc_args;
}

foreach ( $array_arguments as $key ) {
if ( array_key_exists( $key, $assoc_args ) && is_json( $assoc_args[ $key ] ) ) {
$assoc_args[ $key ] = json_decode( $assoc_args[ $key ], $assoc = true );
}
}

return $assoc_args;
}
Loading

0 comments on commit c688e51

Please sign in to comment.