diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 4bbca121ac..f6706f7401 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -18,6 +18,7 @@ Oh! It's worth noting WP-CLI represents a collection of sub-projects, each with * `wp core *` https://github.com/wp-cli/core-command * `wp cron *` https://github.com/wp-cli/cron-command * `wp db *` https://github.com/wp-cli/db-command +* `wp embed *` https://github.com/wp-cli/embed-command * `wp (eval|eval-file)` https://github.com/wp-cli/eval-command * `wp export` https://github.com/wp-cli/export-command * `wp (option|post|comment|user|term|site) *` https://github.com/wp-cli/entity-command diff --git a/.travis.yml b/.travis.yml index ad37613da4..19a6b056a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ php: 7.1 env: global: - WP_CLI_BIN_DIR=/tmp/wp-cli-phar + - TEST_COMMANDS=0 + - TEST_COMMANDS_GLOB=*-command before_script: - export PATH="$HOME/.composer/vendor/bin:$PATH" @@ -28,7 +30,7 @@ jobs: - stage: test script: ./ci/test.sh php: 7.0 - env: WP_VERSION=latest + env: WP_VERSION=latest WP_CLI_TEST_LOG_RUN_TIMES='stdout,40,40' - stage: test script: ./ci/test.sh php: 5.6 diff --git a/README.md b/README.md index ccb14cd92b..190592ef2f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Ongoing maintenance is -The current stable release is [version 1.4.1](https://make.wordpress.org/cli/2017/11/13/version-1-4-1-released/). For announcements, follow [@wpcli on Twitter](https://twitter.com/wpcli) or [sign up for email updates](https://make.wordpress.org/cli/subscribe/). [Check out the roadmap](https://make.wordpress.org/cli/handbook/roadmap/) for an overview of what's planned for upcoming releases. +The current stable release is [version 1.5.0](https://make.wordpress.org/cli/2018/01/31/version-1-5-0-released/). For announcements, follow [@wpcli on Twitter](https://twitter.com/wpcli) or [sign up for email updates](https://make.wordpress.org/cli/subscribe/). [Check out the roadmap](https://make.wordpress.org/cli/handbook/roadmap/) for an overview of what's planned for upcoming releases. [![Build Status](https://travis-ci.org/wp-cli/wp-cli.svg?branch=master)](https://travis-ci.org/wp-cli/wp-cli) [![Dependency Status](https://gemnasium.com/badges/github.com/wp-cli/wp-cli.svg)](https://gemnasium.com/github.com/wp-cli/wp-cli) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/wp-cli/wp-cli.svg)](https://isitmaintained.com/project/wp-cli/wp-cli "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/wp-cli/wp-cli.svg)](https://isitmaintained.com/project/wp-cli/wp-cli "Percentage of issues still open") @@ -73,14 +73,17 @@ If WP-CLI was installed successfully, you should see something like this when yo ```bash $ wp --info -PHP binary: /usr/bin/php5 -PHP version: 5.5.9-1ubuntu4.14 -php.ini used: /etc/php5/cli/php.ini +OS: Darwin 16.7.0 Darwin Kernel Version 16.7.0: Thu Jan 11 22:59:40 PST 2018; root:xnu-3789.73.8~1/RELEASE_X86_64 x86_64 +Shell: /bin/zsh +PHP binary: /usr/local/bin/php +PHP version: 7.0.22 +php.ini used: /etc/local/etc/php/7.0/php.ini WP-CLI root dir: /home/wp-cli/.wp-cli +WP-CLI vendor dir: /home/wp-cli/.wp-cli/vendor WP-CLI packages dir: /home/wp-cli/.wp-cli/packages/ WP-CLI global config: /home/wp-cli/.wp-cli/config.yml WP-CLI project config: -WP-CLI version: 1.4.1 +WP-CLI version: 1.5.0 ``` ### Updating diff --git a/VERSION b/VERSION index 1433486b40..fbffcdf951 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.0-alpha +2.0.0-alpha diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..0feddc1501 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,59 @@ +build: off +clone_folder: c:\projects\wp-cli +platform: x64 +shallow_clone: true +skip_tags: true +version: '{build}' + +services: + - mysql + +branches: + only: + - github-api + +cache: + - '%LOCALAPPDATA%\Composer\files' + - c:\ProgramData\chocolatey\bin -> appveyor.yml + - c:\ProgramData\chocolatey\lib -> appveyor.yml + - c:\projects\wp-cli-tools -> appveyor.yml + +environment: + MYSQL_PWD: Password12! + +init: + - set PATH=c:\projects\wp-cli-tools;C:\Program Files\Git\bin;C:\cygwin64\bin;C:\Program Files\Java\jdk1.8.0\bin;C:\Program Files\MySQL\MySQL Server 5.7\bin;%PATH% + - setx COMPOSER_NO_INTERACTION 1 + - git config --global core.autocrlf input + - ps: Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_BRANCH) - $($env:APPVEYOR_REPO_COMMIT)" + +# BEFORE services are launched. +install: + # Install PHP via WebPI. + - cInst webpicommandline -y + - WebPICMD /Install /Products:"PHP70" /AcceptEULA + + # Install Composer + - ps: mkdir c:\projects\wp-cli-tools -Force + - cd c:\projects\wp-cli-tools + - ps: Invoke-WebRequest https://getcomposer.org/composer.phar -Outfile composer.phar + - ps: Add-Content composer.bat "@php c:\projects\wp-cli-tools\composer.phar %*" + - RefreshEnv + + # Install wp-cli dependencies. + - cd "%APPVEYOR_BUILD_FOLDER%" + - composer install --no-interaction --prefer-source + +# AFTER services have been launched. +before_test: + - mysql -e "CREATE DATABASE wp_cli_test charset 'utf8';" --user=root + - mysql -e "GRANT ALL PRIVILEGES ON wp_cli_test.* TO wp_cli_test@localhost IDENTIFIED BY 'password1'" --user=root + - mysql -e "GRANT ALL PRIVILEGES ON wp_cli_test_scaffold.* TO wp_cli_test@localhost IDENTIFIED BY 'password1'" --user=root + +test_script: + # Run phpunit. + - vendor\bin\phpunit + # Run functional tests. + - vendor\bin\behat features/validation.feature + +on_finish: diff --git a/bin/wp b/bin/wp index 053e24227b..5493622ec3 100755 --- a/bin/wp +++ b/bin/wp @@ -40,6 +40,11 @@ esac # Pass in the path to php so that wp-cli knows which one # to use if it re-launches itself to run other commands. -export WP_CLI_PHP_USED="$php" +WP_CLI_PHP_USED="$php" +case $("$php" -r "echo PHP_OS;") in + WINNT*) + WP_CLI_PHP_USED="$(cygpath -w -a -- "$WP_CLI_PHP_USED")" ;; +esac +export WP_CLI_PHP_USED exec "$php" $WP_CLI_PHP_ARGS "$SCRIPT_PATH" "$@" diff --git a/bin/wp.bat b/bin/wp.bat index 59f953440b..bc21d8113d 100755 --- a/bin/wp.bat +++ b/bin/wp.bat @@ -1,2 +1,2 @@ @ECHO OFF -php "%~dp0../php/boot-fs.php" %* \ No newline at end of file +php %WP_CLI_PHP_ARGS% "%~dp0../php/boot-fs.php" %* diff --git a/ci/test.sh b/ci/test.sh index a1aba18638..f4bc5ced89 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -7,5 +7,17 @@ phpunit BEHAT_TAGS=$(php ci/behat-tags.php) -# Run the functional tests -vendor/bin/behat --format progress $BEHAT_TAGS --strict +if [[ $TEST_COMMANDS -eq 0 || $((TEST_COMMANDS & 1)) > 0 ]]; then + # Run the functional tests + vendor/bin/behat --format progress $BEHAT_TAGS --strict +fi + +if [[ $((TEST_COMMANDS & 2)) > 0 ]]; then + # Run the functional tests of commands. + TEST_COMMANDS_GLOB=${TEST_COMMANDS_GLOB-*-command} + for R in vendor/wp-cli/$TEST_COMMANDS_GLOB; do + if [[ -f "$R/behat.yml" ]]; then BEHAT_YML="--config $R/behat.yml"; fi + BEHAT_TAGS=$(cd $R && php ../../../ci/behat-tags.php) + vendor/bin/behat --format progress $BEHAT_YML $BEHAT_TAGS --strict $R/features + done +fi diff --git a/composer.json b/composer.json index 366c303083..2929dfacdb 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,7 @@ "wp-cli/core-command": "^1.0", "wp-cli/cron-command": "^1.0", "wp-cli/db-command": "^1.0", + "wp-cli/embed-command": "^1.0", "wp-cli/entity-command": "^1.0", "wp-cli/eval-command": "^1.0", "wp-cli/export-command": "^1.0", @@ -69,7 +70,6 @@ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3", "phpunit/phpunit": "3.7.*", "roave/security-advisories": "dev-master", - "wimg/php-compatibility": "^8.0", "wp-coding-standards/wpcs": "^0.13.1" }, "suggest": { @@ -80,6 +80,9 @@ "psr-4": { "": "php/commands/src" } }, "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + }, "autoload-splitter": { "splitter-logic": "WP_CLI\\AutoloadSplitter", "splitter-location": "php/WP_CLI/AutoloadSplitter.php", diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 50c62cafed..0000000000 --- a/composer.lock +++ /dev/null @@ -1,3764 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "content-hash": "0dc5e25ccc983dbe1ac137a5638b8654", - "packages": [ - { - "name": "composer/ca-bundle", - "version": "1.1.0", - "source": { - "type": "git", - "url": "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/composer/ca-bundle.git", - "reference": "943b2c4fcad1ef178d16a713c2468bf7e579c288" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/943b2c4fcad1ef178d16a713c2468bf7e579c288", - "reference": "943b2c4fcad1ef178d16a713c2468bf7e579c288", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35", - "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "time": "2017-11-29T09:37:33+00:00" - }, - { - "name": "composer/composer", - "version": "1.5.6", - "source": { - "type": "git", - "url": "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/composer/composer.git", - "reference": "4f7f9c12753ec43f1e4629e2a71cabe81f2a4eab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/4f7f9c12753ec43f1e4629e2a71cabe81f2a4eab", - "reference": "4f7f9c12753ec43f1e4629e2a71cabe81f2a4eab", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "composer/semver": "^1.0", - "composer/spdx-licenses": "^1.0", - "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", - "php": "^5.3.2 || ^7.0", - "psr/log": "^1.0", - "seld/cli-prompt": "^1.0", - "seld/jsonlint": "^1.4", - "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0", - "symfony/filesystem": "^2.7 || ^3.0", - "symfony/finder": "^2.7 || ^3.0", - "symfony/process": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" - }, - "suggest": { - "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", - "ext-zip": "Enabling the zip extension allows you to unzip archives", - "ext-zlib": "Allow gzip compression of HTTP requests" - }, - "bin": [ - "bin/composer" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\": "src/Composer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", - "homepage": "https://getcomposer.org/", - "keywords": [ - "autoload", - "dependency", - "package" - ], - "time": "2017-12-18T11:09:18+00:00" - }, - { - "name": "composer/semver", - "version": "1.4.2", - "source": { - "type": "git", - "url": "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/composer/semver.git", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "time": "2016-08-30T16:08:34+00:00" - }, - { - "name": "composer/spdx-licenses", - "version": "1.1.6", - "source": { - "type": "git", - "url": "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/composer/spdx-licenses.git", - "reference": "2603a0d7ddc00a015deb576fa5297ca43dee6b1c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/2603a0d7ddc00a015deb576fa5297ca43dee6b1c", - "reference": "2603a0d7ddc00a015deb576fa5297ca43dee6b1c", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Spdx\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "SPDX licenses list and validation library.", - "keywords": [ - "license", - "spdx", - "validator" - ], - "time": "2017-04-03T19:08:52+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "5.2.6", - "source": { - "type": "git", - "url": "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/justinrainbow/json-schema.git", - "reference": "d283e11b6e14c6f4664cf080415c4341293e5bbd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/d283e11b6e14c6f4664cf080415c4341293e5bbd", - "reference": "d283e11b6e14c6f4664cf080415c4341293e5bbd", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.22" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "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/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "time": "2017-10-21T13:15:38+00:00" - }, - { - "name": "mustache/mustache", - "version": "v2.12.0", - "source": { - "type": "git", - "url": "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/bobthecow/mustache.php.git", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "phpunit/phpunit": "~3.7|~4.0|~5.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Mustache": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "A Mustache implementation in PHP.", - "homepage": "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/bobthecow/mustache.php", - "keywords": [ - "mustache", - "templating" - ], - "time": "2017-07-11T12:54:05+00:00" - }, - { - "name": "nb/oxymel", - "version": "v0.1.0", - "source": { - "type": "git", - "url": "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/nb/oxymel.git", - "reference": "cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nb/oxymel/zipball/cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c", - "reference": "cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "type": "library", - "autoload": { - "psr-0": { - "Oxymel": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nikolay Bachiyski", - "email": "nb@nikolay.bg", - "homepage": "http://extrapolate.me/" - } - ], - "description": "A sweet XML builder", - "homepage": "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/nb/oxymel", - "keywords": [ - "xml" - ], - "time": "2013-02-24T15:01:54+00:00" - }, - { - "name": "psr/log", - "version": "1.0.2", - "source": { - "type": "git", - "url": "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/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "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/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2016-10-10T12:19:37+00:00" - }, - { - "name": "ramsey/array_column", - "version": "1.1.3", - "source": { - "type": "git", - "url": "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/ramsey/array_column.git", - "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/array_column/zipball/f8e52eb28e67eb50e613b451dd916abcf783c1db", - "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db", - "shasum": "" - }, - "require-dev": { - "jakub-onderka/php-parallel-lint": "0.8.*", - "phpunit/phpunit": "~4.5", - "satooshi/php-coveralls": "0.6.*", - "squizlabs/php_codesniffer": "~2.2" - }, - "type": "library", - "autoload": { - "files": [ - "src/array_column.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ben Ramsey", - "homepage": "http://benramsey.com" - } - ], - "description": "Provides functionality for array_column() to projects using PHP earlier than version 5.5.", - "homepage": "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/ramsey/array_column", - "keywords": [ - "array", - "array_column", - "column" - ], - "time": "2015-03-20T22:07:39+00:00" - }, - { - "name": "rmccue/requests", - "version": "v1.7.0", - "source": { - "type": "git", - "url": "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/rmccue/Requests.git", - "reference": "87932f52ffad70504d93f04f15690cf16a089546" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", - "reference": "87932f52ffad70504d93f04f15690cf16a089546", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "require-dev": { - "requests/test-server": "dev-master" - }, - "type": "library", - "autoload": { - "psr-0": { - "Requests": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Ryan McCue", - "homepage": "http://ryanmccue.info" - } - ], - "description": "A HTTP library written in PHP, for human beings.", - "homepage": "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/rmccue/Requests", - "keywords": [ - "curl", - "fsockopen", - "http", - "idna", - "ipv6", - "iri", - "sockets" - ], - "time": "2016-10-13T00:11:37+00:00" - }, - { - "name": "seld/cli-prompt", - "version": "1.0.3", - "source": { - "type": "git", - "url": "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/Seldaek/cli-prompt.git", - "reference": "a19a7376a4689d4d94cab66ab4f3c816019ba8dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/a19a7376a4689d4d94cab66ab4f3c816019ba8dd", - "reference": "a19a7376a4689d4d94cab66ab4f3c816019ba8dd", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\CliPrompt\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", - "keywords": [ - "cli", - "console", - "hidden", - "input", - "prompt" - ], - "time": "2017-03-18T11:32:45+00:00" - }, - { - "name": "seld/jsonlint", - "version": "1.6.2", - "source": { - "type": "git", - "url": "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/Seldaek/jsonlint.git", - "reference": "7a30649c67ee0d19faacfd9fa2cfb6cc032d9b19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/7a30649c67ee0d19faacfd9fa2cfb6cc032d9b19", - "reference": "7a30649c67ee0d19faacfd9fa2cfb6cc032d9b19", - "shasum": "" - }, - "require": { - "php": "^5.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.5" - }, - "bin": [ - "bin/jsonlint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], - "time": "2017-11-30T15:34:22+00:00" - }, - { - "name": "seld/phar-utils", - "version": "1.0.1", - "source": { - "type": "git", - "url": "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/Seldaek/phar-utils.git", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", - "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Seld\\PharUtils\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "PHAR file format utilities, for when PHP phars you up", - "keywords": [ - "phra" - ], - "time": "2015-10-13T18:44:15+00:00" - }, - { - "name": "symfony/config", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/config.git", - "reference": "f4f3f1d7090c464434bbbc3e8aa2b41149c59196" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/f4f3f1d7090c464434bbbc3e8aa2b41149c59196", - "reference": "f4f3f1d7090c464434bbbc3e8aa2b41149c59196", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/filesystem": "~2.3|~3.0.0" - }, - "require-dev": { - "symfony/yaml": "~2.7|~3.0.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Config Component", - "homepage": "https://symfony.com", - "time": "2017-11-07T11:56:23+00:00" - }, - { - "name": "symfony/console", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/console.git", - "reference": "46270f1ca44f08ebc134ce120fd2c2baf5fd63de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/46270f1ca44f08ebc134ce120fd2c2baf5fd63de", - "reference": "46270f1ca44f08ebc134ce120fd2c2baf5fd63de", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/debug": "^2.7.2|~3.0.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1|~3.0.0", - "symfony/process": "~2.1|~3.0.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2017-11-29T09:33:18+00:00" - }, - { - "name": "symfony/debug", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/debug.git", - "reference": "e72a0340dc2e273b3c4398d8eef9157ba51d8b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/e72a0340dc2e273b3c4398d8eef9157ba51d8b95", - "reference": "e72a0340dc2e273b3c4398d8eef9157ba51d8b95", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/class-loader": "~2.2|~3.0.0", - "symfony/http-kernel": "~2.3.24|~2.5.9|^2.6.2|~3.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "time": "2017-11-19T19:05:05+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/dependency-injection.git", - "reference": "d3e81e5402c38500770eb5595d78a6d85ea9e412" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d3e81e5402c38500770eb5595d78a6d85ea9e412", - "reference": "d3e81e5402c38500770eb5595d78a6d85ea9e412", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "conflict": { - "symfony/expression-language": "<2.6" - }, - "require-dev": { - "symfony/config": "~2.2|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/yaml": "~2.3.42|~2.7.14|~2.8.7|~3.0.7" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DependencyInjection Component", - "homepage": "https://symfony.com", - "time": "2017-11-23T11:13:33+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/event-dispatcher.git", - "reference": "b59aacf238fadda50d612c9de73b74751872a903" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b59aacf238fadda50d612c9de73b74751872a903", - "reference": "b59aacf238fadda50d612c9de73b74751872a903", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2017-11-05T15:25:56+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/filesystem.git", - "reference": "15ceb6736a9eebd0d99f9e05a62296ab6ce1cf2b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/15ceb6736a9eebd0d99f9e05a62296ab6ce1cf2b", - "reference": "15ceb6736a9eebd0d99f9e05a62296ab6ce1cf2b", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2017-11-19T18:39:05+00:00" - }, - { - "name": "symfony/finder", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/finder.git", - "reference": "efeceae6a05a9b2fcb3391333f1d4a828ff44ab8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/efeceae6a05a9b2fcb3391333f1d4a828ff44ab8", - "reference": "efeceae6a05a9b2fcb3391333f1d4a828ff44ab8", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2017-11-05T15:25:56+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "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/symfony/polyfill-mbstring.git", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2017-10-11T12:05:26+00:00" - }, - { - "name": "symfony/process", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/process.git", - "reference": "d25449e031f600807949aab7cadbf267712f4eee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d25449e031f600807949aab7cadbf267712f4eee", - "reference": "d25449e031f600807949aab7cadbf267712f4eee", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2017-11-05T15:25:56+00:00" - }, - { - "name": "symfony/translation", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/translation.git", - "reference": "0c63d56516c4c4c323228ca6348eadb7c91b1daf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/0c63d56516c4c4c323228ca6348eadb7c91b1daf", - "reference": "0c63d56516c4c4c323228ca6348eadb7c91b1daf", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/config": "<2.7" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8", - "symfony/intl": "~2.7.25|^2.8.18|~3.2.5", - "symfony/yaml": "~2.2|~3.0.0" - }, - "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com", - "time": "2017-11-07T14:08:47+00:00" - }, - { - "name": "symfony/yaml", - "version": "v2.8.32", - "source": { - "type": "git", - "url": "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/symfony/yaml.git", - "reference": "968ef42161e4bc04200119da473077f9e7015128" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/968ef42161e4bc04200119da473077f9e7015128", - "reference": "968ef42161e4bc04200119da473077f9e7015128", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2017-11-29T09:33:18+00:00" - }, - { - "name": "wp-cli/autoload-splitter", - "version": "v0.1.5", - "source": { - "type": "git", - "url": "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/wp-cli/autoload-splitter.git", - "reference": "fb4302da26390811d2631c62b42b75976d224bb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/autoload-splitter/zipball/fb4302da26390811d2631c62b42b75976d224bb8", - "reference": "fb4302da26390811d2631c62b42b75976d224bb8", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1" - }, - "type": "composer-plugin", - "extra": { - "class": "WP_CLI\\AutoloadSplitter\\ComposerPlugin" - }, - "autoload": { - "psr-4": { - "WP_CLI\\AutoloadSplitter\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alain Schlesser", - "email": "alain.schlesser@gmail.com", - "homepage": "https://www.alainschlesser.com" - } - ], - "description": "Composer plugin for splitting a generated autoloader into two distinct parts.", - "homepage": "https://wp-cli.org", - "time": "2017-08-03T08:40:17+00:00" - }, - { - "name": "wp-cli/cache-command", - "version": "v1.0.6", - "source": { - "type": "git", - "url": "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/wp-cli/cache-command.git", - "reference": "d82cba9effa198f17847dce5771c8fb20c443ffa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/cache-command/zipball/d82cba9effa198f17847dce5771c8fb20c443ffa", - "reference": "d82cba9effa198f17847dce5771c8fb20c443ffa", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "cache", - "cache add", - "cache decr", - "cache delete", - "cache flush", - "cache get", - "cache incr", - "cache replace", - "cache set", - "cache type", - "transient", - "transient delete", - "transient get", - "transient set", - "transient type" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "cache-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Manages object and transient caches.", - "homepage": "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/wp-cli/cache-command", - "time": "2017-12-14T19:21:19+00:00" - }, - { - "name": "wp-cli/checksum-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/checksum-command.git", - "reference": "a2719a5ba84ffd7d47511967433da7211e0da55a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/checksum-command/zipball/a2719a5ba84ffd7d47511967433da7211e0da55a", - "reference": "a2719a5ba84ffd7d47511967433da7211e0da55a", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "checksum core" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "checksum-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Verifies file integrity by comparing to published checksums.", - "homepage": "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/wp-cli/checksum-command", - "time": "2017-12-08T15:12:46+00:00" - }, - { - "name": "wp-cli/config-command", - "version": "v1.1.7", - "source": { - "type": "git", - "url": "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/wp-cli/config-command.git", - "reference": "e0c378457e6eab81ab0cc322aba86afdad57ec45" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/config-command/zipball/e0c378457e6eab81ab0cc322aba86afdad57ec45", - "reference": "e0c378457e6eab81ab0cc322aba86afdad57ec45", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "config", - "config create", - "config get", - "config path" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "config-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Manage the wp-config.php file.", - "homepage": "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/wp-cli/config-command", - "time": "2017-12-08T15:12:01+00:00" - }, - { - "name": "wp-cli/core-command", - "version": "v1.0.8", - "source": { - "type": "git", - "url": "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/wp-cli/core-command.git", - "reference": "b1c3c1bd10d081c24b606fcc6ed89100d59d4218" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/core-command/zipball/b1c3c1bd10d081c24b606fcc6ed89100d59d4218", - "reference": "b1c3c1bd10d081c24b606fcc6ed89100d59d4218", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "core", - "core check-update", - "core download", - "core install", - "core is-installed", - "core multisite-convert", - "core multisite-install", - "core update", - "core update-db", - "core version" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "core-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Downloads, installs, updates, and manages a WordPress installation.", - "homepage": "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/wp-cli/core-command", - "time": "2017-12-19T22:21:54+00:00" - }, - { - "name": "wp-cli/cron-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/cron-command.git", - "reference": "9da7e36e8f9c14cb171a3c5204cba2865e0ed7ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/cron-command/zipball/9da7e36e8f9c14cb171a3c5204cba2865e0ed7ef", - "reference": "9da7e36e8f9c14cb171a3c5204cba2865e0ed7ef", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "cron", - "cron test", - "cron event", - "cron event delete", - "cron event list", - "cron event run", - "cron event schedule", - "cron schedule", - "cron schedule list" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "cron-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Tests, runs, and deletes WP-Cron events; manages WP-Cron schedules.", - "homepage": "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/wp-cli/cron-command", - "time": "2017-12-08T15:09:54+00:00" - }, - { - "name": "wp-cli/db-command", - "version": "v1.3.2", - "source": { - "type": "git", - "url": "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/wp-cli/db-command.git", - "reference": "162d72acfd6dc1478e2ef249160bffbfa4293371" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/db-command/zipball/162d72acfd6dc1478e2ef249160bffbfa4293371", - "reference": "162d72acfd6dc1478e2ef249160bffbfa4293371", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "db", - "db create", - "db drop", - "db reset", - "db check", - "db optimize", - "db prefix", - "db repair", - "db cli", - "db query", - "db export", - "db import", - "db search", - "db tables", - "db size" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "db-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Performs basic database operations using credentials stored in wp-config.php.", - "homepage": "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/wp-cli/db-command", - "time": "2017-12-08T15:04:48+00:00" - }, - { - "name": "wp-cli/entity-command", - "version": "v1.1.4", - "source": { - "type": "git", - "url": "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/wp-cli/entity-command.git", - "reference": "c3a0c2b478aeb385865e6c406b2ecb1fe1900cb5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/entity-command/zipball/c3a0c2b478aeb385865e6c406b2ecb1fe1900cb5", - "reference": "c3a0c2b478aeb385865e6c406b2ecb1fe1900cb5", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "phpunit/phpunit": "^4.8", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "comment", - "comment approve", - "comment count", - "comment create", - "comment delete", - "comment exists", - "comment generate", - "comment get", - "comment list", - "comment meta", - "comment meta add", - "comment meta delete", - "comment meta get", - "comment meta list", - "comment meta patch", - "comment meta pluck", - "comment meta update", - "comment recount", - "comment spam", - "comment status", - "comment trash", - "comment unapprove", - "comment unspam", - "comment untrash", - "comment update", - "menu", - "menu create", - "menu delete", - "menu item", - "menu item add-custom", - "menu item add-post", - "menu item add-term", - "menu item delete", - "menu item list", - "menu item update", - "menu list", - "menu location", - "menu location assign", - "menu location list", - "menu location remove", - "network meta", - "network meta add", - "network meta delete", - "network meta get", - "network meta list", - "network meta patch", - "network meta pluck", - "network meta update", - "option", - "option add", - "option delete", - "option get", - "option list", - "option patch", - "option pluck", - "option update", - "post", - "post create", - "post delete", - "post edit", - "post generate", - "post get", - "post list", - "post meta", - "post meta add", - "post meta delete", - "post meta get", - "post meta list", - "post meta patch", - "post meta pluck", - "post meta update", - "post term", - "post term add", - "post term list", - "post term remove", - "post term set", - "post update", - "post-type", - "post-type get", - "post-type list", - "site", - "site activate", - "site archive", - "site create", - "site deactivate", - "site delete", - "site empty", - "site list", - "site mature", - "site option", - "site private", - "site public", - "site spam", - "site unarchive", - "site unmature", - "site unspam", - "taxonomy", - "taxonomy get", - "taxonomy list", - "term", - "term create", - "term delete", - "term generate", - "term get", - "term list", - "term meta", - "term meta add", - "term meta delete", - "term meta get", - "term meta list", - "term meta patch", - "term meta pluck", - "term meta update", - "term recount", - "term update", - "user", - "user add-cap", - "user add-role", - "user create", - "user delete", - "user generate", - "user get", - "user import-csv", - "user list", - "user list-caps", - "user meta", - "user meta add", - "user meta delete", - "user meta get", - "user meta list", - "user meta patch", - "user meta pluck", - "user meta update", - "user remove-cap", - "user remove-role", - "user session", - "user session destroy", - "user session list", - "user set-role", - "user spam", - "user term", - "user term add", - "user term list", - "user term remove", - "user term set", - "user unspam", - "user update" - ] - }, - "autoload": { - "psr-4": { - "": "src/", - "WP_CLI\\": "src/WP_CLI" - }, - "files": [ - "entity-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Manage WordPress core entities.", - "homepage": "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/wp-cli/entity-command", - "time": "2017-12-18T20:05:12+00:00" - }, - { - "name": "wp-cli/eval-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/eval-command.git", - "reference": "9640d40ab28cd86590396f08f8c382e659f57321" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/eval-command/zipball/9640d40ab28cd86590396f08f8c382e659f57321", - "reference": "9640d40ab28cd86590396f08f8c382e659f57321", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "eval", - "eval-file" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "eval-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Executes arbitrary PHP code or files.", - "homepage": "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/wp-cli/eval-command", - "time": "2017-12-08T14:33:34+00:00" - }, - { - "name": "wp-cli/export-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/export-command.git", - "reference": "b8469df00d0e357bbc8a51ffd006ace007449b15" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/export-command/zipball/b8469df00d0e357bbc8a51ffd006ace007449b15", - "reference": "b8469df00d0e357bbc8a51ffd006ace007449b15", - "shasum": "" - }, - "require": { - "nb/oxymel": "~0.1.0", - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "export" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "export-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Exports WordPress content to a WXR file.", - "homepage": "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/wp-cli/export-command", - "time": "2017-12-14T19:06:14+00:00" - }, - { - "name": "wp-cli/extension-command", - "version": "v1.1.8", - "source": { - "type": "git", - "url": "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/wp-cli/extension-command.git", - "reference": "7ab2a957383ee63e5082f4854479738c0f2a4474" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/extension-command/zipball/7ab2a957383ee63e5082f4854479738c0f2a4474", - "reference": "7ab2a957383ee63e5082f4854479738c0f2a4474", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "plugin", - "plugin activate", - "plugin deactivate", - "plugin delete", - "plugin get", - "plugin install", - "plugin is-installed", - "plugin list", - "plugin path", - "plugin search", - "plugin status", - "plugin toggle", - "plugin uninstall", - "plugin update", - "theme", - "theme activate", - "theme delete", - "theme disable", - "theme enable", - "theme get", - "theme install", - "theme is-installed", - "theme list", - "theme mod", - "theme mod get", - "theme mod set", - "theme mod remove", - "theme path", - "theme search", - "theme status", - "theme update" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "extension-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Manages plugins and themes, including installs, activations, and updates.", - "homepage": "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/wp-cli/extension-command", - "time": "2017-12-08T17:49:39+00:00" - }, - { - "name": "wp-cli/import-command", - "version": "v1.0.6", - "source": { - "type": "git", - "url": "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/wp-cli/import-command.git", - "reference": "d2c21d590a1bfae6ac4e289a0b57fb1870b5990c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/import-command/zipball/d2c21d590a1bfae6ac4e289a0b57fb1870b5990c", - "reference": "d2c21d590a1bfae6ac4e289a0b57fb1870b5990c", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "bundled": true, - "commands": [ - "import" - ] - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "import-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Imports content from a given WXR file.", - "homepage": "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/wp-cli/import-command", - "time": "2017-12-08T15:13:36+00:00" - }, - { - "name": "wp-cli/language-command", - "version": "v1.0.6", - "source": { - "type": "git", - "url": "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/wp-cli/language-command.git", - "reference": "2a3d1ce5a722a4d70809619a065087aa933f6209" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/language-command/zipball/2a3d1ce5a722a4d70809619a065087aa933f6209", - "reference": "2a3d1ce5a722a4d70809619a065087aa933f6209", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "language", - "language core", - "language core activate", - "language core install", - "language core list", - "language core uninstall", - "language core update" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "language-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Installs, activates, and manages language packs.", - "homepage": "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/wp-cli/language-command", - "time": "2017-12-08T17:50:26+00:00" - }, - { - "name": "wp-cli/media-command", - "version": "v1.1.3", - "source": { - "type": "git", - "url": "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/wp-cli/media-command.git", - "reference": "b591fe02ca32f78877300410799a562f7a301d48" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/media-command/zipball/b591fe02ca32f78877300410799a562f7a301d48", - "reference": "b591fe02ca32f78877300410799a562f7a301d48", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "media", - "media import", - "media regenerate", - "media image-size" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "media-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Imports files as attachments, regenerates thumbnails, or lists registered image sizes.", - "homepage": "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/wp-cli/media-command", - "time": "2017-12-15T15:00:38+00:00" - }, - { - "name": "wp-cli/mustangostang-spyc", - "version": "0.6.3", - "source": { - "type": "git", - "url": "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/wp-cli/spyc.git", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/spyc/zipball/6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "reference": "6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "psr-4": { - "Mustangostang\\": "src/" - }, - "files": [ - "includes/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)", - "homepage": "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/mustangostang/spyc/", - "time": "2017-04-25T11:26:20+00:00" - }, - { - "name": "wp-cli/package-command", - "version": "v1.0.10", - "source": { - "type": "git", - "url": "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/wp-cli/package-command.git", - "reference": "b7d2ee2fc0f4ff052799fdc4308ac2f99031f66a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/package-command/zipball/b7d2ee2fc0f4ff052799fdc4308ac2f99031f66a", - "reference": "b7d2ee2fc0f4ff052799fdc4308ac2f99031f66a", - "shasum": "" - }, - "require": { - "composer/composer": "^1.2.0", - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "package", - "package browse", - "package install", - "package list", - "package update", - "package uninstall" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "package-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Lists, installs, and removes WP-CLI packages.", - "homepage": "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/wp-cli/package-command", - "time": "2017-12-08T15:46:21+00:00" - }, - { - "name": "wp-cli/php-cli-tools", - "version": "v0.11.8", - "source": { - "type": "git", - "url": "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/wp-cli/php-cli-tools.git", - "reference": "363c75349f5dde561e0b416dd00f7aaa76fa2c27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/363c75349f5dde561e0b416dd00f7aaa76fa2c27", - "reference": "363c75349f5dde561e0b416dd00f7aaa76fa2c27", - "shasum": "" - }, - "require": { - "php": ">= 5.3.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "cli": "lib/" - }, - "files": [ - "lib/cli/cli.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "James Logsdon", - "email": "jlogsdon@php.net", - "role": "Developer" - }, - { - "name": "Daniel Bachhuber", - "email": "daniel@handbuilt.co", - "role": "Maintainer" - } - ], - "description": "Console utilities for PHP", - "homepage": "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/wp-cli/php-cli-tools", - "keywords": [ - "cli", - "console" - ], - "time": "2017-10-12T21:50:48+00:00" - }, - { - "name": "wp-cli/rewrite-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/rewrite-command.git", - "reference": "6b1695887e289ffad14c8f4ea86b5f1d92757408" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/rewrite-command/zipball/6b1695887e289ffad14c8f4ea86b5f1d92757408", - "reference": "6b1695887e289ffad14c8f4ea86b5f1d92757408", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "rewrite", - "rewrite flush", - "rewrite list", - "rewrite structure" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "rewrite-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Lists or flushes the site's rewrite rules, updates the permalink structure.", - "homepage": "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/wp-cli/rewrite-command", - "time": "2017-12-08T17:51:04+00:00" - }, - { - "name": "wp-cli/role-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/role-command.git", - "reference": "be7f8ea91922864d0c75e45953fbe41d44bebb25" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/role-command/zipball/be7f8ea91922864d0c75e45953fbe41d44bebb25", - "reference": "be7f8ea91922864d0c75e45953fbe41d44bebb25", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "role", - "role create", - "role delete", - "role exists", - "role list", - "role reset", - "cap", - "cap add", - "cap list", - "cap remove" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "role-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Adds, removes, lists, and resets roles and capabilities.", - "homepage": "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/wp-cli/role-command", - "time": "2017-12-08T17:51:40+00:00" - }, - { - "name": "wp-cli/scaffold-command", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "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/wp-cli/scaffold-command.git", - "reference": "24b4ddeeb9531179921172597064c8f54f9f594d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/scaffold-command/zipball/24b4ddeeb9531179921172597064c8f54f9f594d", - "reference": "24b4ddeeb9531179921172597064c8f54f9f594d", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "scaffold", - "scaffold _s", - "scaffold block", - "scaffold child-theme", - "scaffold plugin", - "scaffold plugin-tests", - "scaffold post-type", - "scaffold taxonomy", - "scaffold theme-tests" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "scaffold-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Generates code for post types, taxonomies, blocks, plugins, child themes, etc.", - "homepage": "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/wp-cli/scaffold-command", - "time": "2017-12-18T14:24:43+00:00" - }, - { - "name": "wp-cli/search-replace-command", - "version": "v1.1.4", - "source": { - "type": "git", - "url": "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/wp-cli/search-replace-command.git", - "reference": "813977a75e915581a93f0cb931c325f9a231a2e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/search-replace-command/zipball/813977a75e915581a93f0cb931c325f9a231a2e9", - "reference": "813977a75e915581a93f0cb931c325f9a231a2e9", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "search-replace" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "search-replace-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Searches/replaces strings in the database.", - "homepage": "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/wp-cli/search-replace-command", - "time": "2017-12-11T15:35:16+00:00" - }, - { - "name": "wp-cli/server-command", - "version": "v1.0.9", - "source": { - "type": "git", - "url": "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/wp-cli/server-command.git", - "reference": "6192e6d7becd07e4c11a8f1560655c73a3b3526a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/server-command/zipball/6192e6d7becd07e4c11a8f1560655c73a3b3526a", - "reference": "6192e6d7becd07e4c11a8f1560655c73a3b3526a", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "server" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "server-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Launches PHP's built-in web server for a specific WordPress installation.", - "homepage": "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/wp-cli/server-command", - "time": "2017-12-14T20:06:24+00:00" - }, - { - "name": "wp-cli/shell-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/shell-command.git", - "reference": "507603a8994d984b6c4d5bd26e31ede6d9cce37e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/shell-command/zipball/507603a8994d984b6c4d5bd26e31ede6d9cce37e", - "reference": "507603a8994d984b6c4d5bd26e31ede6d9cce37e", - "shasum": "" - }, - "require": { - "wp-cli/wp-cli": "*" - }, - "require-dev": { - "behat/behat": "~2.5" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "shell" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/", - "WP_CLI\\": "src/WP_CLI" - }, - "files": [ - "shell-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Opens an interactive PHP console for running and testing PHP code.", - "homepage": "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/wp-cli/shell-command", - "time": "2017-12-08T16:03:53+00:00" - }, - { - "name": "wp-cli/super-admin-command", - "version": "v1.0.6", - "source": { - "type": "git", - "url": "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/wp-cli/super-admin-command.git", - "reference": "2982d2e6514dbb318561d72d0577746a3a37181e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/super-admin-command/zipball/2982d2e6514dbb318561d72d0577746a3a37181e", - "reference": "2982d2e6514dbb318561d72d0577746a3a37181e", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "super-admin", - "super-admin add", - "super-admin list", - "super-admin remove" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "super-admin-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Lists, adds, or removes super admin users on a multisite installation.", - "homepage": "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/wp-cli/super-admin-command", - "time": "2017-12-08T17:43:53+00:00" - }, - { - "name": "wp-cli/widget-command", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "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/wp-cli/widget-command.git", - "reference": "657e0f77d80c892f8f72f90a3a25112c254386df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wp-cli/widget-command/zipball/657e0f77d80c892f8f72f90a3a25112c254386df", - "reference": "657e0f77d80c892f8f72f90a3a25112c254386df", - "shasum": "" - }, - "require-dev": { - "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" - }, - "type": "wp-cli-package", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "commands": [ - "widget", - "widget add", - "widget deactivate", - "widget delete", - "widget list", - "widget move", - "widget reset", - "widget update", - "sidebar", - "sidebar list" - ], - "bundled": true - }, - "autoload": { - "psr-4": { - "": "src/" - }, - "files": [ - "widget-command.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Bachhuber", - "email": "daniel@runcommand.io", - "homepage": "https://runcommand.io" - } - ], - "description": "Adds, moves, and removes widgets; lists sidebars.", - "homepage": "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/wp-cli/widget-command", - "time": "2017-12-08T17:45:57+00:00" - } - ], - "packages-dev": [ - { - "name": "behat/behat", - "version": "v2.5.5", - "source": { - "type": "git", - "url": "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/Behat/Behat.git", - "reference": "c1e48826b84669c97a1efa78459aedfdcdcf2120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/c1e48826b84669c97a1efa78459aedfdcdcf2120", - "reference": "c1e48826b84669c97a1efa78459aedfdcdcf2120", - "shasum": "" - }, - "require": { - "behat/gherkin": "~2.3.0", - "php": ">=5.3.1", - "symfony/config": "~2.3", - "symfony/console": "~2.0", - "symfony/dependency-injection": "~2.0", - "symfony/event-dispatcher": "~2.0", - "symfony/finder": "~2.0", - "symfony/translation": "~2.3", - "symfony/yaml": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "~3.7.19" - }, - "suggest": { - "behat/mink-extension": "for integration with Mink testing framework", - "behat/symfony2-extension": "for integration with Symfony2 web framework", - "behat/yii-extension": "for integration with Yii web framework" - }, - "bin": [ - "bin/behat" - ], - "type": "library", - "autoload": { - "psr-0": { - "Behat\\Behat": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Scenario-oriented BDD framework for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Symfony2" - ], - "time": "2015-06-01T09:37:55+00:00" - }, - { - "name": "behat/gherkin", - "version": "v2.3.5", - "source": { - "type": "git", - "url": "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/Behat/Gherkin.git", - "reference": "2b33963da5525400573560c173ab5c9c057e1852" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/2b33963da5525400573560c173ab5c9c057e1852", - "reference": "2b33963da5525400573560c173ab5c9c057e1852", - "shasum": "" - }, - "require": { - "php": ">=5.3.1", - "symfony/finder": "~2.0" - }, - "require-dev": { - "symfony/config": "~2.0", - "symfony/translation": "~2.0", - "symfony/yaml": "~2.0" - }, - "suggest": { - "symfony/config": "If you want to use Config component to manage resources", - "symfony/translation": "If you want to use Symfony2 translations adapter", - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "2.2-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "DSL", - "Symfony2", - "parser" - ], - "time": "2013-10-15T11:22:17+00:00" - }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.4.4", - "source": { - "type": "git", - "url": "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/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "2e41850d5f7797cbb1af7b030d245b3b24e63a08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/2e41850d5f7797cbb1af7b030d245b3b24e63a08", - "reference": "2e41850d5f7797cbb1af7b030d245b3b24e63a08", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0", - "php": "^5.3|^7", - "squizlabs/php_codesniffer": "*" - }, - "require-dev": { - "composer/composer": "*", - "wimg/php-compatibility": "^8.0" - }, - "suggest": { - "dealerdirect/qa-tools": "All the PHP QA tools you'll need" - }, - "type": "composer-plugin", - "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "f.nijhof@dealerdirect.nl", - "homepage": "http://workingatdealerdirect.eu", - "role": "Developer" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://workingatdealerdirect.eu", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "time": "2017-12-06T16:27:17+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "1.2.18", - "source": { - "type": "git", - "url": "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/sebastianbergmann/php-code-coverage.git", - "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", - "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.2.0@stable", - "phpunit/php-token-stream": ">=1.1.3,<1.3.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*@dev" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.0.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "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/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2014-09-02T10:13:14+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "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/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "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/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "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/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "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/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "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/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "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/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.2.2", - "source": { - "type": "git", - "url": "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/sebastianbergmann/php-token-stream.git", - "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", - "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "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/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2014-03-03T05:10:30+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "3.7.38", - "source": { - "type": "git", - "url": "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/sebastianbergmann/phpunit.git", - "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6", - "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": "~1.2", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.1", - "phpunit/php-timer": "~1.0", - "phpunit/phpunit-mock-objects": "~1.2", - "symfony/yaml": "~2.0" - }, - "require-dev": { - "pear-pear.php.net/pear": "1.9.4" - }, - "suggest": { - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "composer/bin/phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2014-10-17T09:04:17+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "1.2.3", - "source": { - "type": "git", - "url": "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/sebastianbergmann/phpunit-mock-objects.git", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-text-template": ">=1.1.1@stable" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "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/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2013-01-13T10:24:48+00:00" - }, - { - "name": "roave/security-advisories", - "version": "dev-master", - "source": { - "type": "git", - "url": "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/Roave/SecurityAdvisories.git", - "reference": "0703d860f83775029738dc4a519301e8e3d81853" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0703d860f83775029738dc4a519301e8e3d81853", - "reference": "0703d860f83775029738dc4a519301e8e3d81853", - "shasum": "" - }, - "conflict": { - "adodb/adodb-php": "<5.20.6", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "aws/aws-sdk-php": ">=3,<3.2.1", - "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4", - "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", - "cartalyst/sentry": "<=2.1.6", - "codeigniter/framework": "<=3.0.6", - "composer/composer": "<=1.0.0-alpha11", - "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.31", - "contao/core-bundle": ">=4,<4.4.8", - "contao/listing-bundle": ">=4,<4.4.8", - "doctrine/annotations": ">=1,<1.2.7", - "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", - "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2", - "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.3.7", - "drupal/drupal": ">=8,<8.3.7", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.2|>=5.4,<5.4.10.1|>=2017.8,<2017.8.1.1", - "firebase/php-jwt": "<2", - "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", - "gree/jose": "<=2.2", - "gregwar/rst": "<1.0.3", - "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<4.1.26", - "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", - "joomla/session": "<1.3.1", - "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", - "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", - "magento/magento1ee": ">=1.9,<1.14.3.2", - "magento/magento2ce": ">=2,<2.2", - "monolog/monolog": ">=1.8,<1.12", - "namshi/jose": "<2.2", - "onelogin/php-saml": "<2.10.4", - "oro/crm": ">=1.7,<1.7.4", - "oro/platform": ">=1.7,<1.7.4", - "phpmailer/phpmailer": ">=5,<5.2.24", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", - "phpxmlrpc/extras": "<6.0.1", - "pusher/pusher-php-server": "<2.2.1", - "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "shopware/shopware": "<5.2.25", - "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", - "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": ">=3,<3.3", - "silverstripe/userforms": "<3", - "simplesamlphp/saml2": "<1.8.1|>=1.9,<1.9.1|>=1.10,<1.10.3|>=2,<2.3.3", - "simplesamlphp/simplesamlphp": "<1.14.16", - "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", - "socalnick/scn-social-auth": "<1.15.2", - "squizlabs/php_codesniffer": ">=1,<2.8.1", - "swiftmailer/swiftmailer": ">=4,<5.4.5", - "symfony/dependency-injection": ">=2,<2.0.17", - "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", - "symfony/http-foundation": ">=2,<2.3.27|>=2.4,<2.5.11|>=2.6,<2.6.6", - "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", - "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.0.25|>=2.1,<2.1.13|>=2.2,<2.2.9|>=2.3,<2.3.37|>=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8.23,<2.8.25|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.6|>=2.8.23,<2.8.25|>=3,<3.0.6|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-csrf": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/translation": ">=2,<2.0.17", - "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", - "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", - "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", - "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", - "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", - "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", - "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", - "willdurand/js-translation-bundle": "<2.1.1", - "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.5", - "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.4", - "yiisoft/yii2-gii": "<2.0.4", - "yiisoft/yii2-jui": "<2.0.4", - "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", - "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", - "zendframework/zend-diactoros": ">=1,<1.0.4", - "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", - "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", - "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", - "zendframework/zend-validator": ">=2.3,<2.3.6", - "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", - "zendframework/zendframework1": "<1.12.20", - "zendframework/zendopenid": ">=2,<2.0.2", - "zendframework/zendxml": ">=1,<1.0.1", - "zetacomponents/mail": "<1.8.2", - "zf-commons/zfc-user": "<1.2.2", - "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", - "zfr/zfr-oauth2-server-module": "<0.1.2" - }, - "type": "metapackage", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" - } - ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2017-12-24T19:52:27+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "2.9.1", - "source": { - "type": "git", - "url": "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/squizlabs/PHP_CodeSniffer.git", - "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62", - "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "bin": [ - "scripts/phpcs", - "scripts/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", - "keywords": [ - "phpcs", - "standards" - ], - "time": "2017-05-22T02:43:20+00:00" - }, - { - "name": "wimg/php-compatibility", - "version": "8.0.1", - "source": { - "type": "git", - "url": "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/wimg/PHPCompatibility.git", - "reference": "4c4385fb891dff0501009670f988d4fe36785249" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/wimg/PHPCompatibility/zipball/4c4385fb891dff0501009670f988d4fe36785249", - "reference": "4c4385fb891dff0501009670f988d4fe36785249", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.2 || ^3.0.2" - }, - "conflict": { - "squizlabs/php_codesniffer": "2.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "PHPCompatibility\\": "PHPCompatibility/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0" - ], - "authors": [ - { - "name": "Wim Godden", - "role": "lead" - } - ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", - "keywords": [ - "compatibility", - "phpcs", - "standards" - ], - "time": "2017-08-07T19:39:05+00:00" - }, - { - "name": "wp-coding-standards/wpcs", - "version": "0.13.1", - "source": { - "type": "git", - "url": "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/WordPress-Coding-Standards/WordPress-Coding-Standards.git", - "reference": "1f64b1a0b5b789822d0303436ee4e30e0135e4dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WordPress-Coding-Standards/WordPress-Coding-Standards/zipball/1f64b1a0b5b789822d0303436ee4e30e0135e4dc", - "reference": "1f64b1a0b5b789822d0303436ee4e30e0135e4dc", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.9.0 || ^3.0.2" - }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1" - }, - "type": "phpcodesniffer-standard", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Contributors", - "homepage": "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/WordPress-Coding-Standards/WordPress-Coding-Standards/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", - "keywords": [ - "phpcs", - "standards", - "wordpress" - ], - "time": "2017-08-05T16:08:58+00:00" - } - ], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": { - "roave/security-advisories": 20 - }, - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": ">=5.3.29" - }, - "platform-dev": [], - "platform-overrides": { - "php": "5.3.29" - } -} diff --git a/features/aliases.feature b/features/aliases.feature index 22569faeab..abd8942127 100644 --- a/features/aliases.feature +++ b/features/aliases.feature @@ -9,23 +9,22 @@ Feature: Create shortcuts to specific WordPress installs path: foo """ - When I try `wp core is-installed` + When I mistakenly try `wp core is-installed` Then STDERR should contain: """ Error: This does not seem to be a WordPress install. """ - And the return code should be 1 When I run `wp @foo core is-installed` Then the return code should be 0 - When I run `cd bar; wp @foo core is-installed` + When I run `cd bar && wp @foo core is-installed` Then the return code should be 0 Scenario: Error when invalid alias provided Given an empty directory - When I try `wp @test option get home` + When I mistakenly try `wp @test option get home` Then STDERR should be: """ Error: Alias '@test' not found. @@ -39,7 +38,7 @@ Feature: Create shortcuts to specific WordPress installs path: foo """ - When I try `wp @test option get home` + When I mistakenly try `wp @test option get home` Then STDERR should be: """ Error: Alias '@test' not found. @@ -60,7 +59,7 @@ Feature: Create shortcuts to specific WordPress installs http://example.com """ - When I try `wp @foo option get home --path=foo` + When I mistakenly try `wp @foo option get home --path=foo` Then STDERR should contain: """ Parameter errors: @@ -89,7 +88,7 @@ Feature: Create shortcuts to specific WordPress installs 1 """ - When I try `wp @foo eval 'echo get_current_user_id();' --user=admin` + When I mistakenly try `wp @foo eval 'echo get_current_user_id();' --user=admin` Then STDERR should contain: """ Parameter errors: @@ -123,15 +122,12 @@ Feature: Create shortcuts to specific WordPress installs path: foo """ - When I run `wp eval --skip-wordpress 'echo realpath( getenv( "RUN_DIR" ) );'` - Then save STDOUT as {TEST_DIR} - When I run `wp cli alias` Then STDOUT should be YAML containing: """ @all: Run command against every registered alias. @foo: - path: {TEST_DIR}/foo + path: {RUN_DIR}/foo """ When I run `wp cli aliases` @@ -139,13 +135,13 @@ Feature: Create shortcuts to specific WordPress installs """ @all: Run command against every registered alias. @foo: - path: {TEST_DIR}/foo + path: {RUN_DIR}/foo """ When I run `wp cli alias --format=json` Then STDOUT should be JSON containing: """ - {"@all":"Run command against every registered alias.","@foo":{"path":"{TEST_DIR}/foo"}} + {"@all":"Run command against every registered alias.","@foo":{"path":"{RUN_DIR}/foo"}} """ Scenario: Defining a project alias completely overrides a global alias @@ -167,7 +163,7 @@ Feature: Create shortcuts to specific WordPress installs @foo: path: none-existent-install """ - When I try `WP_CLI_CONFIG_PATH=config.yml wp @foo option get home` + When I mistakenly try `WP_CLI_CONFIG_PATH=config.yml wp @foo option get home` Then STDERR should contain: """ Error: This does not seem to be a WordPress install. @@ -204,7 +200,7 @@ Feature: Create shortcuts to specific WordPress installs http://google.com """ - When I try `wp @invalid option get home` + When I mistakenly try `wp @invalid option get home` Then STDERR should be: """ Error: Group '@invalid' contains one or more invalid aliases: @baz @@ -287,7 +283,7 @@ Feature: Create shortcuts to specific WordPress installs Scenario: Error when '@all' is used without aliases defined Given an empty directory - When I try `wp @all option get home` + When I mistakenly try `wp @all option get home` Then STDERR should be: """ Error: Cannot use '@all' when no aliases are registered. @@ -317,7 +313,7 @@ Feature: Create shortcuts to specific WordPress installs http://subsite.example.com """ - When I try `wp @subsite option get siteurl --url=subsite.example.com` + When I mistakenly try `wp @subsite option get siteurl --url=subsite.example.com` Then STDERR should be: """ Error: Parameter errors: @@ -338,7 +334,7 @@ Feature: Create shortcuts to specific WordPress installs - @bar """ - When I try `wp core is-installed --allow-root --debug` + When I mistakenly try `wp core is-installed --allow-root --debug` Then STDERR should contain: """ Error: This does not seem to be a WordPress install. @@ -347,7 +343,6 @@ Feature: Create shortcuts to specific WordPress installs """ core is-installed --allow-root --debug """ - And the return code should be 1 When I try `wp @foo core is-installed --allow-root --debug` Then the return code should be 0 @@ -356,7 +351,7 @@ Feature: Create shortcuts to specific WordPress installs @foo core is-installed --allow-root --debug """ - When I try `cd bar; wp @bar core is-installed --allow-root --debug` + When I try `cd bar && wp @bar core is-installed --allow-root --debug` Then the return code should be 0 And STDERR should contain: """ @@ -392,12 +387,11 @@ Feature: Create shortcuts to specific WordPress installs - @bar """ - When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--ddisable_functions=} @foobar core is-installed` + When I mistakenly try `{INVOKE_WP_CLI_WITH_PHP_ARGS--ddisable_functions=} @foobar core is-installed` Then STDERR should contain: """ Error: Cannot do 'group alias': The PHP functions `proc_open()` and/or `proc_close()` are disabled """ - And the return code should be 1 Examples: | func | diff --git a/features/bootstrap.feature b/features/bootstrap.feature index 8869736a81..b21330243c 100644 --- a/features/bootstrap.feature +++ b/features/bootstrap.feature @@ -148,7 +148,7 @@ Feature: Bootstrap WP-CLI Scenario: Override command bundled with freshly built PHAR Given an empty directory - And a new Phar with the same version + And a new Phar with the same version and cli build And a cli-override-command/cli.php file: """ autoload->files as $file ) { $contents .= ' - ' . dirname( dirname( dirname( __FILE__ ) ) ) . '/' . $file . PHP_EOL; } - @mkdir( sys_get_temp_dir() . '/wp-cli-package-test/' ); - $project_config = sys_get_temp_dir() . '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/wp-cli-package-test/config.yml'; + @mkdir( Utils\get_temp_dir() . 'wp-cli-package-test/' ); + $project_config = Utils\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 __DIR__ . '/../../php/utils.php'; + require __DIR__ . '/../../php/WP_CLI/Process.php'; + require __DIR__ . '/../../php/WP_CLI/ProcessRun.php'; if ( file_exists( __DIR__ . '/../../vendor/autoload.php' ) ) { - require_once __DIR__ . '/../../vendor/autoload.php'; + require __DIR__ . '/../../vendor/autoload.php'; } else if ( file_exists( __DIR__ . '/../../../../autoload.php' ) ) { - require_once __DIR__ . '/../../../../autoload.php'; + require __DIR__ . '/../../../../autoload.php'; } } @@ -81,6 +81,12 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface { 'dbhost' => '127.0.0.1', ); + /** + * Variables for `do_mysqli()`. + */ + private static $dbh = null; // Database handle. + private static $db_selected = false; // Whether database selected (opened). + /** * Array of background process ids started by the current scenario. Used to terminate them at the end of the scenario. */ @@ -118,32 +124,16 @@ private static function get_process_env_variables() { // Ensure we're using the expected `wp` binary $bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ?: realpath( __DIR__ . '/../../bin' ); $vendor_dir = realpath( __DIR__ . '/../../vendor/bin' ); - $path_separator = Utils\is_windows() ? ';' : ':'; $env = array( - 'PATH' => $bin_dir . $path_separator . $vendor_dir . $path_separator . getenv( 'PATH' ), + 'PATH' => $bin_dir . PATH_SEPARATOR . $vendor_dir . PATH_SEPARATOR . getenv( 'PATH' ), 'BEHAT_RUN' => 1, - 'HOME' => sys_get_temp_dir() . '/wp-cli-home', + 'HOME' => Utils\get_temp_dir() . 'wp-cli-home', ); - if ( $config_path = getenv( 'WP_CLI_CONFIG_PATH' ) ) { - $env['WP_CLI_CONFIG_PATH'] = $config_path; - } - if ( $term = getenv( 'TERM' ) ) { - $env['TERM'] = $term; - } - if ( $php_args = getenv( 'WP_CLI_PHP_ARGS' ) ) { - $env['WP_CLI_PHP_ARGS'] = $php_args; - } - if ( $php_used = getenv( 'WP_CLI_PHP_USED' ) ) { - $env['WP_CLI_PHP_USED'] = $php_used; - } - if ( $php = getenv( 'WP_CLI_PHP' ) ) { - $env['WP_CLI_PHP'] = $php; - } - if ( $travis_build_dir = getenv( 'TRAVIS_BUILD_DIR' ) ) { - $env['TRAVIS_BUILD_DIR'] = $travis_build_dir; - } - if ( $github_token = getenv( 'GITHUB_TOKEN' ) ) { - $env['GITHUB_TOKEN'] = $github_token; + $env_keys = array( 'GITHUB_TOKEN', 'SHELL', 'SYSTEMROOT', 'TERM', 'TMP', 'TRAVIS_BUILD_DIR', 'WP_CLI_CONFIG_PATH', 'WP_CLI_PHP', 'WP_CLI_PHP_ARGS', 'WP_CLI_PHP_USED' ); + foreach ( $env_keys as $env_key ) { + if ( $env_val = getenv( $env_key ) ) { + $env[ $env_key ] = $env_val; + } } return $env; } @@ -154,7 +144,7 @@ private static function get_process_env_variables() { */ private static function cache_wp_files() { $wp_version_suffix = ( $wp_version = getenv( 'WP_VERSION' ) ) ? "-$wp_version" : ''; - self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-download-cache' . $wp_version_suffix; + self::$cache_dir = Utils\get_temp_dir() . 'wp-cli-test-core-download-cache' . $wp_version_suffix; if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) return; @@ -186,7 +176,7 @@ public static function prepare( SuiteEvent $event ) { // Remove install cache if any (not setting the static var). $wp_version_suffix = ( $wp_version = getenv( 'WP_VERSION' ) ) ? "-$wp_version" : ''; - $install_cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-install-cache' . $wp_version_suffix; + $install_cache_dir = Utils\get_temp_dir() . 'wp-cli-test-core-install-cache' . $wp_version_suffix; if ( file_exists( $install_cache_dir ) ) { self::remove_dir( $install_cache_dir ); } @@ -201,8 +191,13 @@ public static function afterSuite( SuiteEvent $event ) { self::$composer_local_repository = null; } - if ( self::$log_run_times ) { - self::log_run_times_after_suite( $event ); + if ( ! $event->isCompleted() ) { + // Then ctrl+C hit so cleanup any leftover scenario stuff. + self::afterScenario_cleanup( $event ); + } else { + if ( self::$log_run_times ) { + self::log_run_times_after_suite( $event ); + } } } @@ -228,33 +223,45 @@ public function beforeScenario( $event ) { */ public function afterScenario( $event ) { + self::afterScenario_cleanup( $event, $this ); + + if ( self::$log_run_times ) { + self::log_run_times_after_scenario( $event ); + } + } + + /** + * Helper to cleanup after a scenario. + * If called from afterSuite() then suite was aborted and `$_this` won't be set. + */ + private static function afterScenario_cleanup( $event, $_this = null ) { + if ( $_this ) { + // Remove any background processes first. + foreach ( $_this->running_procs as $proc ) { + $status = proc_get_status( $proc ); + self::terminate_proc( $status['pid'] ); + } + } + if ( self::$run_dir ) { - // remove altered WP install, unless there's an error - if ( $event->getResult() < 4 ) { + // Remove altered WP install unless suite did complete (`$_this` set) and there's an error. + if ( ! $_this || $event->getResult() < 4 ) { self::remove_dir( self::$run_dir ); } self::$run_dir = null; } - // Remove WP-CLI package directory if any. Set to `wp package path` by package-command and scaffold-package-command features, and by cli-info.feature. - if ( isset( $this->variables['PACKAGE_PATH'] ) ) { - self::remove_dir( $this->variables['PACKAGE_PATH'] ); - } - // Remove SUITE_CACHE_DIR if any. if ( self::$suite_cache_dir ) { self::remove_dir( self::$suite_cache_dir ); self::$suite_cache_dir = null; } - // Remove any background processes. - foreach ( $this->running_procs as $proc ) { - $status = proc_get_status( $proc ); - self::terminate_proc( $status['pid'] ); - } - - if ( self::$log_run_times ) { - self::log_run_times_after_scenario( $event ); + if ( $_this ) { + // Remove WP-CLI package directory if any. Set to `wp package path` by package-command and scaffold-package-command features, and by cli-info.feature. + if ( isset( $_this->variables['PACKAGE_PATH'] ) ) { + self::remove_dir( $_this->variables['PACKAGE_PATH'] ); + } } } @@ -280,7 +287,7 @@ private static function terminate_proc( $master_pid ) { $errno = posix_get_last_error(); // Ignore "No such process" error as that's what we want. if ( 3 /*ESRCH*/ !== $errno ) { - throw new RuntimeException( posix_strerror( $errno ) ); + throw new \RuntimeException( posix_strerror( $errno ) ); } } } @@ -292,7 +299,7 @@ public static function create_cache_dir() { if ( self::$suite_cache_dir ) { self::remove_dir( self::$suite_cache_dir ); } - self::$suite_cache_dir = sys_get_temp_dir() . '/' . uniqid( 'wp-cli-test-suite-cache-' . self::$temp_dir_infix . '-', TRUE ); + self::$suite_cache_dir = Utils\get_temp_dir() . uniqid( 'wp-cli-test-suite-cache-' . self::$temp_dir_infix . '-', TRUE ); mkdir( self::$suite_cache_dir ); return self::$suite_cache_dir; } @@ -399,7 +406,7 @@ private function replace_wp_versions( $str ) { $wp_versions = array(); $response = Requests::get( 'https://api.wordpress.org/core/version-check/1.7/', null, array( 'timeout' => 30 ) ); - if ( 200 === $response->status_code && ( $body = json_decode( $response->body ) ) && is_object( $body ) && isset( $body->offers ) && is_array( $body->offers ) ) { + if ( 200 === (int) $response->status_code && ( $body = json_decode( $response->body ) ) && is_object( $body ) && isset( $body->offers ) && is_array( $body->offers ) ) { // Latest version alias. $wp_versions["{WP_VERSION-latest}"] = count( $body->offers ) ? $body->offers[0]->version : ''; foreach ( $body->offers as $offer ) { @@ -421,6 +428,37 @@ private function replace_wp_versions( $str ) { return strtr( $str, $wp_versions ); } + /** + * Substitute "{INVOKE_WP_CLI_WITH_PHP_ARGS-args}" variables. + */ + private function _replace_invoke_wp_cli_with_php_args( $str ) { + static $phar_path = null, $shell_path = null; + + if ( null === $phar_path ) { + $phar_path = false; + if ( ( $bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ) && file_exists( $bin_dir . '/wp' ) && is_executable( $bin_dir . '/wp' ) ) { + $phar_path = $bin_dir . '/wp'; + } else { + $src_dir = dirname( dirname( __DIR__ ) ); + $bin_path = $src_dir . '/bin/wp'; + $vendor_bin_path = $src_dir . '/vendor/bin/wp'; + if ( file_exists( $bin_path ) && is_executable( $bin_path ) ) { + $shell_path = $bin_path; + } elseif ( file_exists( $vendor_bin_path ) && is_executable( $vendor_bin_path ) ) { + $shell_path = $vendor_bin_path; + } else { + $shell_path = 'wp'; + } + } + } + + $str = preg_replace_callback( '/{INVOKE_WP_CLI_WITH_PHP_ARGS-([^}]*)}/', function ( $matches ) use ( $phar_path, $shell_path ) { + return $phar_path ? "php {$matches[1]} {$phar_path}" : ( 'WP_CLI_PHP_ARGS=' . escapeshellarg( $matches[1] ) . ' ' . $shell_path ); + }, $str ); + + return $str; + } + /** * Get the file and line number for the current behat event. */ @@ -443,12 +481,18 @@ private static function get_event_file( $event, &$line ) { */ public function create_run_dir() { if ( !isset( $this->variables['RUN_DIR'] ) ) { - self::$run_dir = $this->variables['RUN_DIR'] = sys_get_temp_dir() . '/' . uniqid( 'wp-cli-test-run-' . self::$temp_dir_infix . '-', TRUE ); + self::$run_dir = $this->variables['RUN_DIR'] = Utils\get_temp_dir() . uniqid( 'wp-cli-test-run-' . self::$temp_dir_infix . '-', TRUE ); mkdir( $this->variables['RUN_DIR'] ); } } - public function build_phar( $version = 'same' ) { + /** + * Build a phar based on whatever's in the directory from which behat is run. + * + * @param string $version Version to set it to. (Note: has no correspondence with the actual version of the contained source.) + * @param string $build If set to "cli" will do a fast build without any bundled commands - "cli" command only. Defaults to '' (full build). + */ + public function build_phar( $version = 'same', $build = '' ) { $this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/' . uniqid( "wp-cli-build-", TRUE ) . '.phar'; // Test running against a package installed as a WP-CLI dependency @@ -464,13 +508,17 @@ public function build_phar( $version = 'same' ) { } $this->proc( Utils\esc_cmd( - 'php -dphar.readonly=0 %1$s %2$s --version=%3$s && chmod +x %2$s', + 'php -dphar.readonly=0 %1$s %2$s --version=%3$s --quiet --build=%4$s', $make_phar_path, $this->variables['PHAR_PATH'], - $version + $version, + $build ) )->run_check(); } + /** + * Download a released phar from github.com. Note: not currently used in any test. + */ public function download_phar( $version = 'same' ) { if ( 'same' === $version ) { $version = WP_CLI_VERSION; @@ -496,7 +544,7 @@ public function download_phar( $version = 'same' ) { * CACHE_DIR is a cache for downloaded test data such as images. Lives until manually deleted. */ private function set_cache_dir() { - $path = sys_get_temp_dir() . '/wp-cli-test-cache'; + $path = Utils\get_temp_dir() . 'wp-cli-test-cache'; if ( ! file_exists( $path ) ) { mkdir( $path ); } @@ -522,20 +570,30 @@ private static function run_sql( $sql_cmd, $assoc_args = array(), $add_database $start_time = microtime( true ); Utils\run_mysql_command( $sql_cmd, array_merge( $assoc_args, $default_assoc_args ) ); if ( self::$log_run_times ) { - self::log_proc_method_run_time( 'run_sql ' . $sql_cmd, $start_time ); + self::log_proc_method_run_time( $sql_cmd . ' ' . Utils\assoc_args_to_str( $assoc_args ), $start_time ); } } + /** + * Create the `$db_settings['dbname']` test database. Called on a "Given a database" step and on the various "Given a WP install..." steps. + */ public function create_db() { $dbname = self::$db_settings['dbname']; - self::run_sql( 'mysql --no-defaults', array( 'execute' => "CREATE DATABASE IF NOT EXISTS $dbname" ) ); + self::do_mysqli( "CREATE DATABASE IF NOT EXISTS `$dbname` CHARSET 'UTF8'" ); } + /** + * Drop the `$db_settings['dbname']` test database. Done at the beginning of every scenario. + */ public function drop_db() { $dbname = self::$db_settings['dbname']; - self::run_sql( 'mysql --no-defaults', array( 'execute' => "DROP DATABASE IF EXISTS $dbname" ) ); + self::do_mysqli( "DROP DATABASE IF EXISTS `$dbname`" ); + self::$db_selected = false; } + /** + * Run a process from the RUN_DIR directory (or a sub-directory of it if given `$path`). + */ public function proc( $command, $assoc_args = array(), $path = '' ) { if ( !empty( $assoc_args ) ) $command .= Utils\assoc_args_to_str( $assoc_args ); @@ -572,7 +630,7 @@ public function background_proc( $cmd ) { if ( !$status['running'] ) { $stderr = is_resource( $pipes[2] ) ? ( ': ' . stream_get_contents( $pipes[2] ) ) : ''; - throw new RuntimeException( sprintf( "Failed to start background process '%s'%s.", $cmd, $stderr ) ); + throw new \RuntimeException( sprintf( "Failed to start background process '%s'%s.", $cmd, $stderr ) ); } else { $this->running_procs[] = $proc; } @@ -602,16 +660,20 @@ public function add_line_to_wp_config( &$wp_config_code, $line ) { $wp_config_code = str_replace( $token, "$line\n\n$token", $wp_config_code ); } + /** + * "Downloads" wp in the sense that it copies the version downloaded and cached by `cache_wp_files()` from CACHE_DIR to RUN_DIR (or a sub-directory of it if given `$subdir`). + */ public function download_wp( $subdir = '' ) { - $dest_dir = $this->variables['RUN_DIR'] . "/$subdir"; + $dest_dir = $this->variables['RUN_DIR']; if ( $subdir ) { - mkdir( $dest_dir ); + $dest_dir = $this->variables['RUN_DIR'] . "/$subdir"; + mkdir( $dest_dir, 0777, true /*recursive*/ ); } self::copy_dir( self::$cache_dir, $dest_dir ); - // disable emailing + // Disable emailing - otherwise will get "sh: 1: -t: not found" error message on Travis (as PHP ini `sendmail_from` not set by default). mkdir( $dest_dir . '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/wp-content/mu-plugins' ); copy( __DIR__ . '/../extra/no-mail.php', $dest_dir . '/wp-content/mu-plugins/no-mail.php' ); } @@ -623,6 +685,7 @@ public function create_config( $subdir = '', $extra_php = false ) { $params['dbprefix'] = $subdir ? preg_replace( '#[^a-zA-Z\_0-9]#', '_', $subdir ) : 'wp_'; $params['skip-salts'] = true; + $params['skip-check'] = true; if( false !== $extra_php ) { $params['extra-php'] = $extra_php; @@ -646,12 +709,14 @@ public function create_config( $subdir = '', $extra_php = false ) { public function install_wp( $subdir = '' ) { $wp_version_suffix = ( $wp_version = getenv( 'WP_VERSION' ) ) ? "-$wp_version" : ''; - self::$install_cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-install-cache' . $wp_version_suffix; + self::$install_cache_dir = Utils\get_temp_dir() . 'wp-cli-test-core-install-cache' . $wp_version_suffix; if ( ! file_exists( self::$install_cache_dir ) ) { mkdir( self::$install_cache_dir ); } - $subdir = $this->replace_variables( $subdir ); + if ( $subdir ) { + $subdir = $this->replace_variables( $subdir ); + } $this->create_db(); $this->create_run_dir(); @@ -675,13 +740,16 @@ public function install_wp( $subdir = '' ) { if ( $install_cache_path && file_exists( $install_cache_path ) ) { self::copy_dir( $install_cache_path, $run_dir ); - self::run_sql( 'mysql --no-defaults', array( 'execute' => "source {$install_cache_path}.sql" ), true /*add_database*/ ); + self::do_mysqli_source( $install_cache_path . '.sql' ); } else { $this->proc( 'wp core install', $install_args, $subdir )->run_check(); if ( $install_cache_path ) { mkdir( $install_cache_path ); self::dir_diff_copy( $run_dir, self::$cache_dir, $install_cache_path ); - self::run_sql( 'mysqldump --no-defaults', array( 'result-file' => "{$install_cache_path}.sql" ), true /*add_database*/ ); + $install_cache_sql = $install_cache_path . '.sql'; + self::run_sql( 'mysqldump --no-defaults --skip-comments', array( 'result-file' => $install_cache_sql ), true /*add_database*/ ); + // Remove conditional and lock tables statements. + file_put_contents( $install_cache_sql, preg_replace( array( '/^\/\*[^*]+\*\/;\n/m', '/^(?:UN)?LOCK TABLES[^;]*;\n/m' ), '', file_get_contents( $install_cache_sql ) ) ); } } } @@ -714,11 +782,10 @@ public function install_wp_with_composer( $vendor_directory = 'vendor' ) { public function composer_add_wp_cli_local_repository() { if ( ! self::$composer_local_repository ) { - self::$composer_local_repository = sys_get_temp_dir() . '/' . uniqid( "wp-cli-composer-local-", TRUE ); + self::$composer_local_repository = Utils\get_temp_dir() . uniqid( "wp-cli-composer-local-", TRUE ); mkdir( self::$composer_local_repository ); - $env = self::get_process_env_variables(); - $src = isset( $env['TRAVIS_BUILD_DIR'] ) ? $env['TRAVIS_BUILD_DIR'] : realpath( __DIR__ . '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/../../' ); + $src = getenv( 'TRAVIS_BUILD_DIR' ) ?: realpath( __DIR__ . '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/../../' ); self::copy_dir( $src, self::$composer_local_repository . '/' ); self::remove_dir( self::$composer_local_repository . '/.git' ); @@ -756,6 +823,69 @@ private function composer_command($cmd) { $this->proc( $this->variables['COMPOSER_PATH'] . ' ' . $cmd )->run_check(); } + /** + * Do a MySQL query with `$db_settings`, using the PHP mysqli extension. Slightly faster than `run_sql()`. + * + * @param string $sql_cmd MySQL query. + * @param bool $open_database Whether to open (select) the database or not. + * @param bool $multi_query Whether the query contains multiple statements or not. + * @param bool $no_log If set won't log timing if using log run times. + */ + private static function do_mysqli( $sql_cmd, $open_database = false, $multi_query = false, $no_log = false ) { + $start_time = microtime( true ); + + if ( null === self::$dbh ) { + if ( ! ( self::$dbh = mysqli_connect( self::$db_settings['dbhost'], self::$db_settings['dbuser'], self::$db_settings['dbpass'] ) ) ) { + throw new \RuntimeException( + sprintf( "Failed to connect to MySQL database host '%s', user '%s', pass '%s'.", self::$db_settings['dbhost'], self::$db_settings['dbuser'], self::$db_settings['dbpass'] ) + ); + } + } + if ( $open_database && ! self::$db_selected ) { + if ( ! mysqli_select_db( self::$dbh, self::$db_settings['dbname'] ) ) { + throw new \RuntimeException( sprintf( "Failed to open MySQL database name '%s'.", self::$db_settings['dbname'] ) ); + } + self::$db_selected = true; + } + if ( $multi_query ) { + if ( ! mysqli_multi_query( self::$dbh, $sql_cmd ) ) { + throw new \RuntimeException( sprintf( "Failed to execute MySQL query '%s': %d: %s", $sql_cmd, mysqli_errno( self::$dbh ), mysqli_error( self::$dbh ) ) ); + } + // Throw away the stored results (otherwise will get "out of sync" errors). + while ( mysqli_more_results( self::$dbh ) ) { + mysqli_next_result( self::$dbh ); + } + } else { + if ( ! mysqli_query( self::$dbh, $sql_cmd ) ) { + throw new \RuntimeException( sprintf( "Failed to execute MySQL query '%s': %d: %s", $sql_cmd, mysqli_errno( self::$dbh ), mysqli_error( self::$dbh ) ) ); + } + } + if ( self::$log_run_times && ! $no_log ) { + self::log_proc_method_run_time( 'do_mysqli ' . $sql_cmd, $start_time ); + } + } + + /** + * Do a MySQL SOURCE command using the PHP mysqli extension. Slightly faster than doing a mysql SOURCE command using `run_sql()`. + * + * @param string $source_file Name of MySQL source ".sql" file. + */ + private static function do_mysqli_source( $source_file ) { + $start_time = microtime( true ); + + static $files = array(); + + if ( ! isset( $files[ $source_file ] ) ) { + $files[ $source_file ] = file_get_contents( $source_file ); + } + + self::do_mysqli( $files[ $source_file ], true /*open_database*/, true /*multi_query*/, true /*no_log*/ ); + + if ( self::$log_run_times ) { + self::log_proc_method_run_time( 'do_mysqli_source ' . $source_file, $start_time ); + } + } + /** * Initialize run time logging. */ diff --git a/features/bootstrap/support.php b/features/bootstrap/support.php index 6aa17c6c39..6ad8fff47e 100644 --- a/features/bootstrap/support.php +++ b/features/bootstrap/support.php @@ -36,7 +36,7 @@ function checkString( $output, $expected, $action, $message = false ) { switch ( $action ) { case 'be': - $r = $expected === rtrim( $output, "\n" ); + $r = $expected === rtrim( _wp_cli_nocr( $output ), "\n" ); break; case 'contain': @@ -198,3 +198,27 @@ function checkThatYamlStringContainsYamlString( $actualYaml, $expectedYaml ) { return compareContents( $expectedValue, $actualValue ); } +/** + * Helper to strip carriage returns from input for Windows compat. + * + * @param string|array String or array of strings to strip. + * @return string|array String or array stripped of carriage returns. + */ +function _wp_cli_nocr( $str_or_arr ) { + if ( is_string( $str_or_arr ) ) { + return str_replace( "\r", '', $str_or_arr ); + } + return array_map( function ( $str ) { + return str_replace( "\r", '', $str ); + }, $str_or_arr ); +} + +/** + * Helper to convert *nix shell escaped command to Windows. Primitive. + * + * @param string $cmd Command to convert. + * @return string Command escaped for Windows - ie. single quotes converted to double quotes and vice versa. + */ +function _wp_cli_esc_cmd_win( $cmd ) { + return str_replace( array( "'", '"', '' ), array( '', "'", '"' ), $cmd ); +} diff --git a/features/class-wp-cli.feature b/features/class-wp-cli.feature new file mode 100644 index 0000000000..190d65ab7d --- /dev/null +++ b/features/class-wp-cli.feature @@ -0,0 +1,17 @@ +Feature: Various utilities for WP-CLI commands + + Scenario Outline: Check that `proc_open()` and `proc_close()` aren't disabled for `WP_CLI::launch()` + When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--ddisable_functions=} --skip-wordpress eval 'WP_CLI::launch( null );'` + Then STDERR should contain: + """ + Error: Cannot do 'launch': The PHP functions `proc_open()` and/or `proc_close()` are disabled + """ + And STDOUT should be empty + And the return code should be 1 + + Examples: + | func | + | proc_open | + | proc_close | + + diff --git a/features/cli-bash-completion.feature b/features/cli-bash-completion.feature index f442d4c92e..85108fd339 100644 --- a/features/cli-bash-completion.feature +++ b/features/cli-bash-completion.feature @@ -12,8 +12,6 @@ Feature: `wp cli completions` tasks """ server """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp co" --point=100` Then STDOUT should contain: @@ -24,8 +22,6 @@ Feature: `wp cli completions` tasks """ core """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp core " --point=100` Then STDOUT should contain: @@ -36,8 +32,6 @@ Feature: `wp cli completions` tasks """ update """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp help " --point=100` Then STDOUT should contain: @@ -48,8 +42,6 @@ Feature: `wp cli completions` tasks """ media """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp help language core " --point=100` Then STDOUT should contain: @@ -60,24 +52,18 @@ Feature: `wp cli completions` tasks """ update """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp core" --point=100` Then STDOUT should contain: """ core """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp core " --point=100` Then STDOUT should contain: """ download """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line='wp bogus-comand ' --point=100` Then STDOUT should be empty @@ -132,16 +118,12 @@ Feature: `wp cli completions` tasks """ server """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp @e" --point=100` Then STDOUT should contain: """ @example """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp @example " --point=100` Then STDOUT should not contain: @@ -156,16 +138,12 @@ Feature: `wp cli completions` tasks """ eval """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp @example plugin " --point=100` Then STDOUT should contain: """ list """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp help language core " --point=100` Then STDOUT should contain: @@ -176,8 +154,6 @@ Feature: `wp cli completions` tasks """ update """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp help " --point=100` Then STDOUT should not contain: @@ -188,24 +164,18 @@ Feature: `wp cli completions` tasks """ post-type """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp help core" --point=100` Then STDOUT should contain: """ core """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp help core " --point=100` Then STDOUT should contain: """ download """ - And STDERR should be empty - And the return code should be 0 Scenario: Bash Completion for global parameters Given an empty directory @@ -279,8 +249,6 @@ Feature: `wp cli completions` tasks """ --quiet= """ - And STDERR should be empty - And the return code should be 0 When I run `wp cli completions --line="wp plugin list --path --p" --point=100` Then STDOUT should contain: diff --git a/features/cli-info.feature b/features/cli-info.feature index 09918b2df4..7b24530af8 100644 --- a/features/cli-info.feature +++ b/features/cli-info.feature @@ -26,7 +26,7 @@ Feature: Review CLI information When I run `wp cli info` Then STDOUT should contain: """ - WP-CLI packages dir: + WP-CLI packages dir """ Scenario: Packages directory path should be slashed correctly diff --git a/features/cli.feature b/features/cli.feature index 0d7d38e939..f2b0a89a72 100644 --- a/features/cli.feature +++ b/features/cli.feature @@ -30,7 +30,7 @@ Feature: `wp cli` tasks Scenario: Ability to set a custom version when building Given an empty directory And save the {SRC_DIR}/VERSION file as {TRUE_VERSION} - And a new Phar with version "1.2.3" + And a new Phar with version "1.2.3" and cli build When I run `{PHAR_PATH} cli version` Then STDOUT should be: @@ -45,19 +45,18 @@ Feature: `wp cli` tasks @github-api Scenario: Check for updates Given an empty directory - And a new Phar with version "0.0.0" + And a new Phar with version "0.0.0" and cli build When I run `{PHAR_PATH} cli check-update` Then STDOUT should contain: """ package_url """ - And STDERR should be empty @github-api Scenario: Do WP-CLI Update Given an empty directory - And a new Phar with version "0.0.0" + And a new Phar with version "0.0.0" and cli build When I run `{PHAR_PATH} --info` Then STDOUT should contain: @@ -78,8 +77,6 @@ Feature: `wp cli` tasks """ Success: """ - And STDERR should be empty - And the return code should be 0 When I run `{PHAR_PATH} --info` Then STDOUT should contain: @@ -100,7 +97,7 @@ Feature: `wp cli` tasks @github-api Scenario: Patch update from 0.14.0 to 0.14.1 Given an empty directory - And a new Phar with version "0.14.0" + And a new Phar with version "0.14.0" and cli build When I run `{PHAR_PATH} --version` Then STDOUT should be: @@ -117,8 +114,6 @@ Feature: `wp cli` tasks """ Success: Updated WP-CLI to 0.14.1 """ - And STDERR should be empty - And the return code should be 0 When I run `{PHAR_PATH} --version` Then STDOUT should be: @@ -129,7 +124,7 @@ Feature: `wp cli` tasks @github-api Scenario: Not a patch update from 0.14.0 Given an empty directory - And a new Phar with version "0.14.0" + And a new Phar with version "0.14.0" and cli build When I run `{PHAR_PATH} cli update --no-patch --yes` Then STDOUT should contain: @@ -140,13 +135,11 @@ Feature: `wp cli` tasks """ 0.14.1 """ - And STDERR should be empty - And the return code should be 0 - @require-php-5.6 + @github-api Scenario: Install WP-CLI nightly Given an empty directory - And a new Phar with version "0.14.0" + And a new Phar with version "0.14.0" and cli build When I run `{PHAR_PATH} cli update --nightly --yes` Then STDOUT should contain: @@ -158,13 +151,10 @@ Feature: `wp cli` tasks Success: Updated WP-CLI to the latest nightly release. """ - And STDERR should be empty - And the return code should be 0 - @github-api Scenario: Install WP-CLI stable Given an empty directory - And a new Phar with version "0.14.0" + And a new Phar with version "0.14.0" and cli build And a session file: """ y @@ -187,8 +177,6 @@ Feature: `wp cli` tasks """ Success: Updated WP-CLI to the latest stable release. """ - And STDERR should be empty - And the return code should be 0 When I run `{PHAR_PATH} cli check-update` Then STDOUT should be: @@ -202,6 +190,125 @@ Feature: `wp cli` tasks WP-CLI {UPDATE_VERSION} """ + @github-api + Scenario: Install WP-CLI via auto-update + Given an empty directory + And an empty cache + And a new Phar with version "0.14.0" and cli build + And a session_no file: + """ + n + """ + And a session_yes_no file: + """ + y + n + """ + And a session_yes_yes file: + """ + y + y + """ + + When I run `{PHAR_PATH} cli check-update --field=version | head -1` + Then STDOUT should not be empty + And save STDOUT as {UPDATE_VERSION} + + Given a {SUITE_CACHE_DIR}/wp-cli-update-check file: + """ + 1 + """ + When I run `SHELL_PIPE=0 {PHAR_PATH} help < session_no` + Then STDOUT should contain: + """ + You have version 0.14.0. Would you like to check if an update of WP-CLI is available? [y/n] + """ + And STDOUT should contain: + """ + Continuing with your current command 'help'... + """ + And STDOUT should contain: + """ + wp + """ + And STDOUT should not contain: + """ + Checking for an update + """ + + Given a {SUITE_CACHE_DIR}/wp-cli-update-check file: + """ + 1 + """ + When I run `SHELL_PIPE=0 {PHAR_PATH} help < session_yes_no` + Then STDOUT should contain: + """ + You have version 0.14.0. Would you like to check if an update of WP-CLI is available? [y/n] + """ + And STDOUT should contain: + """ + Checking for an update of WP-CLI... + Update found - invoking 'cli update' (please note that if you decide to update, your current command 'help' will not complete)... + """ + And STDOUT should contain: + """ + You have version 0.14.0. Would you like to update to {UPDATE_VERSION}? [y/n] + """ + And STDOUT should contain: + """ + Continuing with your current command 'help'... + """ + And STDOUT should contain: + """ + wp + """ + And STDOUT should not contain: + """ + Downloading + """ + + Given a {SUITE_CACHE_DIR}/wp-cli-update-check file: + """ + 1 + """ + When I run `SHELL_PIPE=0 {PHAR_PATH} help < session_yes_yes` + Then STDOUT should contain: + """ + You have version 0.14.0. Would you like to check if an update of WP-CLI is available? [y/n] + """ + And STDOUT should contain: + """ + Checking for an update of WP-CLI... + Update found - invoking 'cli update' (please note that if you decide to update, your current command 'help' will not complete)... + You have version 0.14.0. Would you like to update to {UPDATE_VERSION}? [y/n] + """ + And STDOUT should contain: + """ + Downloading from https://github.com/wp-cli/wp-cli/releases/download/v{UPDATE_VERSION}/wp-cli-{UPDATE_VERSION}.phar... + """ + And STDOUT should contain: + """ + New version works. Proceeding to replace. + """ + And STDOUT should contain: + """ + Success: + """ + And STDOUT should not contain: + """ + Continuing + """ + And STDOUT should not contain: + """ + wp + """ + + When I run `{PHAR_PATH} cli check-update` + Then STDOUT should be: + """ + Success: WP-CLI is at the latest version. + """ + Scenario: Dump the list of global parameters with values Given a WP installation @@ -210,5 +317,3 @@ Feature: `wp cli` tasks """ 17"current": """ - And STDERR should be empty - And the return code should be 0 diff --git a/features/command.feature b/features/command.feature index 6c287c777c..6cf3313782 100644 --- a/features/command.feature +++ b/features/command.feature @@ -15,12 +15,6 @@ Feature: WP-CLI Commands wp cap """ - When I run `wp checksum --help` - Then STDOUT should contain: - """ - wp checksum - """ - When I run `wp comment --help` Then STDOUT should contain: """ @@ -260,9 +254,8 @@ Feature: WP-CLI Commands WP_CLI::add_command( 'command example', 'Non_Existent_Class' ); """ - When I try `wp --require=custom-cmd.php help` - Then the return code should be 1 - And STDERR should contain: + When I mistakenly try `wp --require=custom-cmd.php help` + Then STDERR should contain: """ Callable "Non_Existent_Class" does not exist, and cannot be registered as `wp command example`. """ @@ -285,7 +278,7 @@ Feature: WP-CLI Commands WP_CLI::add_command( 'command', 'Custom_Command_Class' ); """ - When I try `wp --require=custom-cmd.php command invalid` + When I mistakenly try `wp --require=custom-cmd.php command invalid` Then STDERR should contain: """ Error: 'invalid' is not a registered subcommand of 'command'. See 'wp help command' for available subcommands. @@ -322,7 +315,7 @@ Feature: WP-CLI Commands My awesome closure command """ - When I try `wp --require=custom-cmd.php foo bar --burrito` + When I mistakenly try `wp --require=custom-cmd.php foo bar --burrito` Then STDERR should contain: """ unknown --burrito parameter @@ -365,7 +358,7 @@ Feature: WP-CLI Commands My awesome function command """ - When I try `wp --require=custom-cmd.php foo bar --burrito` + When I mistakenly try `wp --require=custom-cmd.php foo bar --burrito` Then STDERR should contain: """ unknown --burrito parameter @@ -415,7 +408,7 @@ Feature: WP-CLI Commands My awesome class method command """ - When I try `wp --require=custom-cmd.php foo bar --burrito` + When I mistakenly try `wp --require=custom-cmd.php foo bar --burrito` Then STDERR should contain: """ unknown --burrito parameter @@ -460,7 +453,7 @@ Feature: WP-CLI Commands My awesome class method command """ - When I try `wp --require=custom-cmd.php foo bar --burrito` + When I mistakenly try `wp --require=custom-cmd.php foo bar --burrito` Then STDERR should contain: """ unknown --burrito parameter @@ -501,7 +494,6 @@ Feature: WP-CLI Commands """ bar """ - And STDERR should be empty Scenario: Use an invalid class method as a command Given an empty directory @@ -525,7 +517,7 @@ Feature: WP-CLI Commands WP_CLI::add_command( 'bar', array( $foo, 'bar' ) ); """ - When I try `wp --require=custom-cmd.php bar` + When I mistakenly try `wp --require=custom-cmd.php bar` Then STDERR should contain: """ Error: Callable ["Foo_Class","bar"] does not exist, and cannot be registered as `wp bar`. @@ -623,13 +615,13 @@ Feature: WP-CLI Commands --- """ - When I try `wp foo nana --apple=fuji` + When I mistakenly try `wp foo nana --apple=fuji` Then STDERR should contain: """ Error: Invalid value specified for positional arg. """ - When I try `wp foo hello --apple=fuji --meal=snack` + When I mistakenly try `wp foo hello --apple=fuji --meal=snack` Then STDERR should contain: """ Invalid value specified for 'meal' (A type of meal.) @@ -769,11 +761,104 @@ Feature: WP-CLI Commands When I run `wp help foo` Then STDOUT should contain: """ - EXAMPLES + NAME + + wp foo + + DESCRIPTION + + My awesome function command + + SYNOPSIS + + wp foo + + EXAMPLES + + # Run the custom foo command + + GLOBAL PARAMETERS + """ - And STDOUT should contain: + + # With synopsis, appended. + Given a hello-command.php file: """ - # Run the custom foo command + 'Prints a greeting.', + 'synopsis' => array( + array( + 'type' => 'positional', + 'name' => 'name', + 'description' => 'Name of person to greet.', + 'optional' => false, + 'repeating' => false, + ), + array( + 'type' => 'assoc', + 'name' => 'type', + 'optional' => true, + 'default' => 'success', + 'options' => array( 'success', 'error' ), + ), + array( + 'type' => 'flag', + 'name' => 'honk', + 'optional' => true, + ), + ), + 'when' => 'after_wp_load', + 'longdesc' => "\r\n## EXAMPLES\n\n# Say hello to Newman\nwp example hello Newman\nSuccess: Hello, Newman!", + ) ); + """ + + When I run `wp --require=hello-command.php help example hello` + Then STDOUT should contain: + """ + NAME + + wp example hello + + DESCRIPTION + + Prints a greeting. + + SYNOPSIS + + wp example hello [--type=] [--honk] + + OPTIONS + + + Name of person to greet. + + [--type=] + --- + default: success + options: + - success + - error + --- + + [--honk] + + EXAMPLES + + # Say hello to Newman + wp example hello Newman + Success: Hello, Newman! + + GLOBAL PARAMETERS + """ Scenario: Register a command with default and accepted arguments. @@ -846,7 +931,6 @@ Feature: WP-CLI Commands shop: burrito: """ - And STDERR should be empty When I run `wp --require=test-cmd.php foo ''` Then STDOUT should be YAML containing: @@ -855,7 +939,6 @@ Feature: WP-CLI Commands shop: burrito: """ - And STDERR should be empty When I run `wp --require=test-cmd.php foo apple --burrito=veggies` Then STDOUT should be YAML containing: @@ -864,29 +947,28 @@ Feature: WP-CLI Commands shop: burrito: veggies """ - And STDERR should be empty - When I try `wp --require=test-cmd.php foo apple --burrito=meat` + When I mistakenly try `wp --require=test-cmd.php foo apple --burrito=meat` Then STDERR should contain: """ Error: Parameter errors: Invalid value specified for 'burrito' (This is the burrito argument.) """ - When I try `wp --require=test-cmd.php foo apple --burrito=''` + When I mistakenly try `wp --require=test-cmd.php foo apple --burrito=''` Then STDERR should contain: """ Error: Parameter errors: Invalid value specified for 'burrito' (This is the burrito argument.) """ - When I try `wp --require=test-cmd.php foo apple taco_del_mar` + When I mistakenly try `wp --require=test-cmd.php foo apple taco_del_mar` Then STDERR should contain: """ Error: Invalid value specified for positional arg. """ - When I try `wp --require=test-cmd.php foo apple 'cha cha cha' taco_del_mar` + When I mistakenly try `wp --require=test-cmd.php foo apple 'cha cha cha' taco_del_mar` Then STDERR should contain: """ Error: Invalid value specified for positional arg. @@ -899,7 +981,6 @@ Feature: WP-CLI Commands shop: cha cha cha burrito: """ - And STDERR should be empty Scenario: Register a command with default and accepted arguments, part two Given an empty directory @@ -933,16 +1014,14 @@ Feature: WP-CLI Commands """ burrito: """ - And STDERR should be empty When I run `wp --require=test-cmd.php foo beans` Then STDOUT should be YAML containing: """ burrito: beans """ - And STDERR should be empty - When I try `wp --require=test-cmd.php foo apple` + When I mistakenly try `wp --require=test-cmd.php foo apple` Then STDERR should be: """ Error: Invalid value specified for positional arg. @@ -1047,24 +1126,23 @@ Feature: WP-CLI Commands """ bar """ - And STDERR should be empty Scenario: WP-CLI suggests matching commands when user entry contains typos Given a WP installation - When I try `wp clu` + When I mistakenly try `wp clu` Then STDERR should contain: """ Did you mean 'cli'? """ - When I try `wp cli nfo` + When I mistakenly try `wp cli nfo` Then STDERR should contain: """ Did you mean 'info'? """ - When I try `wp cli beyondlevenshteinthreshold` + When I mistakenly try `wp cli beyondlevenshteinthreshold` Then STDERR should not contain: """ Did you mean @@ -1073,13 +1151,13 @@ Feature: WP-CLI Commands Scenario: WP-CLI suggests matching parameters when user entry contains typos Given an empty directory - When I try `wp cli info --quie` + When I mistakenly try `wp cli info --quie` Then STDERR should contain: """ Did you mean '--quiet'? """ - When I try `wp cli info --forma=json` + When I mistakenly try `wp cli info --forma=json` Then STDERR should contain: """ Did you mean '--format'? @@ -1098,7 +1176,7 @@ Feature: WP-CLI Commands WP_CLI::add_command( 'test-command-2', function () {} ); """ - When I try `wp --require=abort-add-command.php` + When I successfully try `wp --require=abort-add-command.php` Then STDOUT should contain: """ test-command-1 @@ -1111,7 +1189,6 @@ Feature: WP-CLI Commands """ Warning: Aborting the addition of the command 'test-command-2' with reason: Testing hooks.. """ - And the return code should be 0 Scenario: Adding a command can depend on a previous command having been added before Given an empty directory @@ -1192,28 +1269,24 @@ Feature: WP-CLI Commands """ test-command """ - And STDERR should be empty When I run `wp help test-command` Then STDOUT should contain: """ sub-command """ - And STDERR should be empty When I run `wp test-command sub-command` Then STDOUT should contain: """ Success: test-command sub-command """ - And STDERR should be empty When I run `wp unknown-parent child-command` Then STDOUT should contain: """ Success: unknown-parent child-command """ - And STDERR should be empty Scenario: Command additions should work as must-use plugins Given a WP installation @@ -1241,28 +1314,24 @@ Feature: WP-CLI Commands """ test-command """ - And STDERR should be empty When I run `wp help test-command` Then STDOUT should contain: """ sub-command """ - And STDERR should be empty When I run `wp test-command sub-command` Then STDOUT should contain: """ Success: test-command sub-command """ - And STDERR should be empty When I run `wp unknown-parent child-command` Then STDOUT should contain: """ Success: unknown-parent child-command """ - And STDERR should be empty Scenario: Command additions should work when registered on after_wp_load Given a WP installation @@ -1292,28 +1361,24 @@ Feature: WP-CLI Commands """ test-command """ - And STDERR should be empty When I run `wp help test-command` Then STDOUT should contain: """ sub-command """ - And STDERR should be empty When I run `wp test-command sub-command` Then STDOUT should contain: """ Success: test-command sub-command """ - And STDERR should be empty When I run `wp unknown-parent child-command` Then STDOUT should contain: """ Success: unknown-parent child-command """ - And STDERR should be empty Scenario: The command should fire on `after_wp_load` Given a WP installation @@ -1347,21 +1412,18 @@ Feature: WP-CLI Commands """ bool(true) """ - And the return code should be 0 When I run `wp command before_wp_load` Then STDOUT should contain: """ bool(false) """ - And the return code should be 0 - When I try `wp command after_wp_load --path=/tmp` + When I mistakenly try `wp command after_wp_load --path=/tmp` Then STDERR should contain: """ Error: This does not seem to be a WordPress install. """ - And the return code should be 1 Scenario: The command should fire on `before_wp_load` Given a WP installation @@ -1392,20 +1454,16 @@ Feature: WP-CLI Commands """ When I run `wp command before_wp_load` - Then STDERR should be empty - And STDOUT should contain: + Then STDOUT should contain: """ bool(false) """ - And the return code should be 0 When I run `wp command after_wp_load` - Then STDERR should be empty - And STDOUT should contain: + Then STDOUT should contain: """ bool(true) """ - And the return code should be 0 Scenario: Command hook should fires as expected on __invoke() Given a WP installation @@ -1436,14 +1494,12 @@ Feature: WP-CLI Commands """ bool(true) """ - And the return code should be 0 - When I try `wp command --path=/tmp` + When I mistakenly try `wp command --path=/tmp` Then STDERR should contain: """ Error: This does not seem to be a WordPress install. """ - And the return code should be 1 Scenario: Command namespaces can be added and are shown in help Given an empty directory @@ -1466,7 +1522,6 @@ Feature: WP-CLI Commands """ My Command Namespace Description. """ - And STDERR should be empty Scenario: Command namespaces are only added when the command does not exist Given an empty directory @@ -1495,7 +1550,6 @@ Feature: WP-CLI Commands """ My Actual Namespaced Command. """ - And STDERR should be empty Scenario: Command namespaces are replaced by commands of the same name Given an empty directory @@ -1524,7 +1578,6 @@ Feature: WP-CLI Commands """ My Actual Namespaced Command. """ - And STDERR should be empty Scenario: Empty command namespaces show a notice when invoked Given an empty directory @@ -1543,7 +1596,6 @@ Feature: WP-CLI Commands """ The namespace my-namespaced-command does not contain any usable commands in the current context. """ - And STDERR should be empty Scenario: Late-registered command should appear in command usage Given a WP installation diff --git a/features/config.feature b/features/config.feature index e98e7a3008..54eb14d3a8 100644 --- a/features/config.feature +++ b/features/config.feature @@ -108,7 +108,7 @@ Feature: Have a config file eval-file """ - When I try `WP_CLI_CONFIG_PATH=config.yml wp help eval-file` + When I mistakenly try `WP_CLI_CONFIG_PATH=config.yml wp help eval-file` Then STDERR should be: """ Error: The 'eval-file' command has been disabled from the config file. @@ -126,13 +126,13 @@ Feature: Have a config file multisite-convert """ - When I try `WP_CLI_CONFIG_PATH=config.yml wp core multisite-convert` + When I mistakenly try `WP_CLI_CONFIG_PATH=config.yml wp core multisite-convert` Then STDERR should contain: """ command has been disabled """ - When I try `WP_CLI_CONFIG_PATH=config.yml wp help core multisite-convert` + When I mistakenly try `WP_CLI_CONFIG_PATH=config.yml wp help core multisite-convert` Then STDERR should contain: """ Error: The 'core multisite-convert' command has been disabled from the config file. @@ -175,7 +175,7 @@ Feature: Have a config file administrator """ - When I try `wp user create examplejane` + When I mistakenly try `wp user create examplejane` Then STDERR should be: """ Error: Sorry, that email address is already used! @@ -232,17 +232,17 @@ Feature: Have a config file """ When I run `WP_CLI_CONFIG_PATH=test-dir/config.yml wp help` - Then STDERR should be empty + Then STDERR should be empty Scenario: Load WordPress with `--debug` Given a WP installation - When I try `wp option get home --debug` + When I successfully try `wp option get home --debug` Then STDERR should contain: """ No readable global config found """ - Then STDERR should contain: + And STDERR should contain: """ No project config found """ @@ -262,14 +262,13 @@ Feature: Have a config file """ Running command: option get """ - And the return code should be 0 - When I try `wp option get home --debug=bootstrap` + When I successfully try `wp option get home --debug=bootstrap` Then STDERR should contain: """ No readable global config found """ - Then STDERR should contain: + And STDERR should contain: """ No project config found """ @@ -289,34 +288,12 @@ Feature: Have a config file """ Running command: option get """ - And the return code should be 0 - When I try `wp option get home --debug=foo` - Then STDERR should not contain: - """ - No readable global config found - """ - Then STDERR should not contain: - """ - No project config found - """ - And STDERR should not contain: - """ - Begin WordPress load - """ - And STDERR should not contain: - """ - wp-config.php path: - """ - And STDERR should not contain: - """ - Loaded WordPress - """ - And STDERR should not contain: + When I run `wp option get home --debug=foo` + Then STDOUT should contain: """ - Running command: option get + http://example.com """ - And the return code should be 0 Scenario: Missing required files should not fatal WP-CLI Given an empty directory @@ -326,7 +303,7 @@ Feature: Have a config file - missing-file.php """ - When I try `wp help` + When I mistakenly try `wp help` Then STDERR should contain: """ Error: Required file 'missing-file.php' doesn't exist (from project's wp-cli.yml). @@ -346,7 +323,7 @@ Feature: Have a config file - /foo/baz.php """ - When I try `WP_CLI_CONFIG_PATH=config.yml wp help` + When I mistakenly try `WP_CLI_CONFIG_PATH=config.yml wp help` Then STDERR should contain: """ Error: Required file 'baz.php' doesn't exist (from global config.yml). @@ -355,7 +332,7 @@ Feature: Have a config file Scenario: Missing required file as runtime argument Given an empty directory - When I try `wp help --require=foo.php` + When I mistakenly try `wp help --require=foo.php` Then STDERR should contain: """ Error: Required file 'foo.php' doesn't exist (from runtime argument). @@ -526,7 +503,7 @@ if ( !defined('ABSPATH') ) require_once(ABSPATH . 'wp-settings.php'); """ - When I try `wp option get home` + When I mistakenly try `wp option get home` Then STDERR should be: """ Error: Site 'example.dev/' not found. Verify DOMAIN_CURRENT_SITE matches an existing site or use `--url=` to override. diff --git a/features/flags.feature b/features/flags.feature index ce15bc9243..31bf7451ee 100644 --- a/features/flags.feature +++ b/features/flags.feature @@ -27,7 +27,7 @@ Feature: Global flags Scenario: Invalid URL Given a WP multisite installation - When I try `wp post list --url=invalid.example.com` + When I mistakenly try `wp post list --url=invalid.example.com` Then STDERR should be: """ Error: Site 'invalid.example.com' not found. Verify `--url=` matches an existing site. @@ -36,9 +36,8 @@ Feature: Global flags Scenario: Quiet run Given a WP installation - When I try `wp non-existing-command --quiet` - Then the return code should be 1 - And STDERR should be: + When I mistakenly try `wp non-existing-command --quiet` + Then STDERR should be: """ Error: 'non-existing-command' is not a registered wp command. See 'wp help' for available commands. """ @@ -57,9 +56,8 @@ Feature: Global flags """ And the return code should be 0 - When I try `wp eval 'echo CONST_WITHOUT_QUOTES;' --debug` - Then the return code should be 0 - And STDOUT should be: + When I successfully try `wp eval 'echo CONST_WITHOUT_QUOTES;' --debug` + Then STDOUT should be: """ CONST_WITHOUT_QUOTES """ @@ -89,9 +87,8 @@ Feature: Global flags admin """ - When I try `wp --user=non-existing-user eval 'echo wp_get_current_user()->user_login;'` - Then the return code should be 1 - And STDERR should be: + When I mistakenly try `wp --user=non-existing-user eval 'echo wp_get_current_user()->user_login;'` + Then STDERR should be: """ Error: Invalid user ID, email or login: 'non-existing-user' """ @@ -208,13 +205,13 @@ Feature: Global flags Scenario: Enabling/disabling color Given a WP installation - When I try `wp --no-color non-existent-command` + When I mistakenly try `wp --no-color non-existent-command` Then STDERR should be: """ Error: 'non-existent-command' is not a registered wp command. See 'wp help' for available commands. """ - When I try `wp --color non-existent-command` + When I mistakenly try `wp --color non-existent-command` Then STDERR should contain: """ [31;1mError: @@ -269,7 +266,7 @@ Feature: Global flags Scenario: Using --http= requires wp-cli/restful Given an empty directory - When I try `wp --http=foo.dev` + When I mistakenly try `wp --http=foo.dev` Then STDERR should be: """ Error: RESTful WP-CLI needs to be installed. Try 'wp package install wp-cli/restful'. @@ -281,6 +278,7 @@ Feature: Global flags """ Running SSH command: ssh -q '' -T 'WP_CLI_STRICT_ARGS_MODE=1 wp """ + And the return code should be 255 Scenario: SSH flag should support changing directories When I try `wp --debug --ssh=wordpress:/my/path --version` @@ -288,6 +286,7 @@ Feature: Global flags """ Running SSH command: ssh -q 'wordpress' -T 'cd '\''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/my/path'\''; wp """ + And the return code should be 255 Scenario: SSH flag should support Docker When I try `wp --debug --ssh=docker:user@wordpress --version` @@ -295,3 +294,5 @@ Feature: Global flags """ Running SSH command: docker exec --user 'user' 'wordpress' sh -c """ + And the return code should not be 0 + diff --git a/features/framework.feature b/features/framework.feature index 9908eb71ea..4557891625 100644 --- a/features/framework.feature +++ b/features/framework.feature @@ -83,6 +83,7 @@ Feature: Load WP-CLI """ Error: This does not seem to be a WordPress install. """ + And the return code should be 1 Scenario: Globalize global variables in wp-config.php Given an empty directory @@ -159,7 +160,7 @@ Feature: Load WP-CLI }); """ - When I try `wp option get home` + When I successfully try `wp option get home` Then STDERR should contain: """ Warning: Some code is trying to do a URL redirect. @@ -184,17 +185,16 @@ Feature: Load WP-CLI And a invalid-host.php file: """ ` to override. """ - When I try `wp option get home --url=example.io` + When I mistakenly try `wp option get home --url=example.io` Then STDERR should be: """ Error: Site 'example.io' not found. Verify `--url=` matches an existing site. """ Given "define( 'DOMAIN_CURRENT_SITE', 'example.org' );" replaced with " " in the wp-config.php file + # WP < 5.0 have bug which will not find a blog given an empty domain unless wp_blogs.domain empty which was (partly) addressed by https://core.trac.wordpress.org/ticket/42299 + # So empty wp_blogs.domain to make behavior consistent across WP versions. + And I run `wp db query 'UPDATE wp_blogs SET domain = NULL'` When I run `cat wp-config.php` Then STDOUT should not contain: @@ -334,13 +334,17 @@ Feature: Load WP-CLI DOMAIN_CURRENT_SITE """ - When I try `wp option get home` - Then STDERR should be: + # This will work as finds blog with empty domain and thus uses `home` option. + When I run `wp option get home` + Then STDOUT should be: """ - Error: Site not found. Define DOMAIN_CURRENT_SITE in 'wp-config.php' or use `--url=` to override. + http://example.com """ - When I try `wp option get home --url=example.io` + # Undo above. + Given I run `wp db query 'UPDATE wp_blogs SET domain = "example.com"'` + + When I mistakenly try `wp option get home --url=example.io` Then STDERR should be: """ Error: Site 'example.io' not found. Verify `--url=` matches an existing site. diff --git a/features/help.feature b/features/help.feature index 74753b28f9..260951edf3 100644 --- a/features/help.feature +++ b/features/help.feature @@ -9,42 +9,36 @@ Feature: Get help about WP-CLI commands Run 'wp help ' to get more information on a specific command. """ - And STDERR should be empty When I run `wp help core` Then STDOUT should contain: """ wp core """ - And STDERR should be empty When I run `wp help core download` Then STDOUT should contain: """ wp core download """ - And STDERR should be empty When I run `wp help help` Then STDOUT should contain: """ wp help """ - And STDERR should be empty When I run `wp help help` Then STDOUT should contain: """ GLOBAL PARAMETERS """ - And STDERR should be empty When I run `wp post list --post_type=post --posts_per_page=5 --help` Then STDOUT should contain: """ wp post list """ - And STDERR should be empty # Prior to WP 4.3 widgets & others used PHP 4 style constructors and prior to WP 3.9 wpdb used the mysql extension which can all lead (depending on PHP version) to PHP Deprecated notices. @require-wp-4.3 @@ -57,36 +51,33 @@ Feature: Get help about WP-CLI commands Run 'wp help ' to get more information on a specific command. """ - And STDERR should be empty When I run `wp help core` Then STDOUT should contain: """ wp core """ - And STDERR should be empty When I run `wp help core download` Then STDOUT should contain: """ wp core download """ - And STDERR should be empty When I run `wp help help` Then STDOUT should contain: """ wp help """ - And STDERR should be empty When I run `wp help help` Then STDOUT should contain: """ GLOBAL PARAMETERS """ - And STDERR should be empty + # Fixed in trunk, WP isn't compatible with PHP 7.2 due to `__autoload()` in "wp-includes/compat.php". + @less-than-php-7.2 Scenario: Help when WordPress is downloaded but not installed Given an empty directory @@ -96,7 +87,6 @@ Feature: Get help about WP-CLI commands """ wp config create """ - And STDERR should be empty When I run `wp config create {CORE_CONFIG_SETTINGS}` And I run `wp help core install` @@ -104,44 +94,37 @@ Feature: Get help about WP-CLI commands """ wp core install """ - And STDERR should be empty When I run `wp help core is-installed` Then STDOUT should contain: """ wp core is-installed """ - And STDERR should be empty When I run `wp help core` Then STDOUT should contain: """ wp core """ - And STDERR should be empty When I run `wp help config` Then STDOUT should contain: """ wp config """ - And STDERR should be empty When I run `wp help db` Then STDOUT should contain: """ wp db """ - And STDERR should be empty - When I try `wp help non-existent-command` - Then the return code should be 1 - And STDERR should be: + When I mistakenly try `wp help non-existent-command` + Then STDERR should be: """ Warning: Can’t select database. We were able to connect to the database server (which means your username and password is okay) but not able to select the `wp_cli_test` database. Error: 'non-existent-command' is not a registered wp command. See 'wp help' for available commands. """ - And STDOUT should be empty # Bug: if `WP_DEBUG` (or `WP_DEBUG_DISPLAY') is defined falsey than a db error won't be trapped. Given a define-wp-debug-false.php file: @@ -149,192 +132,154 @@ Feature: Get help about WP-CLI commands run 'eval "echo wp_get_current_user()->user_login . PHP_EOL;"'` Then STDOUT should be: @@ -62,8 +60,6 @@ Feature: Run a WP-CLI command admin returned: NULL """ - And STDERR should be empty - And the return code should be 0 When I run `WP_CLI_CONFIG_PATH=config.yml wp run 'user get'` Then STDOUT should be: @@ -71,8 +67,6 @@ Feature: Run a WP-CLI command admin@example.com returned: NULL """ - And STDERR should be empty - And the return code should be 0 Examples: | flag | @@ -87,32 +81,24 @@ Feature: Run a WP-CLI command """ returned: 'http://example.com' """ - And STDERR should be empty - And the return code should be 0 When I run `wp --return run 'eval "echo wp_get_current_user()->user_login . PHP_EOL;"'` Then STDOUT should be: """ returned: 'admin' """ - And STDERR should be empty - And the return code should be 0 When I run `wp --return=stderr run 'eval "echo wp_get_current_user()->user_login . PHP_EOL;"'` Then STDOUT should be: """ returned: '' """ - And STDERR should be empty - And the return code should be 0 When I run `wp --return=return_code run 'eval "echo wp_get_current_user()->user_login . PHP_EOL;"'` Then STDOUT should be: """ returned: 0 """ - And STDERR should be empty - And the return code should be 0 When I run `wp --return=all run 'eval "echo wp_get_current_user()->user_login . PHP_EOL;"'` Then STDOUT should be: @@ -123,16 +109,12 @@ Feature: Run a WP-CLI command 'return_code' => 0, ) """ - And STDERR should be empty - And the return code should be 0 When I run `WP_CLI_CONFIG_PATH=config.yml wp --return run 'user get'` Then STDOUT should be: """ returned: 'admin@example.com' """ - And STDERR should be empty - And the return code should be 0 Examples: | flag | @@ -159,13 +141,11 @@ Feature: Run a WP-CLI command Scenario Outline: Exit on error by default Given a WP installation - When I try `wp run 'eval "WP_CLI::error( var_export( get_current_user_id(), true ) );"'` - Then STDOUT should be empty - And STDERR should be: + When I mistakenly try `wp run 'eval "WP_CLI::error( var_export( get_current_user_id(), true ) );"'` + Then STDERR should be: """ Error: 1 """ - And the return code should be 1 Examples: | flag | @@ -175,7 +155,7 @@ Feature: Run a WP-CLI command Scenario Outline: Override erroring on exit Given a WP installation - When I try `wp run --no-exit_error --return=all 'eval "WP_CLI::error( var_export( get_current_user_id(), true ) );"'` + When I run `wp run --no-exit_error --return=all 'eval "WP_CLI::error( var_export( get_current_user_id(), true ) );"'` Then STDOUT should be: """ returned: array ( @@ -184,16 +164,12 @@ Feature: Run a WP-CLI command 'return_code' => 1, ) """ - And STDERR should be empty - And the return code should be 0 When I run `wp --no-exit_error run 'option pluck foo$bar barfoo'` Then STDOUT should be: """ returned: NULL """ - And STDERR should be empty - And the return code should be 0 Examples: | flag | @@ -214,8 +190,6 @@ Feature: Run a WP-CLI command 'return_code' => 1, ) """ - And STDERR should be empty - And the return code should be 0 When I run `wp run --no-exit_error --return=all 'eval "echo '\'echo\''; WP_CLI::log( '\'log\'' ); WP_CLI::warning( '\'warning\''); WP_CLI::success( '\'success\'' );"'` Then STDOUT should be: @@ -227,8 +201,6 @@ Feature: Run a WP-CLI command 'return_code' => 0, ) """ - And STDERR should be empty - And the return code should be 0 Examples: | flag | @@ -237,16 +209,13 @@ Feature: Run a WP-CLI command Scenario Outline: Installed packages work as expected Given a WP installation - - When I run `wp package install wp-cli/scaffold-package-command` - Then STDERR should be empty + And I run `wp package install wp-cli/scaffold-package-command` When I run `wp run 'help scaffold package'` Then STDOUT should contain: """ wp scaffold package """ - And STDERR should be empty Examples: | flag | @@ -263,8 +232,6 @@ Feature: Run a WP-CLI command Success: Rewrite structure set. returned: NULL """ - And STDERR should be empty - And the return code should be 0 Examples: | flag | @@ -280,8 +247,6 @@ Feature: Run a WP-CLI command http://example.com/?cat=1 returned: NULL """ - And STDERR should be empty - And the return code should be 0 Examples: | flag | diff --git a/features/runner.feature b/features/runner.feature index 4fcb5ae506..22f6bd7459 100644 --- a/features/runner.feature +++ b/features/runner.feature @@ -1,21 +1,22 @@ Feature: Runner WP-CLI Scenario: Path argument should be slashed correctly - When I try `wp no-such-command --path=/foo --debug` + When I mistakenly try `wp no-such-command --path=/foo --debug` Then STDERR should contain: """ ABSPATH defined: /foo/ """ - When I try `wp no-such-command --path=/foo/ --debug` + When I mistakenly try `wp no-such-command --path=/foo/ --debug` Then STDERR should contain: """ ABSPATH defined: /foo/ """ - When I try `wp no-such-command --path=/foo\\ --debug` + When I mistakenly try `wp no-such-command --path=/foo\\ --debug` Then STDERR should contain: """ ABSPATH defined: /foo/ """ + And the return code should be 1 diff --git a/features/skip-plugins.feature b/features/skip-plugins.feature index 4337496286..07c9c23502 100644 --- a/features/skip-plugins.feature +++ b/features/skip-plugins.feature @@ -37,6 +37,7 @@ Feature: Skipping plugins """ Call to undefined function hello_dolly() """ + And the return code should be 255 # No plugins should be loaded when --skip-plugins doesn't have a value When I run `wp --skip-plugins eval 'var_export( defined("AKISMET_VERSION") );var_export( function_exists( "hello_dolly" ) );'` @@ -60,6 +61,7 @@ Feature: Skipping plugins """ Call to undefined function hello_dolly() """ + And the return code should be 255 Scenario: Skipping all plugins via config file Given a WP installation @@ -74,11 +76,12 @@ Feature: Skipping plugins """ Call to undefined function hello_dolly() """ + And the return code should be 255 Scenario: Skip network active plugins Given a WP multisite installation - When I try `wp plugin deactivate akismet hello` + When I successfully try `wp plugin deactivate akismet hello` Then STDERR should be: """ Warning: Plugin 'akismet' isn't active. @@ -88,7 +91,6 @@ Feature: Skipping plugins """ Success: Plugins already deactivated. """ - And the return code should be 0 When I run `wp plugin activate --network akismet hello` And I run `wp eval 'var_export( defined("AKISMET_VERSION") );var_export( function_exists( "hello_dolly" ) );'` diff --git a/features/skip-themes.feature b/features/skip-themes.feature index eb54988ce0..d1aa365a14 100644 --- a/features/skip-themes.feature +++ b/features/skip-themes.feature @@ -1,118 +1,111 @@ Feature: Skipping themes + Background: + Given download: + | path | url | + | {CACHE_DIR}/classic.1.6.zip | https://downloads.wordpress.org/theme/classic.1.6.zip | + | {CACHE_DIR}/espied.1.2.2.zip | https://downloads.wordpress.org/theme/espied.1.2.2.zip | + | {CACHE_DIR}/sidespied.1.0.3.zip | https://downloads.wordpress.org/theme/sidespied.1.0.3.zip | + Scenario: Skipping themes via global flag Given a WP installation - And I run `wp theme install classic` - And I run `wp theme install default --activate` + And I run `wp theme install {CACHE_DIR}/classic.1.6.zip` + And I run `wp theme install {CACHE_DIR}/espied.1.2.2.zip --activate` - When I run `wp eval 'var_export( function_exists( "kubrick_head" ) );'` + When I run `wp eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ true """ - And STDERR should be empty # The specified theme should be skipped - When I run `wp --skip-themes=default eval 'var_export( function_exists( "kubrick_head" ) );'` + When I run `wp --skip-themes=espied eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty # All themes should be skipped - When I run `wp --skip-themes eval 'var_export( function_exists( "kubrick_head" ) );'` + When I run `wp --skip-themes eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty # Skip another theme - When I run `wp --skip-themes=classic eval 'var_export( function_exists( "kubrick_head" ) );'` + When I run `wp --skip-themes=classic eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ true """ - And STDERR should be empty # The specified theme should still show up as an active theme - When I run `wp --skip-themes theme status default` + When I run `wp --skip-themes theme status espied` Then STDOUT should contain: """ Active """ - And STDERR should be empty # Skip several themes - When I run `wp --skip-themes=classic,default eval 'var_export( function_exists( "kubrick_head" ) );'` + When I run `wp --skip-themes=classic,espied eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty Scenario: Skip parent and child themes Given a WP installation - And I run `wp theme install jolene biker` + And I run `wp theme install {CACHE_DIR}/espied.1.2.2.zip {CACHE_DIR}/sidespied.1.0.3.zip` - When I run `wp theme activate jolene` - When I run `wp eval 'var_export( function_exists( "jolene_setup" ) );'` + When I run `wp theme activate espied` + When I run `wp eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ true """ - And STDERR should be empty - When I run `wp --skip-themes=jolene eval 'var_export( function_exists( "jolene_setup" ) );'` + When I run `wp --skip-themes=espied eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty - When I run `wp theme activate biker` - When I run `wp eval 'var_export( function_exists( "jolene_setup" ) );'` + When I run `wp theme activate sidespied` + When I run `wp eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ true """ - And STDERR should be empty - When I run `wp eval 'var_export( function_exists( "biker_setup" ) );'` + When I run `wp eval 'var_export( function_exists( "sidespied_scripts" ) );'` Then STDOUT should be: """ true """ - And STDERR should be empty - When I run `wp --skip-themes=biker eval 'var_export( function_exists( "jolene_setup" ) );'` + When I run `wp --skip-themes=sidespied eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty - When I run `wp --skip-themes=biker eval 'var_export( function_exists( "biker_setup" ) );'` + When I run `wp --skip-themes=sidespied eval 'var_export( function_exists( "sidespied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty - When I run `wp --skip-themes=biker eval 'echo get_template_directory();'` + When I run `wp --skip-themes=sidespied eval 'echo get_template_directory();'` Then STDOUT should contain: """ - wp-content/themes/jolene + wp-content/themes/espied """ - And STDERR should be empty - When I run `wp --skip-themes=biker eval 'echo get_stylesheet_directory();'` + When I run `wp --skip-themes=sidespied eval 'echo get_stylesheet_directory();'` Then STDOUT should contain: """ - wp-content/themes/biker + wp-content/themes/sidespied """ - And STDERR should be empty Scenario: Skipping multiple themes via config file Given a WP installation @@ -120,10 +113,10 @@ Feature: Skipping themes """ skip-themes: - classic - - default + - espied """ - And I run `wp theme install classic --activate` - And I run `wp theme install default` + And I run `wp theme install {CACHE_DIR}/classic.1.6.zip --activate` + And I run `wp theme install {CACHE_DIR}/espied.1.2.2.zip` # The classic theme should show up as an active theme When I run `wp theme status` @@ -131,22 +124,19 @@ Feature: Skipping themes """ A classic """ - And STDERR should be empty - # The default theme should show up as an installed theme + # The espied theme should show up as an installed theme When I run `wp theme status` Then STDOUT should contain: """ - I default + I espied """ - And STDERR should be empty - And I run `wp theme activate default` + And I run `wp theme activate espied` - # The default theme should be skipped - When I run `wp eval 'var_export( function_exists( "kubrick_head" ) );'` + # The espied theme should be skipped + When I run `wp eval 'var_export( function_exists( "espied_scripts" ) );'` Then STDOUT should be: """ false """ - And STDERR should be empty diff --git a/features/steps/given.php b/features/steps/given.php index 3e501c2c7d..c0b00707fb 100644 --- a/features/steps/given.php +++ b/features/steps/given.php @@ -35,12 +35,18 @@ function ( $world ) { $steps->Given( '/^an? ([^\s]+) file:$/', function ( $world, $path, PyStringNode $content ) { $content = (string) $content . "\n"; - $full_path = $world->variables['RUN_DIR'] . "/$path"; - $dir = dirname( $full_path ); + $path = $world->replace_variables( $path ); + if ( ! WP_CLI\Utils\is_path_absolute( $path ) ) { + $path = $world->variables['RUN_DIR'] . "/$path"; + } + if ( 0 !== strpos( $path, sys_get_temp_dir() ) ) { + throw new RuntimeException( sprintf( "Attempted to create a file '%s' that is not in the temp directory '%s'. " . __FILE__ . ':' . __LINE__, $path, sys_get_temp_dir() ) ); + } + $dir = dirname( $path ); if ( ! file_exists( $dir ) ) { mkdir( $dir, 0777, true /*recursive*/ ); } - file_put_contents( $full_path, $content ); + file_put_contents( $path, $content ); } ); @@ -69,31 +75,31 @@ function ( $world ) { } ); -$steps->Given( '/^a WP (install|installation)$/', +$steps->Given( '/^a WP install(?:ation)?$/', function ( $world ) { $world->install_wp(); } ); -$steps->Given( "/^a WP (install|installation) in '([^\s]+)'$/", - function ( $world, $_, $subdir ) { +$steps->Given( "/^a WP install(?:ation)? in '([^\s]+)'$/", + function ( $world, $subdir ) { $world->install_wp( $subdir ); } ); -$steps->Given( '/^a WP (install|installation) with Composer$/', +$steps->Given( '/^a WP install(?:ation)? with Composer$/', function ( $world ) { $world->install_wp_with_composer(); } ); -$steps->Given( "/^a WP (install|installation) with Composer and a custom vendor directory '([^\s]+)'$/", - function ( $world, $_, $vendor_directory ) { +$steps->Given( "/^a WP install(?:ation)? with Composer and a custom vendor directory '([^\s]+)'$/", + function ( $world, $vendor_directory ) { $world->install_wp_with_composer( $vendor_directory ); } ); -$steps->Given( '/^a WP multisite (subdirectory|subdomain)?\s?(install|installation)$/', +$steps->Given( '/^a WP multisite (subdirectory|subdomain)?\s?install(?:ation)?$/', function ( $world, $type = 'subdirectory' ) { $world->install_wp(); $subdomains = ! empty( $type ) && 'subdomain' === $type ? 1 : 0; @@ -158,9 +164,9 @@ function ( $world, $stream, $output_filter, $key ) { } ); -$steps->Given( '/^a new Phar with (?:the same version|version "([^"]+)")$/', - function ( $world, $version = 'same' ) { - $world->build_phar( $version ); +$steps->Given( '/^a new Phar with (?:the same version|version "([^"]+)")(?: and (cli) build)?$/', + function ( $world, $version = 'same', $build = '' ) { + $world->build_phar( $version, $build ); } ); diff --git a/features/steps/when.php b/features/steps/when.php index d23aa0e669..d60216407c 100644 --- a/features/steps/when.php +++ b/features/steps/when.php @@ -2,16 +2,19 @@ use Behat\Gherkin\Node\PyStringNode, Behat\Gherkin\Node\TableNode, - WP_CLI\Process; + WP_CLI\Process, + WP_CLI\Utils; function invoke_proc( $proc, $mode ) { $map = array( - 'run' => 'run_check_stderr', - 'try' => 'run' + 'run' => array( 0, true, null ), + 'try' => array( null, null, null ), + 'mistakenly try' => array( 1, false, true ), + 'successfully try' => array( 0, false, false ), ); - $method = $map[ $mode ]; + $args = $map[ $mode ]; - return $proc->$method(); + return $proc->run_check_args( $args[0] /*return_code*/, $args[1] /*stderr_empty*/, $args[2] /*stdout_empty*/ ); } function capture_email_sends( $stdout ) { @@ -25,9 +28,12 @@ function ( $world, $cmd ) { } ); -$steps->When( '/^I (run|try) `([^`]+)`$/', - function ( $world, $mode, $cmd ) { +$steps->When( '/^I (run|try|mistakenly try|successfully try) `([^`]+)`$/', + function ( $world, $mode, $cmd, $return_code = 0, $stdout_empty = '' ) { $cmd = $world->replace_variables( $cmd ); + if ( Utils\is_windows() ) { + $cmd = _wp_cli_esc_cmd_win( $cmd ); + } $world->result = invoke_proc( $world->proc( $cmd ), $mode ); list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); } @@ -36,6 +42,9 @@ function ( $world, $mode, $cmd ) { $steps->When( "/^I (run|try) `([^`]+)` from '([^\s]+)'$/", function ( $world, $mode, $cmd, $subdir ) { $cmd = $world->replace_variables( $cmd ); + if ( Utils\is_windows() ) { + $cmd = _wp_cli_esc_cmd_win( $cmd ); + } $world->result = invoke_proc( $world->proc( $cmd, array(), $subdir ), $mode ); list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); } diff --git a/features/utils-wp.feature b/features/utils-wp.feature new file mode 100644 index 0000000000..c71ddb903a --- /dev/null +++ b/features/utils-wp.feature @@ -0,0 +1,734 @@ +Feature: Utilities that depend on WordPress code + + Scenario: Clear WP cache + Given a WP install + And a test.php file: + """ + cache ) . ',' . isset( $wp_object_cache->group_ops ) . ',' . isset( $wp_object_cache->stats ) . ',' . isset( $wp_object_cache->memcache_debug ) . "\n"; + WP_CLI\Utils\wp_clear_object_cache(); + echo empty( $wp_object_cache->cache ) . ',' . isset( $wp_object_cache->group_ops ) . ',' . isset( $wp_object_cache->stats ) . ',' . isset( $wp_object_cache->memcache_debug ) . "\n"; + } ); + """ + + When I run `wp post create --post_title="Foo Bar" --porcelain` + And I run `wp --require=test.php eval ''` + Then STDOUT should be: + """ + ,,, + 1,,, + """ + + Scenario: Get WP table names for single site install + Given a WP install + And I run `wp db query "CREATE TABLE xx_wp_posts ( id int );"` + And I run `wp db query "CREATE TABLE wp_xx_posts ( id int );"` + And I run `wp db query "CREATE TABLE wp_posts_xx ( id int );"` + And I run `wp db query "CREATE TABLE wp_categories ( id int );"` + And a table_names.php file: + """ + ...] + * : List tables based on wildcard search, e.g. 'wp_*_options' or 'wp_post?'. + * + * [--scope=] + * : Can be all, global, ms_global, blog, or old tables. Defaults to all. + * + * [--network] + * : List all the tables in a multisite install. Overrides --scope=. + * + * [--all-tables-with-prefix] + * : List all tables that match the table prefix even if not registered on $wpdb. Overrides --network. + * + * [--all-tables] + * : List all tables in the database, regardless of the prefix, and even if not registered on $wpdb. Overrides --all-tables-with-prefix. + */ + function test_wp_get_table_names( $args, $assoc_args ) { + if ( $tables = WP_CLI\Utils\wp_get_table_names( $args, $assoc_args ) ) { + echo implode( PHP_EOL, $tables ) . PHP_EOL; + } + } + WP_CLI::add_command( 'get_table_names', 'test_wp_get_table_names' ); + """ + + When I run `wp --require=table_names.php get_table_names` + Then STDOUT should contain: + """ + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + wp_usermeta + wp_users + """ + And save STDOUT as {DEFAULT_STDOUT} + + When I run `wp --require=table_names.php get_table_names --scope=all` + Then STDOUT should be: + """ + {DEFAULT_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --scope=blog` + Then STDOUT should contain: + """ + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + """ + + When I run `wp --require=table_names.php get_table_names --scope=global` + Then STDOUT should be: + """ + wp_usermeta + wp_users + """ + + When I run `wp --require=table_names.php get_table_names --scope=ms_global` + Then STDOUT should be empty + + When I run `wp --require=table_names.php get_table_names --scope=old` + Then STDOUT should be: + """ + wp_categories + """ + + When I run `wp --require=table_names.php get_table_names --network` + Then STDOUT should be: + """ + {DEFAULT_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --all-tables-with-prefix` + Then STDOUT should contain: + """ + wp_categories + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_posts_xx + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + wp_usermeta + wp_users + wp_xx_posts + """ + + When I run `wp --require=table_names.php get_table_names --all-tables` + Then STDOUT should contain: + """ + wp_categories + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_posts_xx + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + wp_usermeta + wp_users + wp_xx_posts + xx_wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts'` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*'` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts'` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --scope=blog` + Then STDOUT should be: + """ + wp_posts + """ + + When I try `wp --require=table_names.php get_table_names '*_posts' --scope=global` + Then STDERR should be: + """ + Error: Couldn't find any tables matching: *_posts + """ + And STDOUT should be empty + + When I run `wp --require=table_names.php get_table_names '*_posts' --network` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_posts + wp_xx_posts + """ + + When I run `wp --require=table_names.php get_table_names '*wp_posts' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + wp_posts_xx + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_posts + wp_xx_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --all-tables` + Then STDOUT should be: + """ + wp_posts + wp_xx_posts + xx_wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*wp_posts' --all-tables` + Then STDOUT should be: + """ + wp_posts + xx_wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*' --all-tables` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + wp_posts_xx + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts' --all-tables` + Then STDOUT should be: + """ + wp_posts + wp_xx_posts + """ + + When I try `wp --require=table_names.php get_table_names non_existent_table` + Then STDERR should be: + """ + Error: Couldn't find any tables matching: non_existent_table + """ + And STDOUT should be empty + + When I run `wp --require=table_names.php get_table_names wp_posts non_existent_table` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names wp_posts non_existent_table 'wp_?ption*'` + Then STDOUT should be: + """ + wp_options + wp_posts + """ + + Scenario: Get WP table names for multisite install + Given a WP multisite install + And I run `wp db query "CREATE TABLE xx_wp_posts ( id int );"` + And I run `wp db query "CREATE TABLE xx_wp_2_posts ( id int );"` + And I run `wp db query "CREATE TABLE wp_xx_posts ( id int );"` + And I run `wp db query "CREATE TABLE wp_2_xx_posts ( id int );"` + And I run `wp db query "CREATE TABLE wp_posts_xx ( id int );"` + And I run `wp db query "CREATE TABLE wp_2_posts_xx ( id int );"` + And I run `wp db query "CREATE TABLE wp_categories ( id int );"` + And I run `wp db query "CREATE TABLE wp_sitecategories ( id int );"` + And a table_names.php file: + """ + ...] + * : List tables based on wildcard search, e.g. 'wp_*_options' or 'wp_post?'. + * + * [--scope=] + * : Can be all, global, ms_global, blog, or old tables. Defaults to all. + * + * [--network] + * : List all the tables in a multisite install. Overrides --scope=. + * + * [--all-tables-with-prefix] + * : List all tables that match the table prefix even if not registered on $wpdb. Overrides --network. + * + * [--all-tables] + * : List all tables in the database, regardless of the prefix, and even if not registered on $wpdb. Overrides --all-tables-with-prefix. + */ + function test_wp_get_table_names( $args, $assoc_args ) { + if ( $tables = WP_CLI\Utils\wp_get_table_names( $args, $assoc_args ) ) { + echo implode( PHP_EOL, $tables ) . PHP_EOL; + } + } + WP_CLI::add_command( 'get_table_names', 'test_wp_get_table_names' ); + """ + + # With no subsite. + When I run `wp --require=table_names.php get_table_names` + Then STDOUT should contain: + """ + wp_blog_versions + wp_blogs + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_registration_log + wp_signups + wp_site + wp_sitemeta + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + wp_usermeta + wp_users + """ + And save STDOUT as {DEFAULT_STDOUT} + + When I run `wp --require=table_names.php get_table_names --scope=all` + Then STDOUT should be: + """ + {DEFAULT_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --scope=blog` + Then STDOUT should contain: + """ + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + """ + + When I run `wp --require=table_names.php get_table_names --scope=global` + Then STDOUT should be: + """ + wp_blog_versions + wp_blogs + wp_registration_log + wp_signups + wp_site + wp_sitemeta + wp_usermeta + wp_users + """ + And save STDOUT as {GLOBAL_STDOUT} + + When I run `wp --require=table_names.php get_table_names --scope=ms_global` + Then STDOUT should be: + """ + wp_blog_versions + wp_blogs + wp_registration_log + wp_signups + wp_site + wp_sitemeta + """ + + When I run `wp --require=table_names.php get_table_names --scope=old` + Then STDOUT should be: + """ + wp_categories + """ + + When I run `wp --require=table_names.php get_table_names --network` + Then STDOUT should be: + """ + {DEFAULT_STDOUT} + """ + + # With subsite. + Given I run `wp site create --slug=foo` + When I run `wp --require=table_names.php get_table_names` + Then STDOUT should be: + """ + {DEFAULT_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --url=example.com/foo --scope=blog` + Then STDOUT should contain: + """ + wp_2_commentmeta + wp_2_comments + wp_2_links + wp_2_options + wp_2_postmeta + wp_2_posts + wp_2_term_relationships + wp_2_term_taxonomy + """ + # Leave out wp_2_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_2_terms + """ + And save STDOUT as {SUBSITE_BLOG_STDOUT} + + When I run `wp --require=table_names.php get_table_names --url=example.com/foo` + Then STDOUT should be: + """ + {SUBSITE_BLOG_STDOUT} + {GLOBAL_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --network` + Then STDOUT should be: + """ + {SUBSITE_BLOG_STDOUT} + {DEFAULT_STDOUT} + """ + And save STDOUT as {NETWORK_STDOUT} + + When I run `wp --require=table_names.php get_table_names --network --url=example.com/foo` + Then STDOUT should be: + """ + {NETWORK_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --all-tables-with-prefix` + Then STDOUT should contain: + """ + wp_2_commentmeta + wp_2_comments + wp_2_links + wp_2_options + wp_2_postmeta + wp_2_posts + wp_2_posts_xx + wp_2_term_relationships + wp_2_term_taxonomy + """ + # Leave out wp_2_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_2_terms + wp_2_xx_posts + wp_blog_versions + wp_blogs + wp_categories + wp_commentmeta + wp_comments + wp_links + wp_options + wp_postmeta + wp_posts + wp_posts_xx + wp_registration_log + wp_signups + wp_site + wp_sitecategories + wp_sitemeta + wp_term_relationships + wp_term_taxonomy + """ + # Leave out wp_termmeta for old WP compat. + And STDOUT should contain: + """ + wp_terms + wp_usermeta + wp_users + wp_xx_posts + """ + And save STDOUT as {ALL_TABLES_WITH_PREFIX_STDOUT} + + # Network overriden by all-tables-with-prefix. + When I run `wp --require=table_names.php get_table_names --all-tables-with-prefix --network` + Then STDOUT should contain: + """ + {ALL_TABLES_WITH_PREFIX_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names --all-tables` + Then STDOUT should be: + """ + {ALL_TABLES_WITH_PREFIX_STDOUT} + xx_wp_2_posts + xx_wp_posts + """ + And save STDOUT as {ALL_TABLES_STDOUT} + + # Network overriden by all-tables. + When I run `wp --require=table_names.php get_table_names --all-tables --network` + Then STDOUT should be: + """ + {ALL_TABLES_STDOUT} + """ + + When I run `wp --require=table_names.php get_table_names '*_posts'` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --network` + Then STDOUT should be: + """ + wp_2_posts + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*'` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*' --network` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts'` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts' --network` + Then STDOUT should be: + """ + wp_2_posts + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --scope=blog` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --scope=blog --network` + Then STDOUT should be: + """ + wp_2_posts + wp_posts + """ + + When I try `wp --require=table_names.php get_table_names '*_posts' --scope=global` + Then STDERR should be: + """ + Error: Couldn't find any tables matching: *_posts + """ + And STDOUT should be empty + + # Note: BC change 1.5.0, network does not override scope. + When I try `wp --require=table_names.php get_table_names '*_posts' --scope=global --network` + Then STDERR should be: + """ + Error: Couldn't find any tables matching: *_posts + """ + And STDOUT should be empty + + When I run `wp --require=table_names.php get_table_names '*_posts' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_2_posts + wp_2_xx_posts + wp_posts + wp_xx_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + wp_posts_xx + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts' --all-tables-with-prefix` + Then STDOUT should be: + """ + wp_2_posts + wp_2_xx_posts + wp_posts + wp_xx_posts + """ + + When I run `wp --require=table_names.php get_table_names '*_posts' --all-tables` + Then STDOUT should be: + """ + wp_2_posts + wp_2_xx_posts + wp_posts + wp_xx_posts + xx_wp_2_posts + xx_wp_posts + """ + + When I run `wp --require=table_names.php get_table_names '*wp_posts' --all-tables` + Then STDOUT should be: + """ + wp_posts + xx_wp_posts + """ + + When I run `wp --require=table_names.php get_table_names 'wp_post*' --all-tables` + Then STDOUT should be: + """ + wp_postmeta + wp_posts + wp_posts_xx + """ + + When I run `wp --require=table_names.php get_table_names 'wp*osts' --all-tables` + Then STDOUT should be: + """ + wp_2_posts + wp_2_xx_posts + wp_posts + wp_xx_posts + """ + + When I try `wp --require=table_names.php get_table_names non_existent_table` + Then STDERR should be: + """ + Error: Couldn't find any tables matching: non_existent_table + """ + And STDOUT should be empty + + When I run `wp --require=table_names.php get_table_names wp_posts non_existent_table` + Then STDOUT should be: + """ + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names wp_posts non_existent_table 'wp_?ption*'` + Then STDOUT should be: + """ + wp_options + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names wp_posts non_existent_table 'wp_*ption?'` + Then STDOUT should be: + """ + wp_options + wp_posts + """ + + When I run `wp --require=table_names.php get_table_names wp_posts non_existent_table 'wp_*ption?' --network` + Then STDOUT should be: + """ + wp_2_options + wp_options + wp_posts + """ + + Given an enable_sitecategories.php file: + """ + } --skip-wordpress eval 'WP_CLI\Utils\run_mysql_command( null, array() );'` + Then STDERR should contain: + """ + Error: Cannot do 'run_mysql_command': The PHP functions `proc_open()` and/or `proc_close()` are disabled + """ + And STDOUT should be empty + And the return code should be 1 + + Examples: + | func | + | proc_open | + | proc_close | + + Scenario Outline: Check that `proc_open()` and `proc_close()` aren't disabled for `Utils\launch_editor_for_input()` + When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--ddisable_functions=} --skip-wordpress eval 'WP_CLI\Utils\launch_editor_for_input( null, null );'` + Then STDERR should contain: + """ + Error: Cannot do 'launch_editor_for_input': The PHP functions `proc_open()` and/or `proc_close()` are disabled + """ + And STDOUT should be empty + And the return code should be 1 + + Examples: + | func | + | proc_open | + | proc_close | + + # INI directive `sys_temp_dir` introduced PHP 5.5.0. + @require-php-5.5 + Scenario: Check `Utils\get_temp_dir()` when `sys_temp_dir` directive set + # `sys_temp_dir` set to unwritable. + When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--dsys_temp_dir=\\no_such_directory/} --skip-wordpress eval 'echo WP_CLI\Utils\get_temp_dir();'` + Then STDERR should contain: + """ + Warning: Temp directory isn't writable + """ + And STDERR should contain: + """ + /no_such_directory/ + """ + And STDOUT should be: + """ + /no_such_directory/ + """ + And the return code should be 0 + + # `sys_temp_dir` unset. + When I run `{INVOKE_WP_CLI_WITH_PHP_ARGS--dsys_temp_dir=} --skip-wordpress eval 'echo WP_CLI\Utils\get_temp_dir();'` + Then STDOUT should match /\/$/ diff --git a/features/validation.feature b/features/validation.feature index 15e9c040db..bcd8c8efac 100644 --- a/features/validation.feature +++ b/features/validation.feature @@ -17,9 +17,8 @@ Feature: Argument validation Given an empty directory And WP files - When I try `wp core config` - Then the return code should be 1 - And STDERR should contain: + When I mistakenly try `wp core config` + Then STDERR should contain: """ Parameter errors: """ @@ -28,9 +27,8 @@ Feature: Argument validation missing --dbname parameter """ - When I try `wp core config --invalid --other-invalid` - Then the return code should be 1 - And STDERR should contain: + When I mistakenly try `wp core config --invalid --other-invalid` + Then STDERR should contain: """ unknown --invalid parameter """ @@ -39,10 +37,8 @@ Feature: Argument validation unknown --other-invalid parameter """ - When I try `wp core version invalid` - Then the return code should be 1 - And STDERR should contain: + When I mistakenly try `wp core version invalid` + Then STDERR should contain: """ Error: Too many positional arguments: invalid """ - And STDOUT should be empty diff --git a/php/WP_CLI/Bootstrap/AutoloaderStep.php b/php/WP_CLI/Bootstrap/AutoloaderStep.php index 4818771edf..b7cf3ce015 100644 --- a/php/WP_CLI/Bootstrap/AutoloaderStep.php +++ b/php/WP_CLI/Bootstrap/AutoloaderStep.php @@ -63,20 +63,26 @@ public function process( BootstrapState $state ) { * @return string|false Name of the custom vendor folder or false if none. */ protected function get_custom_vendor_folder() { - $maybe_composer_json = WP_CLI_ROOT . '/../../../composer.json'; - if ( ! is_readable( $maybe_composer_json ) ) { - return false; - } + static $result = null; + + if ( null === $result ) { + $maybe_composer_json = WP_CLI_ROOT . '/../../../composer.json'; + if ( ! is_readable( $maybe_composer_json ) ) { + return $result = false; + } - $composer = json_decode( file_get_contents( $maybe_composer_json ) ); + $composer = json_decode( file_get_contents( $maybe_composer_json ) ); + + if ( ! empty( $composer->config ) + && ! empty( $composer->config->{'vendor-dir'} ) + ) { + return $result = $composer->config->{'vendor-dir'}; + } - if ( ! empty( $composer->config ) - && ! empty( $composer->config->{'vendor-dir'} ) - ) { - return $composer->config->{'vendor-dir'}; + return $result = false; } - return false; + return $result; } /** diff --git a/php/WP_CLI/Bootstrap/RunnerInstance.php b/php/WP_CLI/Bootstrap/RunnerInstance.php index dceddb99b1..e2736ddc06 100644 --- a/php/WP_CLI/Bootstrap/RunnerInstance.php +++ b/php/WP_CLI/Bootstrap/RunnerInstance.php @@ -20,11 +20,11 @@ final class RunnerInstance { */ public function __invoke() { if ( ! class_exists( 'WP_CLI\Runner' ) ) { - require_once WP_CLI_ROOT . '/php/WP_CLI/Runner.php'; + require WP_CLI_ROOT . '/php/WP_CLI/Runner.php'; } if ( ! class_exists( 'WP_CLI\Configurator' ) ) { - require_once WP_CLI_ROOT . '/php/WP_CLI/Configurator.php'; + require WP_CLI_ROOT . '/php/WP_CLI/Configurator.php'; } return \WP_CLI::get_runner(); diff --git a/php/WP_CLI/Completions.php b/php/WP_CLI/Completions.php index 9de6ee6e4d..957fd30cc2 100644 --- a/php/WP_CLI/Completions.php +++ b/php/WP_CLI/Completions.php @@ -6,6 +6,7 @@ class Completions { private $words; private $opts = array(); + private $cur_word; public function __construct( $line ) { // TODO: properly parse single and double quotes diff --git a/php/WP_CLI/Configurator.php b/php/WP_CLI/Configurator.php index d099eddec8..27f46640f5 100644 --- a/php/WP_CLI/Configurator.php +++ b/php/WP_CLI/Configurator.php @@ -3,6 +3,7 @@ namespace WP_CLI; use Mustangostang\Spyc; +use WP_CLI\Utils; /** * Handles file- and runtime-based configuration values. @@ -323,10 +324,15 @@ private static function load_yml( $yml_file ) { $config['config create'] = $config['core config']; unset( $config['core config'] ); } - // 'core verify-checksums' -> 'checksum core' - if ( isset( $config['core verify-checksums'] ) ) { - $config['checksum core'] = $config['core verify-checksums']; - unset( $config['core verify-checksums'] ); + // 'checksum core' -> 'core verify-checksums' + if ( isset( $config['checksum core'] ) ) { + $config['core verify-checksums'] = $config['checksum core']; + unset( $config['checksum core'] ); + } + // 'checksum plugin' -> 'plugin verify-checksums' + if ( isset( $config['checksum plugin'] ) ) { + $config['plugin verify-checksums'] = $config['checksum plugin']; + unset( $config['checksum plugin'] ); } return $config; @@ -349,8 +355,9 @@ private static function arrayify( &$val ) { */ private static function absolutize( &$path, $base ) { if ( ! empty( $path ) && ! \WP_CLI\Utils\is_path_absolute( $path ) ) { - $path = $base . DIRECTORY_SEPARATOR . $path; + $path = $base . '/' . $path; } + $path = Utils\normalize_directory_separators( $path ); } } diff --git a/php/WP_CLI/Dispatcher/CommandFactory.php b/php/WP_CLI/Dispatcher/CommandFactory.php index 50af3ee28d..b1b4cbc7b2 100644 --- a/php/WP_CLI/Dispatcher/CommandFactory.php +++ b/php/WP_CLI/Dispatcher/CommandFactory.php @@ -16,7 +16,7 @@ class CommandFactory { * Create a new CompositeCommand (or Subcommand if class has __invoke()) * * @param string $name Represents how the command should be invoked - * @param string $callable A subclass of WP_CLI_Command, a function, or a closure + * @param object|string|array $callable A subclass of WP_CLI_Command, a function, or a closure * @param mixed $parent The new command's parent Composite (or Root) command */ public static function create( $name, $callable, $parent ) { @@ -60,11 +60,9 @@ public static function clear_file_contents_cache() { * Create a new Subcommand instance. * * @param mixed $parent The new command's parent Composite command - * @param string $name Represents how the command should be invoked + * @param string|bool $name Represents how the command should be invoked. If false it's set from the docblock or reflection name. * @param mixed $callable A callable function or closure, or class name and method * @param object $reflection Reflection instance, for doc parsing - * @param string $class A subclass of WP_CLI_Command - * @param string $method Class method to be called upon invocation. */ private static function create_subcommand( $parent, $name, $callable, $reflection ) { $doc_comment = self::get_doc_comment( $reflection ); @@ -149,7 +147,7 @@ private static function create_namespace( $parent, $name, $callable ) { /** * Check whether a method is actually callable. * - * @param ReflectionMethod $method + * @param \ReflectionMethod $method * @return bool */ private static function is_good_method( $method ) { @@ -159,7 +157,7 @@ private static function is_good_method( $method ) { /** * Gets the document comment. Caters for PHP directive `opcache.save comments` being disabled. * - * @param ReflectionMethod|ReflectionClass|ReflectionFunction $reflection Reflection instance. + * @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 ) { diff --git a/php/WP_CLI/Dispatcher/CompositeCommand.php b/php/WP_CLI/Dispatcher/CompositeCommand.php index a00358f78b..55b52f0e35 100644 --- a/php/WP_CLI/Dispatcher/CompositeCommand.php +++ b/php/WP_CLI/Dispatcher/CompositeCommand.php @@ -12,7 +12,8 @@ */ class CompositeCommand { - protected $name, $shortdesc, $synopsis, $docparser; + protected $name, $shortdesc, $longdesc, $docparser; + protected $synopsis; // Used by Subcommand. protected $parent, $subcommands = array(); @@ -117,7 +118,7 @@ public function get_shortdesc() { * @param string */ public function set_shortdesc( $shortdesc ) { - $this->shortdesc = $shortdesc; + $this->shortdesc = Utils\normalize_eols( $shortdesc ); } /** @@ -136,7 +137,7 @@ public function get_longdesc() { * @param string */ public function set_longdesc( $longdesc ) { - $this->longdesc = $longdesc; + $this->longdesc = Utils\normalize_eols( $longdesc ); } /** @@ -252,7 +253,7 @@ private static function get_aliases( $subcommands ) { /** * Composite commands can only be known by one name. * - * @return false + * @return false|string */ public function get_alias() { return false; diff --git a/php/WP_CLI/DocParser.php b/php/WP_CLI/DocParser.php index 303f0b8e69..033d6f3847 100644 --- a/php/WP_CLI/DocParser.php +++ b/php/WP_CLI/DocParser.php @@ -64,15 +64,7 @@ public function get_longdesc() { $longdesc = substr( $this->docComment, strlen( $shortdesc ) ); - $lines = array(); - foreach ( explode( "\n", $longdesc ) as $line ) { - if ( 0 === strpos( $line, '@' ) ) { - break; - } - - $lines[] = $line; - } - $longdesc = trim( implode( $lines, "\n" ) ); + $longdesc = trim( preg_replace( '/^@.*\z/ms', '', $longdesc ) ); return $longdesc; } diff --git a/php/WP_CLI/Extractor.php b/php/WP_CLI/Extractor.php index 4e1e9a66e1..fbb65d83dc 100644 --- a/php/WP_CLI/Extractor.php +++ b/php/WP_CLI/Extractor.php @@ -38,29 +38,32 @@ public static function extract( $tarball_or_zip, $dest ) { * @param string $dest */ private static function extract_zip( $zipfile, $dest ) { + if ( class_exists( 'PharData' ) && ! getenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' ) ) { + try { + self::copy_overwrite_files( 'phar://' . $zipfile, $dest, 1 /*strip_components*/ ); + return; + } catch ( \Exception $e ) { + WP_CLI::warning( sprintf( 'Falling back to ZipArchive. PharData failed: %s.', $e->getMessage() ) ); + } + } if ( ! class_exists( 'ZipArchive' ) ) { - throw new \Exception( 'Extracting a zip file requires ZipArchive.' ); + throw new \Exception( 'Extracting a zip file requires ZipArchive or PharData.' ); } $zip = new ZipArchive(); $res = $zip->open( $zipfile ); - if ( true === $res ) { - $tempdir = implode( - DIRECTORY_SEPARATOR, - array( - dirname( $zipfile ), - Utils\basename( $zipfile, '.zip' ), - $zip->getNameIndex( 0 ), - ) - ); - - $zip->extractTo( dirname( $tempdir ) ); - $zip->close(); - - self::copy_overwrite_files( $tempdir, $dest ); - self::rmdir( dirname( $tempdir ) ); - } else { - throw new \Exception( sprintf( "ZipArchive failed to unzip '%s': %s.", $zipfile, self::zip_error_msg( $res ) ) ); + if ( true !== $res ) { + throw new \Exception( sprintf( "ZipArchive failed to open '%s': %s.", $zipfile, self::zip_error_msg( $res ) ) ); + } + $tempdir = Utils\get_temp_dir() . uniqid( 'wp-cli-extract-zip-', true ); + + if ( ! $zip->extractTo( $tempdir ) ) { + throw new \Exception( sprintf( "ZipArchive failed to extract '%s' to temporary directory '%s'.", $zipfile, $tempdir ) ); + } + if ( ! $zip->close() ) { + throw new \Exception( sprintf( "ZipArchive failed to close '%s'.", $zipfile ) ); } + self::copy_overwrite_files( $tempdir, $dest, 1 /*strip_components*/ ); + self::rmdir( $tempdir ); } /** @@ -71,31 +74,43 @@ private static function extract_zip( $zipfile, $dest ) { */ private static function extract_tarball( $tarball, $dest ) { - if ( class_exists( 'PharData' ) ) { + if ( class_exists( 'PharData' ) && ! getenv( 'WP_CLI_TEST_EXTRACTOR_SHELL' ) ) { try { - $phar = new PharData( $tarball ); - $tempdir = implode( - DIRECTORY_SEPARATOR, - array( - dirname( $tarball ), - Utils\basename( $tarball, '.tar.gz' ), - $phar->getFilename(), - ) - ); - - $phar->extractTo( dirname( $tempdir ), null, true ); - - self::copy_overwrite_files( $tempdir, $dest ); + // Check if PHP bug #75273 likely to be triggered and copy to temporary tar if so. + $pre_tar = false; + // Only checking Ubuntu (and derivatives) as unfortunately Debian doesn't sign its PHP build so not easy to check for it. + if ( false !== strpos( PHP_VERSION, 'ubuntu' ) ) { + $tar_size = filesize( $tarball ); + $pre_tar = $tar_size > 4 * 1024 * 1024 && $tar_size % ( 8 * 1024 ) < 10; + } + if ( $pre_tar ) { + $temp_tar = Utils\get_temp_dir() . uniqid( 'wp-cli-extract-', true ) . '.tar'; + if ( false === ( $contents = file_get_contents( 'compress.zlib://' . $tarball ) ) || false === file_put_contents( $temp_tar, $contents ) ) { + throw new \Exception( sprintf( "Failed to create temporary tar '%s'.", $temp_tar ) ); + } + + self::copy_overwrite_files( 'phar://' . $temp_tar, $dest, 1 /*strip_components*/ ); + unlink( $temp_tar ); + } else { - self::rmdir( dirname( $tempdir ) ); + self::copy_overwrite_files( 'phar://' . $tarball, $dest, 1 /*strip_components*/ ); + } return; } catch ( \Exception $e ) { - WP_CLI::warning( "PharData failed, falling back to 'tar xz' (" . $e->getMessage() . ')' ); + // Can fail on Debian due to PHP bug #75273 so don't warn but fall through silently if message matches. + if ( $pre_tar || sprintf( 'unable to decompress gzipped phar archive "%s" to temporary file', $tarball ) !== $e->getMessage() ) { + WP_CLI::warning( sprintf( "Falling back to 'tar xz'. PharData failed: %s.", $e->getMessage() ) ); + } // Fall through to trying `tar xz` below } } - // Note: directory must exist for tar --directory to work. - $cmd = Utils\esc_cmd( 'tar xz --strip-components=1 --directory=%s -f %s', $dest, $tarball ); + + // Directory must exist for tar --directory to work. + if ( ! file_exists( $dest ) ) { + mkdir( $dest, 0777, true ); + } + // Need --force-local for Windows to avoid "C:" being interpreted as referring to remote machine. + $cmd = Utils\esc_cmd( 'tar xz --force-local --strip-components=1 --directory=%2$s -f %1$s', $tarball, $dest ); $process_run = WP_CLI::launch( $cmd, false /*exit_on_error*/, true /*return_detailed*/ ); if ( 0 !== $process_run->return_code ) { throw new \Exception( sprintf( 'Failed to execute `%s`: %s.', $cmd, self::tar_error_msg( $process_run ) ) ); @@ -108,41 +123,38 @@ private static function extract_tarball( $tarball, $dest ) { * @param string $source * @param string $dest */ - public static function copy_overwrite_files( $source, $dest ) { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( $source, RecursiveDirectoryIterator::SKIP_DOTS ), - RecursiveIteratorIterator::SELF_FIRST - ); - - $error = 0; + public static function copy_overwrite_files( $source, $dest, $strip_components = 0 ) { + if ( false === ( $files = scandir( $source ) ) ) { + $error = error_get_last(); + throw new \Exception( sprintf( "Failed to scan source directory '%s': %s", $source, $error['message'] ) ); + } - if ( ! is_dir( $dest ) ) { - mkdir( $dest, 0777, true ); + if ( ! file_exists( $dest ) && ! mkdir( $dest, 0777, true ) ) { + $error = error_get_last(); + throw new \Exception( sprintf( "Failed to create destination directory '%s': %s", $dest, $error['message'] ) ); } - foreach ( $iterator as $item ) { + foreach ( array_diff( $files, array( '.', '..' ) ) as $file ) { - $dest_path = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); + $source_file = $source . '/' . $file; + $dest_file = $dest . '/' . $file; - if ( $item->isDir() ) { - if ( ! is_dir( $dest_path ) ) { - mkdir( $dest_path ); + if ( $strip_components > 0 ) { + if ( is_dir( $source_file ) ) { + self::copy_overwrite_files( $source_file, $dest, $strip_components - 1 ); } } else { - if ( file_exists( $dest_path ) && is_writable( $dest_path ) ) { - copy( $item, $dest_path ); - } elseif ( ! file_exists( $dest_path ) ) { - copy( $item, $dest_path ); + + if ( is_dir( $source_file ) ) { + self::copy_overwrite_files( $source_file, $dest_file ); } else { - $error = 1; - WP_CLI::warning( "Unable to copy '" . $iterator->getSubPathName() . "' to current directory." ); + if ( ! copy( $source_file, $dest_file ) ) { + $error = error_get_last(); + throw new \Exception( sprintf( "Failed to copy '%s' to target '%s': %s.", $source_file, $dest_file, $error['message'] ) ); + } } } } - - if ( $error ) { - throw new \Exception( 'There was an error overwriting existing files.' ); - } } /** diff --git a/php/WP_CLI/FileCache.php b/php/WP_CLI/FileCache.php index ff7da52149..ab0ff8acc0 100644 --- a/php/WP_CLI/FileCache.php +++ b/php/WP_CLI/FileCache.php @@ -40,6 +40,10 @@ class FileCache { * @var string key allowed chars (regex class) */ protected $whitelist; + /** + * @var bool finderDummy dummy to autoload Finder classes. + */ + static private $finderDummy = null; /** * @param string $cacheDir location of the cache @@ -57,6 +61,13 @@ public function __construct( $cacheDir, $ttl, $maxSize, $whitelist = 'a-z0-9._-' $this->enabled = false; } + if ( null === self::$finderDummy && $this->enabled ) { + /* + * Make sure Finder & helper classes used in clean() are loaded as otherwise calling clean() in a `register_shutdown_function` + * can fail in certain circumstances - a PHP issue not easily reproducible but witnessed on Travis and locally. + */ + self::$finderDummy = (bool) $this->get_finder()->files()->name( basename( __FILE__ ) )->in( __DIR__ )->date( '> 1999' )->sortByAccessedTime()->getIterator()->current()->getSize(); + } } /** diff --git a/php/WP_CLI/Process.php b/php/WP_CLI/Process.php index 584f679b9c..315aa869ad 100644 --- a/php/WP_CLI/Process.php +++ b/php/WP_CLI/Process.php @@ -49,7 +49,7 @@ class Process { * * @return Process */ - public static function create( $command, $cwd = null, $env = array() ) { + public static function create( $command, $cwd = null, $env = null ) { $proc = new self; $proc->command = $command; @@ -133,4 +133,25 @@ public function run_check_stderr() { return $r; } + + /** + * Run the command, but throw an Exception if the non-null args aren't as given. + * + * @return ProcessRun + */ + public function run_check_args( $return_code = null, $stderr_empty = null, $stdout_empty = null ) { + $r = $this->run(); + + if ( null !== $return_code && $r->return_code !== $return_code ) { + throw new \RuntimeException( $r ); + } + if ( null !== $stderr_empty && empty( $r->stderr ) !== $stderr_empty ) { + throw new \RuntimeException( $r ); + } + if ( null !== $stdout_empty && empty( $r->stdout ) !== $stdout_empty ) { + throw new \RuntimeException( $r ); + } + + return $r; + } } diff --git a/php/WP_CLI/Runner.php b/php/WP_CLI/Runner.php index 50a6eae5cb..aff0881042 100644 --- a/php/WP_CLI/Runner.php +++ b/php/WP_CLI/Runner.php @@ -616,9 +616,14 @@ private static function back_compat_conversions( $args, $assoc_args ) { list( $args[0], $args[1] ) = array( 'language', 'core' ); } - // core verify-checksums -> checksum core - if ( array( 'core', 'verify-checksums' ) == array_slice( $args, 0, 2 ) ) { - list( $args[0], $args[1] ) = array( 'checksum', 'core' ); + // checksum core -> core verify-checksums + if ( array( 'checksum', 'core' ) == array_slice( $args, 0, 2 ) ) { + list( $args[0], $args[1] ) = array( 'core', 'verify-checksums' ); + } + + // checksum plugin -> plugin verify-checksums + if ( array( 'checksum', 'plugin' ) == array_slice( $args, 0, 2 ) ) { + list( $args[0], $args[1] ) = array( 'plugin', 'verify-checksums' ); } // site create --site_id= -> site create --network_id= @@ -723,6 +728,29 @@ private static function back_compat_conversions( $args, $assoc_args ) { } } + // config get --[global|constant]= --> config get --type=constant|variable + // config get --> config list + if ( count( $args ) === 2 + && 'config' === $args[0] + && 'get' === $args[1] ) { + if ( isset( $assoc_args['global'] ) ) { + $name = $assoc_args['global']; + $type = 'variable'; + unset( $assoc_args['global'] ); + } elseif ( isset( $assoc_args['constant'] ) ) { + $name = $assoc_args['constant']; + $type = 'constant'; + unset( $assoc_args['constant'] ); + } + if ( ! empty( $name ) && ! empty( $type ) ) { + $args[] = $name; + $assoc_args['type'] = $type; + } else { + // We had a 'config get' without a '', so assume 'list' was wanted. + $args[1] = 'list'; + } + } + return array( $args, $assoc_args ); } @@ -763,11 +791,22 @@ public function get_required_files() { * @return bool */ private function wp_exists() { + return file_exists( ABSPATH . 'wp-includes/version.php' ); + } + + /** + * Are WordPress core files readable? + * + * @return bool + */ + private function wp_is_readable() { return is_readable( ABSPATH . 'wp-includes/version.php' ); } private function check_wp_version() { - if ( ! $this->wp_exists() ) { + $wp_exists = $this->wp_exists(); + $wp_is_readable = $this->wp_is_readable(); + if ( ! $wp_exists || ! $wp_is_readable ) { $this->show_synopsis_if_composite_command(); // If the command doesn't exist use as error. $args = $this->cmd_starts_with( array( 'help' ) ) ? array_slice( $this->arguments, 1 ) : $this->arguments; @@ -778,6 +817,12 @@ private function check_wp_version() { } WP_CLI::error( $suggestion_or_disabled ); } + + if ( $wp_exists && ! $wp_is_readable ) { + WP_CLI::error( + 'It seems, the WordPress core files do not have the proper file permissions.' + ); + } WP_CLI::error( "This does not seem to be a WordPress install.\n" . 'Pass --path=`path/to/wordpress` or run `wp core download`.' @@ -1578,7 +1623,7 @@ private function auto_check_update() { } // Only check for update when a human is operating. - if ( ! function_exists( 'posix_isatty' ) || ! posix_isatty( STDOUT ) ) { + if ( Utils\isPiped() ) { return; } @@ -1610,6 +1655,18 @@ private function auto_check_update() { // In case the operation fails, ensure the timestamp has been updated. $cache->write( $cache_key, time() ); + // Ask before doing remote request. + $current_command = implode( ' ', $this->arguments ); + $msg = sprintf( + "(Note you can control whether and when to auto-check for updates with the 'WP_CLI_DISABLE_AUTO_CHECK_UPDATE' and 'WP_CLI_AUTO_CHECK_UPDATE_DAYS' environment variables.)\n" . + 'You have version %s. Would you like to check if an update of WP-CLI is available?', WP_CLI_VERSION + ); + if ( ! WP_CLI::ask( $msg ) ) { + WP_CLI::log( sprintf( "Continuing with your current command '%s'...", $current_command ) ); + return; + } + + WP_CLI::log( 'Checking for an update of WP-CLI...' ); // Check whether any updates are available. ob_start(); WP_CLI::run_command( @@ -1624,9 +1681,12 @@ private function auto_check_update() { } // Looks like an update is available, so let's prompt to update. + WP_CLI::log( sprintf( "Update found - invoking 'cli update' (please note that if you decide to update, your current command '%s' will not complete)...", $current_command ) ); + WP_CLI::run_command( array( 'cli', 'update' ) ); - // If the Phar was replaced, we can't proceed with the original process. - exit; + + // If the Phar was replaced, we've exited so don't get here. + WP_CLI::log( sprintf( "Continuing with your current command '%s'...", $current_command ) ); } /** diff --git a/php/class-wp-cli.php b/php/class-wp-cli.php index 18cefd9beb..5e3817c55f 100644 --- a/php/class-wp-cli.php +++ b/php/class-wp-cli.php @@ -510,9 +510,9 @@ public static function add_command( $name, $callable, $args = array() ) { $long_desc = ''; $bits = explode( ' ', $synopsis ); foreach ( $args['synopsis'] as $key => $arg ) { - $long_desc .= $bits[ $key ] . PHP_EOL; + $long_desc .= $bits[ $key ] . "\n"; if ( ! empty( $arg['description'] ) ) { - $long_desc .= ': ' . $arg['description'] . PHP_EOL; + $long_desc .= ': ' . $arg['description'] . "\n"; } $yamlify = array(); foreach ( array( 'default', 'options' ) as $key ) { @@ -522,13 +522,16 @@ public static function add_command( $name, $callable, $args = array() ) { } if ( ! empty( $yamlify ) ) { $long_desc .= Spyc::YAMLDump( $yamlify ); - $long_desc .= '---' . PHP_EOL; + $long_desc .= '---' . "\n"; } - $long_desc .= PHP_EOL; + $long_desc .= "\n"; } if ( ! empty( $long_desc ) ) { - $long_desc = rtrim( $long_desc, PHP_EOL ); - $long_desc = '## OPTIONS' . PHP_EOL . PHP_EOL . $long_desc; + $long_desc = rtrim( $long_desc, "\r\n" ); + $long_desc = '## OPTIONS' . "\n\n" . $long_desc; + if ( ! empty( $args['longdesc'] ) ) { + $long_desc .= "\n\n" . ltrim( $args['longdesc'], "\r\n" ); + } $leaf_command->set_longdesc( $long_desc ); } } @@ -816,15 +819,32 @@ public static function error_multi_line( $message_lines ) { * @param array $assoc_args Skips prompt if 'yes' is provided. */ public static function confirm( $question, $assoc_args = array() ) { + if ( ! self::ask( $question, $assoc_args ) ) { + exit; + } + } + + /** + * Ask for confirmation before doing something. + * + * If 'y' is provided to the question, it returns true. If + * 'n' or any other response is provided to the question, it returns false. + * + * @access public + * @category Input + * + * @param string $question Question to display before the prompt. + * @param array $assoc_args Skips prompt if 'yes' is provided. + */ + public static function ask( $question, $assoc_args = array() ) { if ( ! \WP_CLI\Utils\get_flag_value( $assoc_args, 'yes' ) ) { fwrite( STDOUT, $question . ' [y/n] ' ); $answer = strtolower( trim( fgets( STDIN ) ) ); - if ( 'y' != $answer ) { - exit; - } + return 'y' === $answer; } + return true; } /** diff --git a/php/commands/help.php b/php/commands/help.php index 91b810b347..d9b830c104 100644 --- a/php/commands/help.php +++ b/php/commands/help.php @@ -44,13 +44,16 @@ private static function show_help( $command ) { $out = substr_replace( $out, $subcommands_header, $matches[1][1], strlen( $subcommands ) ); } - $out .= self::parse_reference_links( $command->get_longdesc() ); + if ( $longdesc = self::parse_reference_links( $command->get_longdesc() ) ) { + // Normalize newlines between parts. + $out = rtrim( $out, "\n" ) . "\n\n" . ltrim( $longdesc, "\n" ); + } // definition lists $out = preg_replace_callback( '/([^\n]+)\n: (.+?)(\n\n|$)/s', array( __CLASS__, 'rewrap_param_desc' ), $out ); - // Ensure all non-section headers are indented. - $out = preg_replace( '#^([^\s^\#])#m', "\t$1", $out ); + // Ensure lines with no leading whitespace that aren't section headers are indented. + $out = preg_replace( '/^((?! |\t|##).)/m', "\t$1", $out ); $tab = str_repeat( ' ', 2 ); diff --git a/php/commands/src/CLI_Command.php b/php/commands/src/CLI_Command.php index fa2474fd78..6465e6e613 100644 --- a/php/commands/src/CLI_Command.php +++ b/php/commands/src/CLI_Command.php @@ -130,18 +130,29 @@ public function info( $_, $assoc_args ) { WP_CLI::line( json_encode( $info ) ); } else { - WP_CLI::line( "OS:\t" . $system_os ); - WP_CLI::line( "Shell:\t" . $shell ); - WP_CLI::line( "PHP binary:\t" . $php_bin ); - WP_CLI::line( "PHP version:\t" . PHP_VERSION ); - WP_CLI::line( "php.ini used:\t" . get_cfg_var( 'cfg_file_path' ) ); - WP_CLI::line( "WP-CLI root dir:\t" . WP_CLI_ROOT ); - WP_CLI::line( "WP-CLI vendor dir:\t" . WP_CLI_VENDOR_DIR ); - WP_CLI::line( "WP_CLI phar path:\t" . ( defined( 'WP_CLI_PHAR_PATH' ) ? WP_CLI_PHAR_PATH : '' ) ); - WP_CLI::line( "WP-CLI packages dir:\t" . $packages_dir ); - WP_CLI::line( "WP-CLI global config:\t" . $runner->global_config_path ); - WP_CLI::line( "WP-CLI project config:\t" . $runner->project_config_path ); - WP_CLI::line( "WP-CLI version:\t" . WP_CLI_VERSION ); + + $info = array( + array( 'OS', $system_os ), + array( 'Shell', $shell ), + array( 'PHP binary', $php_bin ), + array( 'PHP version', PHP_VERSION ), + array( 'php.ini used', get_cfg_var( 'cfg_file_path' ) ), + array( 'WP-CLI root dir', WP_CLI_ROOT ), + array( 'WP-CLI vendor dir', WP_CLI_VENDOR_DIR ), + array( 'WP_CLI phar path', ( defined( 'WP_CLI_PHAR_PATH' ) ? WP_CLI_PHAR_PATH : '' ) ), + array( 'WP-CLI packages dir', $packages_dir ), + array( 'WP-CLI global config', $runner->global_config_path ), + array( 'WP-CLI project config', $runner->project_config_path ), + array( 'WP-CLI version', WP_CLI_VERSION ), + ); + + $info_table = new \cli\Table(); + $info_table->setRows( $info ); + $info_table->setRenderer( new \cli\table\Ascii() ); + $lines = array_slice( $info_table->getDisplayLines(), 2 ); + foreach ( $lines as $line ) { + \WP_CLI::line( $line ); + } } } @@ -269,11 +280,15 @@ public function update( $_, $assoc_args ) { } if ( Utils\get_flag_value( $assoc_args, 'nightly' ) ) { - WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to the latest nightly?', WP_CLI_VERSION ), $assoc_args ); + if ( ! WP_CLI::ask( sprintf( 'You have version %s. Would you like to update to the latest nightly?', WP_CLI_VERSION ), $assoc_args ) ) { + return; + } $download_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar'; $md5_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar.md5'; } elseif ( Utils\get_flag_value( $assoc_args, 'stable' ) ) { - WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to the latest stable release?', WP_CLI_VERSION ), $assoc_args ); + if ( ! WP_CLI::ask( sprintf( 'You have version %s. Would you like to update to the latest stable release?', WP_CLI_VERSION ), $assoc_args ) ) { + return; + } $download_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar'; $md5_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar.md5'; } else { @@ -288,7 +303,9 @@ public function update( $_, $assoc_args ) { $newest = $updates[0]; - WP_CLI::confirm( sprintf( 'You have version %s. Would you like to update to %s?', WP_CLI_VERSION, $newest['version'] ), $assoc_args ); + if ( ! WP_CLI::ask( sprintf( 'You have version %s. Would you like to update to %s?', WP_CLI_VERSION, $newest['version'] ), $assoc_args ) ) { + return; + } $download_url = $newest['package_url']; $md5_url = str_replace( '.phar', '.phar.md5', $download_url ); @@ -322,7 +339,7 @@ public function update( $_, $assoc_args ) { $php_binary = Utils\get_php_binary(); $process = WP_CLI\Process::create( "{$php_binary} $temp --info {$allow_root}" ); $result = $process->run(); - if ( 0 !== $result->return_code || false === stripos( $result->stdout, 'WP-CLI version:' ) ) { + if ( 0 !== $result->return_code || false === stripos( $result->stdout, 'WP-CLI version' ) ) { $multi_line = explode( PHP_EOL, $result->stderr ); WP_CLI::error_multi_line( $multi_line ); WP_CLI::error( 'The downloaded PHAR is broken, try running wp cli update again.' ); @@ -350,13 +367,16 @@ class_exists( '\cli\Colors' ); // This autoloads \cli\Colors - after we move the $updated_version = $newest['version']; } WP_CLI::success( sprintf( 'Updated WP-CLI to %s.', $updated_version ) ); + // As the Phar was replaced, we must exit to avoid PHP errors. + exit; } /** * Returns update information. */ private function get_updates( $assoc_args ) { - $url = 'https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100'; + // Get by last updated descending. + $url = 'https://api.github.com/repos/wp-cli/wp-cli/releases?sort=updated&per_page=100'; $options = array( 'timeout' => 30, @@ -369,13 +389,65 @@ private function get_updates( $assoc_args ) { $headers['Authorization'] = 'token ' . $github_token; } - $response = Utils\http_request( 'GET', $url, null, $headers, $options ); + $release_data = array(); + $cache_data = null; - if ( ! $response->success || 200 !== $response->status_code ) { - WP_CLI::error( sprintf( 'Failed to get latest version (HTTP code %d).', $response->status_code ) ); + // See if there's cached data. This is a transient, valid only for `max-age` seconds. + $cache = WP_CLI::get_cache(); + $cache_key = 'github_releases'; + + if ( $cache->has( $cache_key ) && ( $cache_data = unserialize( $cache->read( $cache_key ) ) ) ) { + // Do some basic integrity checking. + if ( ! isset( $cache_data['time'], $cache_data['max_age'], $cache_data['release_data'] ) + || ! is_int( $cache_data['time'] ) || ! is_int( $cache_data['max_age'] ) || ! is_array( $cache_data['release_data'] ) ) { + $cache_data = null; + } elseif ( time() <= $cache_data['time'] + $cache_data['max_age'] ) { + $release_data = $cache_data['release_data']; + } } - $release_data = json_decode( $response->body ); + if ( ! $release_data ) { + // No cached transient. + $max_age = $time = 0; + do { + $response = Utils\http_request( 'GET', $url, null, $headers, $options ); + + if ( ! $response->success || 200 !== (int) $response->status_code ) { + $msg = sprintf( 'Failed to get latest version (HTTP code %d) (%susing GITHUB_TOKEN).', $response->status_code, $github_token ? '' : 'NOT ' ); + if ( 403 === $response->status_code && $cache_data ) { + WP_CLI::warning( $msg . ' - using stale cache data.' ); + $max_age = 0; // Make sure not to write stale data to cache. + $release_data = $cache_data['release_data']; + break; + } + WP_CLI::error( $msg ); + } + + if ( ! $max_age && isset( $response->headers['cache-control'] ) && preg_match( '/max-age=([0-9]+)/', $response->headers['cache-control'], $matches ) ) { + $max_age = (int) $matches[1]; + if ( empty( $response->headers['date'] ) || false === ( $time = Utils\strtotime_gmt( $response->headers['date'] ) ) ) { + $time = time(); + } + } + + // Reduce the data to what's used. + $release_data = array_merge( $release_data, array_map( + function ( $v ) { + return (object) array( + 'tag_name' => $v->tag_name, + 'assets' => array( (object) array( 'browser_download_url' => $v->assets[0]->browser_download_url ) ), + ); + }, json_decode( $response->body ) ) + ); + + // Loop while have paged data ("next" link). + } while ( ! empty( $response->headers['link'] ) && preg_match( '/<([^>]+)>; rel="next"/', $response->headers['link'], $matches ) && ( $url = $matches[1] ) ); + + if ( $max_age ) { + $cache->write( $cache_key, serialize( compact( 'max_age', 'time', 'release_data' ) ) ); + } + } + unset( $cache_data, $response ); $updates = array( 'major' => false, @@ -421,7 +493,7 @@ private function get_updates( $assoc_args ) { if ( empty( $updates ) && preg_match( '#-alpha-(.+)$#', WP_CLI_VERSION, $matches ) ) { $version_url = 'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/NIGHTLY_VERSION'; $response = Utils\http_request( 'GET', $version_url ); - if ( ! $response->success || 200 !== $response->status_code ) { + if ( ! $response->success || 200 !== (int) $response->status_code ) { WP_CLI::error( sprintf( 'Failed to get current nightly version (HTTP code %d)', $response->status_code ) ); } $nightly_version = trim( $response->body ); diff --git a/php/dispatcher.php b/php/dispatcher.php index bd11a3927e..90b8761dc7 100644 --- a/php/dispatcher.php +++ b/php/dispatcher.php @@ -5,8 +5,8 @@ /** * Get the path to a command, e.g. "core download" * - * @param WP_CLI\Dispatcher\Subcommand $command - * @return string + * @param CompositeCommand $command + * @return array */ function get_path( $command ) { $path = array(); diff --git a/php/utils-wp.php b/php/utils-wp.php index b7ec062e9a..96b3aaba37 100644 --- a/php/utils-wp.php +++ b/php/utils-wp.php @@ -277,6 +277,7 @@ function wp_get_cache_type() { * * @access public * @category System + * @deprecated 1.5.0 */ function wp_clear_object_cache() { global $wpdb, $wp_object_cache; @@ -287,13 +288,19 @@ function wp_clear_object_cache() { return; } - $wp_object_cache->group_ops = array(); - $wp_object_cache->stats = array(); - $wp_object_cache->memcache_debug = array(); - $wp_object_cache->cache = array(); - - if ( is_callable( $wp_object_cache, '__remoteset' ) ) { - $wp_object_cache->__remoteset(); // important + // The following are Memcached (Redux) plugin specific (see https://core.trac.wordpress.org/ticket/31463). + if ( isset( $wp_object_cache->group_ops ) ) { + $wp_object_cache->group_ops = array(); + } + if ( isset( $wp_object_cache->stats ) ) { + $wp_object_cache->stats = array(); + } + if ( isset( $wp_object_cache->memcache_debug ) ) { + $wp_object_cache->memcache_debug = array(); + } + // Used by `WP_Object_Cache` also. + if ( isset( $wp_object_cache->cache ) ) { + $wp_object_cache->cache = array(); } } @@ -309,117 +316,58 @@ function wp_clear_object_cache() { function wp_get_table_names( $args, $assoc_args = array() ) { global $wpdb; - // Prioritize any supplied $args as tables - if ( ! empty( $args ) ) { - $new_tables = array(); - $get_tables_for_glob = function( $glob ) { - global $wpdb; - static $all_tables = array(); - if ( ! $all_tables ) { - $all_tables = $wpdb->get_col( 'SHOW TABLES' ); - } - $tables = array(); - foreach ( $all_tables as $table ) { - if ( fnmatch( $glob, $table ) ) { - $tables[] = $table; - } - } - return $tables; - }; - foreach ( $args as $key => $table ) { - if ( false !== strpos( $table, '*' ) || false !== strpos( $table, '?' ) ) { - $expanded_tables = $get_tables_for_glob( $table ); - if ( empty( $expanded_tables ) ) { - \WP_CLI::error( "Couldn't find any tables matching: {$table}" ); - } - $new_tables = array_merge( $new_tables, $expanded_tables ); - } else { - $new_tables[] = $table; - } - } - return $new_tables; - } - - // Fall back to flag if no tables were passed - $table_type = 'WordPress'; - if ( get_flag_value( $assoc_args, 'network' ) ) { - $table_type = 'network'; - } - if ( get_flag_value( $assoc_args, 'all-tables-with-prefix' ) ) { - $table_type = 'all-tables-with-prefix'; - } + $tables = array(); if ( get_flag_value( $assoc_args, 'all-tables' ) ) { - $table_type = 'all-tables'; - } - - $network = 'network' == $table_type; + $tables = $wpdb->get_col( 'SHOW TABLES' ); - if ( 'all-tables' == $table_type ) { - return $wpdb->get_col( 'SHOW TABLES' ); - } + } elseif ( get_flag_value( $assoc_args, 'all-tables-with-prefix' ) ) { + $tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', esc_like( $wpdb->get_blog_prefix() ) . '%' ) ); - $prefix = $network ? $wpdb->base_prefix : $wpdb->prefix; - // '_' is a special wildcard for MySQL LIKE queries - // so it needs to be escaped with '\', but then '\' needs to be escaped as well - $sql_prefix = str_replace( '_', '\\_', $prefix ); - $matching_tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $sql_prefix . '%' ) ); - - if ( 'all-tables-with-prefix' == $table_type ) { - return $matching_tables; - } - - $filter_sitecategories = function( $matching_tables ) { - global $wpdb; - // Only include sitecategories when it's actually enabled. - if ( ! global_terms_enabled() ) { - $key = array_search( $wpdb->sitecategories, $matching_tables ); - if ( false !== $key ) { - unset( $matching_tables[ $key ] ); + } else { + $scope = get_flag_value( $assoc_args, 'scope', 'all' ); + + // Note: BC change 1.5.0, taking scope into consideration for network also. + if ( get_flag_value( $assoc_args, 'network' ) && is_multisite() ) { + $network_global_scope = in_array( $scope, array( 'all', 'global', 'ms_global' ), true ) ? ( 'all' === $scope ? 'global' : $scope ) : ''; + $wp_tables = array_values( $wpdb->tables( $network_global_scope ) ); + if ( in_array( $scope, array( 'all', 'blog' ), true ) ) { + // Do directly for compat with old WP versions. Note: private, deleted, archived sites are not excluded. + $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = $wpdb->siteid" ); + foreach ( $blog_ids as $blog_id ) { + $wp_tables = array_merge( $wp_tables, array_values( $wpdb->tables( 'blog', true /*prefix*/, $blog_id ) ) ); + } } + } else { + $wp_tables = array_values( $wpdb->tables( $scope ) ); } - return $matching_tables; - }; - if ( $scope = get_flag_value( $assoc_args, 'scope' ) ) { - $matching_tables = $wpdb->tables( $scope ); - $matching_tables = $filter_sitecategories( $matching_tables ); - return $matching_tables; - } - - $allowed_tables = array(); - $allowed_table_types = array( 'tables', 'global_tables' ); - if ( $network ) { - $allowed_table_types[] = 'ms_global_tables'; - } - foreach ( $allowed_table_types as $table_type ) { - foreach ( $wpdb->$table_type as $table ) { - $allowed_tables[] = $prefix . $table; + if ( ! global_terms_enabled() ) { + // Only include sitecategories when it's actually enabled. + $wp_tables = array_values( array_diff( $wp_tables, array( $wpdb->sitecategories ) ) ); } - } - // Given our matching tables, also allow site-specific tables on the network - foreach ( $matching_tables as $key => $matched_table ) { - - if ( in_array( $matched_table, $allowed_tables ) ) { - continue; - } + // Note: BC change 1.5.0, tables are sorted (via TABLES view). + // @codingStandardsIgnoreLine + $tables = $wpdb->get_col( sprintf( "SHOW TABLES WHERE %s IN ('%s')", esc_sql_ident( 'Tables_in_' . $wpdb->dbname ), implode( "', '", $wpdb->_escape( $wp_tables ) ) ) ); + } - if ( $network ) { - $valid_table = false; - foreach ( array_merge( $wpdb->tables, $wpdb->old_tables ) as $maybe_site_table ) { - if ( preg_match( "#{$prefix}([\d]+)_{$maybe_site_table}#", $matched_table ) ) { - $valid_table = true; - } - } - if ( $valid_table ) { - continue; + // Filter by `$args`. + if ( $args ) { + $args_tables = array(); + foreach ( $args as $arg ) { + if ( false !== strpos( $arg, '*' ) || false !== strpos( $arg, '?' ) ) { + $args_tables = array_merge( $args_tables, array_filter( $tables, function ( $v ) use ( $arg ) { + return fnmatch( $arg, $v ); + } ) ); + } else { + $args_tables[] = $arg; } } - - unset( $matching_tables[ $key ] ); - + $args_tables = array_values( array_unique( $args_tables ) ); + if ( ! ( $tables = array_values( array_intersect( $tables, $args_tables ) ) ) ) { + \WP_CLI::error( sprintf( "Couldn't find any tables matching: %s", implode( ' ', $args ) ) ); + } } - $filter_sitecategories( $matching_tables ); - return array_values( $matching_tables ); + return $tables; } diff --git a/php/utils.php b/php/utils.php index be27ede7a1..b67a55e1e7 100644 --- a/php/utils.php +++ b/php/utils.php @@ -350,7 +350,8 @@ function pick_fields( $item, $fields ) { * @access public * @category Input * - * @param string $content Some form of text to edit (e.g. post content) + * @param string $input Some form of text to edit (e.g. post content) + * @param string $filename Optional. File name to use as base for temporary file. Default 'WP-CLI'. * @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' ) { @@ -362,7 +363,7 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) { do { $tmpfile = basename( $filename ); $tmpfile = preg_replace( '|\.[^.]*$|', '', $tmpfile ); - $tmpfile .= '-' . substr( md5( mt_rand() ), 0, 6 ); + $tmpfile .= '-' . substr( md5( (string) mt_rand() ), 0, 6 ); $tmpfile = $tmpdir . $tmpfile . '.tmp'; $fp = fopen( $tmpfile, 'xb' ); if ( ! $fp && is_writable( $tmpdir ) && file_exists( $tmpfile ) ) { @@ -772,6 +773,30 @@ function trailingslashit( $string ) { return rtrim( $string, '/\\' ) . '/'; } +/** + * Normalizes directory separators to slashes. + * + * @access public + * @category System + * + * @param string $path Path to convert. + * + * @return string Path with all backslashes replaced by slashes. + */ +function normalize_directory_separators( $path ) { + return str_replace( '\\', '/', $path ); +} + +/** + * Convert Windows EOLs to *nix. + * + * @param string $str String to convert. + * @return string String with carriage return / newline pairs reduced to newlines. + */ +function normalize_eols( $str ) { + return str_replace( "\r\n", "\n", $str ); +} + /** * Get the system's temp directory. Warns user if it isn't writable. * @@ -788,7 +813,7 @@ function get_temp_dir() { } // `sys_get_temp_dir()` introduced PHP 5.2.1. Will always return something. - $temp = trailingslashit( sys_get_temp_dir() ); + $temp = trailingslashit( normalize_directory_separators( sys_get_temp_dir() ) ); if ( ! is_writable( $temp ) ) { \WP_CLI::warning( "Temp directory isn't writable: {$temp}" ); @@ -1101,7 +1126,9 @@ function glob_brace( $pattern, $dummy_flags = null ) { function get_suggestion( $target, array $options, $threshold = 2 ) { $suggestion_map = array( + 'add' => 'create', 'check' => 'check-update', + 'capability' => 'cap', 'clear' => 'flush', 'decrement' => 'decr', 'del' => 'delete', @@ -1117,6 +1144,7 @@ function get_suggestion( $target, array $options, $threshold = 2 ) { 'regen' => 'regenerate', 'rep' => 'replace', 'repl' => 'replace', + 'trash' => 'delete', 'v' => 'version', ); @@ -1127,6 +1155,7 @@ function get_suggestion( $target, array $options, $threshold = 2 ) { if ( empty( $options ) ) { return ''; } + $levenshtein = array(); foreach ( $options as $option ) { $distance = levenshtein( $option, $target ); $levenshtein[ $option ] = $distance; @@ -1247,6 +1276,30 @@ function check_proc_available( $context = null, $return = false ) { return true; } +/** + * Wrapper around strtotime() that always interprets the string with a default timezone of UTC. + * + * @param string $str A date/time string. + * @param int $now Optional. The timestamp which is used as a base for the calculation of relative dates. + * + * @return int|bool Returns a timestamp on success, false otherwise. + */ +function strtotime_gmt( $str, $now = null ) { + $get = date_default_timezone_get(); + if ( 'UTC' !== $get ) { + date_default_timezone_set( 'UTC' ); + } + if ( null === $now ) { + $ret = strtotime( $str ); + } else { + $ret = strtotime( $str, $now ); + } + if ( 'UTC' !== $get ) { + date_default_timezone_set( $get ); + } + return $ret; +} + /** * Returns past tense of verb, with limited accuracy. Only regular verbs catered for, apart from "reset". * @@ -1323,12 +1376,21 @@ function get_php_binary() { * @param array $env Array of environment variables. * @param array $other_options Array of additional options (Windows only). * - * @return string Command stripped of any environment variable settings. + * @return resource|false Returns resource representing the process on success; false on failure. */ function proc_open_compat( $cmd, $descriptorspec, &$pipes, $cwd = null, $env = null, $other_options = null ) { if ( is_windows() ) { // Need to encompass the whole command in double quotes - PHP bug https://bugs.php.net/bug.php?id=49139 $cmd = '"' . _proc_open_compat_win_env( $cmd, $env ) . '"'; + // If environment array set, make sure certain entries are set otherwise can cause Windows `getaddrinfo()` and `mysqli_connect()` to crap out. + if ( null !== $env ) { + $env_names = array( 'HOME', 'PATH', 'SYSTEMROOT', 'TERM', 'TMP' ); // Probably only SYSTEMROOT and TMP are needed, but the others can't hurt. + foreach ( $env_names as $env_name ) { + if ( ! isset( $env[ $env_name ] ) && ( $env_val = getenv( $env_name ) ) ) { + $env[ $env_name ] = $env_val; + } + } + } } return proc_open( $cmd, $descriptorspec, $pipes, $cwd, $env, $other_options ); } @@ -1375,6 +1437,24 @@ function esc_like( $text ) { return addcslashes( $text, '_%\\' ); } +/** + * Escapes (backticks) MySQL identifiers (aka schema object names) - i.e. column names, table names, and database/index/alias/view etc names. + * See https://dev.mysql.com/doc/refman/5.5/en/identifiers.html + * + * @param string|array $idents A single identifier or an array of identifiers. + * @return string|array An escaped string if given a string, or an array of escaped strings if given an array of strings. + */ +function esc_sql_ident( $idents ) { + $backtick = function ( $v ) { + // Escape any backticks in the identifier by doubling. + return '`' . str_replace( '`', '``', $v ) . '`'; + }; + if ( is_string( $idents ) ) { + return $backtick( $idents ); + } + return array_map( $backtick, $idents ); +} + /** * Check whether a given string is a valid JSON representation. * @@ -1385,7 +1465,7 @@ function esc_like( $text ) { * @return bool Whether the provided string is a valid JSON representation. */ function is_json( $argument, $ignore_scalars = true ) { - if ( empty( $argument ) || ! is_string( $argument ) ) { + if ( ! is_string( $argument ) || '' === $argument ) { return false; } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index e34339ca53..8d83f89d54 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -18,21 +18,23 @@ */tests/* */utils/* */vendor/* + */.phan/* - - - - - - - + + + + + + + + diff --git a/templates/man.mustache b/templates/man.mustache index fbb2e9bd6b..b04c5ffb6f 100644 --- a/templates/man.mustache +++ b/templates/man.mustache @@ -24,5 +24,4 @@ {{#subcommands}} {{.}} {{/subcommands}} - {{/has-subcommands}} diff --git a/tests/test-behat-tags.php b/tests/test-behat-tags.php index 11ea159de2..159226e11c 100644 --- a/tests/test-behat-tags.php +++ b/tests/test-behat-tags.php @@ -121,8 +121,8 @@ function test_behat_tags_extension() { if ( ! extension_loaded( 'imagick' ) ) { $expecteds[] = '~@require-extension-imagick'; } - if ( ! extension_loaded( 'intl' ) ) { - $expecteds[] = '~@require-extension-intl'; + if ( ! extension_loaded( 'curl' ) ) { + $expecteds[] = '~@require-extension-curl'; } $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) ); $output = exec( "cd {$this->temp_dir}; php $behat_tags" ); diff --git a/tests/test-doc-parser.php b/tests/test-doc-parser.php index 7fdf261c06..a751189f35 100644 --- a/tests/test-doc-parser.php +++ b/tests/test-doc-parser.php @@ -40,6 +40,7 @@ function test_no_longdesc() { ); $this->assertEquals( 'Rock and roll!', $doc->get_shortdesc() ); + error_log( __FUNCTION__ . " longdesc=" . $doc->get_longdesc() . "" ); $this->assertEquals( '', $doc->get_longdesc() ); $this->assertEquals( '', $doc->get_synopsis() ); $this->assertEquals( 'rock-on', $doc->get_tag('alias') ); diff --git a/tests/test-extractor.php b/tests/test-extractor.php index 330b993902..209fc419c0 100644 --- a/tests/test-extractor.php +++ b/tests/test-extractor.php @@ -7,20 +7,23 @@ class Extractor_Test extends PHPUnit_Framework_TestCase { static $copy_overwrite_files_prefix = 'wp-cli-test-utils-copy-overwrite-files-'; - static $expected_wp = array( - 'index1.php', - 'license2.php', - 'wp-admin/', - 'wp-admin/about3.php', - 'wp-admin/includes/', - 'wp-admin/includes/file4.php', - 'wp-admin/widgets5.php', - 'wp-config6.php', - 'wp-includes/', - 'wp-includes/file7.php', - 'xmlrpc8.php', + static $expected_top = array( + 'wordpress/', + 'wordpress/index1.php', + 'wordpress/license2.php', + 'wordpress/wp-admin/', + 'wordpress/wp-admin/about3.php', + 'wordpress/wp-admin/includes/', + 'wordpress/wp-admin/includes/file4.php', + 'wordpress/wp-admin/widgets5.php', + 'wordpress/wp-config6.php', + 'wordpress/wp-includes/', + 'wordpress/wp-includes/file7.php', + 'wordpress/xmlrpc8.php', ); + static $expected_wp = null; + static $logger = null; static $prev_logger = null; @@ -35,6 +38,13 @@ public function setUp() { self::$logger = new \WP_CLI\Loggers\Execution; WP_CLI::set_logger( self::$logger ); + // Init expected_wp. + if ( null === self::$expected_wp ) { + self::$expected_wp = array_values( array_filter( array_map( function ( $v ) { + return substr( $v, strlen( 'wordpress/' ) ); + }, self::$expected_top ) ) ); + } + // Remove any failed tests detritus. $temp_dirs = Utils\get_temp_dir() . self::$copy_overwrite_files_prefix . '*'; foreach ( glob( $temp_dirs ) as $temp_dir ) { @@ -55,10 +65,6 @@ public function test_rmdir() { $this->assertTrue( is_dir( $wp_dir ) ); Extractor::rmdir( $wp_dir ); $this->assertFalse( file_exists( $wp_dir ) ); - - $this->assertTrue( is_dir( $temp_dir ) ); - Extractor::rmdir( $temp_dir ); - $this->assertFalse( file_exists( $temp_dir ) ); } public function test_err_rmdir() { @@ -77,12 +83,80 @@ public function test_copy_overwrite_files() { $dest_dir = $temp_dir . '/dest'; + // Top level src dir, no strip_components. + + Extractor::copy_overwrite_files( $src_dir, $dest_dir ); + + $files = self::recursive_scandir( $dest_dir ); + + $this->assertSame( self::$expected_top, $files ); + $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); + + // Wordpress dir, no strip_components. + Extractor::copy_overwrite_files( $wp_dir, $dest_dir ); $files = self::recursive_scandir( $dest_dir ); $this->assertSame( self::$expected_wp, $files ); $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); + + // Top level src dir, strip_components 1. + + Extractor::copy_overwrite_files( $src_dir, $dest_dir, 1 /*strip_components*/ ); + + $files = self::recursive_scandir( $dest_dir ); + + $this->assertSame( self::$expected_wp, $files ); + $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); + + // Wordpress dir, strip_components 1. + + Extractor::copy_overwrite_files( $wp_dir, $dest_dir, 1 /*strip_components*/ ); + + $files = self::recursive_scandir( $dest_dir ); + + $expected = array( + 'about3.php', + 'file7.php', + 'includes/', + 'includes/file4.php', + 'widgets5.php', + ); + $this->assertSame( $expected, $files ); + + // Top level src dir, strip_components 2 (same as Wordpress dir, strip_components 1). + + Extractor::copy_overwrite_files( $src_dir, $dest_dir, 2 /*strip_components*/ ); + + $files = self::recursive_scandir( $dest_dir ); + + $expected = array( + 'about3.php', + 'file7.php', + 'includes/', + 'includes/file4.php', + 'widgets5.php', + ); + $this->assertSame( $expected, $files ); + $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); + + // Top level src dir, strip_components 3. + + Extractor::copy_overwrite_files( $src_dir, $dest_dir, 3 /*strip_components*/ ); + + $files = self::recursive_scandir( $dest_dir ); + + $expected = array( + 'file4.php', + ); + $this->assertSame( $expected, $files ); + $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); // Clean up. Extractor::rmdir( $temp_dir ); @@ -104,6 +178,8 @@ public function test_extract_tarball() { $this->markTestSkipped( 'tar not installed.' ); } + $extractor_shell = getenv( 'WP_CLI_TEST_EXTRACTOR_SHELL' ); + list( $temp_dir, $src_dir, $wp_dir ) = self::create_test_directory_structure(); $tarball = $temp_dir . '/test.tar.gz'; @@ -112,25 +188,55 @@ public function test_extract_tarball() { // Create test tarball. $output = array(); $return_var = -1; - // Need --force-local for Windows to avoid "C:" being interpreted as being on remote machine. - exec( Utils\esc_cmd( 'tar czvf %1$s --force-local --directory=%2$s/src wordpress', $tarball, $temp_dir ), $output, $return_var ); + // Need --force-local for Windows to avoid "C:" being interpreted as being on remote machine, and redirect for Mac as outputs verbosely on STDERR. + $cmd = 'tar czvf %1$s' . ( Utils\is_windows() ? ' --force-local' : '' ) . ' --directory=%2$s/src wordpress 2>&1'; + exec( Utils\esc_cmd( $cmd, $tarball, $temp_dir ), $output, $return_var ); $this->assertSame( 0, $return_var ); $this->assertFalse( empty( $output ) ); + + // Normalize (Mac) output. + $output = array_map( function ( $v ) { + if ( 'a ' === substr( $v, 0, 2 ) ) { + $v = substr( $v, 2 ); + } + if ( '/' !== substr( $v, -1 ) && false === strpos( $v, '.' ) ) { + $v .= '/'; + } + return $v; + }, $output ); sort( $output ); + $this->assertSame( self::recursive_scandir( $src_dir ), $output ); // Test. + putenv( 'WP_CLI_TEST_EXTRACTOR_SHELL' ); + Extractor::extract( $tarball, $dest_dir ); + + $files = self::recursive_scandir( $dest_dir ); + $this->assertSame( self::$expected_wp, $files ); + $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); + + self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. + + putenv( 'WP_CLI_TEST_EXTRACTOR_SHELL=1' ); Extractor::extract( $tarball, $dest_dir ); $files = self::recursive_scandir( $dest_dir ); $this->assertSame( self::$expected_wp, $files ); $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); // Clean up. Extractor::rmdir( $temp_dir ); + + putenv( false === $extractor_shell ? 'WP_CLI_TEST_EXTRACTOR_SHELL' : "WP_CLI_TEST_EXTRACTOR_SHELL=$extractor_shell" ); } public function test_err_extract_tarball() { + $extractor_shell = getenv( 'WP_CLI_TEST_EXTRACTOR_SHELL' ); + + putenv( 'WP_CLI_TEST_EXTRACTOR_SHELL' ); // Non-existent. $msg = ''; try { @@ -139,7 +245,7 @@ public function test_err_extract_tarball() { $msg = $e->getMessage(); } $this->assertTrue( false !== strpos( $msg, 'no-such-tar' ) ); - $this->assertTrue( 0 === strpos( self::$logger->stderr, 'Warning: PharData failed' ) ); + $this->assertTrue( 0 === strpos( self::$logger->stderr, "Warning: Falling back to 'tar xz'. PharData failed" ) ); $this->assertTrue( false !== strpos( self::$logger->stderr, 'no-such-tar' ) ); self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. @@ -155,8 +261,38 @@ public function test_err_extract_tarball() { } unlink( $zero_tar ); $this->assertTrue( false !== strpos( $msg, 'zero-tar' ) ); - $this->assertTrue( 0 === strpos( self::$logger->stderr, 'Warning: PharData failed' ) ); + $this->assertTrue( 0 === strpos( self::$logger->stderr, "Warning: Falling back to 'tar xz'. PharData failed" ) ); $this->assertTrue( false !== strpos( self::$logger->stderr, 'zero-tar' ) ); + + self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. + + putenv( 'WP_CLI_TEST_EXTRACTOR_SHELL=1' ); + // Non-existent. + $msg = ''; + try { + Extractor::extract( 'no-such-tar.tar.gz', 'dest-dir' ); + } catch ( \Exception $e ) { + $msg = $e->getMessage(); + } + $this->assertTrue( false !== strpos( $msg, 'no-such-tar' ) ); + $this->assertTrue( empty( self::$logger->stderr ) ); + + self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. + + // Zero-length. + $zero_tar = Utils\get_temp_dir() . 'zero-tar.tar.gz'; + touch( $zero_tar ); + $msg = ''; + try { + Extractor::extract( $zero_tar, 'dest-dir' ); + } catch ( \Exception $e ) { + $msg = $e->getMessage(); + } + unlink( $zero_tar ); + $this->assertTrue( false !== strpos( $msg, 'zero-tar' ) ); + $this->assertTrue( empty( self::$logger->stderr ) ); + + putenv( false === $extractor_shell ? 'WP_CLI_TEST_EXTRACTOR_SHELL' : "WP_CLI_TEST_EXTRACTOR_SHELL=$extractor_shell" ); } public function test_extract_zip() { @@ -164,6 +300,8 @@ public function test_extract_zip() { $this->markTestSkipped( 'ZipArchive not installed.' ); } + $extractor_zip_archive = getenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' ); + list( $temp_dir, $src_dir, $wp_dir ) = self::create_test_directory_structure(); $zipfile = $temp_dir . '/test.zip'; @@ -186,14 +324,25 @@ public function test_extract_zip() { $this->assertTrue( $result ); // Test. + putenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' ); Extractor::extract( $zipfile, $dest_dir ); $files = self::recursive_scandir( $dest_dir ); $this->assertSame( self::$expected_wp, $files ); $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); + + putenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE=1' ); + Extractor::extract( $zipfile, $dest_dir ); + + $files = self::recursive_scandir( $dest_dir ); + $this->assertSame( self::$expected_wp, $files ); + $this->assertTrue( empty( self::$logger->stderr ) ); + Extractor::rmdir( $dest_dir ); // Clean up. Extractor::rmdir( $temp_dir ); + putenv( false === $extractor_zip_archive ? 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' : "WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE=$extractor_zip_archive" ); } public function test_err_extract_zip() { @@ -201,6 +350,41 @@ public function test_err_extract_zip() { $this->markTestSkipped( 'ZipArchive not installed.' ); } + $extractor_zip_archive = getenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' ); + + putenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' ); + // Non-existent. + $msg = ''; + try { + Extractor::extract( 'no-such-zip.zip', 'dest-dir' ); + } catch ( \Exception $e ) { + $msg = $e->getMessage(); + } + $this->assertTrue( false !== strpos( $msg, 'no-such-zip' ) ); + $this->assertFalse( empty( self::$logger->stderr ) ); + + self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. + + // Zero-length - NO error surprisingly with PharData. + $zero_zip = Utils\get_temp_dir() . 'zero-zip.zip'; + $dest_dir = Utils\get_temp_dir() . 'dest-dir'; + touch( $zero_zip ); + $msg = ''; + try { + Extractor::extract( $zero_zip, $dest_dir ); + } catch ( \Exception $e ) { + $msg = $e->getMessage(); + } + unlink( $zero_zip ); + $this->assertTrue( '' === $msg ); + $this->assertFalse( empty( self::$logger->stderr ) ); + $this->assertTrue( is_dir( $dest_dir ) ); + $this->assertSame( array(), self::recursive_scandir( $dest_dir ) ); + rmdir( $dest_dir ); + + self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. + + putenv( 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE=1' ); // Non-existent. $msg = ''; try { @@ -213,18 +397,24 @@ public function test_err_extract_zip() { self::$logger->stderr = self::$logger->stdout = ''; // Reset logger. - // Zero-length. + // Zero-length - No error surprisingly with ZipArchive either. $zero_zip = Utils\get_temp_dir() . 'zero-zip.zip'; + $dest_dir = Utils\get_temp_dir() . 'dest-dir'; touch( $zero_zip ); $msg = ''; try { - Extractor::extract( $zero_zip, 'dest-dir' ); + Extractor::extract( $zero_zip, $dest_dir ); } catch ( \Exception $e ) { $msg = $e->getMessage(); } unlink( $zero_zip ); - $this->assertTrue( false !== strpos( $msg, 'zero-zip' ) ); + $this->assertTrue( '' === $msg ); $this->assertTrue( empty( self::$logger->stderr ) ); + $this->assertTrue( is_dir( $dest_dir ) ); + $this->assertSame( array(), self::recursive_scandir( $dest_dir ) ); + rmdir( $dest_dir ); + + putenv( false === $extractor_zip_archive ? 'WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE' : "WP_CLI_TEST_EXTRACTOR_ZIP_ARCHIVE=$extractor_zip_archive" ); } public function test_err_extract() { @@ -246,13 +436,12 @@ private function create_test_directory_structure() { mkdir( $src_dir ); $wp_dir = $src_dir . '/wordpress'; - mkdir( $wp_dir ); - foreach ( self::$expected_wp as $file ) { + foreach ( self::$expected_top as $file ) { if ( 0 === substr_compare( $file, '/', -1 ) ) { - mkdir( $wp_dir . '/' . $file ); + mkdir( $src_dir . '/' . $file ); } else { - touch( $wp_dir . '/' . $file ); + touch( $src_dir . '/' . $file ); } } diff --git a/tests/test-file-cache.php b/tests/test-file-cache.php index 1ab58e6fbf..bf1c0128ec 100644 --- a/tests/test-file-cache.php +++ b/tests/test-file-cache.php @@ -2,11 +2,50 @@ use WP_CLI\FileCache; use WP_CLI\Utils; +use Symfony\Component\Finder\Finder; require_once dirname( __DIR__ ) . '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/php/class-wp-cli.php'; class FileCacheTest extends PHPUnit_Framework_TestCase { + /** + * Test that no new classes are loaded in clean() as this can cause problems if it's called in a register_shutdown_function. + */ + public function testFinderLoaded() { + $max_size = 32; + $ttl = 60; + + $cache_dir = Utils\get_temp_dir() . '/' . uniqid( "wp-cli-test-file-cache", TRUE ); + + $cache = new FileCache( $cache_dir, $ttl, $max_size ); + + $after_construct_classes = get_declared_classes(); + + // Less than time to live file. + $cache->write( 'ttl', 'ttl' ); + touch( $cache_dir . '/ttl', time() - ( $ttl + 1 ) ); + + // Greater than max size file. + $cache->write( 'max_size', str_repeat( 'm', $max_size + 1 ) ); + + // Check no change in loaded classes. + $after_write_classes = get_declared_classes(); + $after_write_diff = array_diff( $after_write_classes, $after_construct_classes ); + $this->assertEmpty( $after_write_diff ); + + $cache->clean(); + + // Should be no change in loaded classes. + $after_clean_classes = get_declared_classes(); + $after_clean_diff = array_diff( $after_clean_classes, $after_write_classes ); + $this->assertEmpty( $after_clean_diff ); + + $this->assertFalse( file_exists( $cache_dir . '/max_size' ) ); + $this->assertFalse( file_exists( $cache_dir . '/ttl' ) ); + + rmdir( $cache_dir ); + } + /** * Test get_root() deals with backslashed directory. */ diff --git a/tests/test-make-phar-strip-comments.php b/tests/test-make-phar-strip-comments.php new file mode 100644 index 0000000000..094d7317bc --- /dev/null +++ b/tests/test-make-phar-strip-comments.php @@ -0,0 +1,198 @@ +assertSame( $source, $actual ); + $this->assertSame( substr_count( $actual, "\n" ), substr_count( $source, "\n" ) ); + $expected = <<<'EOD' +assertSame( $expected, $actual ); + $this->assertSame( substr_count( $actual, "\n" ), substr_count( $source, "\n" ) ); + + } + + /** + * Test inline comment behaviour. + */ + function testInlineComments() { + + // Doc comment and inline comments. + $source = <<<'EOD' +assertSame( $expected, $actual ); + $this->assertSame( substr_count( $actual, "\n" ), substr_count( $source, "\n" ) ); + + $expected = <<<'EOD' +assertSame( $expected, $actual ); + $this->assertSame( substr_count( $actual, "\n" ), substr_count( $source, "\n" ) ); + } + + /** + * Test keeps copyright comments. + */ + function testCopyright() { + + // Doc comment and inline copyrights. + $source = <<<'EOD' +assertSame( $expected, $actual ); + + $expected = <<<'EOD' +assertSame( $expected, $actual ); + } + + /** + * Test keeps Behat comments. + */ + public function testBehat() { + + $source = <<<'EOD' +assertSame( $expected, $actual ); + $this->assertSame( substr_count( $actual, "\n" ), substr_count( $source, "\n" ) ); + } + +} diff --git a/tests/test-utils.php b/tests/test-utils.php index 52d7cf4f68..6e4bb19cf9 100644 --- a/tests/test-utils.php +++ b/tests/test-utils.php @@ -319,6 +319,17 @@ public function testGetHomeDir() { putenv( false === $homepath ? 'HOMEPATH' : "HOME=$homepath" ); } + public function testStrtotimeGmt() { + $get = date_default_timezone_get(); + + $date = 'Fri, 30 May 2008 04:20:12'; + + date_default_timezone_set( 'America/Godthab' ); // -2h in northern summer. + $this->assertSame( strtotime( $date ) - 2 * 60 * 60, Utils\strtotime_gmt( $date ) ); + + date_default_timezone_set( $get ); + } + public function testTrailingslashit() { $this->assertSame( 'a/', Utils\trailingslashit( 'a' ) ); $this->assertSame( 'a/', Utils\trailingslashit( 'a/' ) ); @@ -326,30 +337,12 @@ public function testTrailingslashit() { $this->assertSame( 'a/', Utils\trailingslashit( 'a\\//\\' ) ); } + public function testNormalizeEols() { + $this->assertSame( "\na\ra\na\n", Utils\normalize_eols( "\r\na\ra\r\na\r\n" ) ); + } + public function testGetTempDir() { $this->assertTrue( '/' === substr( Utils\get_temp_dir(), -1 ) ); - - // INI directive `sys_temp_dir` introduced PHP 5.5.0. - if ( version_compare( PHP_VERSION, '5.5.0', '>=' ) ) { - - // `sys_temp_dir` set to unwritable. - - $cmd = 'php ' . escapeshellarg( '-dsys_temp_dir=\\tmp\\' ) . ' php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'echo WP_CLI\\Utils\\get_temp_dir();' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, 'Warning' ) ); - $this->assertTrue( false !== strpos( $output, 'writable' ) ); - $this->assertTrue( false !== strpos( $output, '\\tmp/' ) ); - - // `sys_temp_dir` unset. - - $cmd = 'php ' . escapeshellarg( '-dsys_temp_dir=' ) . ' php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'echo WP_CLI\\Utils\\get_temp_dir();' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( '/' === substr( $output, -1 ) ); - } } public function testHttpRequestBadAddress() { @@ -447,38 +440,6 @@ public function testHttpRequestBadCAcert() { $class_wp_cli_logger->setValue( $prev_logger ); } - public function testRunMysqlCommandProcDisabled() { - $err_msg = 'Error: Cannot do \'run_mysql_command\': The PHP functions `proc_open()` and/or `proc_close()` are disabled'; - - $cmd = 'php -ddisable_functions=proc_open php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'WP_CLI\\Utils\\run_mysql_command( null, array() );' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, $err_msg ) ); - - $cmd = 'php -ddisable_functions=proc_close php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'WP_CLI\\Utils\\run_mysql_command( null, array() );' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, $err_msg ) ); - } - - public function testLaunchEditorForInputProcDisabled() { - $err_msg = 'Error: Cannot do \'launch_editor_for_input\': The PHP functions `proc_open()` and/or `proc_close()` are disabled'; - - $cmd = 'php -ddisable_functions=proc_open php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'WP_CLI\\Utils\\launch_editor_for_input( null, null );' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, $err_msg ) ); - - $cmd = 'php -ddisable_functions=proc_close php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'WP_CLI\\Utils\\launch_editor_for_input( null, null );' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, $err_msg ) ); - } - /** * @dataProvider dataPastTenseVerb */ @@ -524,13 +485,16 @@ public function testExpandGlobs( $path, $expected ) { $dir = __DIR__ . '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/data/expand_globs/'; $expected = array_map( function ( $v ) use ( $dir ) { return $dir . $v; }, $expected ); + sort( $expected ); putenv( 'WP_CLI_TEST_EXPAND_GLOBS_NO_GLOB_BRACE=0' ); $out = Utils\expand_globs( $dir . $path ); + sort( $out ); $this->assertSame( $expected, $out ); putenv( 'WP_CLI_TEST_EXPAND_GLOBS_NO_GLOB_BRACE=1' ); $out = Utils\expand_globs( $dir . $path ); + sort( $out ); $this->assertSame( $expected, $out ); putenv( false === $expand_globs_no_glob_brace ? 'WP_CLI_TEST_EXPAND_GLOBS_NO_GLOB_BRACE' : "WP_CLI_TEST_EXPAND_GLOBS_NO_GLOB_BRACE=$expand_globs_no_glob_brace" ); @@ -707,6 +671,10 @@ public function dataIsJson() { array( '{"key1":"value1","key2":"value2"}', false, true ), array( '["value1","value2"]', true, true ), array( '["value1","value2"]', false, true ), + array( '0', true, false ), + array( '0', false, true ), + array( '', true, false ), + array( '', false, false ), ); } diff --git a/tests/test-wp-cli.php b/tests/test-wp-cli.php index 209fef8c73..a2a68f83bd 100644 --- a/tests/test-wp-cli.php +++ b/tests/test-wp-cli.php @@ -2,22 +2,6 @@ class WP_CLI_Test extends PHPUnit_Framework_TestCase { - public function testLaunchProcDisabled() { - $err_msg = 'Error: Cannot do \'launch\': The PHP functions `proc_open()` and/or `proc_close()` are disabled'; - - $cmd = 'php -ddisable_functions=proc_open php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'WP_CLI::launch( null );' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, $err_msg ) ); - - $cmd = 'php -ddisable_functions=proc_close php/boot-fs.php --skip-wordpress eval ' . escapeshellarg( 'WP_CLI::launch( null );' ) . ' 2>&1'; - $output = array(); - exec( $cmd, $output ); - $output = trim( implode( "\n", $output ) ); - $this->assertTrue( false !== strpos( $output, $err_msg ) ); - } - public function testGetPHPBinary() { $this->assertSame( WP_CLI\Utils\get_php_binary(), WP_CLI::get_php_binary() ); } diff --git a/utils/contrib-list.php b/utils/contrib-list.php index 5ab696c54e..3abd62de4c 100644 --- a/utils/contrib-list.php +++ b/utils/contrib-list.php @@ -149,7 +149,7 @@ private static function get_project_milestone_pull_requests( $project, $mileston $request_url = false; // Set $request_url to 'rel="next" if present' if ( ! empty( $headers['Link'] ) ) { - $bits = explode( ',', $headers['Link'] ); + $bits = trim( explode( ',', $headers['Link'] ) ); foreach( $bits as $bit ) { if ( false !== stripos( $bit, 'rel="next"' ) ) { $hrefandrel = explode( '; ', $bit ); diff --git a/utils/git-setup-pre-commit-hook b/utils/git-setup-pre-commit-hook new file mode 100755 index 0000000000..70c1dfedc8 --- /dev/null +++ b/utils/git-setup-pre-commit-hook @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Setup Git pre-commit hook. +# + +# stop when composer.json file is not found +if [[ ! -f "composer.json" ]]; then + echo "unable to locate composer.json file." + exit 1 +fi + +# determine the phpcs binary location +if [[ -f "vendor/bin/phpcs" ]]; then + phpcs_bin="./vendor/bin/phpcs" +elif [[ -n $COMPOSER_BIN_DIR && -f "$COMPOSER_BIN_DIR/phpcs" ]]; then + phpcs_bin="./$COMPOSER_BIN_DIR/phpcs" +elif [[ -n $COMPOSER_VENDOR_DIR && -f "$COMPOSER_VENDOR_DIR/bin/phpcs" ]]; then + phpcs_bin="./$COMPOSER_VENDOR_DIR/bin/phpcs" +else + echo "pre-commit hook: unable to locate phpcs binary file." + exit +fi + +# create the pre-commit file or notify to add command if it already exists +pre_commit_file="$(git rev-parse --git-dir)/hooks/pre-commit" + +if [[ ! -f "$pre_commit_file" ]]; then + echo -e "#!/bin/bash\nset -ex\n$phpcs_bin" > $pre_commit_file + chmod +x $pre_commit_file +elif ! grep -q "phpcs" $pre_commit_file; then + echo -e "hook file already exists: $pre_commit_file\n\nplease add the below command to your pre-commit file:\n\n$phpcs_bin" +fi diff --git a/utils/make-phar-strip-comments.php b/utils/make-phar-strip-comments.php new file mode 100644 index 0000000000..beb9ebd686 --- /dev/null +++ b/utils/make-phar-strip-comments.php @@ -0,0 +1,32 @@ +parse_args( array_slice( $GLOBALS['argv'], 1 ) ); if ( ! isset( $args[0] ) || empty( $args[0] ) ) { - echo "usage: php -dphar.readonly=0 $argv[0] [--quiet] [--version=same|patch|minor|major|x.y.z] [--store-version]\n"; + fwrite( STDERR, "usage: php -dphar.readonly=0 $argv[0] [--quiet] [--version=same|patch|minor|major|x.y.z] [--store-version] [--build=cli]" . PHP_EOL ); exit(1); } @@ -50,8 +51,29 @@ function add_file( $phar, $path ) { $key = str_replace( WP_CLI_BASE_PATH, '', $path ); - if ( !BE_QUIET ) - echo "$key - $path\n"; + if ( ! BE_QUIET ) { + echo "$key - $path" . PHP_EOL; + } + + // Warning: needs to be synced with ScaffoldPackageCommand::package_tests() `$copy_source[ $wp_cli_root ]`. + static $scaffold_package_phps = array( + 'features/bootstrap/FeatureContext.php' => true, + 'features/bootstrap/support.php' => true, + 'php/WP_CLI/Process.php' => true, + 'php/WP_CLI/ProcessRun.php' => true, + 'php/utils.php' => true, + 'ci/behat-tags.php' => true, + 'features/steps/given.php' => true, + 'features/steps/when.php' => true, + 'features/steps/then.php' => true, + 'features/extra/no-mail.php' => true, + ); + + $file_contents = file_get_contents( $path ); + if ( 0 === substr_compare( $path, '.php', -4 ) && ! isset( $scaffold_package_phps[ substr( $key, 1 ) ] ) ) { // `substr()` to remove initial slash. + $is_wp_cli_command = preg_match( '/\/(?:php\/commands|[^\/]+?-command\/src)\//', $path ); + $file_contents = make_phar_strip_comments( $file_contents, $is_wp_cli_command /*keep_doc_comments*/ ); + } $basename = basename( $path ); if ( 0 === strpos( $basename, 'autoload_' ) && preg_match( '/(?:classmap|files|namespaces|psr4|static)\.php$/', $basename ) ) { @@ -60,11 +82,11 @@ function add_file( $phar, $path ) { if ( null === $strip_res ) { if ( 'cli' === BUILD ) { $strips = array( - '\/(?:behat|composer|gherkin)\/src', + '\/(?:behat|composer|gherkin)\/src\/', '\/phpunit\/', - '\/nb\/oxymel', + '\/nb\/oxymel\/', '-command\/src\/', - '\/wp-cli\/[^\n]+-command\/', + '\/wp-cli\/(?:[^\n]+?-command|wp-config-transformer)\/', '\/symfony\/(?!finder|polyfill-mbstring)[^\/]+\/', '\/(?:dealerdirect|squizlabs|wimg)\/', ); @@ -82,21 +104,69 @@ function add_file( $phar, $path ) { return '/^[^,\n]+?' . $v . '[^,\n]+?, *\n/m'; }, $strips ); } - $phar[ $key ] = preg_replace( $strip_res, '', file_get_contents( $path ) ); + $phar[ $key ] = preg_replace( $strip_res, '', $file_contents ); } else { - $phar[ $key ] = file_get_contents( $path ); + $phar[ $key ] = $file_contents; } } function set_file_contents( $phar, $path, $content ) { $key = str_replace( WP_CLI_BASE_PATH, '', $path ); - if ( !BE_QUIET ) - echo "$key - $path\n"; + if ( ! BE_QUIET ) { + echo "$key - $path" . PHP_EOL; + } $phar[ $key ] = $content; } +function get_composer_versions( $current_version ) { + $composer_lock_path = WP_CLI_ROOT . '/composer.lock'; + if ( ! ( $get_composer_lock = file_get_contents( $composer_lock_path ) ) || ! ( $composer_lock = json_decode( $get_composer_lock, true ) ) ) { + fwrite( STDERR, sprintf( "Warning: Failed to read '%s'." . PHP_EOL, $composer_lock_path ) ); + return ''; + } + if ( ! isset( $composer_lock['packages'] ) ) { + fwrite( STDERR, sprintf( "Warning: No packages in '%s'." . PHP_EOL, $composer_lock_path ) ); + return ''; + } + $vendor_versions = array( implode( ' ', array( 'wp-cli/wp-cli', $current_version, date( 'c' ) ) ) ); + $missing_names = $missing_versions = $missing_references = 0; + foreach ( $composer_lock['packages'] as $package ) { + if ( isset( $package['name'] ) ) { + $vendor_version = array( $package['name'] ); + if ( isset( $package['version'] ) ) { + $vendor_version[] = $package['version']; + } else { + $vendor_version[] = 'unknown_version'; + $missing_versions++; + } + if ( isset( $package['source'] ) && isset( $package['source']['reference'] ) ) { + $vendor_version[] = $package['source']['reference']; + } elseif( isset( $package['dist'] ) && isset( $package['dist']['reference'] ) ) { + $vendor_version[] = $package['dist']['reference']; + } else { + $vendor_version[] = 'unknown_reference'; + $missing_references++; + } + $vendor_versions[] = implode( ' ', $vendor_version ); + } else { + $vendor_versions[] = implode( ' ', array( 'unknown_package', 'unknown_version', 'unknown_reference' ) ); + $missing_names++; + } + } + if ( $missing_names ) { + fwrite( STDERR, sprintf( "Warning: %d package names missing from '%s'." . PHP_EOL, $missing_names, $composer_lock_path ) ); + } + if ( $missing_versions ) { + fwrite( STDERR, sprintf( "Warning: %d package versions missing from '%s'." . PHP_EOL, $missing_versions, $composer_lock_path ) ); + } + if ( $missing_references ) { + fwrite( STDERR, sprintf( "Warning: %d package references missing from '%s'." . PHP_EOL, $missing_references, $composer_lock_path ) ); + } + return implode( "\n", $vendor_versions ); +} + if ( file_exists( DEST_PATH ) ) { unlink( DEST_PATH ); } @@ -149,6 +219,7 @@ function set_file_contents( $phar, $path, $content ) { ->in(WP_CLI_VENDOR_DIR . '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/symfony/filesystem') ->in(WP_CLI_VENDOR_DIR . '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/symfony/process') ->in(WP_CLI_VENDOR_DIR . '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/justinrainbow/json-schema') + ->exclude('demo') ->exclude('nb/oxymel/OxymelTest.php') ->exclude('composer/spdx-licenses') ->exclude('composer/composer/src/Composer/Command') @@ -235,6 +306,7 @@ function set_file_contents( $phar, $path, $content ) { } add_file( $phar, WP_CLI_VENDOR_DIR . '/rmccue/requests/library/Requests/Transport/cacert.pem' ); +set_file_contents( $phar, WP_CLI_ROOT . '/COMPOSER_VERSIONS', get_composer_versions( $current_version ) ); set_file_contents( $phar, WP_CLI_ROOT . '/VERSION', $current_version ); $phar_boot = str_replace( WP_CLI_BASE_PATH, '', WP_CLI_ROOT . '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/php/boot-phar.php' ); @@ -253,5 +325,5 @@ function set_file_contents( $phar, $path, $content ) { chmod( DEST_PATH, 0755 ); // Make executable. if ( ! BE_QUIET ) { - echo "Generated " . DEST_PATH . "\n"; + echo "Generated " . DEST_PATH . PHP_EOL; }