diff --git a/.gitignore b/.gitignore index 930cf9e..fb1c9c4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ node_modules/ vendor/ *.zip *.tar.gz +*.log diff --git a/.travis.yml b/.travis.yml index e96554a..74a27ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ sudo: false +dist: trusty language: php +php: 7.2 notifications: email: @@ -13,28 +15,56 @@ branches: cache: directories: - - vendor - $HOME/.composer/cache env: global: - - WP_CLI_BIN_DIR=/tmp/wp-cli-phar + - PATH="$TRAVIS_BUILD_DIR/vendor/bin:$PATH" + - WP_CLI_BIN_DIR="$TRAVIS_BUILD_DIR/vendor/bin" -matrix: - include: - - php: 7.1 - env: WP_VERSION=latest - - php: 7.0 - env: WP_VERSION=latest - - php: 5.6 - env: WP_VERSION=latest - - php: 5.6 - env: WP_VERSION=trunk - - php: 5.4 - env: WP_VERSION=latest - -before_script: +before_install: + - | + # Remove Xdebug for a huge performance increase: + if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then + phpenv config-rm xdebug.ini + else + echo "xdebug.ini does not exist" + fi + - | + # Raise PHP memory limit to 2048MB + echo 'memory_limit = 2048M' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - composer validate - - bash bin/install-package-tests.sh -script: ./bin/test.sh +install: + - composer install + - composer prepare-tests + +script: + - composer phpunit + - composer behat || composer behat-rerun + +jobs: + include: + - stage: sniff + script: + - composer lint + - composer phpcs + env: BUILD=sniff + - stage: test + php: 7.2 + env: WP_VERSION=latest + - stage: test + php: 7.1 + env: WP_VERSION=latest + - stage: test + php: 7.0 + env: WP_VERSION=latest + - stage: test + php: 5.6 + env: WP_VERSION=latest + - stage: test + php: 5.6 + env: WP_VERSION=trunk + - stage: test + php: 5.4 + env: WP_VERSION=latest diff --git a/bin/install-package-tests.sh b/bin/install-package-tests.sh deleted file mode 100755 index e7e5979..0000000 --- a/bin/install-package-tests.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -PACKAGE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../ && pwd )" - -download() { - if [ `which curl` ]; then - curl -s "$1" > "$2"; - elif [ `which wget` ]; then - wget -nv -O "$2" "$1" - fi -} - -install_wp_cli() { - - # the Behat test suite will pick up the executable found in $WP_CLI_BIN_DIR - mkdir -p $WP_CLI_BIN_DIR - download https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar $WP_CLI_BIN_DIR/wp - chmod +x $WP_CLI_BIN_DIR/wp - -} - -download_behat() { - - cd $PACKAGE_DIR - download https://getcomposer.org/installer installer - php installer - php composer.phar require --dev behat/behat='~2.5' - -} - -install_db() { - mysql -e 'CREATE DATABASE IF NOT EXISTS wp_cli_test;' -uroot - mysql -e 'GRANT ALL PRIVILEGES ON wp_cli_test.* TO "wp_cli_test"@"localhost" IDENTIFIED BY "password1"' -uroot -} - -install_wp_cli -download_behat -install_db diff --git a/bin/test.sh b/bin/test.sh deleted file mode 100755 index 38749ad..0000000 --- a/bin/test.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -set -ex - -BEHAT_TAGS=$(php utils/behat-tags.php) - -# Run the functional tests -vendor/bin/behat --format progress $BEHAT_TAGS --strict diff --git a/composer.json b/composer.json index 4b09770..173ec24 100644 --- a/composer.json +++ b/composer.json @@ -1,25 +1,24 @@ { "name": "wp-cli/profile-command", - "description": "Quickly identify what's slow with WordPress.", "type": "wp-cli-package", + "description": "Quickly identify what's slow with WordPress.", "homepage": "https://runcommand.io/wp/profile/", "license": "MIT", "authors": [], - "minimum-stability": "dev", - "prefer-stable": true, - "autoload": { - "files": [ "command.php" ] - }, "require": { "php": ">=5.4", - "wp-cli/wp-cli": "*" + "wp-cli/wp-cli": "^2" }, "require-dev": { - "behat/behat": "~2.5" + "wp-cli/wp-cli-tests": "^2.0.7" + }, + "config": { + "process-timeout": 7200, + "sort-packages": true }, "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" }, "commands": [ "profile stage", @@ -38,5 +37,26 @@ "post": "bin/readme/overview-body.md" } } + }, + "autoload": { + "files": [ + "command.php" + ] + }, + "minimum-stability": "dev", + "prefer-stable": true, + "scripts": { + "behat": "run-behat-tests", + "behat-rerun": "rerun-behat-tests", + "lint": "run-linter-tests", + "phpcs": "run-phpcs-tests", + "phpunit": "run-php-unit-tests", + "prepare-tests": "install-package-tests", + "test": [ + "@lint", + "@phpcs", + "@phpunit", + "@behat" + ] } } diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php deleted file mode 100644 index c9846ae..0000000 --- a/features/bootstrap/FeatureContext.php +++ /dev/null @@ -1,342 +0,0 @@ -autoload->files ) ) { - $contents = 'require:' . PHP_EOL; - foreach( $composer->autoload->files as $file ) { - $contents .= ' - ' . dirname( dirname( dirname( __FILE__ ) ) ) . '/' . $file; - } - @mkdir( sys_get_temp_dir() . '/wp-cli-package-test/' ); - $project_config = sys_get_temp_dir() . '/wp-cli-package-test/config.yml'; - file_put_contents( $project_config, $contents ); - putenv( 'WP_CLI_CONFIG_PATH=' . $project_config ); - } - } -// Inside WP-CLI -} else { - require_once __DIR__ . '/../../php/utils.php'; - require_once __DIR__ . '/../../php/WP_CLI/Process.php'; - require_once __DIR__ . '/../../php/WP_CLI/ProcessRun.php'; - require_once __DIR__ . '/../../vendor/autoload.php'; -} - -/** - * Features context. - */ -class FeatureContext extends BehatContext implements ClosuredContextInterface { - - private static $cache_dir, $suite_cache_dir; - - private static $db_settings = array( - 'dbname' => 'wp_cli_test', - 'dbuser' => 'wp_cli_test', - 'dbpass' => 'password1', - 'dbhost' => '127.0.0.1', - ); - - private $running_procs = array(); - - public $variables = array(); - - /** - * Get the environment variables required for launched `wp` processes - * @beforeSuite - */ - 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" ); - $env = array( - 'PATH' => $bin_dir . ':' . getenv( 'PATH' ), - 'BEHAT_RUN' => 1, - 'HOME' => '/tmp/wp-cli-home', - ); - if ( $config_path = getenv( 'WP_CLI_CONFIG_PATH' ) ) { - $env['WP_CLI_CONFIG_PATH'] = $config_path; - } - return $env; - } - - // We cache the results of `wp core download` to improve test performance - // Ideally, we'd cache at the HTTP layer for more reliable tests - private static function cache_wp_files() { - self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test core-download-cache'; - - if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) - return; - - $cmd = Utils\esc_cmd( 'wp core download --force --path=%s', self::$cache_dir ); - if ( getenv( 'WP_VERSION' ) ) { - $cmd .= Utils\esc_cmd( ' --version=%s', getenv( 'WP_VERSION' ) ); - } - Process::create( $cmd, null, self::get_process_env_variables() )->run_check(); - } - - /** - * @BeforeSuite - */ - public static function prepare( SuiteEvent $event ) { - $result = Process::create( 'wp cli info', null, self::get_process_env_variables() )->run_check(); - echo PHP_EOL; - echo $result->stdout; - echo PHP_EOL; - self::cache_wp_files(); - $result = Process::create( Utils\esc_cmd( 'wp core version --path=%s', self::$cache_dir ) , null, self::get_process_env_variables() )->run_check(); - echo 'WordPress ' . $result->stdout; - echo PHP_EOL; - } - - /** - * @AfterSuite - */ - public static function afterSuite( SuiteEvent $event ) { - if ( self::$suite_cache_dir ) { - Process::create( Utils\esc_cmd( 'rm -r %s', self::$suite_cache_dir ), null, self::get_process_env_variables() )->run(); - } - } - - /** - * @BeforeScenario - */ - public function beforeScenario( $event ) { - $this->variables['SRC_DIR'] = realpath( __DIR__ . '/../..' ); - } - - /** - * @AfterScenario - */ - public function afterScenario( $event ) { - if ( isset( $this->variables['RUN_DIR'] ) ) { - // remove altered WP install, unless there's an error - if ( $event->getResult() < 4 ) { - $this->proc( Utils\esc_cmd( 'rm -r %s', $this->variables['RUN_DIR'] ) )->run(); - } - } - - // Remove WP-CLI package directory - if ( isset( $this->variables['PACKAGE_PATH'] ) ) { - $this->proc( Utils\esc_cmd( 'rm -rf %s', $this->variables['PACKAGE_PATH'] ) )->run(); - } - - foreach ( $this->running_procs as $proc ) { - self::terminate_proc( $proc ); - } - } - - /** - * Terminate a process and any of its children. - */ - private static function terminate_proc( $proc ) { - $status = proc_get_status( $proc ); - - $master_pid = $status['pid']; - - $output = `ps -o ppid,pid,command | grep $master_pid`; - - foreach ( explode( PHP_EOL, $output ) as $line ) { - if ( preg_match( '/^\s*(\d+)\s+(\d+)/', $line, $matches ) ) { - $parent = $matches[1]; - $child = $matches[2]; - - if ( $parent == $master_pid ) { - if ( ! posix_kill( (int) $child, 9 ) ) { - throw new RuntimeException( posix_strerror( posix_get_last_error() ) ); - } - } - } - } - - if ( ! posix_kill( (int) $master_pid, 9 ) ) { - throw new RuntimeException( posix_strerror( posix_get_last_error() ) ); - } - } - - public static function create_cache_dir() { - self::$suite_cache_dir = sys_get_temp_dir() . '/' . uniqid( "wp-cli-test-suite-cache-", TRUE ); - mkdir( self::$suite_cache_dir ); - return self::$suite_cache_dir; - } - - /** - * Initializes context. - * Every scenario gets it's own context object. - * - * @param array $parameters context parameters (set them up through behat.yml) - */ - public function __construct( array $parameters ) { - $this->drop_db(); - $this->set_cache_dir(); - $this->variables['CORE_CONFIG_SETTINGS'] = Utils\assoc_args_to_str( self::$db_settings ); - } - - public function getStepDefinitionResources() { - return glob( __DIR__ . '/../steps/*.php' ); - } - - public function getHookDefinitionResources() { - return array(); - } - - public function replace_variables( $str ) { - return preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str ); - } - - private function _replace_var( $matches ) { - $cmd = $matches[0]; - - foreach ( array_slice( $matches, 1 ) as $key ) { - $cmd = str_replace( '{' . $key . '}', $this->variables[ $key ], $cmd ); - } - - return $cmd; - } - - public function create_run_dir() { - if ( !isset( $this->variables['RUN_DIR'] ) ) { - $this->variables['RUN_DIR'] = sys_get_temp_dir() . '/' . uniqid( "wp-cli-test-run-", TRUE ); - mkdir( $this->variables['RUN_DIR'] ); - } - } - - public function build_phar( $version = 'same' ) { - $this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/' . uniqid( "wp-cli-build-", TRUE ) . '.phar'; - - $this->proc( Utils\esc_cmd( - 'php -dphar.readonly=0 %1$s %2$s --version=%3$s && chmod +x %2$s', - __DIR__ . '/../../utils/make-phar.php', - $this->variables['PHAR_PATH'], - $version - ) )->run_check(); - } - - private function set_cache_dir() { - $path = sys_get_temp_dir() . '/wp-cli-test-cache'; - $this->proc( Utils\esc_cmd( 'mkdir -p %s', $path ) )->run_check(); - $this->variables['CACHE_DIR'] = $path; - } - - private static function run_sql( $sql ) { - Utils\run_mysql_command( 'mysql --no-defaults', array( - 'execute' => $sql, - 'host' => self::$db_settings['dbhost'], - 'user' => self::$db_settings['dbuser'], - 'pass' => self::$db_settings['dbpass'], - ) ); - } - - public function create_db() { - $dbname = self::$db_settings['dbname']; - self::run_sql( "CREATE DATABASE IF NOT EXISTS $dbname" ); - } - - public function drop_db() { - $dbname = self::$db_settings['dbname']; - self::run_sql( "DROP DATABASE IF EXISTS $dbname" ); - } - - public function proc( $command, $assoc_args = array(), $path = '' ) { - if ( !empty( $assoc_args ) ) - $command .= Utils\assoc_args_to_str( $assoc_args ); - - $env = self::get_process_env_variables(); - if ( isset( $this->variables['SUITE_CACHE_DIR'] ) ) { - $env['WP_CLI_CACHE_DIR'] = $this->variables['SUITE_CACHE_DIR']; - } - - if ( isset( $this->variables['RUN_DIR'] ) ) { - $cwd = "{$this->variables['RUN_DIR']}/{$path}"; - } else { - $cwd = null; - } - - return Process::create( $command, $cwd, $env ); - } - - /** - * Start a background process. Will automatically be closed when the tests finish. - */ - public function background_proc( $cmd ) { - $descriptors = array( - 0 => STDIN, - 1 => array( 'pipe', 'w' ), - 2 => array( 'pipe', 'w' ), - ); - - $proc = proc_open( $cmd, $descriptors, $pipes, $this->variables['RUN_DIR'], self::get_process_env_variables() ); - - sleep(1); - - $status = proc_get_status( $proc ); - - if ( !$status['running'] ) { - throw new RuntimeException( stream_get_contents( $pipes[2] ) ); - } else { - $this->running_procs[] = $proc; - } - } - - public function move_files( $src, $dest ) { - rename( $this->variables['RUN_DIR'] . "/$src", $this->variables['RUN_DIR'] . "/$dest" ); - } - - public function add_line_to_wp_config( &$wp_config_code, $line ) { - $token = "/* That's all, stop editing!"; - - $wp_config_code = str_replace( $token, "$line\n\n$token", $wp_config_code ); - } - - public function download_wp( $subdir = '' ) { - $dest_dir = $this->variables['RUN_DIR'] . "/$subdir"; - - if ( $subdir ) { - mkdir( $dest_dir ); - } - - $this->proc( Utils\esc_cmd( "cp -r %s/* %s", self::$cache_dir, $dest_dir ) )->run_check(); - - // disable emailing - mkdir( $dest_dir . '/wp-content/mu-plugins' ); - copy( __DIR__ . '/../extra/no-mail.php', $dest_dir . '/wp-content/mu-plugins/no-mail.php' ); - } - - public function create_config( $subdir = '' ) { - $params = self::$db_settings; - $params['dbprefix'] = $subdir ?: 'wp_'; - - $params['skip-salts'] = true; - $this->proc( 'wp core config', $params, $subdir )->run_check(); - } - - public function install_wp( $subdir = '' ) { - $this->create_db(); - $this->create_run_dir(); - $this->download_wp( $subdir ); - - $this->create_config( $subdir ); - - $install_args = array( - 'url' => 'http://example.com', - 'title' => 'WP CLI Site', - 'admin_user' => 'admin', - 'admin_email' => 'admin@example.com', - 'admin_password' => 'password1' - ); - - $this->proc( 'wp core install', $install_args, $subdir )->run_check(); - } -} - diff --git a/features/bootstrap/Process.php b/features/bootstrap/Process.php deleted file mode 100644 index 74939c5..0000000 --- a/features/bootstrap/Process.php +++ /dev/null @@ -1,75 +0,0 @@ -command = $command; - $proc->cwd = $cwd; - $proc->env = $env; - - return $proc; - } - - private $command, $cwd, $env; - - private function __construct() {} - - /** - * Run the command. - * - * @return ProcessRun - */ - public function run() { - $cwd = $this->cwd; - - $descriptors = array( - 0 => STDIN, - 1 => array( 'pipe', 'w' ), - 2 => array( 'pipe', 'w' ), - ); - - $proc = proc_open( $this->command, $descriptors, $pipes, $cwd, $this->env ); - - $stdout = stream_get_contents( $pipes[1] ); - fclose( $pipes[1] ); - - $stderr = stream_get_contents( $pipes[2] ); - fclose( $pipes[2] ); - - return new ProcessRun( array( - 'stdout' => $stdout, - 'stderr' => $stderr, - 'return_code' => proc_close( $proc ), - 'command' => $this->command, - 'cwd' => $cwd, - 'env' => $this->env - ) ); - } - - /** - * Run the command, but throw an Exception on error. - * - * @return ProcessRun - */ - public function run_check() { - $r = $this->run(); - - if ( $r->return_code || !empty( $r->STDERR ) ) { - throw new \RuntimeException( $r ); - } - - return $r; - } -} diff --git a/features/bootstrap/ProcessRun.php b/features/bootstrap/ProcessRun.php deleted file mode 100644 index 4611cfb..0000000 --- a/features/bootstrap/ProcessRun.php +++ /dev/null @@ -1,33 +0,0 @@ - $value ) { - $this->$key = $value; - } - } - - /** - * Return properties of executed command as a string. - * - * @return string - */ - public function __toString() { - $out = "$ $this->command\n"; - $out .= "$this->stdout\n$this->stderr"; - $out .= "cwd: $this->cwd\n"; - $out .= "exit status: $this->return_code"; - - return $out; - } - -} diff --git a/features/bootstrap/support.php b/features/bootstrap/support.php deleted file mode 100644 index 75ee5fb..0000000 --- a/features/bootstrap/support.php +++ /dev/null @@ -1,194 +0,0 @@ - $value ) { - if ( ! compareContents( $value, $actual->$name ) ) - return false; - } - } else if ( is_array( $expected ) ) { - foreach ( $expected as $key => $value ) { - if ( ! compareContents( $value, $actual[$key] ) ) - return false; - } - } else { - return $expected === $actual; - } - - return true; -} - -/** - * Compare two strings containing JSON to ensure that @a $actualJson contains at - * least what the JSON string @a $expectedJson contains. - * - * @return whether or not @a $actualJson contains @a $expectedJson - * @retval true @a $actualJson contains @a $expectedJson - * @retval false @a $actualJson does not contain @a $expectedJson - * - * @param[in] $actualJson the JSON string to be tested - * @param[in] $expectedJson the expected JSON string - * - * Examples: - * expected: {'a':1,'array':[1,3,5]} - * - * 1 ) - * actual: {'a':1,'b':2,'c':3,'array':[1,2,3,4,5]} - * return: true - * - * 2 ) - * actual: {'b':2,'c':3,'array':[1,2,3,4,5]} - * return: false - * element 'a' is missing from the root object - * - * 3 ) - * actual: {'a':0,'b':2,'c':3,'array':[1,2,3,4,5]} - * return: false - * the value of element 'a' is not 1 - * - * 4 ) - * actual: {'a':1,'b':2,'c':3,'array':[1,2,4,5]} - * return: false - * the contents of 'array' does not include 3 - */ -function checkThatJsonStringContainsJsonString( $actualJson, $expectedJson ) { - $actualValue = json_decode( $actualJson ); - $expectedValue = json_decode( $expectedJson ); - - if ( !$actualValue ) { - return false; - } - - return compareContents( $expectedValue, $actualValue ); -} - -/** - * Compare two strings to confirm $actualCSV contains $expectedCSV - * Both strings are expected to have headers for their CSVs. - * $actualCSV must match all data rows in $expectedCSV - * - * @param string A CSV string - * @param array A nested array of values - * @return bool Whether $actualCSV contains $expectedCSV - */ -function checkThatCsvStringContainsValues( $actualCSV, $expectedCSV ) { - $actualCSV = array_map( 'str_getcsv', explode( PHP_EOL, $actualCSV ) ); - - if ( empty( $actualCSV ) ) - return false; - - // Each sample must have headers - $actualHeaders = array_values( array_shift( $actualCSV ) ); - $expectedHeaders = array_values( array_shift( $expectedCSV ) ); - - // Each expectedCSV must exist somewhere in actualCSV in the proper column - $expectedResult = 0; - foreach ( $expectedCSV as $expected_row ) { - $expected_row = array_combine( $expectedHeaders, $expected_row ); - foreach ( $actualCSV as $actual_row ) { - - if ( count( $actualHeaders ) != count( $actual_row ) ) - continue; - - $actual_row = array_intersect_key( array_combine( $actualHeaders, $actual_row ), $expected_row ); - if ( $actual_row == $expected_row ) - $expectedResult++; - } - } - - return $expectedResult >= count( $expectedCSV ); -} - -/** - * Compare two strings containing YAML to ensure that @a $actualYaml contains at - * least what the YAML string @a $expectedYaml contains. - * - * @return whether or not @a $actualYaml contains @a $expectedJson - * @retval true @a $actualYaml contains @a $expectedJson - * @retval false @a $actualYaml does not contain @a $expectedJson - * - * @param[in] $actualYaml the YAML string to be tested - * @param[in] $expectedYaml the expected YAML string - */ -function checkThatYamlStringContainsYamlString( $actualYaml, $expectedYaml ) { - $actualValue = spyc_load( $actualYaml ); - $expectedValue = spyc_load( $expectedYaml ); - - if ( !$actualValue ) { - return false; - } - - return compareContents( $expectedValue, $actualValue ); -} - diff --git a/features/bootstrap/utils.php b/features/bootstrap/utils.php deleted file mode 100644 index 0332ad1..0000000 --- a/features/bootstrap/utils.php +++ /dev/null @@ -1,764 +0,0 @@ -config ) && ! empty( $composer->config->{'vendor-dir'} ) ) { - array_unshift( $vendor_paths, WP_CLI_ROOT . '/../../../' . $composer->config->{'vendor-dir'} ); - } - } - return $vendor_paths; -} - -// Using require() directly inside a class grants access to private methods to the loaded code -function load_file( $path ) { - require_once $path; -} - -function load_command( $name ) { - $path = WP_CLI_ROOT . "/php/commands/$name.php"; - - if ( is_readable( $path ) ) { - include_once $path; - } -} - -function load_all_commands() { - $cmd_dir = WP_CLI_ROOT . '/php/commands'; - - $iterator = new \DirectoryIterator( $cmd_dir ); - - foreach ( $iterator as $filename ) { - if ( '.php' != substr( $filename, -4 ) ) - continue; - - include_once "$cmd_dir/$filename"; - } -} - -/** - * Like array_map(), except it returns a new iterator, instead of a modified array. - * - * Example: - * - * $arr = array('Football', 'Socker'); - * - * $it = iterator_map($arr, 'strtolower', function($val) { - * return str_replace('foo', 'bar', $val); - * }); - * - * foreach ( $it as $val ) { - * var_dump($val); - * } - * - * @param array|object Either a plain array or another iterator - * @param callback The function to apply to an element - * @return object An iterator that applies the given callback(s) - */ -function iterator_map( $it, $fn ) { - if ( is_array( $it ) ) { - $it = new \ArrayIterator( $it ); - } - - if ( !method_exists( $it, 'add_transform' ) ) { - $it = new Transform( $it ); - } - - foreach ( array_slice( func_get_args(), 1 ) as $fn ) { - $it->add_transform( $fn ); - } - - return $it; -} - -/** - * Search for file by walking up the directory tree until the first file is found or until $stop_check($dir) returns true - * @param string|array The files (or file) to search for - * @param string|null The directory to start searching from; defaults to CWD - * @param callable Function which is passed the current dir each time a directory level is traversed - * @return null|string Null if the file was not found - */ -function find_file_upward( $files, $dir = null, $stop_check = null ) { - $files = (array) $files; - if ( is_null( $dir ) ) { - $dir = getcwd(); - } - while ( @is_readable( $dir ) ) { - // Stop walking up when the supplied callable returns true being passed the $dir - if ( is_callable( $stop_check ) && call_user_func( $stop_check, $dir ) ) { - return null; - } - - foreach ( $files as $file ) { - $path = $dir . DIRECTORY_SEPARATOR . $file; - if ( file_exists( $path ) ) { - return $path; - } - } - - $parent_dir = dirname( $dir ); - if ( empty($parent_dir) || $parent_dir === $dir ) { - break; - } - $dir = $parent_dir; - } - return null; -} - -function is_path_absolute( $path ) { - // Windows - if ( isset($path[1]) && ':' === $path[1] ) - return true; - - return $path[0] === '/'; -} - -/** - * Composes positional arguments into a command string. - * - * @param array - * @return string - */ -function args_to_str( $args ) { - return ' ' . implode( ' ', array_map( 'escapeshellarg', $args ) ); -} - -/** - * Composes associative arguments into a command string. - * - * @param array - * @return string - */ -function assoc_args_to_str( $assoc_args ) { - $str = ''; - - foreach ( $assoc_args as $key => $value ) { - if ( true === $value ) - $str .= " --$key"; - else - $str .= " --$key=" . escapeshellarg( $value ); - } - - return $str; -} - -/** - * Given a template string and an arbitrary number of arguments, - * returns the final command, with the parameters escaped. - */ -function esc_cmd( $cmd ) { - if ( func_num_args() < 2 ) - trigger_error( 'esc_cmd() requires at least two arguments.', E_USER_WARNING ); - - $args = func_get_args(); - - $cmd = array_shift( $args ); - - return vsprintf( $cmd, array_map( 'escapeshellarg', $args ) ); -} - -function locate_wp_config() { - static $path; - - if ( null === $path ) { - if ( file_exists( ABSPATH . 'wp-config.php' ) ) - $path = ABSPATH . 'wp-config.php'; - elseif ( file_exists( ABSPATH . '../wp-config.php' ) && ! file_exists( ABSPATH . '/../wp-settings.php' ) ) - $path = ABSPATH . '../wp-config.php'; - else - $path = false; - - if ( $path ) - $path = realpath( $path ); - } - - return $path; -} - -function wp_version_compare( $since, $operator ) { - return version_compare( str_replace( array( '-src' ), '', $GLOBALS['wp_version'] ), $since, $operator ); -} - -/** - * Render a collection of items as an ASCII table, JSON, CSV, YAML, list of ids, or count. - * - * Given a collection of items with a consistent data structure: - * - * ``` - * $items = array( - * array( - * 'key' => 'foo', - * 'value' => 'bar', - * ) - * ); - * ``` - * - * Render `$items` as an ASCII table: - * - * ``` - * WP_CLI\Utils\format_items( 'table', $items, array( 'key', 'value' ) ); - * - * # +-----+-------+ - * # | key | value | - * # +-----+-------+ - * # | foo | bar | - * # +-----+-------+ - * ``` - * - * Or render `$items` as YAML: - * - * ``` - * WP_CLI\Utils\format_items( 'yaml', $items, array( 'key', 'value' ) ); - * - * # --- - * # - - * # key: foo - * # value: bar - * ``` - * - * @access public - * @category Output - * - * @param string $format Format to use: 'table', 'json', 'csv', 'yaml', 'ids', 'count' - * @param array $items An array of items to output. - * @param array|string $fields Named fields for each item of data. Can be array or comma-separated list. - * @return null - */ -function format_items( $format, $items, $fields ) { - $assoc_args = compact( 'format', 'fields' ); - $formatter = new \WP_CLI\Formatter( $assoc_args ); - $formatter->display_items( $items ); -} - -/** - * Write data as CSV to a given file. - * - * @access public - * - * @param resource $fd File descriptor - * @param array $rows Array of rows to output - * @param array $headers List of CSV columns (optional) - */ -function write_csv( $fd, $rows, $headers = array() ) { - if ( ! empty( $headers ) ) { - fputcsv( $fd, $headers ); - } - - foreach ( $rows as $row ) { - if ( ! empty( $headers ) ) { - $row = pick_fields( $row, $headers ); - } - - fputcsv( $fd, array_values( $row ) ); - } -} - -/** - * Pick fields from an associative array or object. - * - * @param array|object Associative array or object to pick fields from - * @param array List of fields to pick - * @return array - */ -function pick_fields( $item, $fields ) { - $item = (object) $item; - - $values = array(); - - foreach ( $fields as $field ) { - $values[ $field ] = isset( $item->$field ) ? $item->$field : null; - } - - return $values; -} - -/** - * Launch system's $EDITOR for the user to edit some text. - * - * @access public - * @category Input - * - * @param string $content Some form of text to edit (e.g. post content) - * @return string|bool Edited text, if file is saved from editor; false, if no change to file. - */ -function launch_editor_for_input( $input, $filename = 'WP-CLI' ) { - - $tmpdir = get_temp_dir(); - - do { - $tmpfile = basename( $filename ); - $tmpfile = preg_replace( '|\.[^.]*$|', '', $tmpfile ); - $tmpfile .= '-' . substr( md5( rand() ), 0, 6 ); - $tmpfile = $tmpdir . $tmpfile . '.tmp'; - $fp = @fopen( $tmpfile, 'x' ); - if ( ! $fp && is_writable( $tmpdir ) && file_exists( $tmpfile ) ) { - $tmpfile = ''; - continue; - } - if ( $fp ) { - fclose( $fp ); - } - } while( ! $tmpfile ); - - if ( ! $tmpfile ) { - \WP_CLI::error( 'Error creating temporary file.' ); - } - - $output = ''; - file_put_contents( $tmpfile, $input ); - - $editor = getenv( 'EDITOR' ); - if ( !$editor ) { - if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) - $editor = 'notepad'; - else - $editor = 'vi'; - } - - $descriptorspec = array( STDIN, STDOUT, STDERR ); - $process = proc_open( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes ); - $r = proc_close( $process ); - if ( $r ) { - exit( $r ); - } - - $output = file_get_contents( $tmpfile ); - - unlink( $tmpfile ); - - if ( $output === $input ) - return false; - - return $output; -} - -/** - * @param string MySQL host string, as defined in wp-config.php - * @return array - */ -function mysql_host_to_cli_args( $raw_host ) { - $assoc_args = array(); - - $host_parts = explode( ':', $raw_host ); - if ( count( $host_parts ) == 2 ) { - list( $assoc_args['host'], $extra ) = $host_parts; - $extra = trim( $extra ); - if ( is_numeric( $extra ) ) { - $assoc_args['port'] = intval( $extra ); - $assoc_args['protocol'] = 'tcp'; - } else if ( $extra !== '' ) { - $assoc_args['socket'] = $extra; - } - } else { - $assoc_args['host'] = $raw_host; - } - - return $assoc_args; -} - -function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) { - if ( !$descriptors ) - $descriptors = array( STDIN, STDOUT, STDERR ); - - if ( isset( $assoc_args['host'] ) ) { - $assoc_args = array_merge( $assoc_args, mysql_host_to_cli_args( $assoc_args['host'] ) ); - } - - $pass = $assoc_args['pass']; - unset( $assoc_args['pass'] ); - - $old_pass = getenv( 'MYSQL_PWD' ); - putenv( 'MYSQL_PWD=' . $pass ); - - $final_cmd = $cmd . assoc_args_to_str( $assoc_args ); - - $proc = proc_open( $final_cmd, $descriptors, $pipes ); - if ( !$proc ) - exit(1); - - $r = proc_close( $proc ); - - putenv( 'MYSQL_PWD=' . $old_pass ); - - if ( $r ) exit( $r ); -} - -/** - * Render PHP or other types of files using Mustache templates. - * - * IMPORTANT: Automatic HTML escaping is disabled! - */ -function mustache_render( $template_name, $data = array() ) { - if ( ! file_exists( $template_name ) ) - $template_name = WP_CLI_ROOT . "/templates/$template_name"; - - $template = file_get_contents( $template_name ); - - $m = new \Mustache_Engine( array( - 'escape' => function ( $val ) { return $val; } - ) ); - - return $m->render( $template, $data ); -} - -/** - * Create a progress bar to display percent completion of a given operation. - * - * Progress bar is written to STDOUT, and disabled when command is piped. Progress - * advances with `$progress->tick()`, and completes with `$progress->finish()`. - * Process bar also indicates elapsed time and expected total time. - * - * ``` - * # `wp user generate` ticks progress bar each time a new user is created. - * # - * # $ wp user generate --count=500 - * # Generating users 22 % [=======> ] 0:05 / 0:23 - * - * $progress = \WP_CLI\Utils\make_progress_bar( 'Generating users', $count ); - * for ( $i = 0; $i < $count; $i++ ) { - * // uses wp_insert_user() to insert the user - * $progress->tick(); - * } - * $progress->finish(); - * ``` - * - * @access public - * @category Output - * - * @param string $message Text to display before the progress bar. - * @param integer $count Total number of ticks to be performed. - * @return cli\progress\Bar|WP_CLI\NoOp - */ -function make_progress_bar( $message, $count ) { - if ( \cli\Shell::isPiped() ) - return new \WP_CLI\NoOp; - - return new \cli\progress\Bar( $message, $count ); -} - -function parse_url( $url ) { - $url_parts = \parse_url( $url ); - - if ( !isset( $url_parts['scheme'] ) ) { - $url_parts = parse_url( 'http://' . $url ); - } - - return $url_parts; -} - -/** - * Check if we're running in a Windows environment (cmd.exe). - */ -function is_windows() { - return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; -} - -/** - * Replace magic constants in some PHP source code. - * - * @param string $source The PHP code to manipulate. - * @param string $path The path to use instead of the magic constants - */ -function replace_path_consts( $source, $path ) { - $replacements = array( - '__FILE__' => "'$path'", - '__DIR__' => "'" . dirname( $path ) . "'" - ); - - $old = array_keys( $replacements ); - $new = array_values( $replacements ); - - return str_replace( $old, $new, $source ); -} - -/** - * Make a HTTP request to a remote URL. - * - * Wraps the Requests HTTP library to ensure every request includes a cert. - * - * ``` - * # `wp core download` verifies the hash for a downloaded WordPress archive - * - * $md5_response = Utils\http_request( 'GET', $download_url . '.md5' ); - * if ( 20 != substr( $md5_response->status_code, 0, 2 ) ) { - * WP_CLI::error( "Couldn't access md5 hash for release (HTTP code {$response->status_code})" ); - * } - * ``` - * - * @access public - * - * @param string $method HTTP method (GET, POST, DELETE, etc.) - * @param string $url URL to make the HTTP request to. - * @param array $headers Add specific headers to the request. - * @param array $options - * @return object - */ -function http_request( $method, $url, $data = null, $headers = array(), $options = array() ) { - - $cert_path = '/rmccue/requests/library/Requests/Transport/cacert.pem'; - if ( inside_phar() ) { - // cURL can't read Phar archives - $options['verify'] = extract_from_phar( - WP_CLI_ROOT . '/vendor' . $cert_path ); - } else { - foreach( get_vendor_paths() as $vendor_path ) { - if ( file_exists( $vendor_path . $cert_path ) ) { - $options['verify'] = $vendor_path . $cert_path; - break; - } - } - if ( empty( $options['verify'] ) ){ - WP_CLI::error_log( "Cannot find SSL certificate." ); - } - } - - try { - $request = \Requests::request( $url, $headers, $data, $method, $options ); - return $request; - } catch( \Requests_Exception $ex ) { - // Handle SSL certificate issues gracefully - \WP_CLI::warning( $ex->getMessage() ); - $options['verify'] = false; - try { - return \Requests::request( $url, $headers, $data, $method, $options ); - } catch( \Requests_Exception $ex ) { - \WP_CLI::error( $ex->getMessage() ); - } - } -} - -/** - * Increments a version string using the "x.y.z-pre" format - * - * Can increment the major, minor or patch number by one - * If $new_version == "same" the version string is not changed - * If $new_version is not a known keyword, it will be used as the new version string directly - * - * @param string $current_version - * @param string $new_version - * @return string - */ -function increment_version( $current_version, $new_version ) { - // split version assuming the format is x.y.z-pre - $current_version = explode( '-', $current_version, 2 ); - $current_version[0] = explode( '.', $current_version[0] ); - - switch ( $new_version ) { - case 'same': - // do nothing - break; - - case 'patch': - $current_version[0][2]++; - - $current_version = array( $current_version[0] ); // drop possible pre-release info - break; - - case 'minor': - $current_version[0][1]++; - $current_version[0][2] = 0; - - $current_version = array( $current_version[0] ); // drop possible pre-release info - break; - - case 'major': - $current_version[0][0]++; - $current_version[0][1] = 0; - $current_version[0][2] = 0; - - $current_version = array( $current_version[0] ); // drop possible pre-release info - break; - - default: // not a keyword - $current_version = array( array( $new_version ) ); - break; - } - - // reconstruct version string - $current_version[0] = implode( '.', $current_version[0] ); - $current_version = implode( '-', $current_version ); - - return $current_version; -} - -/** - * Compare two version strings to get the named semantic version. - * - * @access public - * - * @param string $new_version - * @param string $original_version - * @return string $name 'major', 'minor', 'patch' - */ -function get_named_sem_ver( $new_version, $original_version ) { - - if ( ! Comparator::greaterThan( $new_version, $original_version ) ) { - return ''; - } - - $parts = explode( '-', $original_version ); - $bits = explode( '.', $parts[0] ); - $major = $bits[0]; - if ( isset( $bits[1] ) ) { - $minor = $bits[1]; - } - if ( isset( $bits[2] ) ) { - $patch = $bits[2]; - } - - if ( ! is_null( $minor ) && Semver::satisfies( $new_version, "{$major}.{$minor}.x" ) ) { - return 'patch'; - } else if ( Semver::satisfies( $new_version, "{$major}.x.x" ) ) { - return 'minor'; - } else { - return 'major'; - } -} - -/** - * Return the flag value or, if it's not set, the $default value. - * - * Because flags can be negated (e.g. --no-quiet to negate --quiet), this - * function provides a safer alternative to using - * `isset( $assoc_args['quiet'] )` or similar. - * - * @access public - * @category Input - * - * @param array $assoc_args Arguments array. - * @param string $flag Flag to get the value. - * @param mixed $default Default value for the flag. Default: NULL - * @return mixed - */ -function get_flag_value( $assoc_args, $flag, $default = null ) { - return isset( $assoc_args[ $flag ] ) ? $assoc_args[ $flag ] : $default; -} - -/** - * Get the system's temp directory. Warns user if it isn't writable. - * - * @access public - * @category System - * - * @return string - */ -function get_temp_dir() { - static $temp = ''; - - $trailingslashit = function( $path ) { - return rtrim( $path ) . '/'; - }; - - if ( $temp ) - return $trailingslashit( $temp ); - - if ( function_exists( 'sys_get_temp_dir' ) ) { - $temp = sys_get_temp_dir(); - } else if ( ini_get( 'upload_tmp_dir' ) ) { - $temp = ini_get( 'upload_tmp_dir' ); - } else { - $temp = '/tmp/'; - } - - if ( ! @is_writable( $temp ) ) { - \WP_CLI::warning( "Temp directory isn't writable: {$temp}" ); - } - - return $trailingslashit( $temp ); -} - -/** - * Parse a SSH url for its host, port, and path. - * - * Similar to parse_url(), but adds support for defined SSH aliases. - * - * ``` - * host OR host/path/to/wordpress OR host:port/path/to/wordpress - * ``` - * - * @access public - * - * @return mixed - */ -function parse_ssh_url( $url, $component = -1 ) { - preg_match( '#^([^:/~]+)(:([\d]+))?((/|~)(.+))?$#', $url, $matches ); - $bits = array(); - foreach( array( - 1 => 'host', - 3 => 'port', - 4 => 'path', - ) as $i => $key ) { - if ( ! empty( $matches[ $i ] ) ) { - $bits[ $key ] = $matches[ $i ]; - } - } - switch ( $component ) { - case PHP_URL_HOST: - return isset( $bits['host'] ) ? $bits['host'] : null; - case PHP_URL_PATH: - return isset( $bits['path'] ) ? $bits['path'] : null; - case PHP_URL_PORT: - return isset( $bits['port'] ) ? $bits['port'] : null; - default: - return $bits; - } -} diff --git a/features/extra/no-mail.php b/features/extra/no-mail.php deleted file mode 100644 index de7a422..0000000 --- a/features/extra/no-mail.php +++ /dev/null @@ -1,7 +0,0 @@ -Given( '/^an empty directory$/', - function ( $world ) { - $world->create_run_dir(); - } -); - -$steps->Given( '/^an empty cache/', - function ( $world ) { - $world->variables['SUITE_CACHE_DIR'] = FeatureContext::create_cache_dir(); - } -); - -$steps->Given( '/^an? ([^\s]+) file:$/', - function ( $world, $path, PyStringNode $content ) { - $content = (string) $content . "\n"; - $full_path = $world->variables['RUN_DIR'] . "/$path"; - Process::create( \WP_CLI\utils\esc_cmd( 'mkdir -p %s', dirname( $full_path ) ) )->run_check(); - file_put_contents( $full_path, $content ); - } -); - -$steps->Given( '/^WP files$/', - function ( $world ) { - $world->download_wp(); - } -); - -$steps->Given( '/^wp-config\.php$/', - function ( $world ) { - $world->create_config(); - } -); - -$steps->Given( '/^a database$/', - function ( $world ) { - $world->create_db(); - } -); - -$steps->Given( '/^a WP install$/', - function ( $world ) { - $world->install_wp(); - } -); - -$steps->Given( "/^a WP install in '([^\s]+)'$/", - function ( $world, $subdir ) { - $world->install_wp( $subdir ); - } -); - -$steps->Given( '/^a WP multisite (subdirectory|subdomain)?\s?install$/', - function ( $world, $type = 'subdirectory' ) { - $world->install_wp(); - $subdomains = ! empty( $type ) && 'subdomain' === $type ? 1 : 0; - $world->proc( 'wp core install-network', array( 'title' => 'WP CLI Network', 'subdomains' => $subdomains ) )->run_check(); - } -); - -$steps->Given( '/^these installed and active plugins:$/', - function( $world, $stream ) { - $plugins = implode( ' ', array_map( 'trim', explode( PHP_EOL, (string)$stream ) ) ); - $world->proc( "wp plugin install $plugins --activate" )->run_check(); - } -); - -$steps->Given( '/^a custom wp-content directory$/', - function ( $world ) { - $wp_config_path = $world->variables['RUN_DIR'] . "/wp-config.php"; - - $wp_config_code = file_get_contents( $wp_config_path ); - - $world->move_files( 'wp-content', 'my-content' ); - $world->add_line_to_wp_config( $wp_config_code, - "define( 'WP_CONTENT_DIR', dirname(__FILE__) . '/my-content' );" ); - - $world->move_files( 'my-content/plugins', 'my-plugins' ); - $world->add_line_to_wp_config( $wp_config_code, - "define( 'WP_PLUGIN_DIR', __DIR__ . '/my-plugins' );" ); - - file_put_contents( $wp_config_path, $wp_config_code ); - } -); - -$steps->Given( '/^download:$/', - function ( $world, TableNode $table ) { - foreach ( $table->getHash() as $row ) { - $path = $world->replace_variables( $row['path'] ); - if ( file_exists( $path ) ) { - // assume it's the same file and skip re-download - continue; - } - - Process::create( \WP_CLI\Utils\esc_cmd( 'curl -sSL %s > %s', $row['url'], $path ) )->run_check(); - } - } -); - -$steps->Given( '/^save (STDOUT|STDERR) ([\'].+[^\'])?as \{(\w+)\}$/', - function ( $world, $stream, $output_filter, $key ) { - - $stream = strtolower( $stream ); - - if ( $output_filter ) { - $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/'; - if ( false !== preg_match( $output_filter, $world->result->$stream, $matches ) ) - $output = array_pop( $matches ); - else - $output = ''; - } else { - $output = $world->result->$stream; - } - $world->variables[ $key ] = trim( $output, "\n" ); - } -); - -$steps->Given( '/^a new Phar(?: with version "([^"]+)")$/', - function ( $world, $version ) { - $world->build_phar( $version ); - } -); - -$steps->Given( '/^save the (.+) file ([\'].+[^\'])?as \{(\w+)\}$/', - function ( $world, $filepath, $output_filter, $key ) { - $full_file = file_get_contents( $world->replace_variables( $filepath ) ); - - if ( $output_filter ) { - $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/'; - if ( false !== preg_match( $output_filter, $full_file, $matches ) ) - $output = array_pop( $matches ); - else - $output = ''; - } else { - $output = $full_file; - } - $world->variables[ $key ] = trim( $output, "\n" ); - } -); - -$steps->Given('/^a misconfigured WP_CONTENT_DIR constant directory$/', - function($world) { - $wp_config_path = $world->variables['RUN_DIR'] . "/wp-config.php"; - - $wp_config_code = file_get_contents( $wp_config_path ); - - $world->add_line_to_wp_config( $wp_config_code, - "define( 'WP_CONTENT_DIR', '' );" ); - - file_put_contents( $wp_config_path, $wp_config_code ); - } -); \ No newline at end of file diff --git a/features/steps/then.php b/features/steps/then.php deleted file mode 100644 index d1df934..0000000 --- a/features/steps/then.php +++ /dev/null @@ -1,201 +0,0 @@ -Then( '/^the return code should be (\d+)$/', - function ( $world, $return_code ) { - if ( $return_code != $world->result->return_code ) { - throw new RuntimeException( $world->result ); - } - } -); - -$steps->Then( '/^(STDOUT|STDERR) should (be|contain|not contain):$/', - function ( $world, $stream, $action, PyStringNode $expected ) { - - $stream = strtolower( $stream ); - - $expected = $world->replace_variables( (string) $expected ); - - checkString( $world->result->$stream, $expected, $action, $world->result ); - } -); - -$steps->Then( '/^(STDOUT|STDERR) should be a number$/', - function ( $world, $stream ) { - - $stream = strtolower( $stream ); - - assertNumeric( trim( $world->result->$stream, "\n" ) ); - } -); - -$steps->Then( '/^(STDOUT|STDERR) should not be a number$/', - function ( $world, $stream ) { - - $stream = strtolower( $stream ); - - assertNotNumeric( trim( $world->result->$stream, "\n" ) ); - } -); - -$steps->Then( '/^STDOUT should be a table containing rows:$/', - function ( $world, TableNode $expected ) { - $output = $world->result->stdout; - $actual_rows = explode( "\n", rtrim( $output, "\n" ) ); - - $expected_rows = array(); - foreach ( $expected->getRows() as $row ) { - $expected_rows[] = $world->replace_variables( implode( "\t", $row ) ); - } - - compareTables( $expected_rows, $actual_rows, $output ); - } -); - -$steps->Then( '/^STDOUT should end with a table containing rows:$/', - function ( $world, TableNode $expected ) { - $output = $world->result->stdout; - $actual_rows = explode( "\n", rtrim( $output, "\n" ) ); - - $expected_rows = array(); - foreach ( $expected->getRows() as $row ) { - $expected_rows[] = $world->replace_variables( implode( "\t", $row ) ); - } - - $start = array_search( $expected_rows[0], $actual_rows ); - - if ( false === $start ) - throw new \Exception( $world->result ); - - compareTables( $expected_rows, array_slice( $actual_rows, $start ), $output ); - } -); - -$steps->Then( '/^STDOUT should be JSON containing:$/', - function ( $world, PyStringNode $expected ) { - $output = $world->result->stdout; - $expected = $world->replace_variables( (string) $expected ); - - if ( !checkThatJsonStringContainsJsonString( $output, $expected ) ) { - throw new \Exception( $world->result ); - } -}); - -$steps->Then( '/^STDOUT should be a JSON array containing:$/', - function ( $world, PyStringNode $expected ) { - $output = $world->result->stdout; - $expected = $world->replace_variables( (string) $expected ); - - $actualValues = json_decode( $output ); - $expectedValues = json_decode( $expected ); - - $missing = array_diff( $expectedValues, $actualValues ); - if ( !empty( $missing ) ) { - throw new \Exception( $world->result ); - } -}); - -$steps->Then( '/^STDOUT should be CSV containing:$/', - function ( $world, TableNode $expected ) { - $output = $world->result->stdout; - - $expected_rows = $expected->getRows(); - foreach ( $expected as &$row ) { - foreach ( $row as &$value ) { - $value = $world->replace_variables( $value ); - } - } - - if ( ! checkThatCsvStringContainsValues( $output, $expected_rows ) ) - throw new \Exception( $world->result ); - } -); - -$steps->Then( '/^STDOUT should be YAML containing:$/', - function ( $world, PyStringNode $expected ) { - $output = $world->result->stdout; - $expected = $world->replace_variables( (string) $expected ); - - if ( !checkThatYamlStringContainsYamlString( $output, $expected ) ) { - throw new \Exception( $world->result ); - } -}); - -$steps->Then( '/^(STDOUT|STDERR) should be empty$/', - function ( $world, $stream ) { - - $stream = strtolower( $stream ); - - if ( !empty( $world->result->$stream ) ) { - throw new \Exception( $world->result ); - } - } -); - -$steps->Then( '/^(STDOUT|STDERR) should not be empty$/', - function ( $world, $stream ) { - - $stream = strtolower( $stream ); - - if ( '' === rtrim( $world->result->$stream, "\n" ) ) { - throw new Exception( $world->result ); - } - } -); - -$steps->Then( '/^the (.+) (file|directory) should (exist|not exist|be:|contain:|not contain:)$/', - function ( $world, $path, $type, $action, $expected = null ) { - $path = $world->replace_variables( $path ); - - // If it's a relative path, make it relative to the current test dir - if ( '/' !== $path[0] ) - $path = $world->variables['RUN_DIR'] . "/$path"; - - if ( 'file' == $type ) { - $test = 'file_exists'; - } else if ( 'directory' == $type ) { - $test = 'is_dir'; - } - - switch ( $action ) { - case 'exist': - if ( ! $test( $path ) ) { - throw new Exception( $world->result ); - } - break; - case 'not exist': - if ( $test( $path ) ) { - throw new Exception( $world->result ); - } - break; - default: - if ( ! $test( $path ) ) { - throw new Exception( "$path doesn't exist." ); - } - $action = substr( $action, 0, -1 ); - $expected = $world->replace_variables( (string) $expected ); - if ( 'file' == $type ) { - $contents = file_get_contents( $path ); - } else if ( 'directory' == $type ) { - $files = glob( rtrim( $path, '/' ) . '/*' ); - foreach( $files as &$file ) { - $file = str_replace( $path . '/', '', $file ); - } - $contents = implode( PHP_EOL, $files ); - } - checkString( $contents, $expected, $action ); - } - } -); - -$steps->Then( '/^an email should (be sent|not be sent)$/', function( $world, $expected ) { - if ( 'be sent' === $expected ) { - assertNotEquals( 0, $world->email_sends ); - } else if ( 'not be sent' === $expected ) { - assertEquals( 0, $world->email_sends ); - } else { - throw new Exception( 'Invalid expectation' ); - } -}); diff --git a/features/steps/when.php b/features/steps/when.php deleted file mode 100644 index afe3f7a..0000000 --- a/features/steps/when.php +++ /dev/null @@ -1,54 +0,0 @@ - 'run_check', - 'try' => 'run' - ); - $method = $map[ $mode ]; - - return $proc->$method(); -} - -function capture_email_sends( $stdout ) { - $stdout = preg_replace( '#WP-CLI test suite: Sent email to.+\n?#', '', $stdout, -1, $email_sends ); - return array( $stdout, $email_sends ); -} - -$steps->When( '/^I launch in the background `([^`]+)`$/', - function ( $world, $cmd ) { - $world->background_proc( $cmd ); - } -); - -$steps->When( '/^I (run|try) `([^`]+)`$/', - function ( $world, $mode, $cmd ) { - $cmd = $world->replace_variables( $cmd ); - $world->result = invoke_proc( $world->proc( $cmd ), $mode ); - list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); - } -); - -$steps->When( "/^I (run|try) `([^`]+)` from '([^\s]+)'$/", - function ( $world, $mode, $cmd, $subdir ) { - $cmd = $world->replace_variables( $cmd ); - $world->result = invoke_proc( $world->proc( $cmd, array(), $subdir ), $mode ); - list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); - } -); - -$steps->When( '/^I (run|try) the previous command again$/', - function ( $world, $mode ) { - if ( !isset( $world->result ) ) - throw new \Exception( 'No previous command.' ); - - $proc = Process::create( $world->result->command, $world->result->cwd, $world->result->env ); - $world->result = invoke_proc( $proc, $mode ); - list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); - } -); - diff --git a/inc/class-command.php b/inc/class-command.php index 29c12d1..a44f978 100644 --- a/inc/class-command.php +++ b/inc/class-command.php @@ -96,7 +96,7 @@ class Command { public function stage( $args, $assoc_args ) { global $wpdb; - $focus = Utils\get_flag_value( $assoc_args, 'all', isset( $args[0] ) ? $args[0] : null ); + $focus = Utils\get_flag_value( $assoc_args, 'all', isset( $args[0] ) ? $args[0] : null ); $order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' ); $orderby = Utils\get_flag_value( $assoc_args, 'orderby', null ); @@ -110,7 +110,7 @@ class Command { $profiler->run(); if ( $focus ) { - $base = array( + $base = array( 'hook', 'callback_count', ); @@ -125,7 +125,7 @@ class Command { 'request_count', ); } else { - $base = array( + $base = array( 'stage', ); $metrics = array( @@ -141,9 +141,9 @@ class Command { 'request_count', ); } - $fields = array_merge( $base, $metrics ); + $fields = array_merge( $base, $metrics ); $formatter = new Formatter( $assoc_args, $fields ); - $loggers = $profiler->get_loggers(); + $loggers = $profiler->get_loggers(); if ( Utils\get_flag_value( $assoc_args, 'spotlight' ) ) { $loggers = self::shine_spotlight( $loggers, $metrics ); } @@ -221,7 +221,7 @@ class Command { } else { $base = array( 'hook', 'callback_count' ); } - $metrics = array( + $metrics = array( 'time', 'query_time', 'query_count', @@ -231,9 +231,9 @@ class Command { 'request_time', 'request_count', ); - $fields = array_merge( $base, $metrics ); + $fields = array_merge( $base, $metrics ); $formatter = new Formatter( $assoc_args, $fields ); - $loggers = $profiler->get_loggers(); + $loggers = $profiler->get_loggers(); if ( Utils\get_flag_value( $assoc_args, 'spotlight' ) ) { $loggers = self::shine_spotlight( $loggers, $metrics ); } @@ -289,9 +289,14 @@ class Command { $order = Utils\get_flag_value( $assoc_args, 'order', 'ASC' ); $orderby = Utils\get_flag_value( $assoc_args, 'orderby', null ); - self::profile_eval_ish( $assoc_args, function() use ( $statement ) { - eval( $statement ); - }, $order, $orderby ); + self::profile_eval_ish( + $assoc_args, + function() use ( $statement ) { + eval( $statement ); // phpcs:ignore Squiz.PHP.Eval.Discouraged -- no other way oround here + }, + $order, + $orderby + ); } /** @@ -348,22 +353,27 @@ class Command { WP_CLI::error( "'$file' does not exist." ); } - self::profile_eval_ish( $assoc_args, function() use ( $file ) { - self::include_file( $file ); - }, $order, $orderby ); + self::profile_eval_ish( + $assoc_args, + function() use ( $file ) { + self::include_file( $file ); + }, + $order, + $orderby + ); } /** * Profile an eval or eval-file statement. */ - private static function profile_eval_ish( $assoc_args, $profile_callback ) { - $hook = Utils\get_flag_value( $assoc_args, 'hook' ); - $type = $focus = false; + private static function profile_eval_ish( $assoc_args, $profile_callback, $order = 'ASC', $orderby = null ) { + $hook = Utils\get_flag_value( $assoc_args, 'hook' ); + $type = $focus = false; $fields = array(); if ( $hook ) { $type = 'hook'; if ( true !== $hook ) { - $focus = $hook; + $focus = $hook; $fields[] = 'callback'; $fields[] = 'location'; } else { @@ -382,16 +392,19 @@ class Command { $logger->stop(); $loggers = array( $logger ); } - $fields = array_merge( $fields, array( - 'time', - 'query_time', - 'query_count', - 'cache_ratio', - 'cache_hits', - 'cache_misses', - 'request_time', - 'request_count', - ) ); + $fields = array_merge( + $fields, + array( + 'time', + 'query_time', + 'query_count', + 'cache_ratio', + 'cache_hits', + 'cache_misses', + 'request_time', + 'request_count', + ) + ); $formatter = new Formatter( $assoc_args, $fields ); $formatter->display_items( $loggers, false, $order, $orderby ); } @@ -414,9 +427,9 @@ class Command { */ private static function shine_spotlight( $loggers, $metrics ) { - foreach( $loggers as $k => $logger ) { + foreach ( $loggers as $k => $logger ) { $non_zero = false; - foreach( $metrics as $metric ) { + foreach ( $metrics as $metric ) { switch ( $metric ) { // 100% cache ratio is fine by us case 'cache_ratio': diff --git a/inc/class-formatter.php b/inc/class-formatter.php index fa0d5c6..1a44f53 100644 --- a/inc/class-formatter.php +++ b/inc/class-formatter.php @@ -14,7 +14,7 @@ class Formatter { $format_args = array( 'format' => 'table', 'fields' => $fields, - 'field' => null + 'field' => null, ); foreach ( array( 'format', 'fields', 'field' ) as $key ) { @@ -33,7 +33,7 @@ class Formatter { $format_args['fields'] = array_map( 'trim', $format_args['fields'] ); - $this->args = $format_args; + $this->args = $format_args; $this->formatter = new \WP_CLI\Formatter( $assoc_args, $fields, $prefix ); } @@ -61,7 +61,7 @@ class Formatter { $b = number_format( $b, 4 ); if ( 0 === $a - $b ) { return 0; - } else if ( $a - $b < 0 ) { + } elseif ( $a - $b < 0 ) { return -1; } else { return 1; @@ -90,21 +90,24 @@ class Formatter { } if ( $orderby ) { - usort( $items, function( $a, $b ) use ( $order, $orderby ) { - list( $first, $second ) = 'ASC' === $order ? array( $a, $b ) : array( $b, $a ); + usort( + $items, + function( $a, $b ) use ( $order, $orderby ) { + list( $first, $second ) = 'ASC' === $order ? array( $a, $b ) : array( $b, $a ); - if ( is_numeric( $first->$orderby ) && is_numeric( $second->$orderby ) ) { - return $this->compare_float( $first->$orderby, $second->$orderby ); + if ( is_numeric( $first->$orderby ) && is_numeric( $second->$orderby ) ) { + return $this->compare_float( $first->$orderby, $second->$orderby ); + } + + return strcmp( $first->$orderby, $second->$orderby ); } - - return strcmp( $first->$orderby, $second->$orderby ); - }); + ); } $location_index = array_search( 'location', $fields ); foreach ( $items as $item ) { $values = array_values( \WP_CLI\Utils\pick_fields( $item, $fields ) ); - foreach( $values as $i => $value ) { + foreach ( $values as $i => $value ) { if ( ! is_null( $this->total_cell_index ) && $this->total_cell_index === $i ) { continue; } @@ -135,7 +138,7 @@ class Formatter { $table->addRow( $values ); } if ( $include_total ) { - foreach( $totals as $i => $value ) { + foreach ( $totals as $i => $value ) { if ( null === $value ) { continue; } @@ -153,7 +156,7 @@ class Formatter { $table->setFooters( $totals ); } - foreach( $table->getDisplayLines() as $line ) { + foreach ( $table->getDisplayLines() as $line ) { \WP_CLI::line( $line ); } diff --git a/inc/class-logger.php b/inc/class-logger.php index 963bdd6..040ee0f 100644 --- a/inc/class-logger.php +++ b/inc/class-logger.php @@ -4,29 +4,29 @@ namespace runcommand\Profile; class Logger { - public $time = 0; - public $query_count = 0; - public $query_time = 0; - public $cache_hits = 0; - public $cache_misses = 0; - public $cache_ratio = null; - public $hook_count = 0; - public $hook_time = 0; + public $time = 0; + public $query_count = 0; + public $query_time = 0; + public $cache_hits = 0; + public $cache_misses = 0; + public $cache_ratio = null; + public $hook_count = 0; + public $hook_time = 0; public $request_count = 0; - public $request_time = 0; + public $request_time = 0; - private $start_time = null; - private $query_offset = null; - private $cache_hit_offset = null; - private $cache_miss_offset = null; - private $hook_start_time = null; - private $hook_depth = 0; + private $start_time = null; + private $query_offset = null; + private $cache_hit_offset = null; + private $cache_miss_offset = null; + private $hook_start_time = null; + private $hook_depth = 0; private $request_start_time = null; public static $active_loggers = array(); public function __construct( $definition = array() ) { - foreach( $definition as $k => $v ) { + foreach ( $definition as $k => $v ) { $this->$k = $v; } } @@ -36,12 +36,12 @@ class Logger { */ public function start() { global $wpdb, $wp_object_cache; - $this->start_time = microtime( true ); + $this->start_time = microtime( true ); $this->query_offset = ! empty( $wpdb->queries ) ? count( $wpdb->queries ) : 0; if ( false === ( $key = array_search( $this, self::$active_loggers ) ) ) { self::$active_loggers[] = $this; } - $this->cache_hit_offset = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; + $this->cache_hit_offset = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; $this->cache_miss_offset = ! empty( $wp_object_cache->cache_misses ) ? $wp_object_cache->cache_misses : 0; } @@ -61,7 +61,7 @@ class Logger { if ( ! is_null( $this->start_time ) ) { $this->time += microtime( true ) - $this->start_time; } - if ( ! is_null( $this->query_offset ) && isset( $wpdb ) ) { + if ( ! is_null( $this->query_offset ) && isset( $wpdb ) && ! empty( $wpdb->queries ) ) { for ( $i = $this->query_offset; $i < count( $wpdb->queries ); $i++ ) { $this->query_time += $wpdb->queries[ $i ][1]; $this->query_count++; @@ -69,20 +69,20 @@ class Logger { } if ( ! is_null( $this->cache_hit_offset ) && ! is_null( $this->cache_miss_offset ) && isset( $wp_object_cache ) ) { - $cache_hits = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; - $cache_misses = ! empty( $wp_object_cache->cache_misses ) ? $wp_object_cache->cache_misses : 0; - $this->cache_hits = $cache_hits - $this->cache_hit_offset; + $cache_hits = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; + $cache_misses = ! empty( $wp_object_cache->cache_misses ) ? $wp_object_cache->cache_misses : 0; + $this->cache_hits = $cache_hits - $this->cache_hit_offset; $this->cache_misses = $cache_misses - $this->cache_miss_offset; - $cache_total = $this->cache_hits + $this->cache_misses; + $cache_total = $this->cache_hits + $this->cache_misses; if ( $cache_total ) { - $ratio = ( $this->cache_hits / $cache_total ) * 100; + $ratio = ( $this->cache_hits / $cache_total ) * 100; $this->cache_ratio = round( $ratio, 2 ) . '%'; } } - $this->start_time = null; - $this->query_offset = null; - $this->cache_hit_offset = null; + $this->start_time = null; + $this->query_offset = null; + $this->cache_hit_offset = null; $this->cache_miss_offset = null; if ( false !== ( $key = array_search( $this, self::$active_loggers ) ) ) { unset( self::$active_loggers[ $key ] ); diff --git a/inc/class-profiler.php b/inc/class-profiler.php index 5c662b0..b6a69cc 100644 --- a/inc/class-profiler.php +++ b/inc/class-profiler.php @@ -8,9 +8,9 @@ class Profiler { private $type; private $focus; - private $loggers = array(); - private $stage_hooks = array( - 'bootstrap' => array( + private $loggers = array(); + private $stage_hooks = array( + 'bootstrap' => array( 'muplugins_loaded', 'plugins_loaded', 'setup_theme', @@ -18,14 +18,14 @@ class Profiler { 'init', 'wp_loaded', ), - 'main_query' => array( + 'main_query' => array( 'parse_request', 'send_headers', 'pre_get_posts', 'the_posts', 'wp', ), - 'template' => array( + 'template' => array( 'template_redirect', 'template_include', 'wp_head', @@ -34,26 +34,26 @@ class Profiler { 'wp_footer', ), ); - private $current_stage_hooks = array(); - private $running_hook = null; - private $previous_filter = null; + private $current_stage_hooks = array(); + private $running_hook = null; + private $previous_filter = null; private $previous_filter_callbacks = null; - private $filter_depth = 0; + private $filter_depth = 0; - private $tick_callback = null; - private $tick_location = null; - private $tick_start_time = null; - private $tick_query_offset = null; - private $tick_cache_hit_offset = null; + private $tick_callback = null; + private $tick_location = null; + private $tick_start_time = null; + private $tick_query_offset = null; + private $tick_cache_hit_offset = null; private $tick_cache_miss_offset = null; public function __construct( $type, $focus ) { - $this->type = $type; + $this->type = $type; $this->focus = $focus; } public function get_loggers() { - foreach( $this->loggers as $i => $logger ) { + foreach ( $this->loggers as $i => $logger ) { if ( is_array( $logger ) ) { $this->loggers[ $i ] = $logger = new Logger( $logger ); } @@ -62,10 +62,10 @@ class Profiler { } if ( ! isset( $logger->location ) ) { list( $name, $location ) = self::get_name_location_from_callback( $logger->callback ); - $logger->callback = $name; - $logger->location = $location; + $logger->callback = $name; + $logger->location = $location; } - $logger->location = self::get_short_location( $logger->location ); + $logger->location = self::get_short_location( $logger->location ); $this->loggers[ $i ] = $logger; } return $this->loggers; @@ -75,29 +75,35 @@ class Profiler { * Run the profiler against WordPress */ public function run() { - WP_CLI::add_wp_hook( 'muplugins_loaded', function(){ - if ( $url = WP_CLI::get_runner()->config['url'] ) { - WP_CLI::set_url( trailingslashit( $url ) ); - } else { - WP_CLI::set_url( home_url( '/' ) ); + WP_CLI::add_wp_hook( + 'muplugins_loaded', + function() { + if ( $url = WP_CLI::get_runner()->config['url'] ) { + WP_CLI::set_url( trailingslashit( $url ) ); + } else { + WP_CLI::set_url( home_url( '/' ) ); + } } - }); - WP_CLI::add_hook( 'after_wp_config_load', function() { - if ( defined( 'SAVEQUERIES' ) && ! SAVEQUERIES ) { - WP_CLI::error( "'SAVEQUERIES' is defined as false, and must be true. Please check your wp-config.php" ); + ); + WP_CLI::add_hook( + 'after_wp_config_load', + function() { + if ( defined( 'SAVEQUERIES' ) && ! SAVEQUERIES ) { + WP_CLI::error( "'SAVEQUERIES' is defined as false, and must be true. Please check your wp-config.php" ); + } + if ( ! defined( 'SAVEQUERIES' ) ) { + define( 'SAVEQUERIES', true ); + } } - if ( ! defined( 'SAVEQUERIES' ) ) { - define( 'SAVEQUERIES', true ); - } - }); + ); if ( 'hook' === $this->type && ':before' === substr( $this->focus, -7, 7 ) ) { $stage_hooks = array(); - foreach( $this->stage_hooks as $hooks ) { + foreach ( $this->stage_hooks as $hooks ) { $stage_hooks = array_merge( $stage_hooks, $hooks ); } $end_hook = substr( $this->focus, 0, -7 ); - $key = array_search( $end_hook, $stage_hooks ); + $key = array_search( $end_hook, $stage_hooks ); if ( isset( $stage_hooks[ $key - 1 ] ) ) { $start_hook = $stage_hooks[ $key - 1 ]; WP_CLI::add_wp_hook( $start_hook, array( $this, 'wp_tick_profile_begin' ), 9999 ); @@ -105,7 +111,7 @@ class Profiler { WP_CLI::add_hook( 'after_wp_config_load', array( $this, 'wp_tick_profile_begin' ) ); } WP_CLI::add_wp_hook( $end_hook, array( $this, 'wp_tick_profile_end' ), -9999 ); - } else if ( 'hook' === $this->type + } elseif ( 'hook' === $this->type && ':after' === substr( $this->focus, -6, 6 ) ) { $start_hook = substr( $this->focus, 0, -6 ); WP_CLI::add_wp_hook( $start_hook, array( $this, 'wp_tick_profile_begin' ), 9999 ); @@ -123,24 +129,24 @@ class Profiler { public function wp_tick_profile_begin( $value = null ) { if ( version_compare( PHP_VERSION, '7.0.0' ) >= 0 ) { - WP_CLI::error( "Profiling intermediate hooks is broken in PHP 7, see https://bugs.php.net/bug.php?id=72966" ); + WP_CLI::error( 'Profiling intermediate hooks is broken in PHP 7, see https://bugs.php.net/bug.php?id=72966' ); } // Disable opcode optimizers. These "optimize" calls out of the stack // and hide calls from the tick handler and backtraces. // Copied from P3 Profiler if ( extension_loaded( 'xcache' ) ) { - @ini_set( 'xcache.optimizer', false ); + @ini_set( 'xcache.optimizer', false ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server. } elseif ( extension_loaded( 'apc' ) ) { - @ini_set( 'apc.optimization', 0 ); + @ini_set( 'apc.optimization', 0 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server. apc_clear_cache(); } elseif ( extension_loaded( 'eaccelerator' ) ) { - @ini_set( 'eaccelerator.optimizer', 0 ); + @ini_set( 'eaccelerator.optimizer', 0 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server. if ( function_exists( 'eaccelerator_optimizer' ) ) { - @eaccelerator_optimizer( false ); + @eaccelerator_optimizer( false ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- disabling eaccelerator on runtime can faild } } elseif ( extension_loaded( 'Zend Optimizer+' ) ) { - @ini_set( 'zend_optimizerplus.optimization_level', 0 ); + @ini_set( 'zend_optimizerplus.optimization_level', 0 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- ini_set can be disabled on server. } register_tick_function( array( $this, 'handle_function_tick' ) ); @@ -162,7 +168,7 @@ class Profiler { */ public function wp_hook_begin() { - foreach( Logger::$active_loggers as $logger ) { + foreach ( Logger::$active_loggers as $logger ) { $logger->start_hook_timer(); } @@ -174,13 +180,18 @@ class Profiler { $this->loggers[ $pseudo_hook ]->stop(); } $callback_count = 0; - $callbacks = self::get_filter_callbacks( $current_filter ); + $callbacks = self::get_filter_callbacks( $current_filter ); if ( false !== $callbacks ) { - foreach( $callbacks as $priority => $cbs ) { + foreach ( $callbacks as $priority => $cbs ) { $callback_count += count( $cbs ); } } - $this->loggers[ $current_filter ] = new Logger( array( 'hook' => $current_filter, 'callback_count' => $callback_count ) ); + $this->loggers[ $current_filter ] = new Logger( + array( + 'hook' => $current_filter, + 'callback_count' => $callback_count, + ) + ); $this->loggers[ $current_filter ]->start(); } @@ -210,24 +221,26 @@ class Profiler { if ( false === $callbacks ) { return; } - $this->previous_filter = $current_filter; + $this->previous_filter = $current_filter; $this->previous_filter_callbacks = $callbacks; - foreach( $callbacks as $priority => $priority_callbacks ) { - foreach( $priority_callbacks as $i => $the_ ) { + foreach ( $callbacks as $priority => $priority_callbacks ) { + foreach ( $priority_callbacks as $i => $the_ ) { $callbacks[ $priority ][ $i ] = array( - 'function' => function() use( $the_, $i ) { + 'function' => function() use ( $the_, $i ) { if ( ! isset( $this->loggers[ $i ] ) ) { - $this->loggers[ $i ] = new Logger( array( - 'callback' => $the_['function'], - ) ); + $this->loggers[ $i ] = new Logger( + array( + 'callback' => $the_['function'], + ) + ); } $this->loggers[ $i ]->start(); $value = call_user_func_array( $the_['function'], func_get_args() ); $this->loggers[ $i ]->stop(); return $value; }, - 'accepted_args' => $the_['accepted_args'], + 'accepted_args' => $the_['accepted_args'], ); } } @@ -239,7 +252,7 @@ class Profiler { */ public function wp_hook_end( $filter_value = null ) { - foreach( Logger::$active_loggers as $logger ) { + foreach ( Logger::$active_loggers as $logger ) { $logger->stop_hook_timer(); } @@ -252,7 +265,7 @@ class Profiler { if ( false !== $key && isset( $this->current_stage_hooks[ $key + 1 ] ) ) { $pseudo_hook = "{$this->current_stage_hooks[$key+1]}:before"; } else { - $pseudo_hook = "{$this->current_stage_hooks[$key]}:after";; + $pseudo_hook = "{$this->current_stage_hooks[$key]}:after"; $this->running_hook = $pseudo_hook; } $this->loggers[ $pseudo_hook ] = new Logger( array( 'hook' => $pseudo_hook ) ); @@ -277,14 +290,14 @@ class Profiler { $callback_hash = md5( serialize( $this->tick_callback . $this->tick_location ) ); if ( ! isset( $this->loggers[ $callback_hash ] ) ) { $this->loggers[ $callback_hash ] = array( - 'callback' => $this->tick_callback, - 'location' => $this->tick_location, - 'time' => 0, - 'query_time' => 0, - 'query_count' => 0, - 'cache_hits' => 0, - 'cache_misses' => 0, - 'cache_ratio' => null, + 'callback' => $this->tick_callback, + 'location' => $this->tick_location, + 'time' => 0, + 'query_time' => 0, + 'query_count' => 0, + 'cache_hits' => 0, + 'cache_misses' => 0, + 'cache_ratio' => null, ); } @@ -298,9 +311,9 @@ class Profiler { } if ( isset( $wp_object_cache ) ) { - $hits = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; + $hits = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; $misses = ! empty( $wp_object_cache->cache_misses ) ? $wp_object_cache->cache_misses : 0; - $this->loggers[ $callback_hash ]['cache_hits'] = ( $hits - $this->tick_cache_hit_offset ) + $this->loggers[ $callback_hash ]['cache_hits']; + $this->loggers[ $callback_hash ]['cache_hits'] = ( $hits - $this->tick_cache_hit_offset ) + $this->loggers[ $callback_hash ]['cache_hits']; $this->loggers[ $callback_hash ]['cache_misses'] = ( $misses - $this->tick_cache_miss_offset ) + $this->loggers[ $callback_hash ]['cache_misses']; $total = $this->loggers[ $callback_hash ]['cache_hits'] + $this->loggers[ $callback_hash ]['cache_misses']; if ( $total ) { @@ -310,7 +323,7 @@ class Profiler { } } - $bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, 2 ); + $bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, 2 ); $frame = $bt[0]; if ( isset( $bt[1] ) ) { $frame = $bt[1]; @@ -319,11 +332,11 @@ class Profiler { $callback = $location = ''; if ( in_array( strtolower( $frame['function'] ), array( 'include', 'require', 'include_once', 'require_once' ) ) ) { $callback = $frame['function'] . " '" . $frame['args'][0] . "'"; - } else if ( isset( $frame['object'] ) && method_exists( $frame['object'], $frame['function'] ) ) { + } elseif ( isset( $frame['object'] ) && method_exists( $frame['object'], $frame['function'] ) ) { $callback = get_class( $frame['object'] ) . '->' . $frame['function'] . '()'; - } else if ( isset( $frame['class'] ) && method_exists( $frame['class'], $frame['function'] ) ) { + } elseif ( isset( $frame['class'] ) && method_exists( $frame['class'], $frame['function'] ) ) { $callback = $frame['class'] . '::' . $frame['function'] . '()'; - } else if ( ! empty( $frame['function'] ) && function_exists( $frame['function'] ) ) { + } elseif ( ! empty( $frame['function'] ) && function_exists( $frame['function'] ) ) { $callback = $frame['function'] . '()'; } elseif ( '__lambda_func' == $frame['function'] || '{closure}' == $frame['function'] ) { $callback = 'function(){}'; @@ -341,11 +354,11 @@ class Profiler { } } - $this->tick_callback = $callback; - $this->tick_location = $location; - $this->tick_start_time = microtime( true ); - $this->tick_query_offset = ! empty( $wpdb->queries ) ? count( $wpdb->queries ) : 0; - $this->tick_cache_hit_offset = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; + $this->tick_callback = $callback; + $this->tick_location = $location; + $this->tick_start_time = microtime( true ); + $this->tick_query_offset = ! empty( $wpdb->queries ) ? count( $wpdb->queries ) : 0; + $this->tick_cache_hit_offset = ! empty( $wp_object_cache->cache_hits ) ? $wp_object_cache->cache_hits : 0; $this->tick_cache_miss_offset = ! empty( $wp_object_cache->cache_misses ) ? $wp_object_cache->cache_misses : 0; } @@ -353,7 +366,7 @@ class Profiler { * Profiling request time for any active Loggers */ public function wp_request_begin( $filter_value = null ) { - foreach( Logger::$active_loggers as $logger ) { + foreach ( Logger::$active_loggers as $logger ) { $logger->start_request_timer(); } return $filter_value; @@ -363,7 +376,7 @@ class Profiler { * Profiling request time for any active Loggers */ public function wp_request_end( $filter_value = null ) { - foreach( Logger::$active_loggers as $logger ) { + foreach ( Logger::$active_loggers as $logger ) { $logger->stop_request_timer(); } return $filter_value; @@ -381,7 +394,7 @@ class Profiler { if ( 'stage' === $this->type && true === $this->focus ) { $hooks = array(); - foreach( $this->stage_hooks as $stage_hook ) { + foreach ( $this->stage_hooks as $stage_hook ) { $hooks = array_merge( $hooks, $stage_hook ); } $this->set_stage_hooks( $hooks ); @@ -390,7 +403,7 @@ class Profiler { if ( 'stage' === $this->type ) { if ( 'bootstrap' === $this->focus ) { $this->set_stage_hooks( $this->stage_hooks['bootstrap'] ); - } else if ( ! $this->focus ) { + } elseif ( ! $this->focus ) { $logger = new Logger( array( 'stage' => 'bootstrap' ) ); $logger->start(); } @@ -412,7 +425,7 @@ class Profiler { if ( 'stage' === $this->type ) { if ( 'main_query' === $this->focus ) { $this->set_stage_hooks( $this->stage_hooks['main_query'] ); - } else if ( ! $this->focus ) { + } elseif ( ! $this->focus ) { $logger = new Logger( array( 'stage' => 'main_query' ) ); $logger->start(); } @@ -433,15 +446,15 @@ class Profiler { define( 'WP_USE_THEMES', true ); // Template is normally loaded in global scope, so we need to replicate - foreach( $GLOBALS as $key => $value ) { - global $$key; + foreach ( $GLOBALS as $key => $value ) { + global ${$key}; // phpcs:ignore PHPCompatibility.PHP.ForbiddenGlobalVariableVariable.NonBareVariableFound -- Syntax is updated to compatible with php 5 and 7. } // Load the theme template. if ( 'stage' === $this->type ) { if ( 'template' === $this->focus ) { $this->set_stage_hooks( $this->stage_hooks['template'] ); - } else if ( ! $this->focus ) { + } elseif ( ! $this->focus ) { $logger = new Logger( array( 'stage' => 'template' ) ); $logger->start(); } @@ -467,20 +480,20 @@ class Profiler { * Get a human-readable name from a callback */ private static function get_name_location_from_callback( $callback ) { - $name = $location = ''; + $name = $location = ''; $reflection = false; if ( is_array( $callback ) && is_object( $callback[0] ) ) { $reflection = new \ReflectionMethod( $callback[0], $callback[1] ); - $name = get_class( $callback[0] ) . '->' . $callback[1] . '()'; + $name = get_class( $callback[0] ) . '->' . $callback[1] . '()'; } elseif ( is_array( $callback ) && method_exists( $callback[0], $callback[1] ) ) { $reflection = new \ReflectionMethod( $callback[0], $callback[1] ); - $name = $callback[0] . '::' . $callback[1] . '()'; + $name = $callback[0] . '::' . $callback[1] . '()'; } elseif ( is_object( $callback ) && is_a( $callback, 'Closure' ) ) { $reflection = new \ReflectionFunction( $callback ); - $name = 'function(){}'; - } else if ( is_string( $callback ) && function_exists( $callback ) ) { + $name = 'function(){}'; + } elseif ( is_string( $callback ) && function_exists( $callback ) ) { $reflection = new \ReflectionFunction( $callback ); - $name = $callback . '()'; + $name = $callback . '()'; } if ( $reflection ) { $location = $reflection->getFileName() . ':' . $reflection->getStartLine(); @@ -498,13 +511,13 @@ class Profiler { $abspath = rtrim( realpath( ABSPATH ), '/' ) . '/'; if ( defined( 'WP_PLUGIN_DIR' ) && 0 === stripos( $location, WP_PLUGIN_DIR ) ) { $location = str_replace( trailingslashit( WP_PLUGIN_DIR ), '', $location ); - } else if ( defined( 'WPMU_PLUGIN_DIR' ) && 0 === stripos( $location, WPMU_PLUGIN_DIR ) ) { + } elseif ( defined( 'WPMU_PLUGIN_DIR' ) && 0 === stripos( $location, WPMU_PLUGIN_DIR ) ) { $location = str_replace( trailingslashit( dirname( WPMU_PLUGIN_DIR ) ), '', $location ); - } else if ( function_exists( 'get_theme_root' ) && 0 === stripos( $location, get_theme_root() ) ) { + } elseif ( function_exists( 'get_theme_root' ) && 0 === stripos( $location, get_theme_root() ) ) { $location = str_replace( trailingslashit( get_theme_root() ), '', $location ); - } else if ( 0 === stripos( $location, $abspath . 'wp-admin/' ) ) { + } elseif ( 0 === stripos( $location, $abspath . 'wp-admin/' ) ) { $location = str_replace( $abspath, '', $location ); - } else if ( 0 === stripos( $location, $abspath . 'wp-includes/' ) ) { + } elseif ( 0 === stripos( $location, $abspath . 'wp-includes/' ) ) { $location = str_replace( $abspath, '', $location ); } return $location; @@ -514,8 +527,8 @@ class Profiler { * Set the hooks for the current stage */ private function set_stage_hooks( $hooks ) { - $this->current_stage_hooks = $hooks; - $pseudo_hook = "{$hooks[0]}:before"; + $this->current_stage_hooks = $hooks; + $pseudo_hook = "{$hooks[0]}:before"; $this->loggers[ $pseudo_hook ] = new Logger( array( 'hook' => $pseudo_hook ) ); $this->loggers[ $pseudo_hook ]->start(); } @@ -563,5 +576,5 @@ class Profiler { $wp_filter[ $filter ] = $callbacks; } } - + } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..4252e09 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,61 @@ + + + Custom ruleset for WP-CLI + + + + + + . + + */.git/* + */ci/* + */features/* + */packages/* + */tests/* + */utils/* + */vendor/* + + + + + + + + + + + + + + + + + + + + + */php/commands/src/CLI_Command.php + + + + + + + + + + + + + + + + + + diff --git a/utils/behat-tags.php b/utils/behat-tags.php deleted file mode 100644 index 9647114..0000000 --- a/utils/behat-tags.php +++ /dev/null @@ -1,46 +0,0 @@ -' ) -); - -# Skip Github API tests by default because of rate limiting. See https://github.com/wp-cli/wp-cli/issues/1612 -$skip_tags[] = '@github-api'; - -if ( !empty( $skip_tags ) ) { - echo '--tags=~' . implode( '&&~', $skip_tags ); -} -