From 72a02839400bf4cf7d70de68b24a176f770cd893 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sat, 17 Jun 2017 20:20:29 +0100 Subject: [PATCH 1/8] Manually load comments if opcache.save_comments disabled. --- ci/test.sh | 5 + features/bootstrap/FeatureContext.php | 3 + php/WP_CLI/Dispatcher/CommandFactory.php | 86 +++++++++- php/WP_CLI/Runner.php | 7 + .../data/commandfactory-doc_comment-class.php | 70 ++++++++ .../commandfactory-doc_comment-function.php | 19 +++ tests/test-commandfactory.php | 154 ++++++++++++++++++ 7 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 tests/data/commandfactory-doc_comment-class.php create mode 100644 tests/data/commandfactory-doc_comment-function.php create mode 100644 tests/test-commandfactory.php diff --git a/ci/test.sh b/ci/test.sh index 2cace66435..905a72e1c9 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -12,3 +12,8 @@ BEHAT_TAGS=$(php ci/behat-tags.php) # Run the functional tests vendor/bin/behat --format progress $BEHAT_TAGS --strict + +# Run the functional tests with opcache.save_comments disabled. +if [[ $TRAVIS_PHP_VERSION = '7.1' && $WP_VERSION = 'latest' ]]; then + WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format progress $BEHAT_TAGS --strict +fi diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 96e1647dfa..6ecb190034 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -76,6 +76,9 @@ private static function get_process_env_variables() { if ( $term = getenv( 'TERM' ) ) { $env['TERM'] = $term; } + if ( $php_args = getenv( 'WP_CLI_PHP_ARGS' ) ) { + $env['WP_CLI_PHP_ARGS'] = $php_args; + } return $env; } diff --git a/php/WP_CLI/Dispatcher/CommandFactory.php b/php/WP_CLI/Dispatcher/CommandFactory.php index bd667fe052..e5ae275c8c 100644 --- a/php/WP_CLI/Dispatcher/CommandFactory.php +++ b/php/WP_CLI/Dispatcher/CommandFactory.php @@ -9,6 +9,9 @@ */ class CommandFactory { + // Cache of file contents, indexed by filename. Only used if opcache.save_comments is disabled. + private static $file_contents = array(); + /** * Create a new CompositeCommand (or Subcommand if class has __invoke()) * @@ -40,6 +43,13 @@ public static function create( $name, $callable, $parent ) { return $command; } + /** + * Clear the file contents cache. + */ + public static function clear_file_contents_cache() { + self::$file_contents = array(); + } + /** * Create a new Subcommand instance. * @@ -51,7 +61,8 @@ public static function create( $name, $callable, $parent ) { * @param string $method Class method to be called upon invocation. */ private static function create_subcommand( $parent, $name, $callable, $reflection ) { - $docparser = new \WP_CLI\DocParser( $reflection->getDocComment() ); + $doc_comment = self::get_doc_comment( $reflection ); + $docparser = new \WP_CLI\DocParser( $doc_comment ); if ( is_array( $callable ) ) { if ( !$name ) @@ -60,6 +71,9 @@ private static function create_subcommand( $parent, $name, $callable, $reflectio if ( !$name ) $name = $reflection->name; } + if ( ! $doc_comment ) { + \WP_CLI::debug( null === $doc_comment ? "Failed to get doc comment for {$name}." : "No doc comment for {$name}.", 'commandfactory' ); + } $when_invoked = function ( $args, $assoc_args ) use ( $callable ) { if ( is_array( $callable ) ) { @@ -82,7 +96,11 @@ private static function create_subcommand( $parent, $name, $callable, $reflectio */ private static function create_composite_command( $parent, $name, $callable ) { $reflection = new \ReflectionClass( $callable ); - $docparser = new \WP_CLI\DocParser( $reflection->getDocComment() ); + $doc_comment = self::get_doc_comment( $reflection ); + if ( ! $doc_comment ) { + \WP_CLI::debug( null === $doc_comment ? "Failed to get doc comment for {$name}." : "No doc comment for {$name}.", 'commandfactory' ); + } + $docparser = new \WP_CLI\DocParser( $doc_comment ); $container = new CompositeCommand( $parent, $name, $docparser ); @@ -110,5 +128,67 @@ private static function create_composite_command( $parent, $name, $callable ) { private static function is_good_method( $method ) { return $method->isPublic() && !$method->isStatic() && 0 !== strpos( $method->getName(), '__' ); } -} + /** + * Gets the document comment. Caters for PHP directive `opcache.save comments` being disabled. + * + * @param ReflectionMethod|ReflectionClass|ReflectionFunction $reflection Reflection instance. + * @return string|false|null Doc comment string if any, false if none (same as `Reflection*::getDocComment()`), null if error. + */ + private static function get_doc_comment( $reflection ) { + $doc_comment = $reflection->getDocComment(); + + if ( false !== $doc_comment || ! ( ini_get( 'opcache.enable_cli' ) && ! ini_get( 'opcache.save_comments' ) ) ) { + // Either have doc comment, or no doc comment and save comments enabled - standard situation. + if ( ! getenv( 'WP_CLI_TEST_GET_DOC_COMMENT' ) ) { + return $doc_comment; + } + } + + $filename = $reflection->getFileName(); + + if ( isset( self::$file_contents[ $filename ] ) ) { + $contents = self::$file_contents[ $filename ]; + } elseif ( is_readable( $filename ) && ( $contents = file_get_contents( $filename ) ) ) { + self::$file_contents[ $filename ] = $contents = explode( "\n", $contents ); + } else { + \WP_CLI::debug( "Could not read contents for filename '{$filename}'.", 'commandfactory' ); + return null; + } + + return self::extract_last_doc_comment( implode( "\n", array_slice( $contents, 0, $reflection->getStartLine() ) ) ); + } + + /** + * Returns the last doc comment if any in `$content`. + * + * @param string $content The content, which should end at the class or function declaration. + * @return string|bool The last doc comment if any, or false if none. + */ + private static function extract_last_doc_comment( $content ) { + $content = trim( $content ); + $comment_end_pos = strrpos( $content, '*/' ); + if ( false === $comment_end_pos ) { + return false; + } + // Make sure comment end belongs to this class/function. + if ( preg_match_all( '/(?:^|[\s;}])(?:class|function)\s+/', substr( $content, $comment_end_pos + 2 ) ) > 1 ) { + return false; + } + $content = substr( $content, 0, $comment_end_pos + 2 ); + if ( false === ( $comment_start_pos = strrpos( $content, '/**' ) ) || $comment_start_pos + 2 === $comment_end_pos ) { + return false; + } + // Make sure comment start belongs to this comment end. + if ( false !== ( $comment_end2_pos = strpos( substr( $content, $comment_start_pos ), '*/' ) ) && $comment_start_pos + $comment_end2_pos < $comment_end_pos ) { + return false; + } + // Allow for '/**' within doc comment. + $subcontent = substr( $content, 0, $comment_start_pos ); + while ( false !== ( $comment_start2_pos = strrpos( $subcontent, '/**' ) ) && false === strpos( $subcontent, '*/', $comment_start2_pos ) ) { + $comment_start_pos = $comment_start2_pos; + $subcontent = substr( $subcontent, 0, $comment_start_pos ); + } + return substr( $content, $comment_start_pos, $comment_end_pos + 2 ); + } +} diff --git a/php/WP_CLI/Runner.php b/php/WP_CLI/Runner.php index 8e8e8ccd48..bcdafca9f5 100644 --- a/php/WP_CLI/Runner.php +++ b/php/WP_CLI/Runner.php @@ -740,6 +740,12 @@ private function check_root() { ); } + private function check_opcache() { + if ( ini_get( 'opcache.enable_cli' ) && ! ini_get( 'opcache.save_comments' ) ) { + WP_CLI::debug( 'Warning: opcache.save_comments disabled.', 'bootstrap' ); + } + } + private function run_alias_group( $aliases ) { $php_bin = WP_CLI::get_php_binary(); @@ -781,6 +787,7 @@ public function start() { WP_CLI::debug( 'argv: ' . implode( ' ', $GLOBALS['argv'] ), 'bootstrap' ); $this->check_root(); + $this->check_opcache(); if ( $this->alias ) { if ( '@all' === $this->alias && ! isset( $this->aliases['@all'] ) ) { WP_CLI::error( "Cannot use '@all' when no aliases are registered." ); diff --git a/tests/data/commandfactory-doc_comment-class.php b/tests/data/commandfactory-doc_comment-class.php new file mode 100644 index 0000000000..a778f63bd2 --- /dev/null +++ b/tests/data/commandfactory-doc_comment-class.php @@ -0,0 +1,70 @@ +] + * + * ## EXAMPLES + * + * $ wp foo command2 --path=/**a/**b/**c/** + */ + +final + protected + static + function + command2() { + } + + /** + * Command3 function + * + * ## OPTIONS + * + * [--path=] + * + * ## EXAMPLES + * + * $ wp foo command3 --path=/**a/**b/**c/** + function*/public function command3( $function ) {} + + function command4() {} +} + +/** + * Basic class + * + * ## EXAMPLES + * + * # Foo. + * $ wp foo --final abstract + class*/abstract class + CommandFactoryTests_Get_Doc_Comment_2_Command + extends WP_CLI_Command + { + function command1() {} + } diff --git a/tests/data/commandfactory-doc_comment-function.php b/tests/data/commandfactory-doc_comment-function.php new file mode 100644 index 0000000000..1130cb676a --- /dev/null +++ b/tests/data/commandfactory-doc_comment-function.php @@ -0,0 +1,19 @@ +setAccessible( true ); + } + + $actual = $extract_last_doc_comment->invoke( null, $content ); + $this->assertSame( $expected, $actual ); + } + + function dataProviderExtractLastDocComment() { + return array( + array( "", false ), + array( "*/", false ), + array( "/*/ ", false ), + array( "/**/", false ), + array( "/***/ */", false ), + array( "/***/", "/***/" ), + array( "\n /**\n \n \t\n */ \t\n \n ", "/**\n \n \t\n */" ), + array( "/**/ /***/ /***/", "/***/" ), + array( "asdfasdf/** /** */", "/** /** */" ), + array( "*//** /** */", "/** /** */" ), + array( "/** *//** /** */", "/** /** */" ), + array( "*//** */ /** /** */", "/** /** */" ), + array( "*//** *//** /** /** */", "/** /** /** */" ), + + array( "/** */class qwer", "/** */" ), + array( "/**1*/class qwer{}/**2*/class asdf", "/**2*/" ), + array( "/** */class qwer {}\nclass asdf", false ), + + array( "/** */function qwer", "/** */" ), + array( "/** */function qwer( \$function ) {}", "/** */" ), + array( "/**1*/function qwer() {}/**2*/function asdf()", "/**2*/" ), + array( "/** */function qwer() {}\nfunction asdf()", false ), + array( "/** */function qwer() {}function asdf()", false ), + array( "/** */function qwer() {};function asdf( \$function )", false ), + ); + } + + function testGetDocComment() { + // Save and set test env var. + $prev_test_get_doc_comment = getenv( 'WP_CLI_TEST_GET_DOC_COMMENT' ); + putenv( 'WP_CLI_TEST_GET_DOC_COMMENT=1' ); + + // Make private function accessible. + $get_doc_comment = new \ReflectionMethod( 'WP_CLI\Dispatcher\CommandFactory', 'get_doc_comment' ); + $get_doc_comment->setAccessible( true ); + + require __DIR__ . '/data/commandfactory-doc_comment-class.php'; + + // Class 1 + + $reflection = new \ReflectionClass( 'CommandFactoryTests_Get_Doc_Comment_1_Command' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Class method 1 + + $reflection = new \ReflectionMethod( 'CommandFactoryTests_Get_Doc_Comment_1_Command', 'command1' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Class method 2 + + $reflection = new \ReflectionMethod( 'CommandFactoryTests_Get_Doc_Comment_1_Command', 'command2' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Class method 3 + + $reflection = new \ReflectionMethod( 'CommandFactoryTests_Get_Doc_Comment_1_Command', 'command3' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Class method 4 + + $reflection = new \ReflectionMethod( 'CommandFactoryTests_Get_Doc_Comment_1_Command', 'command4' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + $this->assertFalse( $actual ); + + // Class 2 + + $reflection = new \ReflectionClass( 'CommandFactoryTests_Get_Doc_Comment_2_Command' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Class method 1 + + $reflection = new \ReflectionMethod( 'CommandFactoryTests_Get_Doc_Comment_2_Command', 'command1' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + $this->assertFalse( $actual ); + + // Functions + + require __DIR__ . '/data/commandfactory-doc_comment-function.php'; + + // Function 1 + + $reflection = new \ReflectionFunction( 'commandfactorytests_get_doc_comment_func_1' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Function 2 + + $reflection = new \ReflectionFunction( 'commandfactorytests_get_doc_comment_func_2' ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Function 3 + + $reflection = new \ReflectionFunction( $commandfactorytests_get_doc_comment_func_3 ); + $expected = $reflection->getDocComment(); + + $actual = $get_doc_comment->invoke( null, $reflection ); + $this->assertSame( $expected, $actual ); + + // Restore. + + putenv( false === $prev_test_get_doc_comment ? 'WP_CLI_TEST_GET_DOC_COMMENT' : "WP_CLI_TEST_GET_DOC_COMMENT=$prev_test_get_doc_comment" ); + $this->assertTrue( true ); + } +} From d4640fdb3609b40b7e197401270fa8b2a906e659 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 09:35:59 +0100 Subject: [PATCH 2/8] Fix preg_match_all PHP 5.3 compat. Add require-opcache-save-comments tag. Ensure cli-info.feature re-runnable. --- ci/behat-tags.php | 4 ++++ features/bootstrap.feature | 1 + features/cli-info.feature | 5 +++++ features/cli.feature | 2 +- php/WP_CLI/Dispatcher/CommandFactory.php | 2 +- php/commands/src/CLI_Command.php | 20 ++++++++++++++----- php/utils.php | 2 +- .../data/commandfactory-doc_comment-class.php | 2 +- 8 files changed, 29 insertions(+), 9 deletions(-) diff --git a/ci/behat-tags.php b/ci/behat-tags.php index ee51fc91ee..58c6ef588b 100644 --- a/ci/behat-tags.php +++ b/ci/behat-tags.php @@ -74,3 +74,7 @@ function extension_tags() { echo '--tags=~' . implode( '&&~', $skip_tags ); } +# Require opcache.save_comments enabled for tests involving non-supporting wp-cli versions. +if ( ! ( ini_get( 'opcache.enable_cli' ) && ! ini_get( 'opcache.save_comments' ) ) ) { + $skip_tags[] = '@require-opcache-save-comments'; +} diff --git a/features/bootstrap.feature b/features/bootstrap.feature index e5af584b1c..d72c22bfad 100644 --- a/features/bootstrap.feature +++ b/features/bootstrap.feature @@ -1,5 +1,6 @@ Feature: Bootstrap WP-CLI + @require-opcache-save-comments Scenario: Basic Composer stack Given an empty directory And a composer.json file: diff --git a/features/cli-info.feature b/features/cli-info.feature index b5cedc1457..812e73cbfd 100644 --- a/features/cli-info.feature +++ b/features/cli-info.feature @@ -1,7 +1,12 @@ Feature: Review CLI information + Background: + When I run `wp package path` + Then save STDOUT as {PACKAGE_PATH} + Scenario: Get the path to the packages directory Given an empty directory + And I run `if [ -d "{PACKAGE_PATH}" ]; then rm -rf {PACKAGE_PATH}; fi` When I run `wp cli info --format=json` Then STDOUT should be JSON containing: diff --git a/features/cli.feature b/features/cli.feature index 4634b09ab7..f2413a7960 100644 --- a/features/cli.feature +++ b/features/cli.feature @@ -137,7 +137,7 @@ Feature: `wp cli` tasks y """ - When I run `{PHAR_PATH} cli check-update --minor --field=version` + When I run `{PHAR_PATH} cli check-update --field=version | head -1` Then STDOUT should not be empty And save STDOUT as {UPDATE_VERSION} diff --git a/php/WP_CLI/Dispatcher/CommandFactory.php b/php/WP_CLI/Dispatcher/CommandFactory.php index e5ae275c8c..8cf6f25d74 100644 --- a/php/WP_CLI/Dispatcher/CommandFactory.php +++ b/php/WP_CLI/Dispatcher/CommandFactory.php @@ -172,7 +172,7 @@ private static function extract_last_doc_comment( $content ) { return false; } // Make sure comment end belongs to this class/function. - if ( preg_match_all( '/(?:^|[\s;}])(?:class|function)\s+/', substr( $content, $comment_end_pos + 2 ) ) > 1 ) { + if ( preg_match_all( '/(?:^|[\s;}])(?:class|function)\s+/', substr( $content, $comment_end_pos + 2 ), $dummy /*needed for PHP 5.3*/ ) > 1 ) { return false; } $content = substr( $content, 0, $comment_end_pos + 2 ); diff --git a/php/commands/src/CLI_Command.php b/php/commands/src/CLI_Command.php index 69437a0198..2f482c69e1 100644 --- a/php/commands/src/CLI_Command.php +++ b/php/commands/src/CLI_Command.php @@ -352,13 +352,23 @@ private function get_updates( $assoc_args ) { $headers = array( 'Accept' => 'application/json' ); - $response = Utils\http_request( 'GET', $url, $headers, $options ); - if ( ! $response->success || 200 !== $response->status_code ) { - WP_CLI::error( sprintf( "Failed to get latest version (HTTP code %d).", $response->status_code ) ); - } + $release_data = array(); + + while ( true ) { + $response = Utils\http_request( 'GET', $url, $headers, $options ); + + if ( ! $response->success || 200 !== $response->status_code ) { + WP_CLI::error( sprintf( "Failed to get latest version (HTTP code %d).", $response->status_code ) ); + } - $release_data = json_decode( $response->body ); + $release_data = array_merge( $release_data, json_decode( $response->body ) ); + + if ( empty( $response->headers['link'] ) || ! preg_match( '/<([^>]+)>; rel="next"/', $response->headers['link'], $matches ) ) { + break; + } + $url = $matches[1]; + } $updates = array( 'major' => false, diff --git a/php/utils.php b/php/utils.php index dd187cd6d5..0ab0747e10 100644 --- a/php/utils.php +++ b/php/utils.php @@ -568,7 +568,7 @@ function http_request( $method, $url, $data = null, $headers = array(), $options } } if ( empty( $options['verify'] ) ){ - WP_CLI::error_log( "Cannot find SSL certificate." ); + WP_CLI::error( "Cannot find SSL certificate." ); } } diff --git a/tests/data/commandfactory-doc_comment-class.php b/tests/data/commandfactory-doc_comment-class.php index a778f63bd2..01b1c5eb25 100644 --- a/tests/data/commandfactory-doc_comment-class.php +++ b/tests/data/commandfactory-doc_comment-class.php @@ -10,7 +10,7 @@ */ class CommandFactoryTests_Get_Doc_Comment_1_Command extends WP_CLI_Command { /** - * Command1 method + * Command1 method * * ## OPTIONS * From 8951d86b79383a7d89a12de1293be93de1737fdc Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 10:03:46 +0100 Subject: [PATCH 3/8] Manually add require-opcache-save-comments tag. Revert github paging fix (do separately). --- ci/behat-tags.php | 4 ---- ci/test.sh | 2 +- php/WP_CLI/Runner.php | 7 ------- php/commands/src/CLI_Command.php | 20 +++++--------------- 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/ci/behat-tags.php b/ci/behat-tags.php index 58c6ef588b..ee51fc91ee 100644 --- a/ci/behat-tags.php +++ b/ci/behat-tags.php @@ -74,7 +74,3 @@ function extension_tags() { echo '--tags=~' . implode( '&&~', $skip_tags ); } -# Require opcache.save_comments enabled for tests involving non-supporting wp-cli versions. -if ( ! ( ini_get( 'opcache.enable_cli' ) && ! ini_get( 'opcache.save_comments' ) ) ) { - $skip_tags[] = '@require-opcache-save-comments'; -} diff --git a/ci/test.sh b/ci/test.sh index 905a72e1c9..09b474b270 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -15,5 +15,5 @@ vendor/bin/behat --format progress $BEHAT_TAGS --strict # Run the functional tests with opcache.save_comments disabled. if [[ $TRAVIS_PHP_VERSION = '7.1' && $WP_VERSION = 'latest' ]]; then - WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format progress $BEHAT_TAGS --strict + WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format progress "$BEHAT_TAGS&&~@require-opcache-save-comments" --strict fi diff --git a/php/WP_CLI/Runner.php b/php/WP_CLI/Runner.php index bcdafca9f5..8e8e8ccd48 100644 --- a/php/WP_CLI/Runner.php +++ b/php/WP_CLI/Runner.php @@ -740,12 +740,6 @@ private function check_root() { ); } - private function check_opcache() { - if ( ini_get( 'opcache.enable_cli' ) && ! ini_get( 'opcache.save_comments' ) ) { - WP_CLI::debug( 'Warning: opcache.save_comments disabled.', 'bootstrap' ); - } - } - private function run_alias_group( $aliases ) { $php_bin = WP_CLI::get_php_binary(); @@ -787,7 +781,6 @@ public function start() { WP_CLI::debug( 'argv: ' . implode( ' ', $GLOBALS['argv'] ), 'bootstrap' ); $this->check_root(); - $this->check_opcache(); if ( $this->alias ) { if ( '@all' === $this->alias && ! isset( $this->aliases['@all'] ) ) { WP_CLI::error( "Cannot use '@all' when no aliases are registered." ); diff --git a/php/commands/src/CLI_Command.php b/php/commands/src/CLI_Command.php index 2f482c69e1..69437a0198 100644 --- a/php/commands/src/CLI_Command.php +++ b/php/commands/src/CLI_Command.php @@ -352,24 +352,14 @@ private function get_updates( $assoc_args ) { $headers = array( 'Accept' => 'application/json' ); + $response = Utils\http_request( 'GET', $url, $headers, $options ); - $release_data = array(); - - while ( true ) { - $response = Utils\http_request( 'GET', $url, $headers, $options ); - - if ( ! $response->success || 200 !== $response->status_code ) { - WP_CLI::error( sprintf( "Failed to get latest version (HTTP code %d).", $response->status_code ) ); - } - - $release_data = array_merge( $release_data, json_decode( $response->body ) ); - - if ( empty( $response->headers['link'] ) || ! preg_match( '/<([^>]+)>; rel="next"/', $response->headers['link'], $matches ) ) { - break; - } - $url = $matches[1]; + if ( ! $response->success || 200 !== $response->status_code ) { + WP_CLI::error( sprintf( "Failed to get latest version (HTTP code %d).", $response->status_code ) ); } + $release_data = json_decode( $response->body ); + $updates = array( 'major' => false, 'minor' => false, From 62eecd4b9266f91cd22fd5412f6eb611956cd5e4 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 10:33:51 +0100 Subject: [PATCH 4/8] Add separate travis builds for opcache.save_comments. --- .travis.yml | 6 ++++++ ci/test.sh | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3c0571b72..ff5e049247 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,12 @@ matrix: env: WP_VERSION=3.7.11 - php: 5.3 env: WP_VERSION=3.7.11 DEPLOY_BRANCH=master + - php: 7.1 + env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 + - php: 5.6 + env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 + - php: 5.3 + env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 before_script: - export PATH="$HOME/.composer/vendor/bin:$PATH" diff --git a/ci/test.sh b/ci/test.sh index 09b474b270..0ec1967595 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -11,9 +11,9 @@ vendor/bin/phpunit BEHAT_TAGS=$(php ci/behat-tags.php) # Run the functional tests -vendor/bin/behat --format progress $BEHAT_TAGS --strict - -# Run the functional tests with opcache.save_comments disabled. -if [[ $TRAVIS_PHP_VERSION = '7.1' && $WP_VERSION = 'latest' ]]; then +if [[ -n "$SAVE_COMMENTS_DISABLED" ]]; then + # Run the functional tests with opcache.save_comments disabled. WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format progress "$BEHAT_TAGS&&~@require-opcache-save-comments" --strict +else + vendor/bin/behat --format progress $BEHAT_TAGS --strict fi From a483fc9815a9d4a1f73e0c13b715ced3d4007362 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 11:37:34 +0100 Subject: [PATCH 5/8] Perm travis matrix. Use Symfony Filesystem::remove. Try --format=failed. --- .travis.yml | 6 +++--- ci/test.sh | 2 +- features/bootstrap/FeatureContext.php | 14 ++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff5e049247..4ef094d97c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,11 +22,11 @@ matrix: env: WP_VERSION=3.7.11 - php: 5.3 env: WP_VERSION=3.7.11 DEPLOY_BRANCH=master - - php: 7.1 + - php: 7.0 env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 - - php: 5.6 + - php: 5.5 env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 - - php: 5.3 + - php: 5.4 env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 before_script: diff --git a/ci/test.sh b/ci/test.sh index 0ec1967595..d3c38c2c1d 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -13,7 +13,7 @@ BEHAT_TAGS=$(php ci/behat-tags.php) # Run the functional tests if [[ -n "$SAVE_COMMENTS_DISABLED" ]]; then # Run the functional tests with opcache.save_comments disabled. - WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format progress "$BEHAT_TAGS&&~@require-opcache-save-comments" --strict + time WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format failed "$BEHAT_TAGS&&~@require-opcache-save-comments" --strict else vendor/bin/behat --format progress $BEHAT_TAGS --strict fi diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 6ecb190034..2eb157e53b 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -8,6 +8,9 @@ use \WP_CLI\Process; use \WP_CLI\Utils; +use Symfony\Component\Filesystem\Filesystem, + Symfony\Component\Filesystem\Exception\IOExceptionInterface; + // Inside a community package if ( file_exists( __DIR__ . '/utils.php' ) ) { require_once __DIR__ . '/utils.php'; @@ -44,7 +47,7 @@ */ class FeatureContext extends BehatContext implements ClosuredContextInterface { - private static $cache_dir, $suite_cache_dir; + private static $cache_dir, $suite_cache_dir, $fs; private static $db_settings = array( 'dbname' => 'wp_cli_test', @@ -116,7 +119,7 @@ public static function prepare( SuiteEvent $event ) { */ 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(); + self::$fs->remove( self::$suite_cache_dir ); } } @@ -134,13 +137,13 @@ public function afterScenario( $event ) { if ( isset( $this->variables['RUN_DIR'] ) ) { // remove altered WP install, unless there's an error if ( $event->getResult() < 4 && 0 === strpos( $this->variables['RUN_DIR'], sys_get_temp_dir() ) ) { - $this->proc( Utils\esc_cmd( 'rm -rf %s', $this->variables['RUN_DIR'] ) )->run(); + self::$fs->remove( $this->variables['RUN_DIR'] ); } } // Remove WP-CLI package directory if ( isset( $this->variables['PACKAGE_PATH'] ) ) { - $this->proc( Utils\esc_cmd( 'rm -rf %s', $this->variables['PACKAGE_PATH'] ) )->run(); + self::$fs->remove( $this->variables['PACKAGE_PATH'] ); } foreach ( $this->running_procs as $proc ) { @@ -195,6 +198,9 @@ 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 ); + if ( ! self::$fs ) { + self::$fs = new FileSystem; + } } public function getStepDefinitionResources() { From 763b300d21ef5fad3d52f9d3ae0641b3ffea5574 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 12:13:38 +0100 Subject: [PATCH 6/8] Ok don't try --format=failed. --- ci/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test.sh b/ci/test.sh index d3c38c2c1d..0ec1967595 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -13,7 +13,7 @@ BEHAT_TAGS=$(php ci/behat-tags.php) # Run the functional tests if [[ -n "$SAVE_COMMENTS_DISABLED" ]]; then # Run the functional tests with opcache.save_comments disabled. - time WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format failed "$BEHAT_TAGS&&~@require-opcache-save-comments" --strict + WP_CLI_PHP_ARGS='-dopcache.enable_cli=1 -dopcache.save_comments=0' vendor/bin/behat --format progress "$BEHAT_TAGS&&~@require-opcache-save-comments" --strict else vendor/bin/behat --format progress $BEHAT_TAGS --strict fi From 18fec6c663ada2d94673b10e0bdf1cf6795b0d55 Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 13:30:50 +0100 Subject: [PATCH 7/8] Do only one SAVE_COMMENTS build in travis. Add empty X directory step in behat. Avoid shelling out if poss. --- .travis.yml | 6 +----- features/bootstrap/FeatureContext.php | 13 +++++++++++-- features/cli-info.feature | 2 +- features/steps/given.php | 11 ++++++++++- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ef094d97c..6154627854 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,11 +22,7 @@ matrix: env: WP_VERSION=3.7.11 - php: 5.3 env: WP_VERSION=3.7.11 DEPLOY_BRANCH=master - - php: 7.0 - env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 - - php: 5.5 - env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 - - php: 5.4 + - php: 7.1 env: WP_VERSION=latest SAVE_COMMENTS_DISABLED=1 before_script: diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 2eb157e53b..d1f3d087c6 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -316,7 +316,9 @@ public function download_phar( $version = 'same' ) { 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(); + if ( ! file_exists( $path ) ) { + mkdir( $path, 0777, true ); + } $this->variables['CACHE_DIR'] = $path; } @@ -384,6 +386,13 @@ public function move_files( $src, $dest ) { rename( $this->variables['RUN_DIR'] . "/$src", $this->variables['RUN_DIR'] . "/$dest" ); } + /** + * Remove a directory (recursive). + */ + public function remove_dir( $dir ) { + self::$fs->remove( $dir ); + } + public function add_line_to_wp_config( &$wp_config_code, $line ) { $token = "/* That's all, stop editing!"; @@ -397,7 +406,7 @@ public function download_wp( $subdir = '' ) { mkdir( $dest_dir ); } - $this->proc( Utils\esc_cmd( "cp -r %s/* %s", self::$cache_dir, $dest_dir ) )->run_check(); + self::$fs-mirror( self::$cache_dir, $dest_dir ); // disable emailing mkdir( $dest_dir . '/wp-content/mu-plugins' ); diff --git a/features/cli-info.feature b/features/cli-info.feature index 812e73cbfd..bbef21cffd 100644 --- a/features/cli-info.feature +++ b/features/cli-info.feature @@ -6,7 +6,7 @@ Feature: Review CLI information Scenario: Get the path to the packages directory Given an empty directory - And I run `if [ -d "{PACKAGE_PATH}" ]; then rm -rf {PACKAGE_PATH}; fi` + And an empty {PACKAGE_PATH} directory When I run `wp cli info --format=json` Then STDOUT should be JSON containing: diff --git a/features/steps/given.php b/features/steps/given.php index c785df1137..3e92c1b8f0 100644 --- a/features/steps/given.php +++ b/features/steps/given.php @@ -10,6 +10,12 @@ function ( $world ) { } ); +$steps->Given( '/^an empty ([^\s]+) directory$/', + function ( $world, $dir ) { + $world->remove_dir( $world->replace_variables( $dir ) ); + } +); + $steps->Given( '/^an empty cache/', function ( $world ) { $world->variables['SUITE_CACHE_DIR'] = FeatureContext::create_cache_dir(); @@ -20,7 +26,10 @@ function ( $world ) { 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(); + $dir = dirname( $full_path ); + if ( ! file_exists( $dir ) ) { + mkdir( $dir, 0777, true ); + } file_put_contents( $full_path, $content ); } ); From 53076d3fa4ebfc2e6a45c4d4f74d1f064a99fd9b Mon Sep 17 00:00:00 2001 From: gitlost Date: Sun, 18 Jun 2017 13:39:37 +0100 Subject: [PATCH 8/8] Fix mirror() call, tab in cli-info.feature. --- features/bootstrap/FeatureContext.php | 2 +- features/cli-info.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index d1f3d087c6..7dc5bfa564 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -406,7 +406,7 @@ public function download_wp( $subdir = '' ) { mkdir( $dest_dir ); } - self::$fs-mirror( self::$cache_dir, $dest_dir ); + self::$fs->mirror( self::$cache_dir, $dest_dir ); // disable emailing mkdir( $dest_dir . '/wp-content/mu-plugins' ); diff --git a/features/cli-info.feature b/features/cli-info.feature index bbef21cffd..19cd0a09e0 100644 --- a/features/cli-info.feature +++ b/features/cli-info.feature @@ -6,7 +6,7 @@ Feature: Review CLI information Scenario: Get the path to the packages directory Given an empty directory - And an empty {PACKAGE_PATH} directory + And an empty {PACKAGE_PATH} directory When I run `wp cli info --format=json` Then STDOUT should be JSON containing: