├── wp-cli.yml ├── .gitignore ├── .distignore ├── features ├── extra │ └── no-mail.php ├── bootstrap │ ├── ProcessRun.php │ ├── Process.php │ ├── support.php │ ├── FeatureContext.php │ └── utils.php ├── test-db2utf8-command.feature └── steps │ ├── when.php │ ├── given.php │ └── then.php ├── composer.json ├── .editorconfig ├── .travis.yml ├── bin └── install-package-tests.sh ├── LICENSE.txt ├── utils └── behat-tags.php ├── command.php ├── README.md └── composer.lock /wp-cli.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - command.php 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | wp-cli.local.yml 3 | node_modules/ 4 | vendor/ 5 | composer.phar 6 | installer 7 | -------------------------------------------------------------------------------- /.distignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .git 3 | .gitignore 4 | .editorconfig 5 | .travis.yml 6 | circle.yml 7 | bin/ 8 | features/ 9 | utils/ 10 | -------------------------------------------------------------------------------- /features/extra/no-mail.php: -------------------------------------------------------------------------------- 1 | =0.23.0" 14 | }, 15 | "require-dev": { 16 | "behat/behat": "~2.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [{.jshintrc,*.json,*.yml,*.feature}] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [{*.txt,wp-config-sample.php}] 21 | end_of_line = crlf 22 | 23 | [composer.json] 24 | indent_style = space 25 | indent_size = 4 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | addons: 4 | apt: 5 | packages: 6 | - mysql-server-5.6 7 | - mysql-client-core-5.6 8 | - mysql-client-5.6 9 | 10 | language: php 11 | 12 | notifications: 13 | email: 14 | on_success: never 15 | on_failure: change 16 | 17 | branches: 18 | only: 19 | - master 20 | 21 | php: 22 | - 5.6 23 | - 7.0 24 | 25 | cache: 26 | - composer 27 | - $HOME/.composer/cache 28 | 29 | env: 30 | global: 31 | - WP_CLI_BIN_DIR=/tmp/wp-cli-phar 32 | 33 | before_script: 34 | - composer validate 35 | - bash bin/install-package-tests.sh 36 | 37 | script: ./vendor/bin/behat --strict 38 | -------------------------------------------------------------------------------- /features/bootstrap/ProcessRun.php: -------------------------------------------------------------------------------- 1 | $value ) { 15 | $this->$key = $value; 16 | } 17 | } 18 | 19 | /** 20 | * Return properties of executed command as a string. 21 | * 22 | * @return string 23 | */ 24 | public function __toString() { 25 | $out = "$ $this->command\n"; 26 | $out .= "$this->stdout\n$this->stderr"; 27 | $out .= "cwd: $this->cwd\n"; 28 | $out .= "exit status: $this->return_code"; 29 | 30 | return $out; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /bin/install-package-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | PACKAGE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../ && pwd )" 6 | 7 | download() { 8 | if [ `which curl` ]; then 9 | curl -s "$1" > "$2"; 10 | elif [ `which wget` ]; then 11 | wget -nv -O "$2" "$1" 12 | fi 13 | } 14 | 15 | install_wp_cli() { 16 | 17 | # the Behat test suite will pick up the executable found in $WP_CLI_BIN_DIR 18 | mkdir -p $WP_CLI_BIN_DIR 19 | download https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar $WP_CLI_BIN_DIR/wp 20 | chmod +x $WP_CLI_BIN_DIR/wp 21 | 22 | } 23 | 24 | download_behat() { 25 | 26 | cd $PACKAGE_DIR 27 | download https://getcomposer.org/installer installer 28 | php installer 29 | php composer.phar require --dev behat/behat='~2.5' 30 | 31 | } 32 | 33 | install_db() { 34 | mysql -e 'CREATE DATABASE IF NOT EXISTS wp_cli_test;' -uroot 35 | mysql -e 'GRANT ALL PRIVILEGES ON wp_cli_test.* TO "wp_cli_test"@"localhost" IDENTIFIED BY "password1"' -uroot 36 | } 37 | 38 | install_wp_cli 39 | download_behat 40 | install_db 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 weLaika 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /utils/behat-tags.php: -------------------------------------------------------------------------------- 1 | ' ) 38 | ); 39 | 40 | # Skip Github API tests by default because of rate limiting. See https://github.com/wp-cli/wp-cli/issues/1612 41 | $skip_tags[] = '@github-api'; 42 | 43 | if ( !empty( $skip_tags ) ) { 44 | echo '--tags=~' . implode( '&&~', $skip_tags ); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /command.php: -------------------------------------------------------------------------------- 1 | get_results( 'SHOW TABLES' ); 18 | $outputColumnName = "Tables_in_" . DB_NAME; 19 | 20 | foreach ($sqlTablesResults as $value) { 21 | array_push($tables, $value->$outputColumnName); 22 | } 23 | 24 | return $tables; 25 | } 26 | 27 | function fix_tables( $tables = array() ) { 28 | if ( empty($tables) ) { 29 | WP_CLI::error( "Empty table list." ); 30 | return; 31 | } 32 | 33 | global $wpdb; 34 | 35 | foreach ($tables as $table) { 36 | $wpdb->get_results( "alter table $table CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci" ); 37 | WP_CLI::success( "Table ${table} converted to utf8" ); 38 | } 39 | } 40 | 41 | 42 | /** 43 | * Convert database's tables in UTF8. 44 | */ 45 | 46 | public function __invoke() { 47 | $this->fix_tables($this->table_list()); 48 | } 49 | } 50 | 51 | WP_CLI::add_command( 'db2utf8', 'ConvertTablesToUTF8' ); 52 | -------------------------------------------------------------------------------- /features/test-db2utf8-command.feature: -------------------------------------------------------------------------------- 1 | Feature: Test db2utf8 command 2 | 3 | Scenario: The default charset on tables is utf8mb4_unicode_ci 4 | Given a WP install 5 | 6 | When I run `wp db query "SELECT table_collation FROM information_schema.tables WHERE table_schema = DATABASE();"` 7 | 8 | Then STDOUT should not contain: 9 | """ 10 | utf8_general_ci 11 | """ 12 | 13 | Scenario: The updated charset on tables is utf8_general_ci 14 | Given a WP install 15 | 16 | When I run `wp db2utf8` 17 | 18 | Then STDOUT should contain: 19 | """ 20 | Success: Table wp_commentmeta converted to utf8 21 | Success: Table wp_comments converted to utf8 22 | Success: Table wp_links converted to utf8 23 | Success: Table wp_options converted to utf8 24 | Success: Table wp_postmeta converted to utf8 25 | Success: Table wp_posts converted to utf8 26 | Success: Table wp_term_relationships converted to utf8 27 | Success: Table wp_term_taxonomy converted to utf8 28 | Success: Table wp_termmeta converted to utf8 29 | Success: Table wp_terms converted to utf8 30 | Success: Table wp_usermeta converted to utf8 31 | Success: Table wp_users converted to utf8 32 | """ 33 | 34 | When I run `wp db query "SELECT table_collation FROM information_schema.tables WHERE table_schema = DATABASE();"` 35 | 36 | Then STDOUT should contain: 37 | """ 38 | utf8_general_ci 39 | utf8_general_ci 40 | utf8_general_ci 41 | utf8_general_ci 42 | utf8_general_ci 43 | utf8_general_ci 44 | utf8_general_ci 45 | utf8_general_ci 46 | utf8_general_ci 47 | utf8_general_ci 48 | utf8_general_ci 49 | utf8_general_ci 50 | """ 51 | -------------------------------------------------------------------------------- /features/bootstrap/Process.php: -------------------------------------------------------------------------------- 1 | command = $command; 19 | $proc->cwd = $cwd; 20 | $proc->env = $env; 21 | 22 | return $proc; 23 | } 24 | 25 | private $command, $cwd, $env; 26 | 27 | private function __construct() {} 28 | 29 | /** 30 | * Run the command. 31 | * 32 | * @return ProcessRun 33 | */ 34 | public function run() { 35 | $cwd = $this->cwd; 36 | 37 | $descriptors = array( 38 | 0 => STDIN, 39 | 1 => array( 'pipe', 'w' ), 40 | 2 => array( 'pipe', 'w' ), 41 | ); 42 | 43 | $proc = proc_open( $this->command, $descriptors, $pipes, $cwd, $this->env ); 44 | 45 | $stdout = stream_get_contents( $pipes[1] ); 46 | fclose( $pipes[1] ); 47 | 48 | $stderr = stream_get_contents( $pipes[2] ); 49 | fclose( $pipes[2] ); 50 | 51 | return new ProcessRun( array( 52 | 'stdout' => $stdout, 53 | 'stderr' => $stderr, 54 | 'return_code' => proc_close( $proc ), 55 | 'command' => $this->command, 56 | 'cwd' => $cwd, 57 | 'env' => $this->env 58 | ) ); 59 | } 60 | 61 | /** 62 | * Run the command, but throw an Exception on error. 63 | * 64 | * @return ProcessRun 65 | */ 66 | public function run_check() { 67 | $r = $this->run(); 68 | 69 | if ( $r->return_code || !empty( $r->STDERR ) ) { 70 | throw new \RuntimeException( $r ); 71 | } 72 | 73 | return $r; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /features/steps/when.php: -------------------------------------------------------------------------------- 1 | 'run_check', 10 | 'try' => 'run' 11 | ); 12 | $method = $map[ $mode ]; 13 | 14 | return $proc->$method(); 15 | } 16 | 17 | function capture_email_sends( $stdout ) { 18 | $stdout = preg_replace( '#WP-CLI test suite: Sent email to.+\n?#', '', $stdout, -1, $email_sends ); 19 | return array( $stdout, $email_sends ); 20 | } 21 | 22 | $steps->When( '/^I launch in the background `([^`]+)`$/', 23 | function ( $world, $cmd ) { 24 | $world->background_proc( $cmd ); 25 | } 26 | ); 27 | 28 | $steps->When( '/^I (run|try) `([^`]+)`$/', 29 | function ( $world, $mode, $cmd ) { 30 | $cmd = $world->replace_variables( $cmd ); 31 | $world->result = invoke_proc( $world->proc( $cmd ), $mode ); 32 | list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); 33 | } 34 | ); 35 | 36 | $steps->When( "/^I (run|try) `([^`]+)` from '([^\s]+)'$/", 37 | function ( $world, $mode, $cmd, $subdir ) { 38 | $cmd = $world->replace_variables( $cmd ); 39 | $world->result = invoke_proc( $world->proc( $cmd, array(), $subdir ), $mode ); 40 | list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); 41 | } 42 | ); 43 | 44 | $steps->When( '/^I (run|try) the previous command again$/', 45 | function ( $world, $mode ) { 46 | if ( !isset( $world->result ) ) 47 | throw new \Exception( 'No previous command.' ); 48 | 49 | $proc = Process::create( $world->result->command, $world->result->cwd, $world->result->env ); 50 | $world->result = invoke_proc( $proc, $mode ); 51 | list( $world->result->stdout, $world->email_sends ) = capture_email_sends( $world->result->stdout ); 52 | } 53 | ); 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | welaika/wp-cli-db2utf8 2 | =============== 3 | 4 | [![Build Status](https://travis-ci.org/welaika/wp-cli-db2utf8.svg?branch=master)](https://travis-ci.org/welaika/wp-cli-db2utf8) 5 | 6 | Alter WP tables char set and collation to UTF8. 7 | 8 | Used when default UTF8-mb4 is not an option for you staging or production 9 | environment - MT Grid anyone? 10 | 11 | Use at your own risk and **only on new installations** with fresh database: 12 | altering tables this way could lead to unwanted data loss. 13 | 14 | ### Install 15 | 16 | wp package install welaika/wp-cli-db2utf8 17 | 18 | ### Use 19 | 20 | Use it from inside the root of a WordPress installation 21 | 22 | NAME 23 | 24 | wp db2utf8 25 | 26 | DESCRIPTION 27 | 28 | Convert database's tables in UTF8. Used to downgrade the UTF8-mb4 encoding 29 | 30 | SYNOPSIS 31 | 32 | wp db2utf8 33 | 34 | 35 | ## Author 36 | 37 | made with ❤️ and ☕️ by [weLaika](http://dev.welaika.com) 38 | 39 | ## License 40 | 41 | (The MIT License) 42 | 43 | Copyright © 2017 [weLaika](http://dev.welaika.com) 44 | 45 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | -------------------------------------------------------------------------------- /features/steps/given.php: -------------------------------------------------------------------------------- 1 | Given( '/^an empty directory$/', 8 | function ( $world ) { 9 | $world->create_run_dir(); 10 | } 11 | ); 12 | 13 | $steps->Given( '/^an empty cache/', 14 | function ( $world ) { 15 | $world->variables['SUITE_CACHE_DIR'] = FeatureContext::create_cache_dir(); 16 | } 17 | ); 18 | 19 | $steps->Given( '/^an? ([^\s]+) file:$/', 20 | function ( $world, $path, PyStringNode $content ) { 21 | $content = (string) $content . "\n"; 22 | $full_path = $world->variables['RUN_DIR'] . "/$path"; 23 | Process::create( \WP_CLI\utils\esc_cmd( 'mkdir -p %s', dirname( $full_path ) ) )->run_check(); 24 | file_put_contents( $full_path, $content ); 25 | } 26 | ); 27 | 28 | $steps->Given( '/^WP files$/', 29 | function ( $world ) { 30 | $world->download_wp(); 31 | } 32 | ); 33 | 34 | $steps->Given( '/^wp-config\.php$/', 35 | function ( $world ) { 36 | $world->create_config(); 37 | } 38 | ); 39 | 40 | $steps->Given( '/^a database$/', 41 | function ( $world ) { 42 | $world->create_db(); 43 | } 44 | ); 45 | 46 | $steps->Given( '/^a WP install$/', 47 | function ( $world ) { 48 | $world->install_wp(); 49 | } 50 | ); 51 | 52 | $steps->Given( "/^a WP install in '([^\s]+)'$/", 53 | function ( $world, $subdir ) { 54 | $world->install_wp( $subdir ); 55 | } 56 | ); 57 | 58 | $steps->Given( '/^a WP multisite (subdirectory|subdomain)?\s?install$/', 59 | function ( $world, $type = 'subdirectory' ) { 60 | $world->install_wp(); 61 | $subdomains = ! empty( $type ) && 'subdomain' === $type ? 1 : 0; 62 | $world->proc( 'wp core install-network', array( 'title' => 'WP CLI Network', 'subdomains' => $subdomains ) )->run_check(); 63 | } 64 | ); 65 | 66 | $steps->Given( '/^these installed and active plugins:$/', 67 | function( $world, $stream ) { 68 | $plugins = implode( ' ', array_map( 'trim', explode( PHP_EOL, (string)$stream ) ) ); 69 | $world->proc( "wp plugin install $plugins --activate" )->run_check(); 70 | } 71 | ); 72 | 73 | $steps->Given( '/^a custom wp-content directory$/', 74 | function ( $world ) { 75 | $wp_config_path = $world->variables['RUN_DIR'] . "/wp-config.php"; 76 | 77 | $wp_config_code = file_get_contents( $wp_config_path ); 78 | 79 | $world->move_files( 'wp-content', 'my-content' ); 80 | $world->add_line_to_wp_config( $wp_config_code, 81 | "define( 'WP_CONTENT_DIR', dirname(__FILE__) . '/my-content' );" ); 82 | 83 | $world->move_files( 'my-content/plugins', 'my-plugins' ); 84 | $world->add_line_to_wp_config( $wp_config_code, 85 | "define( 'WP_PLUGIN_DIR', __DIR__ . '/my-plugins' );" ); 86 | 87 | file_put_contents( $wp_config_path, $wp_config_code ); 88 | } 89 | ); 90 | 91 | $steps->Given( '/^download:$/', 92 | function ( $world, TableNode $table ) { 93 | foreach ( $table->getHash() as $row ) { 94 | $path = $world->replace_variables( $row['path'] ); 95 | if ( file_exists( $path ) ) { 96 | // assume it's the same file and skip re-download 97 | continue; 98 | } 99 | 100 | Process::create( \WP_CLI\Utils\esc_cmd( 'curl -sSL %s > %s', $row['url'], $path ) )->run_check(); 101 | } 102 | } 103 | ); 104 | 105 | $steps->Given( '/^save (STDOUT|STDERR) ([\'].+[^\'])?as \{(\w+)\}$/', 106 | function ( $world, $stream, $output_filter, $key ) { 107 | 108 | $stream = strtolower( $stream ); 109 | 110 | if ( $output_filter ) { 111 | $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/'; 112 | if ( false !== preg_match( $output_filter, $world->result->$stream, $matches ) ) 113 | $output = array_pop( $matches ); 114 | else 115 | $output = ''; 116 | } else { 117 | $output = $world->result->$stream; 118 | } 119 | $world->variables[ $key ] = trim( $output, "\n" ); 120 | } 121 | ); 122 | 123 | $steps->Given( '/^a new Phar(?: with version "([^"]+)")$/', 124 | function ( $world, $version ) { 125 | $world->build_phar( $version ); 126 | } 127 | ); 128 | 129 | $steps->Given( '/^save the (.+) file ([\'].+[^\'])?as \{(\w+)\}$/', 130 | function ( $world, $filepath, $output_filter, $key ) { 131 | $full_file = file_get_contents( $world->replace_variables( $filepath ) ); 132 | 133 | if ( $output_filter ) { 134 | $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/'; 135 | if ( false !== preg_match( $output_filter, $full_file, $matches ) ) 136 | $output = array_pop( $matches ); 137 | else 138 | $output = ''; 139 | } else { 140 | $output = $full_file; 141 | } 142 | $world->variables[ $key ] = trim( $output, "\n" ); 143 | } 144 | ); 145 | 146 | $steps->Given('/^a misconfigured WP_CONTENT_DIR constant directory$/', 147 | function($world) { 148 | $wp_config_path = $world->variables['RUN_DIR'] . "/wp-config.php"; 149 | 150 | $wp_config_code = file_get_contents( $wp_config_path ); 151 | 152 | $world->add_line_to_wp_config( $wp_config_code, 153 | "define( 'WP_CONTENT_DIR', '' );" ); 154 | 155 | file_put_contents( $wp_config_path, $wp_config_code ); 156 | } 157 | ); -------------------------------------------------------------------------------- /features/bootstrap/support.php: -------------------------------------------------------------------------------- 1 | $value ) { 77 | if ( ! compareContents( $value, $actual->$name ) ) 78 | return false; 79 | } 80 | } else if ( is_array( $expected ) ) { 81 | foreach ( $expected as $key => $value ) { 82 | if ( ! compareContents( $value, $actual[$key] ) ) 83 | return false; 84 | } 85 | } else { 86 | return $expected === $actual; 87 | } 88 | 89 | return true; 90 | } 91 | 92 | /** 93 | * Compare two strings containing JSON to ensure that @a $actualJson contains at 94 | * least what the JSON string @a $expectedJson contains. 95 | * 96 | * @return whether or not @a $actualJson contains @a $expectedJson 97 | * @retval true @a $actualJson contains @a $expectedJson 98 | * @retval false @a $actualJson does not contain @a $expectedJson 99 | * 100 | * @param[in] $actualJson the JSON string to be tested 101 | * @param[in] $expectedJson the expected JSON string 102 | * 103 | * Examples: 104 | * expected: {'a':1,'array':[1,3,5]} 105 | * 106 | * 1 ) 107 | * actual: {'a':1,'b':2,'c':3,'array':[1,2,3,4,5]} 108 | * return: true 109 | * 110 | * 2 ) 111 | * actual: {'b':2,'c':3,'array':[1,2,3,4,5]} 112 | * return: false 113 | * element 'a' is missing from the root object 114 | * 115 | * 3 ) 116 | * actual: {'a':0,'b':2,'c':3,'array':[1,2,3,4,5]} 117 | * return: false 118 | * the value of element 'a' is not 1 119 | * 120 | * 4 ) 121 | * actual: {'a':1,'b':2,'c':3,'array':[1,2,4,5]} 122 | * return: false 123 | * the contents of 'array' does not include 3 124 | */ 125 | function checkThatJsonStringContainsJsonString( $actualJson, $expectedJson ) { 126 | $actualValue = json_decode( $actualJson ); 127 | $expectedValue = json_decode( $expectedJson ); 128 | 129 | if ( !$actualValue ) { 130 | return false; 131 | } 132 | 133 | return compareContents( $expectedValue, $actualValue ); 134 | } 135 | 136 | /** 137 | * Compare two strings to confirm $actualCSV contains $expectedCSV 138 | * Both strings are expected to have headers for their CSVs. 139 | * $actualCSV must match all data rows in $expectedCSV 140 | * 141 | * @param string A CSV string 142 | * @param array A nested array of values 143 | * @return bool Whether $actualCSV contains $expectedCSV 144 | */ 145 | function checkThatCsvStringContainsValues( $actualCSV, $expectedCSV ) { 146 | $actualCSV = array_map( 'str_getcsv', explode( PHP_EOL, $actualCSV ) ); 147 | 148 | if ( empty( $actualCSV ) ) 149 | return false; 150 | 151 | // Each sample must have headers 152 | $actualHeaders = array_values( array_shift( $actualCSV ) ); 153 | $expectedHeaders = array_values( array_shift( $expectedCSV ) ); 154 | 155 | // Each expectedCSV must exist somewhere in actualCSV in the proper column 156 | $expectedResult = 0; 157 | foreach ( $expectedCSV as $expected_row ) { 158 | $expected_row = array_combine( $expectedHeaders, $expected_row ); 159 | foreach ( $actualCSV as $actual_row ) { 160 | 161 | if ( count( $actualHeaders ) != count( $actual_row ) ) 162 | continue; 163 | 164 | $actual_row = array_intersect_key( array_combine( $actualHeaders, $actual_row ), $expected_row ); 165 | if ( $actual_row == $expected_row ) 166 | $expectedResult++; 167 | } 168 | } 169 | 170 | return $expectedResult >= count( $expectedCSV ); 171 | } 172 | 173 | /** 174 | * Compare two strings containing YAML to ensure that @a $actualYaml contains at 175 | * least what the YAML string @a $expectedYaml contains. 176 | * 177 | * @return whether or not @a $actualYaml contains @a $expectedJson 178 | * @retval true @a $actualYaml contains @a $expectedJson 179 | * @retval false @a $actualYaml does not contain @a $expectedJson 180 | * 181 | * @param[in] $actualYaml the YAML string to be tested 182 | * @param[in] $expectedYaml the expected YAML string 183 | */ 184 | function checkThatYamlStringContainsYamlString( $actualYaml, $expectedYaml ) { 185 | $actualValue = spyc_load( $actualYaml ); 186 | $expectedValue = spyc_load( $expectedYaml ); 187 | 188 | if ( !$actualValue ) { 189 | return false; 190 | } 191 | 192 | return compareContents( $expectedValue, $actualValue ); 193 | } 194 | 195 | -------------------------------------------------------------------------------- /features/steps/then.php: -------------------------------------------------------------------------------- 1 | Then( '/^the return code should be (\d+)$/', 7 | function ( $world, $return_code ) { 8 | if ( $return_code != $world->result->return_code ) { 9 | throw new RuntimeException( $world->result ); 10 | } 11 | } 12 | ); 13 | 14 | $steps->Then( '/^(STDOUT|STDERR) should (be|contain|not contain):$/', 15 | function ( $world, $stream, $action, PyStringNode $expected ) { 16 | 17 | $stream = strtolower( $stream ); 18 | 19 | $expected = $world->replace_variables( (string) $expected ); 20 | 21 | checkString( $world->result->$stream, $expected, $action, $world->result ); 22 | } 23 | ); 24 | 25 | $steps->Then( '/^(STDOUT|STDERR) should be a number$/', 26 | function ( $world, $stream ) { 27 | 28 | $stream = strtolower( $stream ); 29 | 30 | assertNumeric( trim( $world->result->$stream, "\n" ) ); 31 | } 32 | ); 33 | 34 | $steps->Then( '/^(STDOUT|STDERR) should not be a number$/', 35 | function ( $world, $stream ) { 36 | 37 | $stream = strtolower( $stream ); 38 | 39 | assertNotNumeric( trim( $world->result->$stream, "\n" ) ); 40 | } 41 | ); 42 | 43 | $steps->Then( '/^STDOUT should be a table containing rows:$/', 44 | function ( $world, TableNode $expected ) { 45 | $output = $world->result->stdout; 46 | $actual_rows = explode( "\n", rtrim( $output, "\n" ) ); 47 | 48 | $expected_rows = array(); 49 | foreach ( $expected->getRows() as $row ) { 50 | $expected_rows[] = $world->replace_variables( implode( "\t", $row ) ); 51 | } 52 | 53 | compareTables( $expected_rows, $actual_rows, $output ); 54 | } 55 | ); 56 | 57 | $steps->Then( '/^STDOUT should end with a table containing rows:$/', 58 | function ( $world, TableNode $expected ) { 59 | $output = $world->result->stdout; 60 | $actual_rows = explode( "\n", rtrim( $output, "\n" ) ); 61 | 62 | $expected_rows = array(); 63 | foreach ( $expected->getRows() as $row ) { 64 | $expected_rows[] = $world->replace_variables( implode( "\t", $row ) ); 65 | } 66 | 67 | $start = array_search( $expected_rows[0], $actual_rows ); 68 | 69 | if ( false === $start ) 70 | throw new \Exception( $world->result ); 71 | 72 | compareTables( $expected_rows, array_slice( $actual_rows, $start ), $output ); 73 | } 74 | ); 75 | 76 | $steps->Then( '/^STDOUT should be JSON containing:$/', 77 | function ( $world, PyStringNode $expected ) { 78 | $output = $world->result->stdout; 79 | $expected = $world->replace_variables( (string) $expected ); 80 | 81 | if ( !checkThatJsonStringContainsJsonString( $output, $expected ) ) { 82 | throw new \Exception( $world->result ); 83 | } 84 | }); 85 | 86 | $steps->Then( '/^STDOUT should be a JSON array containing:$/', 87 | function ( $world, PyStringNode $expected ) { 88 | $output = $world->result->stdout; 89 | $expected = $world->replace_variables( (string) $expected ); 90 | 91 | $actualValues = json_decode( $output ); 92 | $expectedValues = json_decode( $expected ); 93 | 94 | $missing = array_diff( $expectedValues, $actualValues ); 95 | if ( !empty( $missing ) ) { 96 | throw new \Exception( $world->result ); 97 | } 98 | }); 99 | 100 | $steps->Then( '/^STDOUT should be CSV containing:$/', 101 | function ( $world, TableNode $expected ) { 102 | $output = $world->result->stdout; 103 | 104 | $expected_rows = $expected->getRows(); 105 | foreach ( $expected as &$row ) { 106 | foreach ( $row as &$value ) { 107 | $value = $world->replace_variables( $value ); 108 | } 109 | } 110 | 111 | if ( ! checkThatCsvStringContainsValues( $output, $expected_rows ) ) 112 | throw new \Exception( $world->result ); 113 | } 114 | ); 115 | 116 | $steps->Then( '/^STDOUT should be YAML containing:$/', 117 | function ( $world, PyStringNode $expected ) { 118 | $output = $world->result->stdout; 119 | $expected = $world->replace_variables( (string) $expected ); 120 | 121 | if ( !checkThatYamlStringContainsYamlString( $output, $expected ) ) { 122 | throw new \Exception( $world->result ); 123 | } 124 | }); 125 | 126 | $steps->Then( '/^(STDOUT|STDERR) should be empty$/', 127 | function ( $world, $stream ) { 128 | 129 | $stream = strtolower( $stream ); 130 | 131 | if ( !empty( $world->result->$stream ) ) { 132 | throw new \Exception( $world->result ); 133 | } 134 | } 135 | ); 136 | 137 | $steps->Then( '/^(STDOUT|STDERR) should not be empty$/', 138 | function ( $world, $stream ) { 139 | 140 | $stream = strtolower( $stream ); 141 | 142 | if ( '' === rtrim( $world->result->$stream, "\n" ) ) { 143 | throw new Exception( $world->result ); 144 | } 145 | } 146 | ); 147 | 148 | $steps->Then( '/^the (.+) (file|directory) should (exist|not exist|be:|contain:|not contain:)$/', 149 | function ( $world, $path, $type, $action, $expected = null ) { 150 | $path = $world->replace_variables( $path ); 151 | 152 | // If it's a relative path, make it relative to the current test dir 153 | if ( '/' !== $path[0] ) 154 | $path = $world->variables['RUN_DIR'] . "/$path"; 155 | 156 | if ( 'file' == $type ) { 157 | $test = 'file_exists'; 158 | } else if ( 'directory' == $type ) { 159 | $test = 'is_dir'; 160 | } 161 | 162 | switch ( $action ) { 163 | case 'exist': 164 | if ( ! $test( $path ) ) { 165 | throw new Exception( $world->result ); 166 | } 167 | break; 168 | case 'not exist': 169 | if ( $test( $path ) ) { 170 | throw new Exception( $world->result ); 171 | } 172 | break; 173 | default: 174 | if ( ! $test( $path ) ) { 175 | throw new Exception( "$path doesn't exist." ); 176 | } 177 | $action = substr( $action, 0, -1 ); 178 | $expected = $world->replace_variables( (string) $expected ); 179 | if ( 'file' == $type ) { 180 | $contents = file_get_contents( $path ); 181 | } else if ( 'directory' == $type ) { 182 | $files = glob( rtrim( $path, '/' ) . '/*' ); 183 | foreach( $files as &$file ) { 184 | $file = str_replace( $path . '/', '', $file ); 185 | } 186 | $contents = implode( PHP_EOL, $files ); 187 | } 188 | checkString( $contents, $expected, $action ); 189 | } 190 | } 191 | ); 192 | 193 | $steps->Then( '/^an email should (be sent|not be sent)$/', function( $world, $expected ) { 194 | if ( 'be sent' === $expected ) { 195 | assertNotEquals( 0, $world->email_sends ); 196 | } else if ( 'not be sent' === $expected ) { 197 | assertEquals( 0, $world->email_sends ); 198 | } else { 199 | throw new Exception( 'Invalid expectation' ); 200 | } 201 | }); 202 | -------------------------------------------------------------------------------- /features/bootstrap/FeatureContext.php: -------------------------------------------------------------------------------- 1 | autoload->files ) ) { 20 | $contents = 'require:' . PHP_EOL; 21 | foreach( $composer->autoload->files as $file ) { 22 | $contents .= ' - ' . dirname( dirname( dirname( __FILE__ ) ) ) . '/' . $file; 23 | } 24 | @mkdir( sys_get_temp_dir() . '/wp-cli-package-test/' ); 25 | $project_config = sys_get_temp_dir() . '/wp-cli-package-test/config.yml'; 26 | file_put_contents( $project_config, $contents ); 27 | putenv( 'WP_CLI_CONFIG_PATH=' . $project_config ); 28 | } 29 | } 30 | // Inside WP-CLI 31 | } else { 32 | require_once __DIR__ . '/../../php/utils.php'; 33 | require_once __DIR__ . '/../../php/WP_CLI/Process.php'; 34 | require_once __DIR__ . '/../../php/WP_CLI/ProcessRun.php'; 35 | require_once __DIR__ . '/../../vendor/autoload.php'; 36 | } 37 | 38 | /** 39 | * Features context. 40 | */ 41 | class FeatureContext extends BehatContext implements ClosuredContextInterface { 42 | 43 | private static $cache_dir, $suite_cache_dir; 44 | 45 | private static $db_settings = array( 46 | 'dbname' => 'wp_cli_test', 47 | 'dbuser' => 'wp_cli_test', 48 | 'dbpass' => 'password1', 49 | 'dbhost' => '127.0.0.1', 50 | ); 51 | 52 | private $running_procs = array(); 53 | 54 | public $variables = array(); 55 | 56 | /** 57 | * Get the environment variables required for launched `wp` processes 58 | * @beforeSuite 59 | */ 60 | private static function get_process_env_variables() { 61 | // Ensure we're using the expected `wp` binary 62 | $bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ?: realpath( __DIR__ . "/../../bin" ); 63 | $env = array( 64 | 'PATH' => $bin_dir . ':' . getenv( 'PATH' ), 65 | 'BEHAT_RUN' => 1, 66 | 'HOME' => '/tmp/wp-cli-home', 67 | ); 68 | if ( $config_path = getenv( 'WP_CLI_CONFIG_PATH' ) ) { 69 | $env['WP_CLI_CONFIG_PATH'] = $config_path; 70 | } 71 | return $env; 72 | } 73 | 74 | // We cache the results of `wp core download` to improve test performance 75 | // Ideally, we'd cache at the HTTP layer for more reliable tests 76 | private static function cache_wp_files() { 77 | self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test core-download-cache'; 78 | 79 | if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) 80 | return; 81 | 82 | $cmd = Utils\esc_cmd( 'wp core download --force --path=%s', self::$cache_dir ); 83 | if ( getenv( 'WP_VERSION' ) ) { 84 | $cmd .= Utils\esc_cmd( ' --version=%s', getenv( 'WP_VERSION' ) ); 85 | } 86 | Process::create( $cmd, null, self::get_process_env_variables() )->run_check(); 87 | } 88 | 89 | /** 90 | * @BeforeSuite 91 | */ 92 | public static function prepare( SuiteEvent $event ) { 93 | $result = Process::create( 'wp cli info', null, self::get_process_env_variables() )->run_check(); 94 | echo PHP_EOL; 95 | echo $result->stdout; 96 | echo PHP_EOL; 97 | self::cache_wp_files(); 98 | $result = Process::create( Utils\esc_cmd( 'wp core version --path=%s', self::$cache_dir ) , null, self::get_process_env_variables() )->run_check(); 99 | echo 'WordPress ' . $result->stdout; 100 | echo PHP_EOL; 101 | } 102 | 103 | /** 104 | * @AfterSuite 105 | */ 106 | public static function afterSuite( SuiteEvent $event ) { 107 | if ( self::$suite_cache_dir ) { 108 | Process::create( Utils\esc_cmd( 'rm -r %s', self::$suite_cache_dir ), null, self::get_process_env_variables() )->run(); 109 | } 110 | } 111 | 112 | /** 113 | * @BeforeScenario 114 | */ 115 | public function beforeScenario( $event ) { 116 | $this->variables['SRC_DIR'] = realpath( __DIR__ . '/../..' ); 117 | } 118 | 119 | /** 120 | * @AfterScenario 121 | */ 122 | public function afterScenario( $event ) { 123 | if ( isset( $this->variables['RUN_DIR'] ) ) { 124 | // remove altered WP install, unless there's an error 125 | if ( $event->getResult() < 4 ) { 126 | $this->proc( Utils\esc_cmd( 'rm -r %s', $this->variables['RUN_DIR'] ) )->run(); 127 | } 128 | } 129 | 130 | // Remove WP-CLI package directory 131 | if ( isset( $this->variables['PACKAGE_PATH'] ) ) { 132 | $this->proc( Utils\esc_cmd( 'rm -rf %s', $this->variables['PACKAGE_PATH'] ) )->run(); 133 | } 134 | 135 | foreach ( $this->running_procs as $proc ) { 136 | self::terminate_proc( $proc ); 137 | } 138 | } 139 | 140 | /** 141 | * Terminate a process and any of its children. 142 | */ 143 | private static function terminate_proc( $proc ) { 144 | $status = proc_get_status( $proc ); 145 | 146 | $master_pid = $status['pid']; 147 | 148 | $output = `ps -o ppid,pid,command | grep $master_pid`; 149 | 150 | foreach ( explode( PHP_EOL, $output ) as $line ) { 151 | if ( preg_match( '/^\s*(\d+)\s+(\d+)/', $line, $matches ) ) { 152 | $parent = $matches[1]; 153 | $child = $matches[2]; 154 | 155 | if ( $parent == $master_pid ) { 156 | if ( ! posix_kill( (int) $child, 9 ) ) { 157 | throw new RuntimeException( posix_strerror( posix_get_last_error() ) ); 158 | } 159 | } 160 | } 161 | } 162 | 163 | if ( ! posix_kill( (int) $master_pid, 9 ) ) { 164 | throw new RuntimeException( posix_strerror( posix_get_last_error() ) ); 165 | } 166 | } 167 | 168 | public static function create_cache_dir() { 169 | self::$suite_cache_dir = sys_get_temp_dir() . '/' . uniqid( "wp-cli-test-suite-cache-", TRUE ); 170 | mkdir( self::$suite_cache_dir ); 171 | return self::$suite_cache_dir; 172 | } 173 | 174 | /** 175 | * Initializes context. 176 | * Every scenario gets it's own context object. 177 | * 178 | * @param array $parameters context parameters (set them up through behat.yml) 179 | */ 180 | public function __construct( array $parameters ) { 181 | $this->drop_db(); 182 | $this->set_cache_dir(); 183 | $this->variables['CORE_CONFIG_SETTINGS'] = Utils\assoc_args_to_str( self::$db_settings ); 184 | } 185 | 186 | public function getStepDefinitionResources() { 187 | return glob( __DIR__ . '/../steps/*.php' ); 188 | } 189 | 190 | public function getHookDefinitionResources() { 191 | return array(); 192 | } 193 | 194 | public function replace_variables( $str ) { 195 | return preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str ); 196 | } 197 | 198 | private function _replace_var( $matches ) { 199 | $cmd = $matches[0]; 200 | 201 | foreach ( array_slice( $matches, 1 ) as $key ) { 202 | $cmd = str_replace( '{' . $key . '}', $this->variables[ $key ], $cmd ); 203 | } 204 | 205 | return $cmd; 206 | } 207 | 208 | public function create_run_dir() { 209 | if ( !isset( $this->variables['RUN_DIR'] ) ) { 210 | $this->variables['RUN_DIR'] = sys_get_temp_dir() . '/' . uniqid( "wp-cli-test-run-", TRUE ); 211 | mkdir( $this->variables['RUN_DIR'] ); 212 | } 213 | } 214 | 215 | public function build_phar( $version = 'same' ) { 216 | $this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/' . uniqid( "wp-cli-build-", TRUE ) . '.phar'; 217 | 218 | $this->proc( Utils\esc_cmd( 219 | 'php -dphar.readonly=0 %1$s %2$s --version=%3$s && chmod +x %2$s', 220 | __DIR__ . '/../../utils/make-phar.php', 221 | $this->variables['PHAR_PATH'], 222 | $version 223 | ) )->run_check(); 224 | } 225 | 226 | private function set_cache_dir() { 227 | $path = sys_get_temp_dir() . '/wp-cli-test-cache'; 228 | $this->proc( Utils\esc_cmd( 'mkdir -p %s', $path ) )->run_check(); 229 | $this->variables['CACHE_DIR'] = $path; 230 | } 231 | 232 | private static function run_sql( $sql ) { 233 | Utils\run_mysql_command( 'mysql --no-defaults', array( 234 | 'execute' => $sql, 235 | 'host' => self::$db_settings['dbhost'], 236 | 'user' => self::$db_settings['dbuser'], 237 | 'pass' => self::$db_settings['dbpass'], 238 | ) ); 239 | } 240 | 241 | public function create_db() { 242 | $dbname = self::$db_settings['dbname']; 243 | self::run_sql( "CREATE DATABASE IF NOT EXISTS $dbname" ); 244 | } 245 | 246 | public function drop_db() { 247 | $dbname = self::$db_settings['dbname']; 248 | self::run_sql( "DROP DATABASE IF EXISTS $dbname" ); 249 | } 250 | 251 | public function proc( $command, $assoc_args = array(), $path = '' ) { 252 | if ( !empty( $assoc_args ) ) 253 | $command .= Utils\assoc_args_to_str( $assoc_args ); 254 | 255 | $env = self::get_process_env_variables(); 256 | if ( isset( $this->variables['SUITE_CACHE_DIR'] ) ) { 257 | $env['WP_CLI_CACHE_DIR'] = $this->variables['SUITE_CACHE_DIR']; 258 | } 259 | 260 | if ( isset( $this->variables['RUN_DIR'] ) ) { 261 | $cwd = "{$this->variables['RUN_DIR']}/{$path}"; 262 | } else { 263 | $cwd = null; 264 | } 265 | 266 | return Process::create( $command, $cwd, $env ); 267 | } 268 | 269 | /** 270 | * Start a background process. Will automatically be closed when the tests finish. 271 | */ 272 | public function background_proc( $cmd ) { 273 | $descriptors = array( 274 | 0 => STDIN, 275 | 1 => array( 'pipe', 'w' ), 276 | 2 => array( 'pipe', 'w' ), 277 | ); 278 | 279 | $proc = proc_open( $cmd, $descriptors, $pipes, $this->variables['RUN_DIR'], self::get_process_env_variables() ); 280 | 281 | sleep(1); 282 | 283 | $status = proc_get_status( $proc ); 284 | 285 | if ( !$status['running'] ) { 286 | throw new RuntimeException( stream_get_contents( $pipes[2] ) ); 287 | } else { 288 | $this->running_procs[] = $proc; 289 | } 290 | } 291 | 292 | public function move_files( $src, $dest ) { 293 | rename( $this->variables['RUN_DIR'] . "/$src", $this->variables['RUN_DIR'] . "/$dest" ); 294 | } 295 | 296 | public function add_line_to_wp_config( &$wp_config_code, $line ) { 297 | $token = "/* That's all, stop editing!"; 298 | 299 | $wp_config_code = str_replace( $token, "$line\n\n$token", $wp_config_code ); 300 | } 301 | 302 | public function download_wp( $subdir = '' ) { 303 | $dest_dir = $this->variables['RUN_DIR'] . "/$subdir"; 304 | 305 | if ( $subdir ) { 306 | mkdir( $dest_dir ); 307 | } 308 | 309 | $this->proc( Utils\esc_cmd( "cp -r %s/* %s", self::$cache_dir, $dest_dir ) )->run_check(); 310 | 311 | // disable emailing 312 | mkdir( $dest_dir . '/wp-content/mu-plugins' ); 313 | copy( __DIR__ . '/../extra/no-mail.php', $dest_dir . '/wp-content/mu-plugins/no-mail.php' ); 314 | } 315 | 316 | public function create_config( $subdir = '' ) { 317 | $params = self::$db_settings; 318 | $params['dbprefix'] = $subdir ?: 'wp_'; 319 | 320 | $params['skip-salts'] = true; 321 | $this->proc( 'wp core config', $params, $subdir )->run_check(); 322 | } 323 | 324 | public function install_wp( $subdir = '' ) { 325 | $this->create_db(); 326 | $this->create_run_dir(); 327 | $this->download_wp( $subdir ); 328 | 329 | $this->create_config( $subdir ); 330 | 331 | $install_args = array( 332 | 'url' => 'http://example.com', 333 | 'title' => 'WP CLI Site', 334 | 'admin_user' => 'admin', 335 | 'admin_email' => 'admin@example.com', 336 | 'admin_password' => 'password1' 337 | ); 338 | 339 | $this->proc( 'wp core install', $install_args, $subdir )->run_check(); 340 | } 341 | } 342 | 343 | -------------------------------------------------------------------------------- /features/bootstrap/utils.php: -------------------------------------------------------------------------------- 1 | config ) && ! empty( $composer->config->{'vendor-dir'} ) ) { 66 | array_unshift( $vendor_paths, WP_CLI_ROOT . '/../../../' . $composer->config->{'vendor-dir'} ); 67 | } 68 | } 69 | return $vendor_paths; 70 | } 71 | 72 | // Using require() directly inside a class grants access to private methods to the loaded code 73 | function load_file( $path ) { 74 | require_once $path; 75 | } 76 | 77 | function load_command( $name ) { 78 | $path = WP_CLI_ROOT . "/php/commands/$name.php"; 79 | 80 | if ( is_readable( $path ) ) { 81 | include_once $path; 82 | } 83 | } 84 | 85 | function load_all_commands() { 86 | $cmd_dir = WP_CLI_ROOT . '/php/commands'; 87 | 88 | $iterator = new \DirectoryIterator( $cmd_dir ); 89 | 90 | foreach ( $iterator as $filename ) { 91 | if ( '.php' != substr( $filename, -4 ) ) 92 | continue; 93 | 94 | include_once "$cmd_dir/$filename"; 95 | } 96 | } 97 | 98 | /** 99 | * Like array_map(), except it returns a new iterator, instead of a modified array. 100 | * 101 | * Example: 102 | * 103 | * $arr = array('Football', 'Socker'); 104 | * 105 | * $it = iterator_map($arr, 'strtolower', function($val) { 106 | * return str_replace('foo', 'bar', $val); 107 | * }); 108 | * 109 | * foreach ( $it as $val ) { 110 | * var_dump($val); 111 | * } 112 | * 113 | * @param array|object Either a plain array or another iterator 114 | * @param callback The function to apply to an element 115 | * @return object An iterator that applies the given callback(s) 116 | */ 117 | function iterator_map( $it, $fn ) { 118 | if ( is_array( $it ) ) { 119 | $it = new \ArrayIterator( $it ); 120 | } 121 | 122 | if ( !method_exists( $it, 'add_transform' ) ) { 123 | $it = new Transform( $it ); 124 | } 125 | 126 | foreach ( array_slice( func_get_args(), 1 ) as $fn ) { 127 | $it->add_transform( $fn ); 128 | } 129 | 130 | return $it; 131 | } 132 | 133 | /** 134 | * Search for file by walking up the directory tree until the first file is found or until $stop_check($dir) returns true 135 | * @param string|array The files (or file) to search for 136 | * @param string|null The directory to start searching from; defaults to CWD 137 | * @param callable Function which is passed the current dir each time a directory level is traversed 138 | * @return null|string Null if the file was not found 139 | */ 140 | function find_file_upward( $files, $dir = null, $stop_check = null ) { 141 | $files = (array) $files; 142 | if ( is_null( $dir ) ) { 143 | $dir = getcwd(); 144 | } 145 | while ( @is_readable( $dir ) ) { 146 | // Stop walking up when the supplied callable returns true being passed the $dir 147 | if ( is_callable( $stop_check ) && call_user_func( $stop_check, $dir ) ) { 148 | return null; 149 | } 150 | 151 | foreach ( $files as $file ) { 152 | $path = $dir . DIRECTORY_SEPARATOR . $file; 153 | if ( file_exists( $path ) ) { 154 | return $path; 155 | } 156 | } 157 | 158 | $parent_dir = dirname( $dir ); 159 | if ( empty($parent_dir) || $parent_dir === $dir ) { 160 | break; 161 | } 162 | $dir = $parent_dir; 163 | } 164 | return null; 165 | } 166 | 167 | function is_path_absolute( $path ) { 168 | // Windows 169 | if ( isset($path[1]) && ':' === $path[1] ) 170 | return true; 171 | 172 | return $path[0] === '/'; 173 | } 174 | 175 | /** 176 | * Composes positional arguments into a command string. 177 | * 178 | * @param array 179 | * @return string 180 | */ 181 | function args_to_str( $args ) { 182 | return ' ' . implode( ' ', array_map( 'escapeshellarg', $args ) ); 183 | } 184 | 185 | /** 186 | * Composes associative arguments into a command string. 187 | * 188 | * @param array 189 | * @return string 190 | */ 191 | function assoc_args_to_str( $assoc_args ) { 192 | $str = ''; 193 | 194 | foreach ( $assoc_args as $key => $value ) { 195 | if ( true === $value ) 196 | $str .= " --$key"; 197 | else 198 | $str .= " --$key=" . escapeshellarg( $value ); 199 | } 200 | 201 | return $str; 202 | } 203 | 204 | /** 205 | * Given a template string and an arbitrary number of arguments, 206 | * returns the final command, with the parameters escaped. 207 | */ 208 | function esc_cmd( $cmd ) { 209 | if ( func_num_args() < 2 ) 210 | trigger_error( 'esc_cmd() requires at least two arguments.', E_USER_WARNING ); 211 | 212 | $args = func_get_args(); 213 | 214 | $cmd = array_shift( $args ); 215 | 216 | return vsprintf( $cmd, array_map( 'escapeshellarg', $args ) ); 217 | } 218 | 219 | function locate_wp_config() { 220 | static $path; 221 | 222 | if ( null === $path ) { 223 | if ( file_exists( ABSPATH . 'wp-config.php' ) ) 224 | $path = ABSPATH . 'wp-config.php'; 225 | elseif ( file_exists( ABSPATH . '../wp-config.php' ) && ! file_exists( ABSPATH . '/../wp-settings.php' ) ) 226 | $path = ABSPATH . '../wp-config.php'; 227 | else 228 | $path = false; 229 | 230 | if ( $path ) 231 | $path = realpath( $path ); 232 | } 233 | 234 | return $path; 235 | } 236 | 237 | function wp_version_compare( $since, $operator ) { 238 | return version_compare( str_replace( array( '-src' ), '', $GLOBALS['wp_version'] ), $since, $operator ); 239 | } 240 | 241 | /** 242 | * Render a collection of items as an ASCII table, JSON, CSV, YAML, list of ids, or count. 243 | * 244 | * Given a collection of items with a consistent data structure: 245 | * 246 | * ``` 247 | * $items = array( 248 | * array( 249 | * 'key' => 'foo', 250 | * 'value' => 'bar', 251 | * ) 252 | * ); 253 | * ``` 254 | * 255 | * Render `$items` as an ASCII table: 256 | * 257 | * ``` 258 | * WP_CLI\Utils\format_items( 'table', $items, array( 'key', 'value' ) ); 259 | * 260 | * # +-----+-------+ 261 | * # | key | value | 262 | * # +-----+-------+ 263 | * # | foo | bar | 264 | * # +-----+-------+ 265 | * ``` 266 | * 267 | * Or render `$items` as YAML: 268 | * 269 | * ``` 270 | * WP_CLI\Utils\format_items( 'yaml', $items, array( 'key', 'value' ) ); 271 | * 272 | * # --- 273 | * # - 274 | * # key: foo 275 | * # value: bar 276 | * ``` 277 | * 278 | * @access public 279 | * @category Output 280 | * 281 | * @param string $format Format to use: 'table', 'json', 'csv', 'yaml', 'ids', 'count' 282 | * @param array $items An array of items to output. 283 | * @param array|string $fields Named fields for each item of data. Can be array or comma-separated list. 284 | * @return null 285 | */ 286 | function format_items( $format, $items, $fields ) { 287 | $assoc_args = compact( 'format', 'fields' ); 288 | $formatter = new \WP_CLI\Formatter( $assoc_args ); 289 | $formatter->display_items( $items ); 290 | } 291 | 292 | /** 293 | * Write data as CSV to a given file. 294 | * 295 | * @access public 296 | * 297 | * @param resource $fd File descriptor 298 | * @param array $rows Array of rows to output 299 | * @param array $headers List of CSV columns (optional) 300 | */ 301 | function write_csv( $fd, $rows, $headers = array() ) { 302 | if ( ! empty( $headers ) ) { 303 | fputcsv( $fd, $headers ); 304 | } 305 | 306 | foreach ( $rows as $row ) { 307 | if ( ! empty( $headers ) ) { 308 | $row = pick_fields( $row, $headers ); 309 | } 310 | 311 | fputcsv( $fd, array_values( $row ) ); 312 | } 313 | } 314 | 315 | /** 316 | * Pick fields from an associative array or object. 317 | * 318 | * @param array|object Associative array or object to pick fields from 319 | * @param array List of fields to pick 320 | * @return array 321 | */ 322 | function pick_fields( $item, $fields ) { 323 | $item = (object) $item; 324 | 325 | $values = array(); 326 | 327 | foreach ( $fields as $field ) { 328 | $values[ $field ] = isset( $item->$field ) ? $item->$field : null; 329 | } 330 | 331 | return $values; 332 | } 333 | 334 | /** 335 | * Launch system's $EDITOR for the user to edit some text. 336 | * 337 | * @access public 338 | * @category Input 339 | * 340 | * @param string $content Some form of text to edit (e.g. post content) 341 | * @return string|bool Edited text, if file is saved from editor; false, if no change to file. 342 | */ 343 | function launch_editor_for_input( $input, $filename = 'WP-CLI' ) { 344 | 345 | $tmpdir = get_temp_dir(); 346 | 347 | do { 348 | $tmpfile = basename( $filename ); 349 | $tmpfile = preg_replace( '|\.[^.]*$|', '', $tmpfile ); 350 | $tmpfile .= '-' . substr( md5( rand() ), 0, 6 ); 351 | $tmpfile = $tmpdir . $tmpfile . '.tmp'; 352 | $fp = @fopen( $tmpfile, 'x' ); 353 | if ( ! $fp && is_writable( $tmpdir ) && file_exists( $tmpfile ) ) { 354 | $tmpfile = ''; 355 | continue; 356 | } 357 | if ( $fp ) { 358 | fclose( $fp ); 359 | } 360 | } while( ! $tmpfile ); 361 | 362 | if ( ! $tmpfile ) { 363 | \WP_CLI::error( 'Error creating temporary file.' ); 364 | } 365 | 366 | $output = ''; 367 | file_put_contents( $tmpfile, $input ); 368 | 369 | $editor = getenv( 'EDITOR' ); 370 | if ( !$editor ) { 371 | if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) 372 | $editor = 'notepad'; 373 | else 374 | $editor = 'vi'; 375 | } 376 | 377 | $descriptorspec = array( STDIN, STDOUT, STDERR ); 378 | $process = proc_open( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes ); 379 | $r = proc_close( $process ); 380 | if ( $r ) { 381 | exit( $r ); 382 | } 383 | 384 | $output = file_get_contents( $tmpfile ); 385 | 386 | unlink( $tmpfile ); 387 | 388 | if ( $output === $input ) 389 | return false; 390 | 391 | return $output; 392 | } 393 | 394 | /** 395 | * @param string MySQL host string, as defined in wp-config.php 396 | * @return array 397 | */ 398 | function mysql_host_to_cli_args( $raw_host ) { 399 | $assoc_args = array(); 400 | 401 | $host_parts = explode( ':', $raw_host ); 402 | if ( count( $host_parts ) == 2 ) { 403 | list( $assoc_args['host'], $extra ) = $host_parts; 404 | $extra = trim( $extra ); 405 | if ( is_numeric( $extra ) ) { 406 | $assoc_args['port'] = intval( $extra ); 407 | $assoc_args['protocol'] = 'tcp'; 408 | } else if ( $extra !== '' ) { 409 | $assoc_args['socket'] = $extra; 410 | } 411 | } else { 412 | $assoc_args['host'] = $raw_host; 413 | } 414 | 415 | return $assoc_args; 416 | } 417 | 418 | function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) { 419 | if ( !$descriptors ) 420 | $descriptors = array( STDIN, STDOUT, STDERR ); 421 | 422 | if ( isset( $assoc_args['host'] ) ) { 423 | $assoc_args = array_merge( $assoc_args, mysql_host_to_cli_args( $assoc_args['host'] ) ); 424 | } 425 | 426 | $pass = $assoc_args['pass']; 427 | unset( $assoc_args['pass'] ); 428 | 429 | $old_pass = getenv( 'MYSQL_PWD' ); 430 | putenv( 'MYSQL_PWD=' . $pass ); 431 | 432 | $final_cmd = $cmd . assoc_args_to_str( $assoc_args ); 433 | 434 | $proc = proc_open( $final_cmd, $descriptors, $pipes ); 435 | if ( !$proc ) 436 | exit(1); 437 | 438 | $r = proc_close( $proc ); 439 | 440 | putenv( 'MYSQL_PWD=' . $old_pass ); 441 | 442 | if ( $r ) exit( $r ); 443 | } 444 | 445 | /** 446 | * Render PHP or other types of files using Mustache templates. 447 | * 448 | * IMPORTANT: Automatic HTML escaping is disabled! 449 | */ 450 | function mustache_render( $template_name, $data = array() ) { 451 | if ( ! file_exists( $template_name ) ) 452 | $template_name = WP_CLI_ROOT . "/templates/$template_name"; 453 | 454 | $template = file_get_contents( $template_name ); 455 | 456 | $m = new \Mustache_Engine( array( 457 | 'escape' => function ( $val ) { return $val; } 458 | ) ); 459 | 460 | return $m->render( $template, $data ); 461 | } 462 | 463 | /** 464 | * Create a progress bar to display percent completion of a given operation. 465 | * 466 | * Progress bar is written to STDOUT, and disabled when command is piped. Progress 467 | * advances with `$progress->tick()`, and completes with `$progress->finish()`. 468 | * Process bar also indicates elapsed time and expected total time. 469 | * 470 | * ``` 471 | * # `wp user generate` ticks progress bar each time a new user is created. 472 | * # 473 | * # $ wp user generate --count=500 474 | * # Generating users 22 % [=======> ] 0:05 / 0:23 475 | * 476 | * $progress = \WP_CLI\Utils\make_progress_bar( 'Generating users', $count ); 477 | * for ( $i = 0; $i < $count; $i++ ) { 478 | * // uses wp_insert_user() to insert the user 479 | * $progress->tick(); 480 | * } 481 | * $progress->finish(); 482 | * ``` 483 | * 484 | * @access public 485 | * @category Output 486 | * 487 | * @param string $message Text to display before the progress bar. 488 | * @param integer $count Total number of ticks to be performed. 489 | * @return cli\progress\Bar|WP_CLI\NoOp 490 | */ 491 | function make_progress_bar( $message, $count ) { 492 | if ( \cli\Shell::isPiped() ) 493 | return new \WP_CLI\NoOp; 494 | 495 | return new \cli\progress\Bar( $message, $count ); 496 | } 497 | 498 | function parse_url( $url ) { 499 | $url_parts = \parse_url( $url ); 500 | 501 | if ( !isset( $url_parts['scheme'] ) ) { 502 | $url_parts = parse_url( 'http://' . $url ); 503 | } 504 | 505 | return $url_parts; 506 | } 507 | 508 | /** 509 | * Check if we're running in a Windows environment (cmd.exe). 510 | */ 511 | function is_windows() { 512 | return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; 513 | } 514 | 515 | /** 516 | * Replace magic constants in some PHP source code. 517 | * 518 | * @param string $source The PHP code to manipulate. 519 | * @param string $path The path to use instead of the magic constants 520 | */ 521 | function replace_path_consts( $source, $path ) { 522 | $replacements = array( 523 | '__FILE__' => "'$path'", 524 | '__DIR__' => "'" . dirname( $path ) . "'" 525 | ); 526 | 527 | $old = array_keys( $replacements ); 528 | $new = array_values( $replacements ); 529 | 530 | return str_replace( $old, $new, $source ); 531 | } 532 | 533 | /** 534 | * Make a HTTP request to a remote URL. 535 | * 536 | * Wraps the Requests HTTP library to ensure every request includes a cert. 537 | * 538 | * ``` 539 | * # `wp core download` verifies the hash for a downloaded WordPress archive 540 | * 541 | * $md5_response = Utils\http_request( 'GET', $download_url . '.md5' ); 542 | * if ( 20 != substr( $md5_response->status_code, 0, 2 ) ) { 543 | * WP_CLI::error( "Couldn't access md5 hash for release (HTTP code {$response->status_code})" ); 544 | * } 545 | * ``` 546 | * 547 | * @access public 548 | * 549 | * @param string $method HTTP method (GET, POST, DELETE, etc.) 550 | * @param string $url URL to make the HTTP request to. 551 | * @param array $headers Add specific headers to the request. 552 | * @param array $options 553 | * @return object 554 | */ 555 | function http_request( $method, $url, $data = null, $headers = array(), $options = array() ) { 556 | 557 | $cert_path = '/rmccue/requests/library/Requests/Transport/cacert.pem'; 558 | if ( inside_phar() ) { 559 | // cURL can't read Phar archives 560 | $options['verify'] = extract_from_phar( 561 | WP_CLI_ROOT . '/vendor' . $cert_path ); 562 | } else { 563 | foreach( get_vendor_paths() as $vendor_path ) { 564 | if ( file_exists( $vendor_path . $cert_path ) ) { 565 | $options['verify'] = $vendor_path . $cert_path; 566 | break; 567 | } 568 | } 569 | if ( empty( $options['verify'] ) ){ 570 | WP_CLI::error_log( "Cannot find SSL certificate." ); 571 | } 572 | } 573 | 574 | try { 575 | $request = \Requests::request( $url, $headers, $data, $method, $options ); 576 | return $request; 577 | } catch( \Requests_Exception $ex ) { 578 | // Handle SSL certificate issues gracefully 579 | \WP_CLI::warning( $ex->getMessage() ); 580 | $options['verify'] = false; 581 | try { 582 | return \Requests::request( $url, $headers, $data, $method, $options ); 583 | } catch( \Requests_Exception $ex ) { 584 | \WP_CLI::error( $ex->getMessage() ); 585 | } 586 | } 587 | } 588 | 589 | /** 590 | * Increments a version string using the "x.y.z-pre" format 591 | * 592 | * Can increment the major, minor or patch number by one 593 | * If $new_version == "same" the version string is not changed 594 | * If $new_version is not a known keyword, it will be used as the new version string directly 595 | * 596 | * @param string $current_version 597 | * @param string $new_version 598 | * @return string 599 | */ 600 | function increment_version( $current_version, $new_version ) { 601 | // split version assuming the format is x.y.z-pre 602 | $current_version = explode( '-', $current_version, 2 ); 603 | $current_version[0] = explode( '.', $current_version[0] ); 604 | 605 | switch ( $new_version ) { 606 | case 'same': 607 | // do nothing 608 | break; 609 | 610 | case 'patch': 611 | $current_version[0][2]++; 612 | 613 | $current_version = array( $current_version[0] ); // drop possible pre-release info 614 | break; 615 | 616 | case 'minor': 617 | $current_version[0][1]++; 618 | $current_version[0][2] = 0; 619 | 620 | $current_version = array( $current_version[0] ); // drop possible pre-release info 621 | break; 622 | 623 | case 'major': 624 | $current_version[0][0]++; 625 | $current_version[0][1] = 0; 626 | $current_version[0][2] = 0; 627 | 628 | $current_version = array( $current_version[0] ); // drop possible pre-release info 629 | break; 630 | 631 | default: // not a keyword 632 | $current_version = array( array( $new_version ) ); 633 | break; 634 | } 635 | 636 | // reconstruct version string 637 | $current_version[0] = implode( '.', $current_version[0] ); 638 | $current_version = implode( '-', $current_version ); 639 | 640 | return $current_version; 641 | } 642 | 643 | /** 644 | * Compare two version strings to get the named semantic version. 645 | * 646 | * @access public 647 | * 648 | * @param string $new_version 649 | * @param string $original_version 650 | * @return string $name 'major', 'minor', 'patch' 651 | */ 652 | function get_named_sem_ver( $new_version, $original_version ) { 653 | 654 | if ( ! Comparator::greaterThan( $new_version, $original_version ) ) { 655 | return ''; 656 | } 657 | 658 | $parts = explode( '-', $original_version ); 659 | $bits = explode( '.', $parts[0] ); 660 | $major = $bits[0]; 661 | if ( isset( $bits[1] ) ) { 662 | $minor = $bits[1]; 663 | } 664 | if ( isset( $bits[2] ) ) { 665 | $patch = $bits[2]; 666 | } 667 | 668 | if ( ! is_null( $minor ) && Semver::satisfies( $new_version, "{$major}.{$minor}.x" ) ) { 669 | return 'patch'; 670 | } else if ( Semver::satisfies( $new_version, "{$major}.x.x" ) ) { 671 | return 'minor'; 672 | } else { 673 | return 'major'; 674 | } 675 | } 676 | 677 | /** 678 | * Return the flag value or, if it's not set, the $default value. 679 | * 680 | * Because flags can be negated (e.g. --no-quiet to negate --quiet), this 681 | * function provides a safer alternative to using 682 | * `isset( $assoc_args['quiet'] )` or similar. 683 | * 684 | * @access public 685 | * @category Input 686 | * 687 | * @param array $assoc_args Arguments array. 688 | * @param string $flag Flag to get the value. 689 | * @param mixed $default Default value for the flag. Default: NULL 690 | * @return mixed 691 | */ 692 | function get_flag_value( $assoc_args, $flag, $default = null ) { 693 | return isset( $assoc_args[ $flag ] ) ? $assoc_args[ $flag ] : $default; 694 | } 695 | 696 | /** 697 | * Get the system's temp directory. Warns user if it isn't writable. 698 | * 699 | * @access public 700 | * @category System 701 | * 702 | * @return string 703 | */ 704 | function get_temp_dir() { 705 | static $temp = ''; 706 | 707 | $trailingslashit = function( $path ) { 708 | return rtrim( $path ) . '/'; 709 | }; 710 | 711 | if ( $temp ) 712 | return $trailingslashit( $temp ); 713 | 714 | if ( function_exists( 'sys_get_temp_dir' ) ) { 715 | $temp = sys_get_temp_dir(); 716 | } else if ( ini_get( 'upload_tmp_dir' ) ) { 717 | $temp = ini_get( 'upload_tmp_dir' ); 718 | } else { 719 | $temp = '/tmp/'; 720 | } 721 | 722 | if ( ! @is_writable( $temp ) ) { 723 | \WP_CLI::warning( "Temp directory isn't writable: {$temp}" ); 724 | } 725 | 726 | return $trailingslashit( $temp ); 727 | } 728 | 729 | /** 730 | * Parse a SSH url for its host, port, and path. 731 | * 732 | * Similar to parse_url(), but adds support for defined SSH aliases. 733 | * 734 | * ``` 735 | * host OR host/path/to/wordpress OR host:port/path/to/wordpress 736 | * ``` 737 | * 738 | * @access public 739 | * 740 | * @return mixed 741 | */ 742 | function parse_ssh_url( $url, $component = -1 ) { 743 | preg_match( '#^([^:/~]+)(:([\d]+))?((/|~)(.+))?$#', $url, $matches ); 744 | $bits = array(); 745 | foreach( array( 746 | 1 => 'host', 747 | 3 => 'port', 748 | 4 => 'path', 749 | ) as $i => $key ) { 750 | if ( ! empty( $matches[ $i ] ) ) { 751 | $bits[ $key ] = $matches[ $i ]; 752 | } 753 | } 754 | switch ( $component ) { 755 | case PHP_URL_HOST: 756 | return isset( $bits['host'] ) ? $bits['host'] : null; 757 | case PHP_URL_PATH: 758 | return isset( $bits['path'] ) ? $bits['path'] : null; 759 | case PHP_URL_PORT: 760 | return isset( $bits['port'] ) ? $bits['port'] : null; 761 | default: 762 | return $bits; 763 | } 764 | } 765 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "6e9c0024afb93dd2f6d40def92011803", 8 | "content-hash": "ac5f436466e4d189d9bb061763be9a2a", 9 | "packages": [ 10 | { 11 | "name": "composer/ca-bundle", 12 | "version": "dev-master", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/composer/ca-bundle.git", 16 | "reference": "ec21a59414b99501e723b63fd664aa8ead9c5680" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/composer/ca-bundle/zipball/ec21a59414b99501e723b63fd664aa8ead9c5680", 21 | "reference": "ec21a59414b99501e723b63fd664aa8ead9c5680", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "ext-openssl": "*", 26 | "ext-pcre": "*", 27 | "php": "^5.3.2 || ^7.0" 28 | }, 29 | "require-dev": { 30 | "psr/log": "^1.0", 31 | "symfony/process": "^2.5 || ^3.0" 32 | }, 33 | "suggest": { 34 | "symfony/process": "This is necessary to reliably check whether openssl_x509_parse is vulnerable on older php versions, but can be ignored on PHP 5.5.6+" 35 | }, 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "1.x-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Composer\\CaBundle\\": "src" 45 | } 46 | }, 47 | "notification-url": "https://packagist.org/downloads/", 48 | "license": [ 49 | "MIT" 50 | ], 51 | "authors": [ 52 | { 53 | "name": "Jordi Boggiano", 54 | "email": "j.boggiano@seld.be", 55 | "homepage": "http://seld.be" 56 | } 57 | ], 58 | "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", 59 | "keywords": [ 60 | "cabundle", 61 | "cacert", 62 | "certificate", 63 | "ssl", 64 | "tls" 65 | ], 66 | "time": "2016-09-04 19:00:06" 67 | }, 68 | { 69 | "name": "composer/composer", 70 | "version": "dev-master", 71 | "source": { 72 | "type": "git", 73 | "url": "https://github.com/composer/composer.git", 74 | "reference": "66efc9af8601234b8ab3a5fb91cfa7a2f30e9bbd" 75 | }, 76 | "dist": { 77 | "type": "zip", 78 | "url": "https://api.github.com/repos/composer/composer/zipball/66efc9af8601234b8ab3a5fb91cfa7a2f30e9bbd", 79 | "reference": "66efc9af8601234b8ab3a5fb91cfa7a2f30e9bbd", 80 | "shasum": "" 81 | }, 82 | "require": { 83 | "composer/ca-bundle": "^1.0", 84 | "composer/semver": "^1.0", 85 | "composer/spdx-licenses": "^1.0", 86 | "justinrainbow/json-schema": "^1.6 || ^2.0 || ^3.0 || ^4.0", 87 | "php": "^5.3.2 || ^7.0", 88 | "psr/log": "^1.0", 89 | "seld/cli-prompt": "^1.0", 90 | "seld/jsonlint": "^1.4", 91 | "seld/phar-utils": "^1.0", 92 | "symfony/console": "^2.5 || ^3.0", 93 | "symfony/filesystem": "^2.5 || ^3.0", 94 | "symfony/finder": "^2.2 || ^3.0", 95 | "symfony/process": "^2.1 || ^3.0" 96 | }, 97 | "require-dev": { 98 | "phpunit/phpunit": "^4.5 || ^5.0.5", 99 | "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" 100 | }, 101 | "suggest": { 102 | "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", 103 | "ext-zip": "Enabling the zip extension allows you to unzip archives", 104 | "ext-zlib": "Allow gzip compression of HTTP requests" 105 | }, 106 | "bin": [ 107 | "bin/composer" 108 | ], 109 | "type": "library", 110 | "extra": { 111 | "branch-alias": { 112 | "dev-master": "1.3-dev" 113 | } 114 | }, 115 | "autoload": { 116 | "psr-4": { 117 | "Composer\\": "src/Composer" 118 | } 119 | }, 120 | "notification-url": "https://packagist.org/downloads/", 121 | "license": [ 122 | "MIT" 123 | ], 124 | "authors": [ 125 | { 126 | "name": "Nils Adermann", 127 | "email": "naderman@naderman.de", 128 | "homepage": "http://www.naderman.de" 129 | }, 130 | { 131 | "name": "Jordi Boggiano", 132 | "email": "j.boggiano@seld.be", 133 | "homepage": "http://seld.be" 134 | } 135 | ], 136 | "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", 137 | "homepage": "https://getcomposer.org/", 138 | "keywords": [ 139 | "autoload", 140 | "dependency", 141 | "package" 142 | ], 143 | "time": "2016-10-14 14:31:13" 144 | }, 145 | { 146 | "name": "composer/semver", 147 | "version": "dev-master", 148 | "source": { 149 | "type": "git", 150 | "url": "https://github.com/composer/semver.git", 151 | "reference": "0bdf393dbafaebf79a8517140f661cedf40e36a4" 152 | }, 153 | "dist": { 154 | "type": "zip", 155 | "url": "https://api.github.com/repos/composer/semver/zipball/0bdf393dbafaebf79a8517140f661cedf40e36a4", 156 | "reference": "0bdf393dbafaebf79a8517140f661cedf40e36a4", 157 | "shasum": "" 158 | }, 159 | "require": { 160 | "php": "^5.3.2 || ^7.0" 161 | }, 162 | "require-dev": { 163 | "phpunit/phpunit": "^4.5 || ^5.0.5", 164 | "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" 165 | }, 166 | "type": "library", 167 | "extra": { 168 | "branch-alias": { 169 | "dev-master": "1.x-dev" 170 | } 171 | }, 172 | "autoload": { 173 | "psr-4": { 174 | "Composer\\Semver\\": "src" 175 | } 176 | }, 177 | "notification-url": "https://packagist.org/downloads/", 178 | "license": [ 179 | "MIT" 180 | ], 181 | "authors": [ 182 | { 183 | "name": "Nils Adermann", 184 | "email": "naderman@naderman.de", 185 | "homepage": "http://www.naderman.de" 186 | }, 187 | { 188 | "name": "Jordi Boggiano", 189 | "email": "j.boggiano@seld.be", 190 | "homepage": "http://seld.be" 191 | }, 192 | { 193 | "name": "Rob Bast", 194 | "email": "rob.bast@gmail.com", 195 | "homepage": "http://robbast.nl" 196 | } 197 | ], 198 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 199 | "keywords": [ 200 | "semantic", 201 | "semver", 202 | "validation", 203 | "versioning" 204 | ], 205 | "time": "2016-08-31 06:04:19" 206 | }, 207 | { 208 | "name": "composer/spdx-licenses", 209 | "version": "dev-master", 210 | "source": { 211 | "type": "git", 212 | "url": "https://github.com/composer/spdx-licenses.git", 213 | "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13" 214 | }, 215 | "dist": { 216 | "type": "zip", 217 | "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/96c6a07b05b716e89a44529d060bc7f5c263cb13", 218 | "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13", 219 | "shasum": "" 220 | }, 221 | "require": { 222 | "php": "^5.3.2 || ^7.0" 223 | }, 224 | "require-dev": { 225 | "phpunit/phpunit": "^4.5 || ^5.0.5", 226 | "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" 227 | }, 228 | "type": "library", 229 | "extra": { 230 | "branch-alias": { 231 | "dev-master": "1.x-dev" 232 | } 233 | }, 234 | "autoload": { 235 | "psr-4": { 236 | "Composer\\Spdx\\": "src" 237 | } 238 | }, 239 | "notification-url": "https://packagist.org/downloads/", 240 | "license": [ 241 | "MIT" 242 | ], 243 | "authors": [ 244 | { 245 | "name": "Nils Adermann", 246 | "email": "naderman@naderman.de", 247 | "homepage": "http://www.naderman.de" 248 | }, 249 | { 250 | "name": "Jordi Boggiano", 251 | "email": "j.boggiano@seld.be", 252 | "homepage": "http://seld.be" 253 | }, 254 | { 255 | "name": "Rob Bast", 256 | "email": "rob.bast@gmail.com", 257 | "homepage": "http://robbast.nl" 258 | } 259 | ], 260 | "description": "SPDX licenses list and validation library.", 261 | "keywords": [ 262 | "license", 263 | "spdx", 264 | "validator" 265 | ], 266 | "time": "2016-09-28 07:17:45" 267 | }, 268 | { 269 | "name": "justinrainbow/json-schema", 270 | "version": "dev-master", 271 | "source": { 272 | "type": "git", 273 | "url": "https://github.com/justinrainbow/json-schema.git", 274 | "reference": "4f10e03582ca40026b88c605f53243711806765b" 275 | }, 276 | "dist": { 277 | "type": "zip", 278 | "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/4f10e03582ca40026b88c605f53243711806765b", 279 | "reference": "4f10e03582ca40026b88c605f53243711806765b", 280 | "shasum": "" 281 | }, 282 | "require": { 283 | "php": ">=5.3.3" 284 | }, 285 | "require-dev": { 286 | "json-schema/json-schema-test-suite": "1.2.0", 287 | "phpdocumentor/phpdocumentor": "~2", 288 | "phpunit/phpunit": "^4.8.22" 289 | }, 290 | "bin": [ 291 | "bin/validate-json" 292 | ], 293 | "type": "library", 294 | "extra": { 295 | "branch-alias": { 296 | "dev-master": "4.0.x-dev" 297 | } 298 | }, 299 | "autoload": { 300 | "psr-4": { 301 | "JsonSchema\\": "src/JsonSchema/" 302 | } 303 | }, 304 | "notification-url": "https://packagist.org/downloads/", 305 | "license": [ 306 | "MIT" 307 | ], 308 | "authors": [ 309 | { 310 | "name": "Bruno Prieto Reis", 311 | "email": "bruno.p.reis@gmail.com" 312 | }, 313 | { 314 | "name": "Justin Rainbow", 315 | "email": "justin.rainbow@gmail.com" 316 | }, 317 | { 318 | "name": "Igor Wiedler", 319 | "email": "igor@wiedler.ch" 320 | }, 321 | { 322 | "name": "Robert Schönthal", 323 | "email": "seroscho@googlemail.com" 324 | } 325 | ], 326 | "description": "A library to validate a json schema.", 327 | "homepage": "https://github.com/justinrainbow/json-schema", 328 | "keywords": [ 329 | "json", 330 | "schema" 331 | ], 332 | "time": "2016-10-10 12:42:26" 333 | }, 334 | { 335 | "name": "mustache/mustache", 336 | "version": "v2.11.1", 337 | "source": { 338 | "type": "git", 339 | "url": "https://github.com/bobthecow/mustache.php.git", 340 | "reference": "a3f6d55996dd28b58f3a909d474189a3c1aa693f" 341 | }, 342 | "dist": { 343 | "type": "zip", 344 | "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/a3f6d55996dd28b58f3a909d474189a3c1aa693f", 345 | "reference": "a3f6d55996dd28b58f3a909d474189a3c1aa693f", 346 | "shasum": "" 347 | }, 348 | "require": { 349 | "php": ">=5.2.4" 350 | }, 351 | "require-dev": { 352 | "friendsofphp/php-cs-fixer": "~1.11", 353 | "phpunit/phpunit": "~3.7|~4.0|~5.0" 354 | }, 355 | "type": "library", 356 | "autoload": { 357 | "psr-0": { 358 | "Mustache": "src/" 359 | } 360 | }, 361 | "notification-url": "https://packagist.org/downloads/", 362 | "license": [ 363 | "MIT" 364 | ], 365 | "authors": [ 366 | { 367 | "name": "Justin Hileman", 368 | "email": "justin@justinhileman.info", 369 | "homepage": "http://justinhileman.com" 370 | } 371 | ], 372 | "description": "A Mustache implementation in PHP.", 373 | "homepage": "https://github.com/bobthecow/mustache.php", 374 | "keywords": [ 375 | "mustache", 376 | "templating" 377 | ], 378 | "time": "2016-07-31 06:18:27" 379 | }, 380 | { 381 | "name": "mustangostang/spyc", 382 | "version": "dev-master", 383 | "source": { 384 | "type": "git", 385 | "url": "https://github.com/mustangostang/spyc.git", 386 | "reference": "dfd9aadc1f5224065d55b42b712c7e99a50a3f4d" 387 | }, 388 | "dist": { 389 | "type": "zip", 390 | "url": "https://api.github.com/repos/mustangostang/spyc/zipball/dfd9aadc1f5224065d55b42b712c7e99a50a3f4d", 391 | "reference": "dfd9aadc1f5224065d55b42b712c7e99a50a3f4d", 392 | "shasum": "" 393 | }, 394 | "require": { 395 | "php": ">=5.3.1" 396 | }, 397 | "require-dev": { 398 | "phpunit/phpunit": "4.3.*@dev" 399 | }, 400 | "type": "library", 401 | "extra": { 402 | "branch-alias": { 403 | "dev-master": "0.5.x-dev" 404 | } 405 | }, 406 | "autoload": { 407 | "files": [ 408 | "Spyc.php" 409 | ] 410 | }, 411 | "notification-url": "https://packagist.org/downloads/", 412 | "license": [ 413 | "MIT" 414 | ], 415 | "authors": [ 416 | { 417 | "name": "mustangostang", 418 | "email": "vlad.andersen@gmail.com" 419 | } 420 | ], 421 | "description": "A simple YAML loader/dumper class for PHP", 422 | "homepage": "https://github.com/mustangostang/spyc/", 423 | "keywords": [ 424 | "spyc", 425 | "yaml", 426 | "yml" 427 | ], 428 | "time": "2016-05-14 08:33:02" 429 | }, 430 | { 431 | "name": "nb/oxymel", 432 | "version": "v0.1.0", 433 | "source": { 434 | "type": "git", 435 | "url": "https://github.com/nb/oxymel.git", 436 | "reference": "cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c" 437 | }, 438 | "dist": { 439 | "type": "zip", 440 | "url": "https://api.github.com/repos/nb/oxymel/zipball/cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c", 441 | "reference": "cbe626ef55d5c4cc9b5e6e3904b395861ea76e3c", 442 | "shasum": "" 443 | }, 444 | "require": { 445 | "php": ">=5.2.4" 446 | }, 447 | "type": "library", 448 | "autoload": { 449 | "psr-0": { 450 | "Oxymel": "" 451 | } 452 | }, 453 | "notification-url": "https://packagist.org/downloads/", 454 | "license": [ 455 | "MIT" 456 | ], 457 | "authors": [ 458 | { 459 | "name": "Nikolay Bachiyski", 460 | "email": "nb@nikolay.bg", 461 | "homepage": "http://extrapolate.me/" 462 | } 463 | ], 464 | "description": "A sweet XML builder", 465 | "homepage": "https://github.com/nb/oxymel", 466 | "keywords": [ 467 | "xml" 468 | ], 469 | "time": "2013-02-24 15:01:54" 470 | }, 471 | { 472 | "name": "psr/log", 473 | "version": "dev-master", 474 | "source": { 475 | "type": "git", 476 | "url": "https://github.com/php-fig/log.git", 477 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" 478 | }, 479 | "dist": { 480 | "type": "zip", 481 | "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 482 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 483 | "shasum": "" 484 | }, 485 | "require": { 486 | "php": ">=5.3.0" 487 | }, 488 | "type": "library", 489 | "extra": { 490 | "branch-alias": { 491 | "dev-master": "1.0.x-dev" 492 | } 493 | }, 494 | "autoload": { 495 | "psr-4": { 496 | "Psr\\Log\\": "Psr/Log/" 497 | } 498 | }, 499 | "notification-url": "https://packagist.org/downloads/", 500 | "license": [ 501 | "MIT" 502 | ], 503 | "authors": [ 504 | { 505 | "name": "PHP-FIG", 506 | "homepage": "http://www.php-fig.org/" 507 | } 508 | ], 509 | "description": "Common interface for logging libraries", 510 | "homepage": "https://github.com/php-fig/log", 511 | "keywords": [ 512 | "log", 513 | "psr", 514 | "psr-3" 515 | ], 516 | "time": "2016-10-10 12:19:37" 517 | }, 518 | { 519 | "name": "ramsey/array_column", 520 | "version": "1.1.3", 521 | "source": { 522 | "type": "git", 523 | "url": "https://github.com/ramsey/array_column.git", 524 | "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db" 525 | }, 526 | "dist": { 527 | "type": "zip", 528 | "url": "https://api.github.com/repos/ramsey/array_column/zipball/f8e52eb28e67eb50e613b451dd916abcf783c1db", 529 | "reference": "f8e52eb28e67eb50e613b451dd916abcf783c1db", 530 | "shasum": "" 531 | }, 532 | "require-dev": { 533 | "jakub-onderka/php-parallel-lint": "0.8.*", 534 | "phpunit/phpunit": "~4.5", 535 | "satooshi/php-coveralls": "0.6.*", 536 | "squizlabs/php_codesniffer": "~2.2" 537 | }, 538 | "type": "library", 539 | "autoload": { 540 | "files": [ 541 | "src/array_column.php" 542 | ] 543 | }, 544 | "notification-url": "https://packagist.org/downloads/", 545 | "license": [ 546 | "MIT" 547 | ], 548 | "authors": [ 549 | { 550 | "name": "Ben Ramsey", 551 | "homepage": "http://benramsey.com" 552 | } 553 | ], 554 | "description": "Provides functionality for array_column() to projects using PHP earlier than version 5.5.", 555 | "homepage": "https://github.com/ramsey/array_column", 556 | "keywords": [ 557 | "array", 558 | "array_column", 559 | "column" 560 | ], 561 | "time": "2015-03-20 22:07:39" 562 | }, 563 | { 564 | "name": "rmccue/requests", 565 | "version": "v1.7.0", 566 | "source": { 567 | "type": "git", 568 | "url": "https://github.com/rmccue/Requests.git", 569 | "reference": "87932f52ffad70504d93f04f15690cf16a089546" 570 | }, 571 | "dist": { 572 | "type": "zip", 573 | "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", 574 | "reference": "87932f52ffad70504d93f04f15690cf16a089546", 575 | "shasum": "" 576 | }, 577 | "require": { 578 | "php": ">=5.2" 579 | }, 580 | "require-dev": { 581 | "requests/test-server": "dev-master" 582 | }, 583 | "type": "library", 584 | "autoload": { 585 | "psr-0": { 586 | "Requests": "library/" 587 | } 588 | }, 589 | "notification-url": "https://packagist.org/downloads/", 590 | "license": [ 591 | "ISC" 592 | ], 593 | "authors": [ 594 | { 595 | "name": "Ryan McCue", 596 | "homepage": "http://ryanmccue.info" 597 | } 598 | ], 599 | "description": "A HTTP library written in PHP, for human beings.", 600 | "homepage": "http://github.com/rmccue/Requests", 601 | "keywords": [ 602 | "curl", 603 | "fsockopen", 604 | "http", 605 | "idna", 606 | "ipv6", 607 | "iri", 608 | "sockets" 609 | ], 610 | "time": "2016-10-13 00:11:37" 611 | }, 612 | { 613 | "name": "seld/cli-prompt", 614 | "version": "dev-master", 615 | "source": { 616 | "type": "git", 617 | "url": "https://github.com/Seldaek/cli-prompt.git", 618 | "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4" 619 | }, 620 | "dist": { 621 | "type": "zip", 622 | "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", 623 | "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", 624 | "shasum": "" 625 | }, 626 | "require": { 627 | "php": ">=5.3" 628 | }, 629 | "type": "library", 630 | "extra": { 631 | "branch-alias": { 632 | "dev-master": "1.x-dev" 633 | } 634 | }, 635 | "autoload": { 636 | "psr-4": { 637 | "Seld\\CliPrompt\\": "src/" 638 | } 639 | }, 640 | "notification-url": "https://packagist.org/downloads/", 641 | "license": [ 642 | "MIT" 643 | ], 644 | "authors": [ 645 | { 646 | "name": "Jordi Boggiano", 647 | "email": "j.boggiano@seld.be" 648 | } 649 | ], 650 | "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", 651 | "keywords": [ 652 | "cli", 653 | "console", 654 | "hidden", 655 | "input", 656 | "prompt" 657 | ], 658 | "time": "2016-04-18 09:31:41" 659 | }, 660 | { 661 | "name": "seld/jsonlint", 662 | "version": "1.4.1", 663 | "source": { 664 | "type": "git", 665 | "url": "https://github.com/Seldaek/jsonlint.git", 666 | "reference": "e827b5254d3e58c736ea2c5616710983d80b0b70" 667 | }, 668 | "dist": { 669 | "type": "zip", 670 | "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e827b5254d3e58c736ea2c5616710983d80b0b70", 671 | "reference": "e827b5254d3e58c736ea2c5616710983d80b0b70", 672 | "shasum": "" 673 | }, 674 | "require": { 675 | "php": "^5.3 || ^7.0" 676 | }, 677 | "bin": [ 678 | "bin/jsonlint" 679 | ], 680 | "type": "library", 681 | "autoload": { 682 | "psr-4": { 683 | "Seld\\JsonLint\\": "src/Seld/JsonLint/" 684 | } 685 | }, 686 | "notification-url": "https://packagist.org/downloads/", 687 | "license": [ 688 | "MIT" 689 | ], 690 | "authors": [ 691 | { 692 | "name": "Jordi Boggiano", 693 | "email": "j.boggiano@seld.be", 694 | "homepage": "http://seld.be" 695 | } 696 | ], 697 | "description": "JSON Linter", 698 | "keywords": [ 699 | "json", 700 | "linter", 701 | "parser", 702 | "validator" 703 | ], 704 | "time": "2016-09-14 15:17:56" 705 | }, 706 | { 707 | "name": "seld/phar-utils", 708 | "version": "dev-master", 709 | "source": { 710 | "type": "git", 711 | "url": "https://github.com/Seldaek/phar-utils.git", 712 | "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" 713 | }, 714 | "dist": { 715 | "type": "zip", 716 | "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", 717 | "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", 718 | "shasum": "" 719 | }, 720 | "require": { 721 | "php": ">=5.3" 722 | }, 723 | "type": "library", 724 | "extra": { 725 | "branch-alias": { 726 | "dev-master": "1.x-dev" 727 | } 728 | }, 729 | "autoload": { 730 | "psr-4": { 731 | "Seld\\PharUtils\\": "src/" 732 | } 733 | }, 734 | "notification-url": "https://packagist.org/downloads/", 735 | "license": [ 736 | "MIT" 737 | ], 738 | "authors": [ 739 | { 740 | "name": "Jordi Boggiano", 741 | "email": "j.boggiano@seld.be" 742 | } 743 | ], 744 | "description": "PHAR file format utilities, for when PHP phars you up", 745 | "keywords": [ 746 | "phra" 747 | ], 748 | "time": "2015-10-13 18:44:15" 749 | }, 750 | { 751 | "name": "symfony/config", 752 | "version": "2.8.x-dev", 753 | "source": { 754 | "type": "git", 755 | "url": "https://github.com/symfony/config.git", 756 | "reference": "f8b1922bbda9d2ac86aecd649399040bce849fde" 757 | }, 758 | "dist": { 759 | "type": "zip", 760 | "url": "https://api.github.com/repos/symfony/config/zipball/f8b1922bbda9d2ac86aecd649399040bce849fde", 761 | "reference": "f8b1922bbda9d2ac86aecd649399040bce849fde", 762 | "shasum": "" 763 | }, 764 | "require": { 765 | "php": ">=5.3.9", 766 | "symfony/filesystem": "~2.3|~3.0.0" 767 | }, 768 | "suggest": { 769 | "symfony/yaml": "To use the yaml reference dumper" 770 | }, 771 | "type": "library", 772 | "extra": { 773 | "branch-alias": { 774 | "dev-master": "2.8-dev" 775 | } 776 | }, 777 | "autoload": { 778 | "psr-4": { 779 | "Symfony\\Component\\Config\\": "" 780 | }, 781 | "exclude-from-classmap": [ 782 | "/Tests/" 783 | ] 784 | }, 785 | "notification-url": "https://packagist.org/downloads/", 786 | "license": [ 787 | "MIT" 788 | ], 789 | "authors": [ 790 | { 791 | "name": "Fabien Potencier", 792 | "email": "fabien@symfony.com" 793 | }, 794 | { 795 | "name": "Symfony Community", 796 | "homepage": "https://symfony.com/contributors" 797 | } 798 | ], 799 | "description": "Symfony Config Component", 800 | "homepage": "https://symfony.com", 801 | "time": "2016-09-14 20:31:12" 802 | }, 803 | { 804 | "name": "symfony/console", 805 | "version": "2.8.x-dev", 806 | "source": { 807 | "type": "git", 808 | "url": "https://github.com/symfony/console.git", 809 | "reference": "7350016c8abcab897046f1aead2b766b84d3eff8" 810 | }, 811 | "dist": { 812 | "type": "zip", 813 | "url": "https://api.github.com/repos/symfony/console/zipball/7350016c8abcab897046f1aead2b766b84d3eff8", 814 | "reference": "7350016c8abcab897046f1aead2b766b84d3eff8", 815 | "shasum": "" 816 | }, 817 | "require": { 818 | "php": ">=5.3.9", 819 | "symfony/debug": "~2.7,>=2.7.2|~3.0.0", 820 | "symfony/polyfill-mbstring": "~1.0" 821 | }, 822 | "require-dev": { 823 | "psr/log": "~1.0", 824 | "symfony/event-dispatcher": "~2.1|~3.0.0", 825 | "symfony/process": "~2.1|~3.0.0" 826 | }, 827 | "suggest": { 828 | "psr/log": "For using the console logger", 829 | "symfony/event-dispatcher": "", 830 | "symfony/process": "" 831 | }, 832 | "type": "library", 833 | "extra": { 834 | "branch-alias": { 835 | "dev-master": "2.8-dev" 836 | } 837 | }, 838 | "autoload": { 839 | "psr-4": { 840 | "Symfony\\Component\\Console\\": "" 841 | }, 842 | "exclude-from-classmap": [ 843 | "/Tests/" 844 | ] 845 | }, 846 | "notification-url": "https://packagist.org/downloads/", 847 | "license": [ 848 | "MIT" 849 | ], 850 | "authors": [ 851 | { 852 | "name": "Fabien Potencier", 853 | "email": "fabien@symfony.com" 854 | }, 855 | { 856 | "name": "Symfony Community", 857 | "homepage": "https://symfony.com/contributors" 858 | } 859 | ], 860 | "description": "Symfony Console Component", 861 | "homepage": "https://symfony.com", 862 | "time": "2016-10-06 01:43:09" 863 | }, 864 | { 865 | "name": "symfony/debug", 866 | "version": "2.8.x-dev", 867 | "source": { 868 | "type": "git", 869 | "url": "https://github.com/symfony/debug.git", 870 | "reference": "8c29235936a47473af16fb91c7c4b7b193c5693c" 871 | }, 872 | "dist": { 873 | "type": "zip", 874 | "url": "https://api.github.com/repos/symfony/debug/zipball/8c29235936a47473af16fb91c7c4b7b193c5693c", 875 | "reference": "8c29235936a47473af16fb91c7c4b7b193c5693c", 876 | "shasum": "" 877 | }, 878 | "require": { 879 | "php": ">=5.3.9", 880 | "psr/log": "~1.0" 881 | }, 882 | "conflict": { 883 | "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" 884 | }, 885 | "require-dev": { 886 | "symfony/class-loader": "~2.2|~3.0.0", 887 | "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2|~3.0.0" 888 | }, 889 | "type": "library", 890 | "extra": { 891 | "branch-alias": { 892 | "dev-master": "2.8-dev" 893 | } 894 | }, 895 | "autoload": { 896 | "psr-4": { 897 | "Symfony\\Component\\Debug\\": "" 898 | }, 899 | "exclude-from-classmap": [ 900 | "/Tests/" 901 | ] 902 | }, 903 | "notification-url": "https://packagist.org/downloads/", 904 | "license": [ 905 | "MIT" 906 | ], 907 | "authors": [ 908 | { 909 | "name": "Fabien Potencier", 910 | "email": "fabien@symfony.com" 911 | }, 912 | { 913 | "name": "Symfony Community", 914 | "homepage": "https://symfony.com/contributors" 915 | } 916 | ], 917 | "description": "Symfony Debug Component", 918 | "homepage": "https://symfony.com", 919 | "time": "2016-09-06 10:55:00" 920 | }, 921 | { 922 | "name": "symfony/dependency-injection", 923 | "version": "2.8.x-dev", 924 | "source": { 925 | "type": "git", 926 | "url": "https://github.com/symfony/dependency-injection.git", 927 | "reference": "7bc01cf6cd52971d57f3c02805ff7c7dd6a63837" 928 | }, 929 | "dist": { 930 | "type": "zip", 931 | "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/7bc01cf6cd52971d57f3c02805ff7c7dd6a63837", 932 | "reference": "7bc01cf6cd52971d57f3c02805ff7c7dd6a63837", 933 | "shasum": "" 934 | }, 935 | "require": { 936 | "php": ">=5.3.9" 937 | }, 938 | "conflict": { 939 | "symfony/expression-language": "<2.6" 940 | }, 941 | "require-dev": { 942 | "symfony/config": "~2.2|~3.0.0", 943 | "symfony/expression-language": "~2.6|~3.0.0", 944 | "symfony/yaml": "~2.3.42|~2.7.14|~2.8.7|~3.0.7" 945 | }, 946 | "suggest": { 947 | "symfony/config": "", 948 | "symfony/expression-language": "For using expressions in service container configuration", 949 | "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", 950 | "symfony/yaml": "" 951 | }, 952 | "type": "library", 953 | "extra": { 954 | "branch-alias": { 955 | "dev-master": "2.8-dev" 956 | } 957 | }, 958 | "autoload": { 959 | "psr-4": { 960 | "Symfony\\Component\\DependencyInjection\\": "" 961 | }, 962 | "exclude-from-classmap": [ 963 | "/Tests/" 964 | ] 965 | }, 966 | "notification-url": "https://packagist.org/downloads/", 967 | "license": [ 968 | "MIT" 969 | ], 970 | "authors": [ 971 | { 972 | "name": "Fabien Potencier", 973 | "email": "fabien@symfony.com" 974 | }, 975 | { 976 | "name": "Symfony Community", 977 | "homepage": "https://symfony.com/contributors" 978 | } 979 | ], 980 | "description": "Symfony DependencyInjection Component", 981 | "homepage": "https://symfony.com", 982 | "time": "2016-10-05 20:47:52" 983 | }, 984 | { 985 | "name": "symfony/event-dispatcher", 986 | "version": "2.8.x-dev", 987 | "source": { 988 | "type": "git", 989 | "url": "https://github.com/symfony/event-dispatcher.git", 990 | "reference": "25c576abd4e0f212e678fe8b2bd9a9a98c7ea934" 991 | }, 992 | "dist": { 993 | "type": "zip", 994 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/25c576abd4e0f212e678fe8b2bd9a9a98c7ea934", 995 | "reference": "25c576abd4e0f212e678fe8b2bd9a9a98c7ea934", 996 | "shasum": "" 997 | }, 998 | "require": { 999 | "php": ">=5.3.9" 1000 | }, 1001 | "require-dev": { 1002 | "psr/log": "~1.0", 1003 | "symfony/config": "~2.0,>=2.0.5|~3.0.0", 1004 | "symfony/dependency-injection": "~2.6|~3.0.0", 1005 | "symfony/expression-language": "~2.6|~3.0.0", 1006 | "symfony/stopwatch": "~2.3|~3.0.0" 1007 | }, 1008 | "suggest": { 1009 | "symfony/dependency-injection": "", 1010 | "symfony/http-kernel": "" 1011 | }, 1012 | "type": "library", 1013 | "extra": { 1014 | "branch-alias": { 1015 | "dev-master": "2.8-dev" 1016 | } 1017 | }, 1018 | "autoload": { 1019 | "psr-4": { 1020 | "Symfony\\Component\\EventDispatcher\\": "" 1021 | }, 1022 | "exclude-from-classmap": [ 1023 | "/Tests/" 1024 | ] 1025 | }, 1026 | "notification-url": "https://packagist.org/downloads/", 1027 | "license": [ 1028 | "MIT" 1029 | ], 1030 | "authors": [ 1031 | { 1032 | "name": "Fabien Potencier", 1033 | "email": "fabien@symfony.com" 1034 | }, 1035 | { 1036 | "name": "Symfony Community", 1037 | "homepage": "https://symfony.com/contributors" 1038 | } 1039 | ], 1040 | "description": "Symfony EventDispatcher Component", 1041 | "homepage": "https://symfony.com", 1042 | "time": "2016-10-13 01:43:15" 1043 | }, 1044 | { 1045 | "name": "symfony/filesystem", 1046 | "version": "2.8.x-dev", 1047 | "source": { 1048 | "type": "git", 1049 | "url": "https://github.com/symfony/filesystem.git", 1050 | "reference": "44b499521defddf2eae17a18c811bbdae4f98bdf" 1051 | }, 1052 | "dist": { 1053 | "type": "zip", 1054 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/44b499521defddf2eae17a18c811bbdae4f98bdf", 1055 | "reference": "44b499521defddf2eae17a18c811bbdae4f98bdf", 1056 | "shasum": "" 1057 | }, 1058 | "require": { 1059 | "php": ">=5.3.9" 1060 | }, 1061 | "type": "library", 1062 | "extra": { 1063 | "branch-alias": { 1064 | "dev-master": "2.8-dev" 1065 | } 1066 | }, 1067 | "autoload": { 1068 | "psr-4": { 1069 | "Symfony\\Component\\Filesystem\\": "" 1070 | }, 1071 | "exclude-from-classmap": [ 1072 | "/Tests/" 1073 | ] 1074 | }, 1075 | "notification-url": "https://packagist.org/downloads/", 1076 | "license": [ 1077 | "MIT" 1078 | ], 1079 | "authors": [ 1080 | { 1081 | "name": "Fabien Potencier", 1082 | "email": "fabien@symfony.com" 1083 | }, 1084 | { 1085 | "name": "Symfony Community", 1086 | "homepage": "https://symfony.com/contributors" 1087 | } 1088 | ], 1089 | "description": "Symfony Filesystem Component", 1090 | "homepage": "https://symfony.com", 1091 | "time": "2016-09-06 10:55:00" 1092 | }, 1093 | { 1094 | "name": "symfony/finder", 1095 | "version": "2.8.x-dev", 1096 | "source": { 1097 | "type": "git", 1098 | "url": "https://github.com/symfony/finder.git", 1099 | "reference": "bc24c8f5674c6f6841f2856b70e5d60784be5691" 1100 | }, 1101 | "dist": { 1102 | "type": "zip", 1103 | "url": "https://api.github.com/repos/symfony/finder/zipball/bc24c8f5674c6f6841f2856b70e5d60784be5691", 1104 | "reference": "bc24c8f5674c6f6841f2856b70e5d60784be5691", 1105 | "shasum": "" 1106 | }, 1107 | "require": { 1108 | "php": ">=5.3.9" 1109 | }, 1110 | "type": "library", 1111 | "extra": { 1112 | "branch-alias": { 1113 | "dev-master": "2.8-dev" 1114 | } 1115 | }, 1116 | "autoload": { 1117 | "psr-4": { 1118 | "Symfony\\Component\\Finder\\": "" 1119 | }, 1120 | "exclude-from-classmap": [ 1121 | "/Tests/" 1122 | ] 1123 | }, 1124 | "notification-url": "https://packagist.org/downloads/", 1125 | "license": [ 1126 | "MIT" 1127 | ], 1128 | "authors": [ 1129 | { 1130 | "name": "Fabien Potencier", 1131 | "email": "fabien@symfony.com" 1132 | }, 1133 | { 1134 | "name": "Symfony Community", 1135 | "homepage": "https://symfony.com/contributors" 1136 | } 1137 | ], 1138 | "description": "Symfony Finder Component", 1139 | "homepage": "https://symfony.com", 1140 | "time": "2016-09-28 00:10:16" 1141 | }, 1142 | { 1143 | "name": "symfony/polyfill-mbstring", 1144 | "version": "dev-master", 1145 | "source": { 1146 | "type": "git", 1147 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1148 | "reference": "53ad9faffe141fbe8f98cd6a7fd9a5e84513f56c" 1149 | }, 1150 | "dist": { 1151 | "type": "zip", 1152 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/53ad9faffe141fbe8f98cd6a7fd9a5e84513f56c", 1153 | "reference": "53ad9faffe141fbe8f98cd6a7fd9a5e84513f56c", 1154 | "shasum": "" 1155 | }, 1156 | "require": { 1157 | "php": ">=5.3.3" 1158 | }, 1159 | "suggest": { 1160 | "ext-mbstring": "For best performance" 1161 | }, 1162 | "type": "library", 1163 | "extra": { 1164 | "branch-alias": { 1165 | "dev-master": "1.2-dev" 1166 | } 1167 | }, 1168 | "autoload": { 1169 | "psr-4": { 1170 | "Symfony\\Polyfill\\Mbstring\\": "" 1171 | }, 1172 | "files": [ 1173 | "bootstrap.php" 1174 | ] 1175 | }, 1176 | "notification-url": "https://packagist.org/downloads/", 1177 | "license": [ 1178 | "MIT" 1179 | ], 1180 | "authors": [ 1181 | { 1182 | "name": "Nicolas Grekas", 1183 | "email": "p@tchwork.com" 1184 | }, 1185 | { 1186 | "name": "Symfony Community", 1187 | "homepage": "https://symfony.com/contributors" 1188 | } 1189 | ], 1190 | "description": "Symfony polyfill for the Mbstring extension", 1191 | "homepage": "https://symfony.com", 1192 | "keywords": [ 1193 | "compatibility", 1194 | "mbstring", 1195 | "polyfill", 1196 | "portable", 1197 | "shim" 1198 | ], 1199 | "time": "2016-08-30 17:06:17" 1200 | }, 1201 | { 1202 | "name": "symfony/process", 1203 | "version": "2.8.x-dev", 1204 | "source": { 1205 | "type": "git", 1206 | "url": "https://github.com/symfony/process.git", 1207 | "reference": "024de37f8a6b9e5e8244d9eb3fcf3e467dd2a93f" 1208 | }, 1209 | "dist": { 1210 | "type": "zip", 1211 | "url": "https://api.github.com/repos/symfony/process/zipball/024de37f8a6b9e5e8244d9eb3fcf3e467dd2a93f", 1212 | "reference": "024de37f8a6b9e5e8244d9eb3fcf3e467dd2a93f", 1213 | "shasum": "" 1214 | }, 1215 | "require": { 1216 | "php": ">=5.3.9" 1217 | }, 1218 | "type": "library", 1219 | "extra": { 1220 | "branch-alias": { 1221 | "dev-master": "2.8-dev" 1222 | } 1223 | }, 1224 | "autoload": { 1225 | "psr-4": { 1226 | "Symfony\\Component\\Process\\": "" 1227 | }, 1228 | "exclude-from-classmap": [ 1229 | "/Tests/" 1230 | ] 1231 | }, 1232 | "notification-url": "https://packagist.org/downloads/", 1233 | "license": [ 1234 | "MIT" 1235 | ], 1236 | "authors": [ 1237 | { 1238 | "name": "Fabien Potencier", 1239 | "email": "fabien@symfony.com" 1240 | }, 1241 | { 1242 | "name": "Symfony Community", 1243 | "homepage": "https://symfony.com/contributors" 1244 | } 1245 | ], 1246 | "description": "Symfony Process Component", 1247 | "homepage": "https://symfony.com", 1248 | "time": "2016-09-29 14:03:54" 1249 | }, 1250 | { 1251 | "name": "symfony/translation", 1252 | "version": "2.8.x-dev", 1253 | "source": { 1254 | "type": "git", 1255 | "url": "https://github.com/symfony/translation.git", 1256 | "reference": "bf0ff95faa9b6c0708efc1986255e3608d0ed3c7" 1257 | }, 1258 | "dist": { 1259 | "type": "zip", 1260 | "url": "https://api.github.com/repos/symfony/translation/zipball/bf0ff95faa9b6c0708efc1986255e3608d0ed3c7", 1261 | "reference": "bf0ff95faa9b6c0708efc1986255e3608d0ed3c7", 1262 | "shasum": "" 1263 | }, 1264 | "require": { 1265 | "php": ">=5.3.9", 1266 | "symfony/polyfill-mbstring": "~1.0" 1267 | }, 1268 | "conflict": { 1269 | "symfony/config": "<2.7" 1270 | }, 1271 | "require-dev": { 1272 | "psr/log": "~1.0", 1273 | "symfony/config": "~2.8", 1274 | "symfony/intl": "~2.4|~3.0.0", 1275 | "symfony/yaml": "~2.2|~3.0.0" 1276 | }, 1277 | "suggest": { 1278 | "psr/log": "To use logging capability in translator", 1279 | "symfony/config": "", 1280 | "symfony/yaml": "" 1281 | }, 1282 | "type": "library", 1283 | "extra": { 1284 | "branch-alias": { 1285 | "dev-master": "2.8-dev" 1286 | } 1287 | }, 1288 | "autoload": { 1289 | "psr-4": { 1290 | "Symfony\\Component\\Translation\\": "" 1291 | }, 1292 | "exclude-from-classmap": [ 1293 | "/Tests/" 1294 | ] 1295 | }, 1296 | "notification-url": "https://packagist.org/downloads/", 1297 | "license": [ 1298 | "MIT" 1299 | ], 1300 | "authors": [ 1301 | { 1302 | "name": "Fabien Potencier", 1303 | "email": "fabien@symfony.com" 1304 | }, 1305 | { 1306 | "name": "Symfony Community", 1307 | "homepage": "https://symfony.com/contributors" 1308 | } 1309 | ], 1310 | "description": "Symfony Translation Component", 1311 | "homepage": "https://symfony.com", 1312 | "time": "2016-09-06 10:55:00" 1313 | }, 1314 | { 1315 | "name": "symfony/yaml", 1316 | "version": "2.8.x-dev", 1317 | "source": { 1318 | "type": "git", 1319 | "url": "https://github.com/symfony/yaml.git", 1320 | "reference": "e7540734bad981fe59f8ef14b6fc194ae9df8d9c" 1321 | }, 1322 | "dist": { 1323 | "type": "zip", 1324 | "url": "https://api.github.com/repos/symfony/yaml/zipball/e7540734bad981fe59f8ef14b6fc194ae9df8d9c", 1325 | "reference": "e7540734bad981fe59f8ef14b6fc194ae9df8d9c", 1326 | "shasum": "" 1327 | }, 1328 | "require": { 1329 | "php": ">=5.3.9" 1330 | }, 1331 | "type": "library", 1332 | "extra": { 1333 | "branch-alias": { 1334 | "dev-master": "2.8-dev" 1335 | } 1336 | }, 1337 | "autoload": { 1338 | "psr-4": { 1339 | "Symfony\\Component\\Yaml\\": "" 1340 | }, 1341 | "exclude-from-classmap": [ 1342 | "/Tests/" 1343 | ] 1344 | }, 1345 | "notification-url": "https://packagist.org/downloads/", 1346 | "license": [ 1347 | "MIT" 1348 | ], 1349 | "authors": [ 1350 | { 1351 | "name": "Fabien Potencier", 1352 | "email": "fabien@symfony.com" 1353 | }, 1354 | { 1355 | "name": "Symfony Community", 1356 | "homepage": "https://symfony.com/contributors" 1357 | } 1358 | ], 1359 | "description": "Symfony Yaml Component", 1360 | "homepage": "https://symfony.com", 1361 | "time": "2016-09-02 01:57:56" 1362 | }, 1363 | { 1364 | "name": "wp-cli/php-cli-tools", 1365 | "version": "v0.11.1", 1366 | "source": { 1367 | "type": "git", 1368 | "url": "https://github.com/wp-cli/php-cli-tools.git", 1369 | "reference": "5311a4b99103c0505db015a334be4952654d6e21" 1370 | }, 1371 | "dist": { 1372 | "type": "zip", 1373 | "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/5311a4b99103c0505db015a334be4952654d6e21", 1374 | "reference": "5311a4b99103c0505db015a334be4952654d6e21", 1375 | "shasum": "" 1376 | }, 1377 | "require": { 1378 | "php": ">= 5.3.0" 1379 | }, 1380 | "type": "library", 1381 | "autoload": { 1382 | "psr-0": { 1383 | "cli": "lib/" 1384 | }, 1385 | "files": [ 1386 | "lib/cli/cli.php" 1387 | ] 1388 | }, 1389 | "notification-url": "https://packagist.org/downloads/", 1390 | "license": [ 1391 | "MIT" 1392 | ], 1393 | "authors": [ 1394 | { 1395 | "name": "James Logsdon", 1396 | "email": "jlogsdon@php.net", 1397 | "role": "Developer" 1398 | }, 1399 | { 1400 | "name": "Daniel Bachhuber", 1401 | "email": "daniel@handbuilt.co", 1402 | "role": "Maintainer" 1403 | } 1404 | ], 1405 | "description": "Console utilities for PHP", 1406 | "homepage": "http://github.com/wp-cli/php-cli-tools", 1407 | "keywords": [ 1408 | "cli", 1409 | "console" 1410 | ], 1411 | "time": "2016-02-08 14:34:01" 1412 | }, 1413 | { 1414 | "name": "wp-cli/wp-cli", 1415 | "version": "dev-master", 1416 | "source": { 1417 | "type": "git", 1418 | "url": "https://github.com/wp-cli/wp-cli.git", 1419 | "reference": "cf29f67b6dba99405385bc1ce4871cd52cc081d4" 1420 | }, 1421 | "dist": { 1422 | "type": "zip", 1423 | "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/cf29f67b6dba99405385bc1ce4871cd52cc081d4", 1424 | "reference": "cf29f67b6dba99405385bc1ce4871cd52cc081d4", 1425 | "shasum": "" 1426 | }, 1427 | "require": { 1428 | "composer/composer": "^1.0.0", 1429 | "composer/semver": "~1.0", 1430 | "mustache/mustache": "~2.4", 1431 | "mustangostang/spyc": "~0.5", 1432 | "nb/oxymel": "~0.1.0", 1433 | "php": ">=5.3.29", 1434 | "ramsey/array_column": "~1.1", 1435 | "rmccue/requests": "~1.6", 1436 | "symfony/config": "~2.7", 1437 | "symfony/console": "~2.7", 1438 | "symfony/debug": "~2.7", 1439 | "symfony/dependency-injection": "~2.7", 1440 | "symfony/event-dispatcher": "~2.7", 1441 | "symfony/filesystem": "~2.7", 1442 | "symfony/finder": "~2.7", 1443 | "symfony/process": "~2.1", 1444 | "symfony/translation": "~2.7", 1445 | "symfony/yaml": "~2.7", 1446 | "wp-cli/php-cli-tools": "~0.11.1" 1447 | }, 1448 | "require-dev": { 1449 | "behat/behat": "2.5.*", 1450 | "phpunit/phpunit": "3.7.*" 1451 | }, 1452 | "suggest": { 1453 | "psy/psysh": "Enhanced `wp shell` functionality" 1454 | }, 1455 | "bin": [ 1456 | "bin/wp.bat", 1457 | "bin/wp" 1458 | ], 1459 | "type": "library", 1460 | "autoload": { 1461 | "psr-0": { 1462 | "WP_CLI": "php" 1463 | }, 1464 | "classmap": [ 1465 | "php/export" 1466 | ] 1467 | }, 1468 | "notification-url": "https://packagist.org/downloads/", 1469 | "license": [ 1470 | "MIT" 1471 | ], 1472 | "description": "A command line interface for WordPress", 1473 | "homepage": "http://wp-cli.org", 1474 | "keywords": [ 1475 | "cli", 1476 | "wordpress" 1477 | ], 1478 | "time": "2016-10-14 15:26:16" 1479 | } 1480 | ], 1481 | "packages-dev": [ 1482 | { 1483 | "name": "behat/behat", 1484 | "version": "2.5.x-dev", 1485 | "source": { 1486 | "type": "git", 1487 | "url": "https://github.com/Behat/Behat.git", 1488 | "reference": "810008fdd1d4b2ec17f14c58cfdbf8fcb76a0022" 1489 | }, 1490 | "dist": { 1491 | "type": "zip", 1492 | "url": "https://api.github.com/repos/Behat/Behat/zipball/810008fdd1d4b2ec17f14c58cfdbf8fcb76a0022", 1493 | "reference": "810008fdd1d4b2ec17f14c58cfdbf8fcb76a0022", 1494 | "shasum": "" 1495 | }, 1496 | "require": { 1497 | "behat/gherkin": "~2.3.0", 1498 | "php": ">=5.3.1", 1499 | "symfony/config": "~2.3", 1500 | "symfony/console": "~2.0", 1501 | "symfony/dependency-injection": "~2.0", 1502 | "symfony/event-dispatcher": "~2.0", 1503 | "symfony/finder": "~2.0", 1504 | "symfony/translation": "~2.3", 1505 | "symfony/yaml": "~2.0" 1506 | }, 1507 | "require-dev": { 1508 | "phpunit/phpunit": "~3.7.19" 1509 | }, 1510 | "suggest": { 1511 | "behat/mink-extension": "for integration with Mink testing framework", 1512 | "behat/symfony2-extension": "for integration with Symfony2 web framework", 1513 | "behat/yii-extension": "for integration with Yii web framework" 1514 | }, 1515 | "bin": [ 1516 | "bin/behat" 1517 | ], 1518 | "type": "library", 1519 | "autoload": { 1520 | "psr-0": { 1521 | "Behat\\Behat": "src/" 1522 | } 1523 | }, 1524 | "notification-url": "https://packagist.org/downloads/", 1525 | "license": [ 1526 | "MIT" 1527 | ], 1528 | "authors": [ 1529 | { 1530 | "name": "Konstantin Kudryashov", 1531 | "email": "ever.zet@gmail.com", 1532 | "homepage": "http://everzet.com" 1533 | } 1534 | ], 1535 | "description": "Scenario-oriented BDD framework for PHP 5.3", 1536 | "homepage": "http://behat.org/", 1537 | "keywords": [ 1538 | "BDD", 1539 | "Behat", 1540 | "Symfony2" 1541 | ], 1542 | "time": "2016-08-29 07:27:43" 1543 | }, 1544 | { 1545 | "name": "behat/gherkin", 1546 | "version": "2.3.x-dev", 1547 | "source": { 1548 | "type": "git", 1549 | "url": "https://github.com/Behat/Gherkin.git", 1550 | "reference": "c32e15d92e1a2ce399a1a1c5be7afd965176e86c" 1551 | }, 1552 | "dist": { 1553 | "type": "zip", 1554 | "url": "https://api.github.com/repos/Behat/Gherkin/zipball/c32e15d92e1a2ce399a1a1c5be7afd965176e86c", 1555 | "reference": "c32e15d92e1a2ce399a1a1c5be7afd965176e86c", 1556 | "shasum": "" 1557 | }, 1558 | "require": { 1559 | "php": ">=5.3.1", 1560 | "symfony/finder": "~2.0" 1561 | }, 1562 | "require-dev": { 1563 | "symfony/config": "~2.0", 1564 | "symfony/translation": "~2.0", 1565 | "symfony/yaml": "~2.0" 1566 | }, 1567 | "suggest": { 1568 | "symfony/config": "If you want to use Config component to manage resources", 1569 | "symfony/translation": "If you want to use Symfony2 translations adapter", 1570 | "symfony/yaml": "If you want to parse features, represented in YAML files" 1571 | }, 1572 | "type": "library", 1573 | "extra": { 1574 | "branch-alias": { 1575 | "dev-develop": "2.2-dev" 1576 | } 1577 | }, 1578 | "autoload": { 1579 | "psr-0": { 1580 | "Behat\\Gherkin": "src/" 1581 | } 1582 | }, 1583 | "notification-url": "https://packagist.org/downloads/", 1584 | "license": [ 1585 | "MIT" 1586 | ], 1587 | "authors": [ 1588 | { 1589 | "name": "Konstantin Kudryashov", 1590 | "email": "ever.zet@gmail.com", 1591 | "homepage": "http://everzet.com" 1592 | } 1593 | ], 1594 | "description": "Gherkin DSL parser for PHP 5.3", 1595 | "homepage": "http://behat.org/", 1596 | "keywords": [ 1597 | "BDD", 1598 | "Behat", 1599 | "DSL", 1600 | "Symfony2", 1601 | "parser" 1602 | ], 1603 | "time": "2014-06-06 00:48:18" 1604 | } 1605 | ], 1606 | "aliases": [], 1607 | "minimum-stability": "dev", 1608 | "stability-flags": [], 1609 | "prefer-stable": false, 1610 | "prefer-lowest": false, 1611 | "platform": [], 1612 | "platform-dev": [] 1613 | } 1614 | --------------------------------------------------------------------------------