diff --git a/.drone.yml b/.drone.yml index 5834f30e3..ace722632 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,21 +6,22 @@ clone: steps: - name: integration-tests-master - image: nextcloudci/user_saml_shibboleth-php7.3:user_saml_shibboleth_php7.3-2 + image: ghcr.io/nextcloud/continuous-integration-user_saml_shibboleth-php8.0:latest environment: CORE_BRANCH: master commands: - - /start.sh & - - sleep 7 + - /start.sh + - /wait-for-services.sh - rm -rf /var/www/html - cd /var/www/ - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server html - cd /var/www/html && git submodule update --init # use local clone - cp -r /drone/src /var/www/html/apps/user_saml - - scl enable rh-php73 "bash -c 'php /var/www/html/occ maintenance:install --database sqlite --admin-pass password; php /var/www/html/occ app:enable user_saml'" + - php /var/www/html/occ maintenance:install --database sqlite --admin-pass password + - php /var/www/html/occ app:enable user_saml - chown -R apache:apache /var/www/html/ - - scl enable rh-php73 "bash -c 'cd /var/www/html/apps/user_saml/tests/integration && vendor/bin/behat'" + - cd /var/www/html/apps/user_saml/tests/integration && vendor/bin/behat trigger: branch: @@ -40,13 +41,13 @@ clone: depth: 1 steps: - - name: integration-tests-master - image: nextcloudci/user_saml_shibboleth-php7.3:user_saml_shibboleth_php7.3-2 + - name: integration-tests-stable23 + image: ghcr.io/nextcloud/continuous-integration-user_saml_shibboleth-php7.3:latest environment: CORE_BRANCH: stable23 commands: - /start.sh & - - sleep 7 + - /wait-for-services.sh - rm -rf /var/www/html - cd /var/www/ - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server html @@ -75,13 +76,13 @@ clone: depth: 1 steps: - - name: integration-tests-master - image: nextcloudci/user_saml_shibboleth-php7.3:user_saml_shibboleth_php7.3-2 + - name: integration-tests-stable22 + image: ghcr.io/nextcloud/continuous-integration-user_saml_shibboleth-php7.3:latest environment: CORE_BRANCH: stable22 commands: - /start.sh & - - sleep 7 + - /wait-for-services.sh - rm -rf /var/www/html - cd /var/www/ - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server html @@ -110,13 +111,13 @@ clone: depth: 1 steps: - - name: integration-tests-master - image: nextcloudci/user_saml_shibboleth-php7.3:user_saml_shibboleth_php7.3-2 + - name: integration-tests-stable21 + image: ghcr.io/nextcloud/continuous-integration-user_saml_shibboleth-php7.3:latest environment: CORE_BRANCH: stable21 commands: - /start.sh & - - sleep 7 + - /wait-for-services.sh - rm -rf /var/www/html - cd /var/www/ - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server html diff --git a/.gitignore b/.gitignore index f351a568f..64e0b0064 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,7 @@ 3rdparty/vendor/onelogin/php-saml/endpoints/ build -vendor +/vendor/ .php_cs.cache + +!/tests/integration/ diff --git a/tests/integration/composer.json b/tests/integration/composer.json index bb2be516c..7836cce7f 100644 --- a/tests/integration/composer.json +++ b/tests/integration/composer.json @@ -1,6 +1,12 @@ { + "require": { + "php": ">=7.3" + }, "require-dev": { "behat/behat": "^3.3", "guzzlehttp/guzzle": "^7.0" + }, + "conflict": { + "psr/container": ">=1.1" } } diff --git a/tests/integration/composer.lock b/tests/integration/composer.lock index 0d86ddb97..81b6c67a3 100644 --- a/tests/integration/composer.lock +++ b/tests/integration/composer.lock @@ -4,41 +4,42 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "342c9ad15194753594ee6d6b8e1bb2fc", + "content-hash": "9ff9f72d6a7cf43bebb439d1db2825a4", "packages": [], "packages-dev": [ { "name": "behat/behat", - "version": "v3.8.1", + "version": "v3.10.0", "source": { "type": "git", "url": "https://github.com/Behat/Behat.git", - "reference": "fbb065457d523d9856d4b50775b4151a7598b510" + "reference": "a55661154079cf881ef643b303bfaf67bae3a09f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/fbb065457d523d9856d4b50775b4151a7598b510", - "reference": "fbb065457d523d9856d4b50775b4151a7598b510", + "url": "https://api.github.com/repos/Behat/Behat/zipball/a55661154079cf881ef643b303bfaf67bae3a09f", + "reference": "a55661154079cf881ef643b303bfaf67bae3a09f", "shasum": "" }, "require": { - "behat/gherkin": "^4.6.0", + "behat/gherkin": "^4.9.0", "behat/transliterator": "^1.2", "ext-mbstring": "*", "php": "^7.2 || ^8.0", "psr/container": "^1.0", - "symfony/config": "^4.4 || ^5.0", - "symfony/console": "^4.4 || ^5.0", - "symfony/dependency-injection": "^4.4 || ^5.0", - "symfony/event-dispatcher": "^4.4 || ^5.0", - "symfony/translation": "^4.4 || ^5.0", - "symfony/yaml": "^4.4 || ^5.0" + "symfony/config": "^4.4 || ^5.0 || ^6.0", + "symfony/console": "^4.4 || ^5.0 || ^6.0", + "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", + "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", + "symfony/translation": "^4.4 || ^5.0 || ^6.0", + "symfony/yaml": "^4.4 || ^5.0 || ^6.0" }, "require-dev": { "container-interop/container-interop": "^1.2", "herrera-io/box": "~1.6.1", "phpunit/phpunit": "^8.5 || ^9.0", - "symfony/process": "^4.4 || ^5.0" + "symfony/process": "^4.4 || ^5.0 || ^6.0", + "vimeo/psalm": "^4.8" }, "suggest": { "ext-dom": "Needed to output test results in JUnit format." @@ -49,11 +50,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.8.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { + "Behat\\Hook\\": "src/Behat/Hook/", + "Behat\\Step\\": "src/Behat/Step/", "Behat\\Behat\\": "src/Behat/Behat/", "Behat\\Testwork\\": "src/Behat/Testwork/" } @@ -85,29 +88,33 @@ "symfony", "testing" ], - "time": "2020-11-07T15:55:18+00:00" + "support": { + "issues": "https://github.com/Behat/Behat/issues", + "source": "https://github.com/Behat/Behat/tree/v3.10.0" + }, + "time": "2021-11-02T20:09:40+00:00" }, { "name": "behat/gherkin", - "version": "v4.6.2", + "version": "v4.9.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31" + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/51ac4500c4dc30cbaaabcd2f25694299df666a31", - "reference": "51ac4500c4dc30cbaaabcd2f25694299df666a31", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", "shasum": "" }, "require": { - "php": ">=5.3.1" + "php": "~7.2|~8.0" }, "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3|~4", - "symfony/yaml": "~2.3|~3|~4" + "cucumber/cucumber": "dev-gherkin-22.0.0", + "phpunit/phpunit": "~8|~9", + "symfony/yaml": "~3|~4|~5" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -115,7 +122,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -134,7 +141,7 @@ "homepage": "http://everzet.com" } ], - "description": "Gherkin DSL parser for PHP 5.3", + "description": "Gherkin DSL parser for PHP", "homepage": "http://behat.org/", "keywords": [ "BDD", @@ -144,7 +151,11 @@ "gherkin", "parser" ], - "time": "2020-03-17T14:03:26+00:00" + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + }, + "time": "2021-10-12T13:05:09+00:00" }, { "name": "behat/transliterator", @@ -189,28 +200,33 @@ "slug", "transliterator" ], + "support": { + "issues": "https://github.com/Behat/Transliterator/issues", + "source": "https://github.com/Behat/Transliterator/tree/v1.3.0" + }, "time": "2020-01-14T16:39:13+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.3.0", + "version": "7.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7008573787b430c1c1f650e3722d9bba59967628" + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", - "reference": "7008573787b430c1c1f650e3722d9bba59967628", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7 || ^2.0", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0" + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" @@ -220,7 +236,7 @@ "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1" + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { "ext-curl": "Required for CURL handler support", @@ -230,35 +246,59 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.3-dev" + "dev-master": "7.4-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", @@ -270,6 +310,10 @@ "rest", "web service" ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -280,28 +324,24 @@ "type": "github" }, { - "url": "https://github.com/alexeyshockov", - "type": "github" - }, - { - "url": "https://github.com/gmponos", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" } ], - "time": "2021-03-23T11:33:13+00:00" + "time": "2021-12-06T18:43:05+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "" }, "require": { @@ -313,59 +353,95 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], - "time": "2021-03-07T09:25:29+00:00" + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.8.1", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1" + "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1", - "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72", + "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -373,30 +449,53 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "2.1-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", @@ -410,7 +509,25 @@ "uri", "url" ], - "time": "2021-03-21T16:25:00+00:00" + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-06T17:43:30+00:00" }, { "name": "psr/container", @@ -459,6 +576,10 @@ "container-interop", "psr" ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, "time": "2017-02-14T16:28:37+00:00" }, { @@ -505,6 +626,10 @@ "psr", "psr-14" ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, "time": "2019-01-08T18:20:26+00:00" }, { @@ -554,8 +679,66 @@ "psr", "psr-18" ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, "time": "2020-06-29T06:28:15+00:00" }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -604,6 +787,9 @@ "request", "response" ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, "time": "2016-08-06T14:39:51+00:00" }, { @@ -644,38 +830,43 @@ } ], "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, "time": "2019-03-08T08:55:37+00:00" }, { "name": "symfony/config", - "version": "v5.1.8", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "11baeefa4c179d6908655a7b6be728f62367c193" + "reference": "d65e1bd990c740e31feb07d2b0927b8d4df9956f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/11baeefa4c179d6908655a7b6be728f62367c193", - "reference": "11baeefa4c179d6908655a7b6be728f62367c193", + "url": "https://api.github.com/repos/symfony/config/zipball/d65e1bd990c740e31feb07d2b0927b8d4df9956f", + "reference": "d65e1bd990c740e31feb07d2b0927b8d4df9956f", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/filesystem": "^4.4|^5.0", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.15" + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" }, "conflict": { "symfony/finder": "<4.4" }, "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/finder": "^4.4|^5.0", - "symfony/messenger": "^4.4|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/yaml": "^4.4|^5.0" + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -703,8 +894,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Config Component", + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.4.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -719,31 +913,33 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2022-01-03T09:50:52+00:00" }, { "name": "symfony/console", - "version": "v5.1.8", + "version": "v5.4.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e" + "reference": "d8111acc99876953f52fe16d4c50eb60940d49ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", - "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "url": "https://api.github.com/repos/symfony/console/zipball/d8111acc99876953f52fe16d4c50eb60940d49ad", + "reference": "d8111acc99876953f52fe16d4c50eb60940d49ad", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.15", - "symfony/service-contracts": "^1.1|^2", - "symfony/string": "^5.1" + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" }, "conflict": { + "psr/log": ">=3", "symfony/dependency-injection": "<4.4", "symfony/dotenv": "<5.1", "symfony/event-dispatcher": "<4.4", @@ -751,16 +947,16 @@ "symfony/process": "<4.4" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0|2.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0" + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" }, "suggest": { "psr/log": "For using the console logger", @@ -791,8 +987,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.5" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -807,27 +1012,27 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2022-02-24T12:45:35+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.1.8", + "version": "v5.2.12", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "829ca6bceaf68036a123a13a979f3c89289eae78" + "reference": "2f0326ab0e142a3600b1b435cb3e852bc96264b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/829ca6bceaf68036a123a13a979f3c89289eae78", - "reference": "829ca6bceaf68036a123a13a979f3c89289eae78", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2f0326ab0e142a3600b1b435cb3e852bc96264b6", + "reference": "2f0326ab0e142a3600b1b435cb3e852bc96264b6", "shasum": "" }, "require": { "php": ">=7.2.5", "psr/container": "^1.0", "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php80": "^1.15", + "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.1.6|^2" }, "conflict": { @@ -838,7 +1043,7 @@ }, "provide": { "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0" + "symfony/service-implementation": "1.0|2.0" }, "require-dev": { "symfony/config": "^5.1", @@ -875,8 +1080,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony DependencyInjection Component", + "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v5.2.12" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -891,20 +1099,20 @@ "type": "tidelift" } ], - "time": "2020-10-27T10:11:13+00:00" + "time": "2021-07-23T15:54:19+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.2.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", - "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", "shasum": "" }, "require": { @@ -913,7 +1121,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -941,6 +1149,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -955,27 +1166,27 @@ "type": "tidelift" } ], - "time": "2020-09-07T11:33:47+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.1.8", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "26f4edae48c913fc183a3da0553fe63bdfbd361a" + "reference": "dec8a9f58d20df252b9cd89f1c6c1530f747685d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/26f4edae48c913fc183a3da0553fe63bdfbd361a", - "reference": "26f4edae48c913fc183a3da0553fe63bdfbd361a", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dec8a9f58d20df252b9cd89f1c6c1530f747685d", + "reference": "dec8a9f58d20df252b9cd89f1c6c1530f747685d", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/event-dispatcher-contracts": "^2", - "symfony/polyfill-php80": "^1.15" + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" }, "conflict": { "symfony/dependency-injection": "<4.4" @@ -985,14 +1196,14 @@ "symfony/event-dispatcher-implementation": "2.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/error-handler": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/stopwatch": "^4.4|^5.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1021,8 +1232,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1037,20 +1251,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.2.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2" + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ba7d54483095a198fa51781bc608d17e84dffa2", - "reference": "0ba7d54483095a198fa51781bc608d17e84dffa2", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", "shasum": "" }, "require": { @@ -1063,7 +1277,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1099,6 +1313,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1113,25 +1330,27 @@ "type": "tidelift" } ], - "time": "2020-09-07T11:33:47+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/filesystem", - "version": "v5.1.8", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "df08650ea7aee2d925380069c131a66124d79177" + "reference": "d53a45039974952af7f7ebc461ccdd4295e29440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/df08650ea7aee2d925380069c131a66124d79177", - "reference": "df08650ea7aee2d925380069c131a66124d79177", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d53a45039974952af7f7ebc461ccdd4295e29440", + "reference": "d53a45039974952af7f7ebc461ccdd4295e29440", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8" + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -1156,8 +1375,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.6" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1172,32 +1394,35 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2022-03-02T12:42:23+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + "reference": "30885182c981ab175d4d034db0f6f469898070ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-ctype": "*" + }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1205,12 +1430,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1234,6 +1459,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1248,20 +1476,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-10-20T20:35:02+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", - "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", "shasum": "" }, "require": { @@ -1273,7 +1501,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1281,12 +1509,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1312,6 +1540,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1326,20 +1557,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-11-23T21:10:46+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "727d1096295d807c309fb01a851577302394c897" + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", - "reference": "727d1096295d807c309fb01a851577302394c897", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "shasum": "" }, "require": { @@ -1351,7 +1582,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1359,12 +1590,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -1393,6 +1624,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1407,32 +1641,35 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", - "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-mbstring": "*" + }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1440,12 +1677,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1470,6 +1707,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1484,20 +1724,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-11-30T18:21:41+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", - "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", "shasum": "" }, "require": { @@ -1506,7 +1746,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1514,12 +1754,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -1546,6 +1786,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1560,20 +1803,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-06-05T21:20:04+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.20.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", - "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", "shasum": "" }, "require": { @@ -1582,7 +1825,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1590,12 +1833,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -1626,6 +1869,88 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-03-04T08:16:47+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1640,7 +1965,7 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-09-13T13:58:11+00:00" }, { "name": "symfony/service-contracts", @@ -1702,6 +2027,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1720,16 +2048,16 @@ }, { "name": "symfony/string", - "version": "v5.1.8", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea" + "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/a97573e960303db71be0dd8fda9be3bca5e0feea", - "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea", + "url": "https://api.github.com/repos/symfony/string/zipball/92043b7d8383e48104e411bc9434b260dbeb5a10", + "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10", "shasum": "" }, "require": { @@ -1740,20 +2068,23 @@ "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "~1.15" }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" + "symfony/var-exporter": "^4.4|^5.0|^6.0" }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, "files": [ "Resources/functions.php" ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, "exclude-from-classmap": [ "/Tests/" ] @@ -1772,7 +2103,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony String component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", @@ -1782,6 +2113,9 @@ "utf-8", "utf8" ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1796,48 +2130,52 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/translation", - "version": "v5.1.8", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "27980838fd261e04379fa91e94e81e662fe5a1b6" + "reference": "a7ca9fdfffb0174209440c2ffa1dee228e15d95b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/27980838fd261e04379fa91e94e81e662fe5a1b6", - "reference": "27980838fd261e04379fa91e94e81e662fe5a1b6", + "url": "https://api.github.com/repos/symfony/translation/zipball/a7ca9fdfffb0174209440c2ffa1dee228e15d95b", + "reference": "a7ca9fdfffb0174209440c2ffa1dee228e15d95b", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.15", - "symfony/translation-contracts": "^2" + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^2.3" }, "conflict": { "symfony/config": "<4.4", + "symfony/console": "<5.3", "symfony/dependency-injection": "<5.0", "symfony/http-kernel": "<5.0", "symfony/twig-bundle": "<5.0", "symfony/yaml": "<4.4" }, "provide": { - "symfony/translation-implementation": "2.0" + "symfony/translation-implementation": "2.3" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/console": "^4.4|^5.0", - "symfony/dependency-injection": "^5.0", - "symfony/finder": "^4.4|^5.0", - "symfony/http-kernel": "^5.0", - "symfony/intl": "^4.4|^5.0", - "symfony/service-contracts": "^1.1.2|^2", - "symfony/yaml": "^4.4|^5.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" }, "suggest": { "psr/log-implementation": "To use logging capability in translator", @@ -1846,6 +2184,9 @@ }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { "Symfony\\Component\\Translation\\": "" }, @@ -1867,8 +2208,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Translation Component", + "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v5.4.6" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1883,20 +2227,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2022-03-02T12:56:28+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.3.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105" + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e2eaa60b558f26a4b0354e1bbb25636efaaad105", - "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e", "shasum": "" }, "require": { @@ -1908,7 +2252,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1944,6 +2288,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1958,32 +2305,32 @@ "type": "tidelift" } ], - "time": "2020-09-28T13:05:58+00:00" + "time": "2021-08-17T14:20:01+00:00" }, { "name": "symfony/yaml", - "version": "v5.1.8", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f284e032c3cefefb9943792132251b79a6127ca6" + "reference": "e80f87d2c9495966768310fc531b487ce64237a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f284e032c3cefefb9943792132251b79a6127ca6", - "reference": "f284e032c3cefefb9943792132251b79a6127ca6", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2", + "reference": "e80f87d2c9495966768310fc531b487ce64237a2", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-ctype": "~1.8" + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<4.4" + "symfony/console": "<5.3" }, "require-dev": { - "symfony/console": "^4.4|^5.0" + "symfony/console": "^5.3|^6.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -2014,8 +2361,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v5.4.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2030,7 +2380,7 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:03:25+00:00" + "time": "2022-01-26T16:32:32+00:00" } ], "aliases": [], @@ -2038,7 +2388,9 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "php": ">=7.3" + }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.2.0" } diff --git a/tests/integration/vendor/behat/behat/CHANGELOG.md b/tests/integration/vendor/behat/behat/CHANGELOG.md index 19ae19f88..167b643e1 100644 --- a/tests/integration/vendor/behat/behat/CHANGELOG.md +++ b/tests/integration/vendor/behat/behat/CHANGELOG.md @@ -4,58 +4,246 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## 3.10.0 - 2021-11-02 + +## What's Changed +* PHP8 Hook attributes by @rpkamp in https://github.com/Behat/Behat/pull/1372 + +**Full Changelog**: https://github.com/Behat/Behat/compare/v3.9.1...v3.10.0 + +## 3.9.1 - 2021-11-02 + +## What's Changed +* Fix issue 1363 (Symfony 6 compatibility) by @dmaicher in https://github.com/Behat/Behat/pull/1368 +* update branch alias for dev-master by @dmaicher in https://github.com/Behat/Behat/pull/1369 +* Fix SYMFONY_REQUIRE for github action by @dmaicher in https://github.com/Behat/Behat/pull/1370 +* Issue #1373 - Replace %1% with %count% in hu translations by @Sweetchuck in https://github.com/Behat/Behat/pull/1374 + +## New Contributors +* @dmaicher made their first contribution in https://github.com/Behat/Behat/pull/1368 +* @Sweetchuck made their first contribution in https://github.com/Behat/Behat/pull/1374 + +**Full Changelog**: https://github.com/Behat/Behat/compare/v3.9.0...v3.9.1 + +## [3.9.0] - 2021-10-18 + +### What's Changed +* Fix syntax help test and bump gherkin dependency by @ciaranmcnulty in https://github.com/Behat/Behat/pull/1336 +* Remove legacy Symfony compatibility layers (#1305, #1347) by @simonhammes in https://github.com/Behat/Behat/pull/1349 +* Add PHP 8.1 support by @javer in https://github.com/Behat/Behat/pull/1355 +* Introduce reading PHP8 Attributes for Given, When and Then steps by @rpkamp in https://github.com/Behat/Behat/pull/1342 +* Allow Symfony 6 by @Kocal in https://github.com/Behat/Behat/pull/1346 +* Remove minimum-stability dev from composer.json & require Gherkin ^4.9.0 by @pamil in https://github.com/Behat/Behat/pull/1365 +* Allow to manually run GitHub Actions by @pamil in https://github.com/Behat/Behat/pull/1361 +* Add vimeo/psalm (#1307) by @simonhammes in https://github.com/Behat/Behat/pull/1348 + +### New Contributors +* @simonhammes made their first contribution in https://github.com/Behat/Behat/pull/1349 +* @javer made their first contribution in https://github.com/Behat/Behat/pull/1355 +* @Kocal made their first contribution in https://github.com/Behat/Behat/pull/1346 + +## [3.8.1] - 2020-11-07 + +### Fixed + + * [1329](https://github.com/Behat/Behat/pull/1329): Regression when using scalar type hints ([@ciaranmcnulty](https://github.com/ciaranmcnulty)) + +## [3.8.0] - 2020-11-01 + +### Added + * [1198](https://github.com/Behat/Behat/pull/1198): Korean language translations ([@getsolaris](https://github.com/getsolaris)) + * [1252](https://github.com/Behat/Behat/pull/1252): Hungarian language translations ([@kolesar-andras](https://github.com/kolesar-andras)) + * [1217](https://github.com/Behat/Behat/pull/1217): Bulgarian language translations ([@toni-kolev](https://github.com/toni-kolev)) + * [1322](https://github.com/Behat/Behat/pull/1322): Feature title as classname in JUnit output ([@steefmin](https://github.com/steefmin)) + * [1313](https://github.com/Behat/Behat/pull/1313): PHP 8 support ([@ciaranmcnulty](https://github.com/ciaranmcnulty)) + * [1313](https://github.com/Behat/Behat/pull/1323): Further PHP 8 support ([@dgafka](https://github.com/dgafka)) + +### Fixed + + * [#1303](https://github.com/Behat/Behat/pull/1303): Error when running `--debug` with recent Symfony versions ([@jawira](https://github.com/jawira)) + * [#1311](https://github.com/Behat/Behat/pull/1311): Remove symfony deprecation messages about transChoice ([@guilliamxavier](https://github.com/guilliamxavier)) + * [#1318](https://github.com/Behat/Behat/pull/1318): Allow negated filters on scenario hoooks ([@andrewnicols ](https://github.com/andrewnicols)) + +### Changed + * [#1299](https://github.com/Behat/Behat/pull/1299): Removed support for PHP <7.2, Symfony <4.4 ([@upamil](https://github.com/pamil)) + * [#1310](https://github.com/Behat/Behat/pull/1310): Refactoring to use newer language features ([@rpkamp](https://github.com/rpkamp)) + * [#1315](https://github.com/Behat/Behat/pull/1315): Remove BC layer for unsuppored symfony dispatcher ([@rpkamp](https://github.com/rpkamp)) + * [#1314](https://github.com/Behat/Behat/pull/1314): Remove BC layer for unsuppored symfony translator ([@rpkamp](https://github.com/rpkamp)) + * [#1212](https://github.com/Behat/Behat/pull/1212): Updated composer description ([@tkotosz](https://github.com/tkotosz)) + * [#1317](https://github.com/Behat/Behat/pull/1317): Use PHPUnit8 for unit testing ([@phil-davis](https://github.com/phil-davis)) + +## [3.7.0] - 2020-06-03 + +### Added + * [#1236](https://github.com/Behat/Behat/pull/1236): Add support for php 7.4 ([@snapshotpl](https://github.com/snapshotpl)) + +### Fixed + * [#1270](https://github.com/Behat/Behat/pull/1270): Fix issues with PHP version handling in build ([@Sam-Burns](https://github.com/Sam-Burns)) + * [#1282](https://github.com/Behat/Behat/pull/1282): Updated the year on Changelog dates ([@choult](https://github.com/choult)) + * [#1284](https://github.com/Behat/Behat/pull/1284): Restore PHP 5.3/5.4 compat ([@dvdoug](https://github.com/dvdoug), [@Sam-Burns](https://github.com/Sam-Burns), [@pamil](https://github.com/pamil)) + +### Changed + * [#1281](https://github.com/Behat/Behat/pull/1281): Make container-interop/container-interop optional dependency ([@upyx](https://github.com/upyx)) + +## [3.6.1] - 2020-02-06 +### Fixed + * [#1275](https://github.com/Behat/Behat/pull/1275): fix php 7.1 deprecation for ReflectionType::__toString + * [#1278](https://github.com/Behat/Behat/pull/1278): Fix fatal when unexpected symfony/event-dispatcher version is installed + +## [3.6.0] - 2020-02-04 +### Added + * [#1244](https://github.com/Behat/Behat/pull/1244): Hide internal steps from stack traces in very verbose mode +### Fixed + * [#1238](https://github.com/Behat/Behat/pull/1238): Don't run Junit output if ext-dom is not present (and suggest in composer) +### Changed + * [#1256](https://github.com/Behat/Behat/pull/1256): Update dependencies to support Symfony 5.x + * [#1171](https://github.com/Behat/Behat/pull/1171): Remove symfony/class-loader dependency + * [#1170](https://github.com/Behat/Behat/pull/1170): Switch to PSR-4 autoloading + * [#1230](https://github.com/Behat/Behat/pull/1230): PHP 7.3 support + * [#1230](https://github.com/Behat/Behat/pull/1230): Suggest ext-dom for JUnit support + +## [3.5.0] - 2018-08-10 +### Added + * [#1144](https://github.com/Behat/Behat/pull/1144): Allow to use arrays as context parameters + * [#1081](https://github.com/Behat/Behat/pull/1081): Allow passing null as a named context parameter + * [#1083](https://github.com/Behat/Behat/pull/1083): Time attribute in JUnit output + +### Changed + * [#1153](https://github.com/Behat/Behat/pull/1153): Cache pattern to regex transformations + * [#1155](https://github.com/Behat/Behat/pull/1155): Remove composer suggestions + +### Fixed + * Custom container must be public for symfony 4 + * [#1160](https://github.com/Behat/Behat/pull/1160): Register CLI services as synthetic + * [#1163](https://github.com/Behat/Behat/pull/1163): Allow for new-style symfony serialisation + * [#1130](https://github.com/Behat/Behat/pull/1130): Fix quoteless definition arguments matching with unicode characters + +## [3.4.3] - 2017-11-27 +### Fixed + * BC break due to parameters resolution in Dependency Injection Container + +## [3.4.2] - 2017-11-20 +### Added + * [#1095](https://github.com/Behat/Behat/pull/1095): Support for Symfony 4.x + * [#1096](https://github.com/Behat/Behat/pull/1096): Allow to use latest PHPUnit + +## [3.4.1] - 2017-09-18 +### Fixed + * PHP 5.3 style cleanup. + +## [3.4.0] - 2017-09-10 +### Added + * [#1071](https://github.com/Behat/Behat/pull/1071): Services auto-wiring + * [#1054](https://github.com/Behat/Behat/pull/1054): [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md) + support for helper containers. + * Support for modern PHPUnit. + +### Fixed + * [#1056](https://github.com/Behat/Behat/pull/1056): Make Gherkin aware of the + base path so it can filter correctly + +### Changed + * [#1069](https://github.com/Behat/Behat/pull/1069): Rework argument validators + +### Deprecated + * [#1054](https://github.com/Behat/Behat/pull/1054): Deprecated usage + of `Interop\Container`. Versions prior to `1.2` are not supported, but `1.2` + is a non-breaking change. If you depend heavily on `Interop`, upgrade to + `1.2`, which is still supported by helper containers. Aim to migrate to + `Psr` before Behat 4.0 shows up on horizon + * PHP versions prior to 5.6 and HHVM were dropped from CI build matrix. It + doesn't mean that we'll start using features of 5.6 yet, it just means we + don't get out of our way to support 5.3 and 5.4 anymore. In 4.0 support will + be completely dropped. + +## [3.3.1] - 2017-05-15 +### Added + * [#976](https://github.com/Behat/Behat/pull/1001): Add tests to check that + snippets treat words containing apostrophes as a single word + +### Fixed + * [#993](https://github.com/Behat/Behat/pull/993) Fix mixed arguments + organizer not marking typehinted arguments as "defined" + * [#992](https://github.com/Behat/Behat/pull/993) Do not misinterpret first + argument as a numbered argument if it is in fact typehinted + * [#1028](https://github.com/Behat/Behat/pull/1028) Parent / Child class + argument ambiguity issue with `MixedArgumentResolver` ## [3.3.0] - 2016-12-25 ### Added * [#973](https://github.com/Behat/Behat/pull/974): Added helper containers - * [#973](https://github.com/Behat/Behat/pull/974): Added `SuiteScopedResolverFactory` extension point + * [#973](https://github.com/Behat/Behat/pull/974): Added + `SuiteScopedResolverFactory` extension point + +### Removed + * Removed php 5.3 from the Travis build matrix. You can consider it official + end of support. 5.4 and 5.5 will follow shortly. ## [3.2.3] - 2016-12-25 ### Fixed - * [#971](https://github.com/Behat/Behat/pull/971): Added support for suite names with hyphens + * [#971](https://github.com/Behat/Behat/pull/971): Added support for suite + names with hyphens ## [3.2.2] - 2016-11-05 ### Fixed - * [#959](https://github.com/Behat/Behat/issues/959): Fix transformations not sorted properly on different php version + * [#959](https://github.com/Behat/Behat/issues/959): Fix transformations not + sorted properly on different php version ## [3.2.1] - 2016-09-25 ### Changed - * [#955](https://github.com/Behat/Behat/pull/955): `--snippets-for` is not required now as interactive mode is the new default - * [#954](https://github.com/Behat/Behat/pull/954): Stop execution on missing steps when running with `--stop-on-failure` and `--strict` options + * [#955](https://github.com/Behat/Behat/pull/955): `--snippets-for` is not + required now as interactive mode is the new default + * [#954](https://github.com/Behat/Behat/pull/954): Stop execution on missing + steps when running with `--stop-on-failure` and `--strict` options ## [3.2.0] - 2016-09-20 ### Added - * [#910](https://github.com/Behat/Behat/pull/910): Return type based transformations - * [#903](https://github.com/Behat/Behat/pull/903): Multiline step definitions support + * [#910](https://github.com/Behat/Behat/pull/910): Return type based + transformations + * [#903](https://github.com/Behat/Behat/pull/903): Multiline step definitions + support * [#930](https://github.com/Behat/Behat/pull/930): Whole table transformation * [#935](https://github.com/Behat/Behat/pull/935): Narrative filters in suites * [#936](https://github.com/Behat/Behat/pull/936): Debug command - * [#931](https://github.com/Behat/Behat/pull/931): Exception handlers extension point - * [#870](https://github.com/Behat/Behat/pull/870): Added build-related files and folders to .gitattributes - * [#946](https://github.com/Behat/Behat/pull/946): Official full Windows support with CI ([AppVeyor](http://appveyor.com)) on every build + * [#931](https://github.com/Behat/Behat/pull/931): Exception handlers + extension point + * [#870](https://github.com/Behat/Behat/pull/870): Added build-related files + and folders to .gitattributes + * [#946](https://github.com/Behat/Behat/pull/946): Official full Windows + support with CI ([AppVeyor](http://appveyor.com)) on every build ### Changed * [#922](https://github.com/Behat/Behat/pull/922): Snippets generation revamp - * [#920](https://github.com/Behat/Behat/pull/920): More context for pending/failed steps with progress formatter + * [#920](https://github.com/Behat/Behat/pull/920): More context for + pending/failed steps with progress formatter * [#905](https://github.com/Behat/Behat/pull/905): Transformations refactoring - * [#864](https://github.com/Behat/Behat/pull/864): Use only one autoloader if possible - * [#920](https://github.com/Behat/Behat/pull/920): Improve "No specifications found" error message + * [#864](https://github.com/Behat/Behat/pull/864): Use only one autoloader if + possible + * [#920](https://github.com/Behat/Behat/pull/920): Improve "No specifications + found" error message * Refactor changelog to follow [Keep a Changelog](http://keepachangelog.com/) * Refreshed [CONTRIBUTING.md](CONTRIBUTING.md) * Refreshed Scrutinizer config ### Fixed - * [#911](https://github.com/Behat/Behat/pull/911): Fix context isolation for Scenario Outlines - * [#860](https://github.com/Behat/Behat/pull/860): Include basepath in `generateKey` - * [#857](https://github.com/Behat/Behat/pull/857): Only cache failed scenario's for rerun - * [#933](https://github.com/Behat/Behat/pull/933): Save failed runs with suite information - * [#833](https://github.com/Behat/Behat/pull/833): Properly handle interupts on PHP7 - * [#904](https://github.com/Behat/Behat/pull/904): Provide clearer exception message when long token names used - * [#941](https://github.com/Behat/Behat/pull/941): Transformation should be allowed if printable chars are used + * [#911](https://github.com/Behat/Behat/pull/911): Fix context isolation for + Scenario Outlines + * [#860](https://github.com/Behat/Behat/pull/860): Include basepath in + `generateKey` + * [#857](https://github.com/Behat/Behat/pull/857): Only cache failed + scenario's for rerun + * [#933](https://github.com/Behat/Behat/pull/933): Save failed runs with suite + information + * [#833](https://github.com/Behat/Behat/pull/833): Properly handle interupts + on PHP7 + * [#904](https://github.com/Behat/Behat/pull/904): Provide clearer exception + message when long token names used + * [#941](https://github.com/Behat/Behat/pull/941): Transformation should be + allowed if printable chars are used ### Deprecated - * [#922](https://github.com/Behat/Behat/pull/922): `*SnippetAcceptingContext` interfaces + * [#922](https://github.com/Behat/Behat/pull/922): `*SnippetAcceptingContext` + interfaces * [#905](https://github.com/Behat/Behat/pull/905): `RuntimeTransformation` * [#905](https://github.com/Behat/Behat/pull/905): `Transformation::getPattern` * [#920](https://github.com/Behat/Behat/pull/920): `StepStat` @@ -74,7 +262,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Add Japanese translation (thanks @SNakano) * Add romanian translation for formatters (thanks @Chriton) * Add table row transformations (thanks @ciaranmcnulty) - * Add support for negative numbers without surrounding quotes (thanks @ryancookdev) + * Add support for negative numbers without surrounding quotes (thanks + @ryancookdev) * Handle case when non-existent config file is used (thanks @watermanio) * Handle non-default `error_reporting()` * Handle PHP7 errors implementing `Throwable` @@ -88,7 +277,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). * Allow suite settings with null values to exist (thanks @docteurklein) * Improve "can not generate snippets" message * Improve performance of Turnip parsing (thanks @Sam-Burns) - * Improve the snippet generation by auto-importing needed classes (thanks @stof) + * Improve the snippet generation by auto-importing needed classes (thanks + @stof) ## [3.0.15] - 2015-02-22 ### Changed @@ -793,7 +983,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed * Initial release -[Unreleased]: https://github.com/Behat/Behat/compare/v3.3.0...HEAD +[Unreleased]: https://github.com/Behat/Behat/compare/v3.9.0...master +[3.9.0]: https://github.com/Behat/Behat/compare/v3.8.1...v3.9.0 +[3.8.1]: https://github.com/Behat/Behat/compare/v3.8.0...v3.8.1 +[3.8.0]: https://github.com/Behat/Behat/compare/v3.7.0...v3.8.0 +[3.7.0]: https://github.com/Behat/Behat/compare/v3.6.1...v3.7.0 +[3.6.1]: https://github.com/Behat/Behat/compare/v3.6.0...v3.6.1 +[3.6.0]: https://github.com/Behat/Behat/compare/v3.5.0...v3.6.0 +[3.5.0]: https://github.com/Behat/Behat/compare/v3.4.3...v3.5.0 +[3.4.3]: https://github.com/Behat/Behat/compare/v3.4.2...v3.4.3 +[3.4.2]: https://github.com/Behat/Behat/compare/v3.4.1...v3.4.2 +[3.4.1]: https://github.com/Behat/Behat/compare/v3.4.0...v3.4.1 +[3.4.0]: https://github.com/Behat/Behat/compare/v3.3.1...v3.4.0 +[3.3.1]: https://github.com/Behat/Behat/compare/v3.3.0...v3.3.1 [3.3.0]: https://github.com/Behat/Behat/compare/v3.2.3...v3.3.0 [3.2.3]: https://github.com/Behat/Behat/compare/v3.2.2...v3.2.3 [3.2.2]: https://github.com/Behat/Behat/compare/v3.2.1...v3.2.2 diff --git a/tests/integration/vendor/behat/behat/README.md b/tests/integration/vendor/behat/behat/README.md index e71a77b4e..a666ce704 100644 --- a/tests/integration/vendor/behat/behat/README.md +++ b/tests/integration/vendor/behat/behat/README.md @@ -1,12 +1,11 @@ -![Behat](https://dl.dropboxusercontent.com/u/282797/behat/behat.png) +![Behat](https://github.com/Behat/logo/raw/master/logo.png) Behat is a BDD framework for PHP to help you test business expectations. [![Gitter chat](https://badges.gitter.im/Behat/Behat.svg)](https://gitter.im/Behat/Behat) [![License](https://poser.pugx.org/behat/behat/license.svg)](https://packagist.org/packages/behat/behat) -[![Unix Status](https://travis-ci.org/Behat/Behat.svg?branch=master)](https://travis-ci.org/Behat/Behat) +[![Unix Status](https://github.com/Behat/Behat/workflows/Build/badge.svg)](https://github.com/Behat/Behat/actions?query=workflow%3ABuild) [![Windows status](https://ci.appveyor.com/api/projects/status/9uc5sellmvbv02ei/branch/master?svg=true)](https://ci.appveyor.com/project/everzet/behat/branch/master) -[![HHVM Status](http://hhvm.h4cc.de/badge/behat/behat.svg?branch=master)](http://hhvm.h4cc.de/package/behat/behat) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Behat/Behat/badges/quality-score.png?s=ad84e95fc2405712f88a96d89b4f31dfe5c80fae)](https://scrutinizer-ci.com/g/Behat/Behat/) Installing Behat @@ -15,8 +14,7 @@ Installing Behat The easiest way to install Behat is by using [Composer](https://getcomposer.org): ```bash -$> curl -sS https://getcomposer.org/installer | php -$> php composer.phar require behat/behat +$> composer require --dev behat/behat ``` After that you'll be able to run Behat via: @@ -31,8 +29,7 @@ Installing Development Version Clone the repository and install dependencies via [Composer](https://getcomposer.org): ```bash -$> curl -sS https://getcomposer.org/installer | php -$> php composer.phar install +$> composer install ``` After that you will be able to run development version of Behat via: diff --git a/tests/integration/vendor/behat/behat/behat.yml.dist b/tests/integration/vendor/behat/behat/behat.yml.dist new file mode 100644 index 000000000..b2c042fa1 --- /dev/null +++ b/tests/integration/vendor/behat/behat/behat.yml.dist @@ -0,0 +1,4 @@ +default: + gherkin: + filters: + tags: ~@php8 diff --git a/tests/integration/vendor/behat/behat/composer.json b/tests/integration/vendor/behat/behat/composer.json index 95eb16ba9..f4103d188 100644 --- a/tests/integration/vendor/behat/behat/composer.json +++ b/tests/integration/vendor/behat/behat/composer.json @@ -1,6 +1,6 @@ { "name": "behat/behat", - "description": "Scenario-oriented BDD framework for PHP 5.3", + "description": "Scenario-oriented BDD framework for PHP", "keywords": ["BDD", "ScenarioBDD", "StoryBDD", "Examples", "Scrum", "Agile", "User story", "Symfony", "business", "development", "testing", "documentation"], "homepage": "http://behat.org/", "type": "library", @@ -14,42 +14,49 @@ ], "require": { - "php": ">=5.3.3", + "php": "^7.2 || ^8.0", "ext-mbstring": "*", - "behat/gherkin": "^4.4.4", - "behat/transliterator": "~1.0", - "symfony/console": "~2.5||~3.0", - "symfony/config": "~2.3||~3.0", - "symfony/dependency-injection": "~2.1||~3.0", - "symfony/event-dispatcher": "~2.1||~3.0", - "symfony/translation": "~2.3||~3.0", - "symfony/yaml": "~2.1||~3.0", - "symfony/class-loader": "~2.1||~3.0", - "container-interop/container-interop": "^1.1" + "behat/gherkin": "^4.9.0", + "behat/transliterator": "^1.2", + "symfony/console": "^4.4 || ^5.0 || ^6.0", + "symfony/config": "^4.4 || ^5.0 || ^6.0", + "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", + "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", + "symfony/translation": "^4.4 || ^5.0 || ^6.0", + "symfony/yaml": "^4.4 || ^5.0 || ^6.0", + "psr/container": "^1.0" }, "require-dev": { - "symfony/process": "~2.5|~3.0", - "phpunit/phpunit": "~4.5", - "herrera-io/box": "~1.6.1" + "symfony/process": "^4.4 || ^5.0 || ^6.0", + "phpunit/phpunit": "^8.5 || ^9.0", + "herrera-io/box": "~1.6.1", + "container-interop/container-interop": "^1.2", + "vimeo/psalm": "^4.8" }, "suggest": { - "behat/symfony2-extension": "for integration with Symfony2 web framework", - "behat/yii-extension": "for integration with Yii web framework", - "behat/mink-extension": "for integration with Mink testing framework" + "ext-dom": "Needed to output test results in JUnit format." }, "autoload": { - "psr-0": { - "Behat\\Behat": "src/", - "Behat\\Testwork": "src/" + "psr-4": { + "Behat\\Behat\\": "src/Behat/Behat/", + "Behat\\Testwork\\": "src/Behat/Testwork/", + "Behat\\Step\\": "src/Behat/Step/", + "Behat\\Hook\\": "src/Behat/Hook/" + } + }, + + "autoload-dev": { + "psr-4": { + "Behat\\Tests\\": "tests/" } }, "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "3.x-dev" } }, diff --git a/tests/integration/vendor/behat/behat/i18n.php b/tests/integration/vendor/behat/behat/i18n.php index 44eda3c91..4cb059fbc 100644 --- a/tests/integration/vendor/behat/behat/i18n.php +++ b/tests/integration/vendor/behat/behat/i18n.php @@ -1,217 +1,267 @@ array( - 'snippet_context_choice' => '%1% suite has undefined steps. Please choose the context to generate snippets:', - 'snippet_proposal_title' => '%1% has missing steps. Define them with these snippets:', - 'snippet_missing_title' => 'Use --snippets-for CLI option to generate snippets for following %1% suite steps:', + 'snippet_context_choice' => '%count% suite has undefined steps. Please choose the context to generate snippets:', + 'snippet_proposal_title' => '%count% has missing steps. Define them with these snippets:', + 'snippet_missing_title' => 'Use --snippets-for CLI option to generate snippets for following %count% suite steps:', 'skipped_scenarios_title' => 'Skipped scenarios:', 'failed_scenarios_title' => 'Failed scenarios:', 'failed_hooks_title' => 'Failed hooks:', 'failed_steps_title' => 'Failed steps:', 'pending_steps_title' => 'Pending steps:', - 'scenarios_count' => '{0} No scenarios|{1} 1 scenario|]1,Inf] %1% scenarios', - 'steps_count' => '{0} No steps|{1} 1 step|]1,Inf] %1% steps', - 'passed_count' => '[1,Inf] %1% passed', - 'failed_count' => '[1,Inf] %1% failed', - 'pending_count' => '[1,Inf] %1% pending', - 'undefined_count' => '[1,Inf] %1% undefined', - 'skipped_count' => '[1,Inf] %1% skipped', + 'scenarios_count' => '{0} No scenarios|{1} 1 scenario|]1,Inf] %count% scenarios', + 'steps_count' => '{0} No steps|{1} 1 step|]1,Inf] %count% steps', + 'passed_count' => '[1,Inf] %count% passed', + 'failed_count' => '[1,Inf] %count% failed', + 'pending_count' => '[1,Inf] %count% pending', + 'undefined_count' => '[1,Inf] %count% undefined', + 'skipped_count' => '[1,Inf] %count% skipped', + ), + 'bg' => array( + 'snippet_context_choice' => 'В сет %1% има недекларирани стъпки. Изберете в кой Context да бъдат създадени:', + 'snippet_proposal_title' => '%1% има липсващи стъпки. Можете да ги създадете чрез:', + 'snippet_missing_title' => 'Използвайте този снипет --snippets-for за да генерирате кода за следните стъпки %1% през конзолата:', + 'skipped_scenarios_title' => 'Пропуснати сценарии:', + 'failed_scenarios_title' => 'Провалени сценарии:', + 'failed_hooks_title' => 'Провалени хукове:', + 'failed_steps_title' => 'Провалени стъпки:', + 'pending_steps_title' => 'Изчакващи стъпки:', + 'scenarios_count' => '{0} Няма сценарий|{1} 1 сценарий|]1,Inf] %1% сценарии', + 'steps_count' => '{0} Няма стъпки|{1} 1 стъпка|]1,Inf] %1% стъпки', + 'passed_count' => '[1,Inf] %1% успешни', + 'failed_count' => '[1,Inf] %1% провалени', + 'pending_count' => '[1,Inf] %1% изчакващи', + 'undefined_count' => '[1,Inf] %1% неопределени', + 'skipped_count' => '[1,Inf] %1% пропуснати', ), 'cs' => array( - 'snippet_proposal_title' => '%1% obsahuje chybné kroky. Definujte je za použití následujícího kódu:', - 'snippet_missing_title' => 'Snippety pro následující kroky v sadě %1% nebyly vygenerovány (zkontrolujte správnost konfigurace):', + 'snippet_proposal_title' => '%count% obsahuje chybné kroky. Definujte je za použití následujícího kódu:', + 'snippet_missing_title' => 'Snippety pro následující kroky v sadě %count% nebyly vygenerovány (zkontrolujte správnost konfigurace):', 'failed_scenarios_title' => 'Chybné scénáře:', 'failed_hooks_title' => 'Chybné hooky:', 'failed_steps_title' => 'Chybné kroky:', 'pending_steps_title' => 'Čekající kroky:', - 'scenarios_count' => '{0} Žádný scénář|{1} 1 scénář|{2,3,4} %1% scénáře|]4,Inf] %1% scénářů', - 'steps_count' => '{0} Žádné kroky|{1} 1 krok|{2,3,4} %1% kroky|]4,Inf] %1% kroků', - 'passed_count' => '{1} %1% prošel|{2,3,4} %1% prošly|]4,Inf] %1% prošlo', - 'failed_count' => '{1} %1% selhal|{2,3,4} %1% selhaly|]4,Inf] %1% selhalo', - 'pending_count' => '{1} %1% čeká|{2,3,4} %1% čekají|]4,Inf] %1% čeká', - 'undefined_count' => '{1} %1% nedefinován|{2,3,4} %1% nedefinovány|]4,Inf] %1% nedefinováno', - 'skipped_count' => '{1} %1% přeskočen|{2,3,4} %1% přeskočeny|]4,Inf] %1% přeskočeno', + 'scenarios_count' => '{0} Žádný scénář|{1} 1 scénář|{2,3,4} %count% scénáře|]4,Inf] %count% scénářů', + 'steps_count' => '{0} Žádné kroky|{1} 1 krok|{2,3,4} %count% kroky|]4,Inf] %count% kroků', + 'passed_count' => '{1} %count% prošel|{2,3,4} %count% prošly|]4,Inf] %count% prošlo', + 'failed_count' => '{1} %count% selhal|{2,3,4} %count% selhaly|]4,Inf] %count% selhalo', + 'pending_count' => '{1} %count% čeká|{2,3,4} %count% čekají|]4,Inf] %count% čeká', + 'undefined_count' => '{1} %count% nedefinován|{2,3,4} %count% nedefinovány|]4,Inf] %count% nedefinováno', + 'skipped_count' => '{1} %count% přeskočen|{2,3,4} %count% přeskočeny|]4,Inf] %count% přeskočeno', ), 'de' => array( - 'snippet_proposal_title' => '%1% hat fehlende Schritte. Definiere diese mit den folgenden Snippets:', - 'snippet_missing_title' => 'Snippets für die folgenden Schritte in der %1% Suite wurden nicht generiert (Konfiguration überprüfen):', + 'snippet_proposal_title' => '%count% hat fehlende Schritte. Definiere diese mit den folgenden Snippets:', + 'snippet_missing_title' => 'Snippets für die folgenden Schritte in der %count% Suite wurden nicht generiert (Konfiguration überprüfen):', 'failed_scenarios_title' => 'Fehlgeschlagene Szenarien:', 'failed_hooks_title' => 'Fehlgeschlagene Hooks:', 'failed_steps_title' => 'Fehlgeschlagene Schritte:', 'pending_steps_title' => 'Ausstehende Schritte:', - 'scenarios_count' => '{0} Kein Szenario|{1} 1 Szenario|]1,Inf] %1% Szenarien', - 'steps_count' => '{0} Kein Schritt|{1} 1 Schritt|]1,Inf] %1% Schritte', - 'passed_count' => '[1,Inf] %1% bestanden', - 'failed_count' => '[1,Inf] %1% fehlgeschlagen', - 'pending_count' => '[1,Inf] %1% ausstehend', - 'undefined_count' => '[1,Inf] %1% nicht definiert', - 'skipped_count' => '[1,Inf] %1% übersprungen', + 'scenarios_count' => '{0} Kein Szenario|{1} 1 Szenario|]1,Inf] %count% Szenarien', + 'steps_count' => '{0} Kein Schritt|{1} 1 Schritt|]1,Inf] %count% Schritte', + 'passed_count' => '[1,Inf] %count% bestanden', + 'failed_count' => '[1,Inf] %count% fehlgeschlagen', + 'pending_count' => '[1,Inf] %count% ausstehend', + 'undefined_count' => '[1,Inf] %count% nicht definiert', + 'skipped_count' => '[1,Inf] %count% übersprungen', ), 'es' => array( - 'snippet_proposal_title' => 'A %1% le faltan pasos. Defínelos con estos pasos:', - 'snippet_missing_title' => 'Las plantillas para los siguientes pasos en %1% no fueron generadas (revisa tu configuración):', + 'snippet_proposal_title' => 'A %count% le faltan pasos. Defínelos con estos pasos:', + 'snippet_missing_title' => 'Las plantillas para los siguientes pasos en %count% no fueron generadas (revisa tu configuración):', 'failed_scenarios_title' => 'Escenarios fallidos:', 'failed_hooks_title' => 'Hooks fallidos:', 'failed_steps_title' => 'Pasos fallidos:', 'pending_steps_title' => 'Pasos pendientes:', - 'scenarios_count' => '{0} Ningún escenario|{1} 1 escenario|]1,Inf] %1% escenarios', - 'steps_count' => '{0} Ningún paso|{1} 1 paso|]1,Inf] %1% pasos', - 'passed_count' => '[1,Inf] %1% pasaron', - 'failed_count' => '[1,Inf] %1% fallaron', - 'pending_count' => '[1,Inf] %1% pendientes', - 'undefined_count' => '[1,Inf] %1% por definir', - 'skipped_count' => '[1,Inf] %1% saltadas', + 'scenarios_count' => '{0} Ningún escenario|{1} 1 escenario|]1,Inf] %count% escenarios', + 'steps_count' => '{0} Ningún paso|{1} 1 paso|]1,Inf] %count% pasos', + 'passed_count' => '[1,Inf] %count% pasaron', + 'failed_count' => '[1,Inf] %count% fallaron', + 'pending_count' => '[1,Inf] %count% pendientes', + 'undefined_count' => '[1,Inf] %count% por definir', + 'skipped_count' => '[1,Inf] %count% saltadas', ), 'fr' => array( - 'snippet_proposal_title' => '%1% a des étapes manquantes. Définissez-les avec les modèles suivants :', - 'snippet_missing_title' => 'Les modèles des étapes de la suite %1% n\'ont pas été générés (vérifiez votre configuration):', + 'snippet_proposal_title' => '%count% a des étapes manquantes. Définissez-les avec les modèles suivants :', + 'snippet_missing_title' => 'Les modèles des étapes de la suite %count% n\'ont pas été générés (vérifiez votre configuration):', 'failed_scenarios_title' => 'Scénarios échoués:', 'failed_hooks_title' => 'Hooks échoués:', 'failed_steps_title' => 'Etapes échouées:', 'pending_steps_title' => 'Etapes en attente:', - 'scenarios_count' => '{0} Pas de scénario|{1} 1 scénario|]1,Inf] %1% scénarios', - 'steps_count' => '{0} Pas d\'étape|{1} 1 étape|]1,Inf] %1% étapes', - 'passed_count' => '[1,Inf] %1% succès', - 'failed_count' => '[1,Inf] %1% échecs', - 'pending_count' => '[1,Inf] %1% en attente', - 'undefined_count' => '[1,Inf] %1% indéfinis', - 'skipped_count' => '[1,Inf] %1% ignorés', + 'scenarios_count' => '{0} Pas de scénario|{1} 1 scénario|]1,Inf] %count% scénarios', + 'steps_count' => '{0} Pas d\'étape|{1} 1 étape|]1,Inf] %count% étapes', + 'passed_count' => '[1,Inf] %count% succès', + 'failed_count' => '[1,Inf] %count% échecs', + 'pending_count' => '[1,Inf] %count% en attente', + 'undefined_count' => '[1,Inf] %count% indéfinis', + 'skipped_count' => '[1,Inf] %count% ignorés', + ), + 'hu' => array( + 'snippet_context_choice' => '%count% sorozat meghatározatlan lépéseket tartalmaz. Válaszd ki a környezetet kódrészlet készítéséhez:', + 'snippet_proposal_title' => '%count% lépései hiányosak. Hozd létre az alábbi kódrészletekkel:', + 'snippet_missing_title' => 'Használd a --snippets-for parancssori kapcsolót kódrészletek készítéséhez a %count% sorozat lépéseihez:', + 'skipped_scenarios_title' => 'Kihagyott forgatókönyvek:', + 'failed_scenarios_title' => 'Sikertelen forgatókönyvek:', + 'failed_hooks_title' => 'Sikertelen kampók:', + 'failed_steps_title' => 'Sikertelen lépések:', + 'pending_steps_title' => 'Elintézendő lépések:', + 'scenarios_count' => '{0} Nem volt forgatókönyv|{1} 1 forgatókönyv|]1,Inf] %count% forgatókönyv', + 'steps_count' => '{0} Nem volt lépés|{1} 1 lépés|]1,Inf] %count% lépés', + 'passed_count' => '[1,Inf] %count% sikeres', + 'failed_count' => '[1,Inf] %count% sikertelen', + 'pending_count' => '[1,Inf] %count% elintézendő', + 'undefined_count' => '[1,Inf] %count% meghatározatlan', + 'skipped_count' => '[1,Inf] %count% kihagyott', ), 'it' => array( - 'snippet_proposal_title' => '%1% ha dei passaggi mancanti. Definiscili con questi snippet:', - 'snippet_missing_title' => 'Gli snippet per i seguenti passaggi della suite %1% non sono stati generati (verifica la configurazione):', + 'snippet_proposal_title' => '%count% ha dei passaggi mancanti. Definiscili con questi snippet:', + 'snippet_missing_title' => 'Gli snippet per i seguenti passaggi della suite %count% non sono stati generati (verifica la configurazione):', 'failed_scenarios_title' => 'Scenari falliti:', 'failed_hooks_title' => 'Hook falliti:', 'failed_steps_title' => 'Passaggi falliti:', 'pending_steps_title' => 'Passaggi in sospeso:', - 'scenarios_count' => '{0} Nessuno scenario|{1} 1 scenario|]1,Inf] %1% scenari', - 'steps_count' => '{0} Nessun passaggio|{1} 1 passaggio|]1,Inf] %1% passaggi', - 'passed_count' => '{1} 1 superato|]1,Inf] %1% superati', - 'failed_count' => '{1} 1 fallito|]1,Inf] %1% falliti', - 'pending_count' => '[1,Inf] %1% in sospeso', - 'undefined_count' => '{1} 1 non definito|]1,Inf] %1% non definiti', - 'skipped_count' => '{1} 1 ignorato|]1,Inf] %1% ignorati', + 'scenarios_count' => '{0} Nessuno scenario|{1} 1 scenario|]1,Inf] %count% scenari', + 'steps_count' => '{0} Nessun passaggio|{1} 1 passaggio|]1,Inf] %count% passaggi', + 'passed_count' => '{1} 1 superato|]1,Inf] %count% superati', + 'failed_count' => '{1} 1 fallito|]1,Inf] %count% falliti', + 'pending_count' => '[1,Inf] %count% in sospeso', + 'undefined_count' => '{1} 1 non definito|]1,Inf] %count% non definiti', + 'skipped_count' => '{1} 1 ignorato|]1,Inf] %count% ignorati', ), 'ja' => array( - 'snippet_proposal_title' => '%1% のステップが見つかりません。 次のスニペットで定義できます:', - 'snippet_missing_title' => '以下のステップのスニペットは%1%スイートに生成されませんでした(設定を確認してください):', + 'snippet_proposal_title' => '%count% のステップが見つかりません。 次のスニペットで定義できます:', + 'snippet_missing_title' => '以下のステップのスニペットは%count%スイートに生成されませんでした(設定を確認してください):', 'skipped_scenarios_title' => 'スキップした シナリオ:', 'failed_scenarios_title' => '失敗した シナリオ:', 'failed_hooks_title' => '失敗した フック:', 'failed_steps_title' => '失敗した ステップ:', 'pending_steps_title' => '保留中のステップ:', - 'scenarios_count' => '{0} No scenarios|{1} 1 個のシナリオ|]1,Inf] %1% 個のシナリオ', - 'steps_count' => '{0} ステップがありません|{1} 1 個のステップ|]1,Inf] %1% 個のステップ', - 'passed_count' => '[1,Inf] %1% 個成功', - 'failed_count' => '[1,Inf] %1% 個失敗', - 'pending_count' => '[1,Inf] %1% 個保留', - 'undefined_count' => '[1,Inf] %1% 個未定義', - 'skipped_count' => '[1,Inf] %1% 個スキップ', + 'scenarios_count' => '{0} No scenarios|{1} 1 個のシナリオ|]1,Inf] %count% 個のシナリオ', + 'steps_count' => '{0} ステップがありません|{1} 1 個のステップ|]1,Inf] %count% 個のステップ', + 'passed_count' => '[1,Inf] %count% 個成功', + 'failed_count' => '[1,Inf] %count% 個失敗', + 'pending_count' => '[1,Inf] %count% 個保留', + 'undefined_count' => '[1,Inf] %count% 個未定義', + 'skipped_count' => '[1,Inf] %count% 個スキップ', + ), + 'ko' => array( + 'snippet_proposal_title' => '%1% 정의가 되지 않았습니다. 스니펫을 생성할 컨텍스트를 선택하십시오:', + 'snippet_missing_title' => '%1% 단계가 누락되었습니다. 스니펫을 정의해주세요:', + 'skipped_scenarios_title' => '건너뛴 시나리오:', + 'failed_scenarios_title' => '실패한 시나리오:', + 'failed_hooks_title' => '실패한 훅 연결:', + 'failed_steps_title' => '실패한 단계:', + 'pending_steps_title' => '준비중인 단계:', + 'scenarios_count' => '{0} 없는 시나리오들|{1} 1 시나리오|]1,Inf] %1% 시나리오들', + 'steps_count' => '{0} 없는 단계들|{1} 1 단계|]1,Inf] %1% 단계들', + 'passed_count' => '[1,Inf] %1% 통과', + 'failed_count' => '[1,Inf] %1% 실패', + 'pending_count' => '[1,Inf] %1% 준비중', + 'undefined_count' => '[1,Inf] %1% 정의되지 않았습니다.', + 'skipped_count' => '[1,Inf] %1% 건너뜀', ), 'nl' => array( - 'snippet_proposal_title' => 'Ontbrekende stappen in %1%. Definieer ze met de volgende fragmenten:', - 'snippet_missing_title' => 'Fragmenten voor de volgende stappen in de %1% suite werden niet gegenereerd (controleer de configuratie):', + 'snippet_proposal_title' => 'Ontbrekende stappen in %count%. Definieer ze met de volgende fragmenten:', + 'snippet_missing_title' => 'Fragmenten voor de volgende stappen in de %count% suite werden niet gegenereerd (controleer de configuratie):', 'failed_scenarios_title' => 'Gefaalde scenario\'s:', 'failed_hooks_title' => 'Gefaalde hooks:', 'failed_steps_title' => 'Gefaalde stappen:', 'pending_steps_title' => 'Onafgewerkte stappen:', - 'scenarios_count' => '{0} Geen scenario\'s|{1} 1 scenario|]1,Inf] %1% scenario\'s', - 'steps_count' => '{0} Geen stappen|{1} 1 stap|]1,Inf] %1% stappen', - 'passed_count' => '[1,Inf] %1% geslaagd', - 'failed_count' => '[1,Inf] %1% gefaald', - 'pending_count' => '[1,Inf] %1% wachtende', - 'undefined_count' => '[1,Inf] %1% niet gedefinieerd', - 'skipped_count' => '[1,Inf] %1% overgeslagen', + 'scenarios_count' => '{0} Geen scenario\'s|{1} 1 scenario|]1,Inf] %count% scenario\'s', + 'steps_count' => '{0} Geen stappen|{1} 1 stap|]1,Inf] %count% stappen', + 'passed_count' => '[1,Inf] %count% geslaagd', + 'failed_count' => '[1,Inf] %count% gefaald', + 'pending_count' => '[1,Inf] %count% wachtende', + 'undefined_count' => '[1,Inf] %count% niet gedefinieerd', + 'skipped_count' => '[1,Inf] %count% overgeslagen', ), 'no' => array( - 'snippet_proposal_title' => '%1% mangler steg. Definer dem med disse snuttene:', - 'snippet_missing_title' => 'Snutter for de følgende stegene i %1%-samlingen ble ikke laget. (Sjekk konfigurasjonen din.):', + 'snippet_proposal_title' => '%count% mangler steg. Definer dem med disse snuttene:', + 'snippet_missing_title' => 'Snutter for de følgende stegene i %count%-samlingen ble ikke laget. (Sjekk konfigurasjonen din.):', 'failed_scenarios_title' => 'Feilende scenarier:', 'failed_hooks_title' => 'Feilende hooks:', 'failed_steps_title' => 'Feilende steg:', 'pending_steps_title' => 'Ikke implementerte steg:', - 'scenarios_count' => '{0} Ingen scenarier|{1} 1 scenario|]1,Inf] %1% scenarier', - 'steps_count' => '{0} Ingen steg|{1} 1 steg|]1,Inf] %1% steg', - 'passed_count' => '[1,Inf] %1% ok', - 'failed_count' => '[1,Inf] %1% feilet', - 'pending_count' => '[1,Inf] %1% ikke implementert', - 'undefined_count' => '[1,Inf] %1% ikke definert', - 'skipped_count' => '[1,Inf] %1% hoppet over', + 'scenarios_count' => '{0} Ingen scenarier|{1} 1 scenario|]1,Inf] %count% scenarier', + 'steps_count' => '{0} Ingen steg|{1} 1 steg|]1,Inf] %count% steg', + 'passed_count' => '[1,Inf] %count% ok', + 'failed_count' => '[1,Inf] %count% feilet', + 'pending_count' => '[1,Inf] %count% ikke implementert', + 'undefined_count' => '[1,Inf] %count% ikke definert', + 'skipped_count' => '[1,Inf] %count% hoppet over', ), 'pl' => array( - 'snippet_proposal_title' => '%1% zawiera brakujące kroki. Utwórz je korzystając z tych fragmentów kodu:', - 'snippet_missing_title' => 'Fragmenty kodu dla następujących kroków %1% nie zostały wygenerowane (sprawdź swoją konfigurację):', + 'snippet_proposal_title' => '%count% zawiera brakujące kroki. Utwórz je korzystając z tych fragmentów kodu:', + 'snippet_missing_title' => 'Fragmenty kodu dla następujących kroków %count% nie zostały wygenerowane (sprawdź swoją konfigurację):', 'failed_scenarios_title' => 'Nieudane scenariusze:', 'failed_hooks_title' => 'Nieudane hooki:', 'failed_steps_title' => 'Nieudane kroki', 'pending_steps_title' => 'Oczekujące kroki', - 'scenarios_count' => '{0} Brak scenariuszy|{1} 1 scenariusz|{2,3,4,22,23,24,32,33,34,42,43,44} %1% scenariusze|]4,Inf] %1% scenariuszy', - 'steps_count' => '{0} Brak kroków|{1} 1 krok|{2,3,4,22,23,24,32,33,34,42,43,44} %1% kroki|]4,Inf] %1% kroków', - 'passed_count' => '{1} %1% udany|{2,3,4,22,23,24,32,33,34,42,43,44} %1% udane|]4,Inf] %1% udanych', - 'failed_count' => '{1} %1% nieudany|{2,3,4,22,23,24,32,33,34,42,43,44} %1% nieudane|]4,Inf] %1% nieudanych', - 'pending_count' => '{1} %1% oczekujący|{2,3,4,22,23,24,32,33,34,42,43,44} %1% oczekujące|]4,Inf] %1% oczekujących', - 'undefined_count' => '{1} %1% niezdefiniowany|{2,3,4,22,23,24,32,33,34,42,43,44} %1% niezdefiniowane|]4,Inf] %1% niezdefiniowanych', - 'skipped_count' => '{1} %1% pominięty|{2,3,4,22,23,24,32,33,34,42,43,44} %1% pominięte|]4,Inf] %1% pominiętych', + 'scenarios_count' => '{0} Brak scenariuszy|{1} 1 scenariusz|{2,3,4,22,23,24,32,33,34,42,43,44} %count% scenariusze|]4,Inf] %count% scenariuszy', + 'steps_count' => '{0} Brak kroków|{1} 1 krok|{2,3,4,22,23,24,32,33,34,42,43,44} %count% kroki|]4,Inf] %count% kroków', + 'passed_count' => '{1} %count% udany|{2,3,4,22,23,24,32,33,34,42,43,44} %count% udane|]4,Inf] %count% udanych', + 'failed_count' => '{1} %count% nieudany|{2,3,4,22,23,24,32,33,34,42,43,44} %count% nieudane|]4,Inf] %count% nieudanych', + 'pending_count' => '{1} %count% oczekujący|{2,3,4,22,23,24,32,33,34,42,43,44} %count% oczekujące|]4,Inf] %count% oczekujących', + 'undefined_count' => '{1} %count% niezdefiniowany|{2,3,4,22,23,24,32,33,34,42,43,44} %count% niezdefiniowane|]4,Inf] %count% niezdefiniowanych', + 'skipped_count' => '{1} %count% pominięty|{2,3,4,22,23,24,32,33,34,42,43,44} %count% pominięte|]4,Inf] %count% pominiętych', ), 'pt' => array( - 'snippet_proposal_title' => '%1% contém definições em falta. Defina-as com estes exemplos:', - 'snippet_missing_title' => 'Os exemplos para as seguintes definições da suite %1% não foram gerados (verifique a configuração):', + 'snippet_proposal_title' => '%count% contém definições em falta. Defina-as com estes exemplos:', + 'snippet_missing_title' => 'Os exemplos para as seguintes definições da suite %count% não foram gerados (verifique a configuração):', 'failed_scenarios_title' => 'Cenários que falharam:', 'failed_hooks_title' => 'Hooks que falharam:', 'failed_steps_title' => 'Definições que falharam:', 'pending_steps_title' => 'Definições por definir:', - 'scenarios_count' => '{0} Nenhum cenário|{1} 1 cenário|]1,Inf] %1% cenários', - 'steps_count' => '{0} Nenhuma definição|{1} 1 definição|]1,Inf] %1% definições', - 'passed_count' => '{1} passou|]1,Inf] %1% passaram', - 'failed_count' => '{1} falhou|]1,Inf] %1% falharam', - 'pending_count' => '[1,Inf] %1% por definir', - 'undefined_count' => '{1} indefinido|]1,Inf] %1% indefinidos', - 'skipped_count' => '{1} omitido|]1,Inf] %1% omitidos', + 'scenarios_count' => '{0} Nenhum cenário|{1} 1 cenário|]1,Inf] %count% cenários', + 'steps_count' => '{0} Nenhuma definição|{1} 1 definição|]1,Inf] %count% definições', + 'passed_count' => '{1} passou|]1,Inf] %count% passaram', + 'failed_count' => '{1} falhou|]1,Inf] %count% falharam', + 'pending_count' => '[1,Inf] %count% por definir', + 'undefined_count' => '{1} indefinido|]1,Inf] %count% indefinidos', + 'skipped_count' => '{1} omitido|]1,Inf] %count% omitidos', ), 'pt-BR' => array( - 'snippet_proposal_title' => '%1% possue etapas faltando. Defina elas com esse(s) trecho(s) de código:', - 'snippet_missing_title' => 'Trecho de códigos para as seguintes etapas em %1% suite não foram geradas (verique sua configuração):', + 'snippet_proposal_title' => '%count% possue etapas faltando. Defina elas com esse(s) trecho(s) de código:', + 'snippet_missing_title' => 'Trecho de códigos para as seguintes etapas em %count% suite não foram geradas (verique sua configuração):', 'failed_scenarios_title' => 'Cenários falhados:', 'failed_hooks_title' => 'Hooks falhados:', 'failed_steps_title' => 'Etapas falhadas:', 'pending_steps_title' => 'Etapas pendentes:', - 'scenarios_count' => '{0} Nenhum cenário|{1} 1 cenário|]1,Inf] %1% cenários', - 'steps_count' => '{0} Nenhuma etapa|{1} 1 etapa|]1,Inf] %1% etapas', - 'passed_count' => '[1,Inf] %1% passou', - 'failed_count' => '[1,Inf] %1% falhou', - 'pending_count' => '[1,Inf] %1% pendente', - 'undefined_count' => '[1,Inf] %1% indefinido', - 'skipped_count' => '[1,Inf] %1% pulado', + 'scenarios_count' => '{0} Nenhum cenário|{1} 1 cenário|]1,Inf] %count% cenários', + 'steps_count' => '{0} Nenhuma etapa|{1} 1 etapa|]1,Inf] %count% etapas', + 'passed_count' => '[1,Inf] %count% passou', + 'failed_count' => '[1,Inf] %count% falhou', + 'pending_count' => '[1,Inf] %count% pendente', + 'undefined_count' => '[1,Inf] %count% indefinido', + 'skipped_count' => '[1,Inf] %count% pulado', ), 'ro' => array( - 'snippet_proposal_title' => '%1% are pași lipsa. Puteți implementa pașii cu ajutorul acestor fragmente de cod:', - 'snippet_missing_title' => 'Fragmentele de cod pentru urmatorii pași din suita %1% nu au fost generate (contextul tau implementeaza interfata SnippetAcceptingContext?):', + 'snippet_proposal_title' => '%count% are pași lipsa. Puteți implementa pașii cu ajutorul acestor fragmente de cod:', + 'snippet_missing_title' => 'Fragmentele de cod pentru urmatorii pași din suita %count% nu au fost generate (contextul tau implementeaza interfata SnippetAcceptingContext?):', 'skipped_scenarios_title' => 'Scenarii omise:', 'failed_scenarios_title' => 'Scenarii eșuate:', 'failed_hooks_title' => 'Hook-uri eșuate:', 'failed_steps_title' => 'Pași esuați:', 'pending_steps_title' => 'Pași in așteptare:', - 'scenarios_count' => '{0} Niciun scenariu|{1} 1 scenariu|]1,Inf] %1% scenarii', - 'steps_count' => '{0} Niciun pas|{1} 1 pas|]1,Inf] %1% pasi', - 'passed_count' => '[1,Inf] %1% cu succes', - 'failed_count' => '[1,Inf] %1% fara success', - 'pending_count' => '[1,Inf] %1% in așteptare', - 'undefined_count' => '[1,Inf] %1% fara implementare', - 'skipped_count' => '{1} %1% omis|]1,Inf] %1% omiși', + 'scenarios_count' => '{0} Niciun scenariu|{1} 1 scenariu|]1,Inf] %count% scenarii', + 'steps_count' => '{0} Niciun pas|{1} 1 pas|]1,Inf] %count% pasi', + 'passed_count' => '[1,Inf] %count% cu succes', + 'failed_count' => '[1,Inf] %count% fara success', + 'pending_count' => '[1,Inf] %count% in așteptare', + 'undefined_count' => '[1,Inf] %count% fara implementare', + 'skipped_count' => '{1} %count% omis|]1,Inf] %count% omiși', ), 'ru' => array( - 'snippet_proposal_title' => '%1% не содержит необходимых определений. Вы можете добавить их используя шаблоны:', - 'snippet_missing_title' => 'Шаблоны для следующих шагов в среде %1% не были сгенерированы (проверьте ваши настройки):', + 'snippet_proposal_title' => '%count% не содержит необходимых определений. Вы можете добавить их используя шаблоны:', + 'snippet_missing_title' => 'Шаблоны для следующих шагов в среде %count% не были сгенерированы (проверьте ваши настройки):', 'skipped_scenarios_title' => 'Пропущенные сценарии:', 'failed_scenarios_title' => 'Проваленные сценарии:', 'failed_hooks_title' => 'Проваленные хуки:', 'failed_steps_title' => 'Проваленные шаги:', 'pending_steps_title' => 'Шаги в ожидании:', - 'scenarios_count' => '{0} Нет сценариев|{1,21,31} %1% сценарий|{2,3,4,22,23,24} %1% сценария|]4,Inf] %1% сценариев', - 'steps_count' => '{0} Нет шагов|{1,21,31} %1% шаг|{2,3,4,22,23,24} %1% шага|]4,Inf] %1% шагов', - 'passed_count' => '{1,21,31} %1% пройден|]1,Inf] %1% пройдено', - 'failed_count' => '{1,21,31} %1% провален|]1,Inf] %1% провалено', - 'pending_count' => '[1,Inf] %1% в ожидании', - 'undefined_count' => '{1,21,31} %1% не определен|]1,Inf] %1% не определено', - 'skipped_count' => '{1,21,31} %1% пропущен|]1,Inf] %1% пропущено', + 'scenarios_count' => '{0} Нет сценариев|{1,21,31} %count% сценарий|{2,3,4,22,23,24} %count% сценария|]4,Inf] %count% сценариев', + 'steps_count' => '{0} Нет шагов|{1,21,31} %count% шаг|{2,3,4,22,23,24} %count% шага|]4,Inf] %count% шагов', + 'passed_count' => '{1,21,31} %count% пройден|]1,Inf] %count% пройдено', + 'failed_count' => '{1,21,31} %count% провален|]1,Inf] %count% провалено', + 'pending_count' => '[1,Inf] %count% в ожидании', + 'undefined_count' => '{1,21,31} %count% не определен|]1,Inf] %count% не определено', + 'skipped_count' => '{1,21,31} %count% пропущен|]1,Inf] %count% пропущено', ), ); diff --git a/tests/integration/vendor/behat/behat/psalm.xml b/tests/integration/vendor/behat/behat/psalm.xml new file mode 100644 index 000000000..99f6bdb8d --- /dev/null +++ b/tests/integration/vendor/behat/behat/psalm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/ApplicationFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/ApplicationFactory.php index 22bf764ce..df699077e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/ApplicationFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/ApplicationFactory.php @@ -46,7 +46,7 @@ */ final class ApplicationFactory extends BaseFactory { - const VERSION = '3.3.0'; + public const VERSION = '3.8.1'; /** * {@inheritdoc} @@ -110,19 +110,23 @@ protected function getEnvironmentVariableName() */ protected function getConfigPath() { - $cwd = rtrim(getcwd(), DIRECTORY_SEPARATOR); - $paths = array_filter( - array( - $cwd . DIRECTORY_SEPARATOR . 'behat.yml', - $cwd . DIRECTORY_SEPARATOR . 'behat.yml.dist', - $cwd . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'behat.yml', - $cwd . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'behat.yml.dist', - ), - 'is_file' + $cwd = rtrim(getcwd(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + $configDir = $cwd . 'config' . DIRECTORY_SEPARATOR; + $paths = array( + $cwd . 'behat.yaml', + $cwd . 'behat.yml', + $cwd . 'behat.yaml.dist', + $cwd . 'behat.yml.dist', + $configDir . 'behat.yaml', + $configDir . 'behat.yml', + $configDir . 'behat.yaml.dist', + $configDir . 'behat.yml.dist', ); - if (count($paths)) { - return current($paths); + foreach ($paths as $path) { + if (is_file($path)) { + return $path; + } } return null; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Annotation/DocBlockHelper.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Annotation/DocBlockHelper.php new file mode 100644 index 000000000..6b4d4ae53 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Annotation/DocBlockHelper.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Annotation; + +/** + * Helper class for DocBlock parsing + */ +class DocBlockHelper +{ + /** + * Extracts a description from the provided docblock, + * with support for multiline descriptions. + */ + public function extractDescription(string $docBlock): string + { + // Remove indentation + $description = preg_replace('/^[\s\t]*/m', '', $docBlock); + + // Remove block comment syntax + $description = preg_replace('/^\/\*\*\s*|^\s*\*\s|^\s*\*\/$/m', '', $description); + + // Remove annotations + $description = preg_replace('/^@.*$/m', '', $description); + + // Ignore docs after a "--" separator + if (preg_match('/^--.*$/m', $description)) { + $descriptionParts = preg_split('/^--.*$/m', $description); + $description = array_shift($descriptionParts); + } + + // Trim leading and trailing newlines + return trim($description, "\r\n"); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/ArgumentResolverFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/ArgumentResolverFactory.php new file mode 100644 index 000000000..708c38bc7 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/ArgumentResolverFactory.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Argument; + +use Behat\Testwork\Environment\Environment; + +/** + * Creates argument resolvers for provided environment. + * + * @see ContextEnvironmentHandler + * + * @author Konstantin Kudryashov + */ +interface ArgumentResolverFactory +{ + /** + * Builds argument resolvers for provided suite. + * + * @param Environment $environment + * + * @return ArgumentResolver[] + */ + public function createArgumentResolvers(Environment $environment); +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeArgumentResolverFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeArgumentResolverFactory.php new file mode 100644 index 000000000..fbfd69c83 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeArgumentResolverFactory.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Argument; + +use Behat\Testwork\Environment\Environment; + +/** + * Composite factory. Delegates to other (registered) factories to do the job. + * + * @see ContextEnvironmentHandler + * + * @author Konstantin Kudryashov + */ +final class CompositeArgumentResolverFactory implements ArgumentResolverFactory +{ + /** + * @var ArgumentResolverFactory[] + */ + private $factories = array(); + + /** + * Registers factory. + * + * @param ArgumentResolverFactory $factory + */ + public function registerFactory(ArgumentResolverFactory $factory) + { + $this->factories[] = $factory; + } + + /** + * {@inheritdoc} + */ + public function createArgumentResolvers(Environment $environment) + { + return array_reduce( + $this->factories, + function (array $resolvers, ArgumentResolverFactory $factory) use ($environment) { + return array_merge($resolvers, $factory->createArgumentResolvers($environment)); + }, + array() + ); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeFactory.php index 822896c8a..5b3d93f84 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/CompositeFactory.php @@ -18,6 +18,8 @@ * @see ContextEnvironmentHandler * * @author Konstantin Kudryashov + * + * @deprecated and will be removed in 4.0. Use CompositeArgumentResolverFactory instead */ final class CompositeFactory implements SuiteScopedResolverFactory { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/NullFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/NullFactory.php index 6265cc4c2..c2e070c40 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/NullFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/NullFactory.php @@ -10,6 +10,7 @@ namespace Behat\Behat\Context\Argument; +use Behat\Testwork\Environment\Environment; use Behat\Testwork\Suite\Suite; /** @@ -19,7 +20,7 @@ * * @author Konstantin Kudryashov */ -final class NullFactory implements SuiteScopedResolverFactory +final class NullFactory implements ArgumentResolverFactory, SuiteScopedResolverFactory { /** * {@inheritdoc} @@ -28,4 +29,12 @@ public function generateArgumentResolvers(Suite $suite) { return array(); } + + /** + * {@inheritdoc} + */ + public function createArgumentResolvers(Environment $environment) + { + return array(); + } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactory.php index 60d858826..9f953fc2d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactory.php @@ -18,6 +18,8 @@ * @see ContextEnvironmentHandler * * @author Konstantin Kudryashov + * + * @deprecated since 3.4. Use `ArgumentResolverFactory` instead */ interface SuiteScopedResolverFactory { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactoryAdapter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactoryAdapter.php new file mode 100644 index 000000000..2317c8eb6 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Argument/SuiteScopedResolverFactoryAdapter.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Argument; + +use Behat\Testwork\Environment\Environment; + +/** + * Adapts SuiteScopedResolverFactory to new ArgumentResolverFactory interface. + * + * @see ContextEnvironmentHandler + * + * @author Konstantin Kudryashov + * + * @deprecated since 3.4. Use `ArgumentResolverFactory` instead + */ +final class SuiteScopedResolverFactoryAdapter implements ArgumentResolverFactory +{ + /** + * @var SuiteScopedResolverFactory + */ + private $factory; + + /** + * Initialises adapter. + * + * @param SuiteScopedResolverFactory $factory + */ + public function __construct(SuiteScopedResolverFactory $factory) + { + $this->factory = $factory; + } + + /** + * {@inheritdoc} + */ + public function createArgumentResolvers(Environment $environment) + { + return $this->factory->generateArgumentResolvers($environment->getSuite()); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Attribute/AttributeReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Attribute/AttributeReader.php new file mode 100644 index 000000000..bd15dff7a --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Attribute/AttributeReader.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Attribute; + +use ReflectionMethod; + +/** + * Reads Attributes of a provided context method into a Callee. + * + * @see AttributeContextReader + * + * @author Konstantin Kudryashov + */ +interface AttributeReader +{ + /** + * Reads all callees associated with a provided method. + * + * @param string $contextClass + * @param ReflectionMethod $method + * + * @return array + */ + public function readCallees(string $contextClass, ReflectionMethod $method); +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/ContextSnippetsController.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/ContextSnippetsController.php index 3a6edf63c..3bfbc40bd 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/ContextSnippetsController.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/ContextSnippetsController.php @@ -17,12 +17,12 @@ use Behat\Behat\Context\Snippet\Generator\FixedContextIdentifier; use Behat\Behat\Context\Snippet\Generator\FixedPatternIdentifier; use Behat\Behat\Context\Snippet\Generator\AggregateContextIdentifier; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Behat\Testwork\Cli\Controller; use Symfony\Component\Console\Command\Command as SymfonyCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Translation\TranslatorInterface; /** * Configures which context snippets are generated for. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/InteractiveContextIdentifier.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/InteractiveContextIdentifier.php index c39488ae6..78be23b6d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/InteractiveContextIdentifier.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Cli/InteractiveContextIdentifier.php @@ -12,11 +12,11 @@ use Behat\Behat\Context\Environment\ContextEnvironment; use Behat\Behat\Context\Snippet\Generator\TargetContextIdentifier; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; -use Symfony\Component\Translation\TranslatorInterface; /** * Interactive identifier that asks user for input. @@ -51,13 +51,13 @@ public function __construct(TranslatorInterface $translator, InputInterface $inp $this->input = $input; $this->output = $output; } - + /** * {@inheritdoc} */ public function guessTargetContextClass(ContextEnvironment $environment) { - if ($this->interactionIsNotSupported()) { + if (!$this->input->isInteractive()) { return null; } @@ -68,9 +68,9 @@ public function guessTargetContextClass(ContextEnvironment $environment) return null; } - $message = $this->translator->trans('snippet_context_choice', array('%1%' => $suiteName), 'output'); + $message = $this->translator->trans('snippet_context_choice', array('%count%' => $suiteName), 'output'); $choices = array_values(array_merge(array('None'), $contextClasses)); - $default = current($contextClasses); + $default = 1; $answer = $this->askQuestion('>> ' . $message, $choices, $default); @@ -94,21 +94,4 @@ private function askQuestion($message, $choices, $default) return $helper->ask($this->input, $this->output, $question); } - - /** - * Checks if interactive mode is supported. - * - * @return Boolean - * - * @deprecated there is a better way to do it - `InputInterface::isInteractive()` method. - * Sadly, this doesn't work properly prior Symfony\Console 2.7 and as we need - * to support 2.5+ until the next major, we are forced to do a more explicit - * check for the CLI option. This should be reverted back to proper a - * `InputInterface::isInteractive()` call as soon as we bump dependencies - * to Symfony\Console 3.x in Behat 4.x. - */ - private function interactionIsNotSupported() - { - return $this->input->hasParameterOption('--no-interaction'); - } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassGenerator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassGenerator.php index d2a740926..0a7cb5e19 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassGenerator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassGenerator.php @@ -28,7 +28,7 @@ interface ClassGenerator * @param Suite $suite * @param string $contextClass * - * @return Boolean + * @return bool */ public function supportsSuiteAndClass(Suite $suite, $contextClass); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassResolver.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassResolver.php index 464f68f41..dce0f8049 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassResolver.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextClass/ClassResolver.php @@ -26,7 +26,7 @@ interface ClassResolver * * @param string $contextString * - * @return Boolean + * @return bool */ public function supportsClass($contextString); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextFactory.php index ff255d19c..0d226504d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ContextFactory.php @@ -10,6 +10,7 @@ namespace Behat\Behat\Context; +use Behat\Testwork\Argument\Validator; use Behat\Behat\Context\Argument\ArgumentResolver; use Behat\Behat\Context\Initializer\ContextInitializer; use Behat\Testwork\Argument\ArgumentOrganiser; @@ -34,6 +35,10 @@ final class ContextFactory * @var ContextInitializer[] */ private $contextInitializers = array(); + /** + * @var Validator + */ + private $validator; /** * Initialises factory. @@ -43,6 +48,7 @@ final class ContextFactory public function __construct(ArgumentOrganiser $argumentOrganiser) { $this->argumentOrganiser = $argumentOrganiser; + $this->validator = new Validator(); } /** @@ -96,17 +102,21 @@ public function createContext($class, array $arguments = array(), array $singleU */ private function resolveArguments(ReflectionClass $reflection, array $arguments, array $resolvers) { + $newArguments = $arguments; + foreach ($resolvers as $resolver) { - $arguments = $resolver->resolveArguments($reflection, $arguments); + $newArguments = $resolver->resolveArguments($reflection, $newArguments); } - if (!$reflection->hasMethod('__construct') || !count($arguments)) { - return $arguments; + if (!$reflection->hasMethod('__construct')) { + return $newArguments; } $constructor = $reflection->getConstructor(); + $newArguments = $this->argumentOrganiser->organiseArguments($constructor, $newArguments); + $this->validator->validateArguments($constructor, $newArguments); - return $this->argumentOrganiser->organiseArguments($constructor, $arguments); + return $newArguments; } /** @@ -120,7 +130,7 @@ private function resolveArguments(ReflectionClass $reflection, array $arguments, private function createInstance(ReflectionClass $reflection, array $arguments) { if (count($arguments)) { - return $reflection->newInstanceArgs($arguments); + return $reflection->newInstanceArgs(array_values($arguments)); } return $reflection->newInstance(); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/ContextEnvironment.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/ContextEnvironment.php index 690eebb28..44bcf29b8 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/ContextEnvironment.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/ContextEnvironment.php @@ -25,7 +25,7 @@ interface ContextEnvironment extends Environment /** * Checks if environment has any contexts registered. * - * @return Boolean + * @return bool */ public function hasContexts(); @@ -41,7 +41,7 @@ public function getContextClasses(); * * @param string $class * - * @return Boolean + * @return bool */ public function hasContextClass($class); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/Handler/ContextEnvironmentHandler.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/Handler/ContextEnvironmentHandler.php index d5c3a0fce..d686400dd 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/Handler/ContextEnvironmentHandler.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/Handler/ContextEnvironmentHandler.php @@ -11,6 +11,8 @@ namespace Behat\Behat\Context\Environment\Handler; use Behat\Behat\Context\Argument\SuiteScopedResolverFactory; +use Behat\Behat\Context\Argument\SuiteScopedResolverFactoryAdapter; +use Behat\Behat\Context\Argument\ArgumentResolverFactory; use Behat\Behat\Context\Argument\NullFactory; use Behat\Behat\Context\ContextClass\ClassResolver; use Behat\Behat\Context\ContextFactory; @@ -36,7 +38,7 @@ final class ContextEnvironmentHandler implements EnvironmentHandler */ private $contextFactory; /** - * @var SuiteScopedResolverFactory + * @var ArgumentResolverFactory */ private $resolverFactory; /** @@ -47,12 +49,17 @@ final class ContextEnvironmentHandler implements EnvironmentHandler /** * Initializes handler. * - * @param ContextFactory $factory - * @param SuiteScopedResolverFactory $resolverFactory + * @param ContextFactory $factory + * @param ArgumentResolverFactory|SuiteScopedResolverFactory $resolverFactory */ - public function __construct(ContextFactory $factory, SuiteScopedResolverFactory $resolverFactory = null) + public function __construct(ContextFactory $factory, $resolverFactory = null) { $this->contextFactory = $factory; + + if ($resolverFactory && !$resolverFactory instanceof ArgumentResolverFactory) { + $resolverFactory = new SuiteScopedResolverFactoryAdapter($resolverFactory); + } + $this->resolverFactory = $resolverFactory ?: new NullFactory(); } @@ -108,7 +115,7 @@ public function isolateEnvironment(Environment $uninitializedEnvironment, $testS } $environment = new InitializedContextEnvironment($uninitializedEnvironment->getSuite()); - $resolvers = $this->resolverFactory->generateArgumentResolvers($uninitializedEnvironment->getSuite()); + $resolvers = $this->resolverFactory->createArgumentResolvers($environment); foreach ($uninitializedEnvironment->getContextClassesWithArguments() as $class => $arguments) { $context = $this->contextFactory->createContext($class, $arguments, $resolvers); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/InitializedContextEnvironment.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/InitializedContextEnvironment.php index c26826ecf..053354963 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/InitializedContextEnvironment.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Environment/InitializedContextEnvironment.php @@ -13,8 +13,10 @@ use Behat\Behat\Context\Context; use Behat\Behat\Context\Environment\Handler\ContextEnvironmentHandler; use Behat\Behat\Context\Exception\ContextNotFoundException; +use Behat\Behat\HelperContainer\Environment\ServiceContainerEnvironment; use Behat\Testwork\Call\Callee; use Behat\Testwork\Suite\Suite; +use Psr\Container\ContainerInterface; /** * Context environment based on a list of instantiated context objects. @@ -23,12 +25,16 @@ * * @author Konstantin Kudryashov */ -final class InitializedContextEnvironment implements ContextEnvironment +final class InitializedContextEnvironment implements ContextEnvironment, ServiceContainerEnvironment { /** * @var string */ private $suite; + /** + * @var ContainerInterface + */ + private $serviceContainer; /** * @var Context[] */ @@ -54,6 +60,14 @@ public function registerContext(Context $context) $this->contexts[get_class($context)] = $context; } + /** + * {@inheritdoc} + */ + public function setServiceContainer(ContainerInterface $container = null) + { + $this->serviceContainer = $container; + } + /** * {@inheritdoc} */ @@ -117,6 +131,14 @@ public function getContext($class) return $this->contexts[$class]; } + /** + * {@inheritdoc} + */ + public function getServiceContainer() + { + return $this->serviceContainer; + } + /** * {@inheritdoc} */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AnnotatedContextReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AnnotatedContextReader.php index a3144740e..6fe0b0751 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AnnotatedContextReader.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AnnotatedContextReader.php @@ -11,6 +11,7 @@ namespace Behat\Behat\Context\Reader; use Behat\Behat\Context\Annotation\AnnotationReader; +use Behat\Behat\Context\Annotation\DocBlockHelper; use Behat\Behat\Context\Environment\ContextEnvironment; use Behat\Testwork\Call\Callee; use ReflectionClass; @@ -24,7 +25,7 @@ */ final class AnnotatedContextReader implements ContextReader { - const DOCLINE_TRIMMER_REGEX = '/^\/\*\*\s*|^\s*\*\s*|\s*\*\/$|\s*$/'; + public const DOCLINE_TRIMMER_REGEX = '/^\/\*\*\s*|^\s*\*\s*|\s*\*\/$|\s*$/'; /** * @var string[] @@ -42,6 +43,21 @@ final class AnnotatedContextReader implements ContextReader */ private $readers = array(); + /** + * @var DocBlockHelper + */ + private $docBlockHelper; + + /** + * Initializes reader. + * + * @param DocBlockHelper $docBlockHelper + */ + public function __construct(DocBlockHelper $docBlockHelper) + { + $this->docBlockHelper = $docBlockHelper; + } + /** * Registers annotation reader. * @@ -110,7 +126,7 @@ private function readMethodCallees($class, ReflectionMethod $method) private function readDocBlockCallees($class, ReflectionMethod $method, $docBlock) { $callees = array(); - $description = $this->readDescription($docBlock); + $description = $this->docBlockHelper->extractDescription($docBlock); $docBlock = $this->mergeMultilines($docBlock); foreach (explode("\n", $docBlock) as $docLine) { @@ -144,43 +160,12 @@ private function mergeMultilines($docBlock) return preg_replace("#\\\\$\s*+\*\s*+([^\\\\$]++)#m", '$1', $docBlock); } - /** - * Extracts a description from the provided docblock, - * with support for multiline descriptions. - * - * @param string $docBlock - * - * @return string - */ - private function readDescription($docBlock) - { - // Remove indentation - $description = preg_replace('/^[\s\t]*/m', '', $docBlock); - - // Remove block comment syntax - $description = preg_replace('/^\/\*\*\s*|^\s*\*\s|^\s*\*\/$/m', '', $description); - - // Remove annotations - $description = preg_replace('/^@.*$/m', '', $description); - - // Ignore docs after a "--" separator - if (preg_match('/^--.*$/m', $description)) { - $descriptionParts = preg_split('/^--.*$/m', $description); - $description = array_shift($descriptionParts); - } - - // Trim leading and trailing newlines - $description = trim($description, "\r\n"); - - return $description; - } - /** * Checks if provided doc lien is empty. * * @param string $docLine * - * @return Boolean + * @return bool */ private function isEmpty($docLine) { @@ -192,7 +177,7 @@ private function isEmpty($docLine) * * @param string $docLine * - * @return Boolean + * @return bool */ private function isNotAnnotation($docLine) { @@ -229,7 +214,7 @@ private function readDocLineCallee($class, ReflectionMethod $method, $docLine, $ * * @param string $docLine * - * @return Boolean + * @return bool */ private function isIgnoredAnnotation($docLine) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AttributeContextReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AttributeContextReader.php new file mode 100644 index 000000000..0db164319 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Reader/AttributeContextReader.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Reader; + +use Behat\Behat\Context\Attribute\AttributeReader; +use Behat\Behat\Context\Environment\ContextEnvironment; +use ReflectionClass; +use ReflectionMethod; + +/** + * Reads context callees by Attributes using registered Attribute readers. + * + * @author Konstantin Kudryashov + */ +final class AttributeContextReader implements ContextReader +{ + /** + * @var AttributeReader[] + */ + private $readers = array(); + + /** + * Registers attribute reader. + * + * @param AttributeReader $reader + */ + public function registerAttributeReader(AttributeReader $reader) + { + $this->readers[] = $reader; + } + + /** + * {@inheritdoc} + */ + public function readContextCallees(ContextEnvironment $environment, $contextClass) + { + if (\PHP_MAJOR_VERSION < 8) { + return []; + } + + $reflection = new ReflectionClass($contextClass); + + $callees = array(); + foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + foreach ($this->readMethodCallees($reflection->getName(), $method) as $callee) { + $callees[] = $callee; + } + } + + return $callees; + } + + private function readMethodCallees(string $contextClass, ReflectionMethod $method) + { + $callees = []; + foreach ($this->readers as $reader) { + $callees = array_merge($callees, $reader->readCallees($contextClass, $method)); + } + + return $callees; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ServiceContainer/ContextExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ServiceContainer/ContextExtension.php index de3f35248..1ae669fac 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ServiceContainer/ContextExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/ServiceContainer/ContextExtension.php @@ -39,20 +39,27 @@ final class ContextExtension implements Extension /** * Available services */ - const FACTORY_ID = 'context.factory'; - const CONTEXT_SNIPPET_GENERATOR_ID = 'snippet.generator.context'; - const AGGREGATE_RESOLVER_FACTORY_ID = 'context.argument.aggregate_resolver_factory'; + public const FACTORY_ID = 'context.factory'; + public const CONTEXT_SNIPPET_GENERATOR_ID = 'snippet.generator.context'; + public const AGGREGATE_RESOLVER_FACTORY_ID = 'context.argument.aggregate_resolver_factory'; + private const ENVIRONMENT_HANDLER_ID = EnvironmentExtension::HANDLER_TAG . '.context'; + private const ENVIRONMENT_READER_ID = EnvironmentExtension::READER_TAG . '.context'; + private const SUITE_SETUP_ID = SuiteExtension::SETUP_TAG . '.suite_with_contexts'; + private const ANNOTATED_CONTEXT_READER_ID = self::READER_TAG . '.annotated'; + private const ATTRIBUTED_CONTEXT_READER_ID = self::READER_TAG . '.attributed'; /* * Available extension points */ - const CLASS_RESOLVER_TAG = 'context.class_resolver'; - const ARGUMENT_RESOLVER_TAG = 'context.argument_resolver'; - const INITIALIZER_TAG = 'context.initializer'; - const READER_TAG = 'context.reader'; - const ANNOTATION_READER_TAG = 'context.annotation_reader'; - const CLASS_GENERATOR_TAG = 'context.class_generator'; - const SUITE_SCOPED_RESOLVER_FACTORY_TAG = 'context.argument.suite_resolver_factory'; + public const CLASS_RESOLVER_TAG = 'context.class_resolver'; + public const ARGUMENT_RESOLVER_TAG = 'context.argument_resolver'; + public const INITIALIZER_TAG = 'context.initializer'; + public const READER_TAG = 'context.reader'; + public const ANNOTATION_READER_TAG = 'context.annotation_reader'; + public const ATTRIBUTE_READER_TAG = 'context.attribute_reader'; + public const CLASS_GENERATOR_TAG = 'context.class_generator'; + public const SUITE_SCOPED_RESOLVER_FACTORY_TAG = 'context.argument.suite_resolver_factory'; + public const DOC_BLOCK_HELPER_ID = 'context.docblock_helper'; /** * @var ServiceProcessor @@ -106,6 +113,7 @@ public function load(ContainerBuilder $container, array $config) $this->loadSnippetsController($container); $this->loadDefaultClassGenerators($container); $this->loadDefaultContextReaders($container); + $this->loadDocblockHelper($container); } /** @@ -120,6 +128,7 @@ public function process(ContainerBuilder $container) $this->processContextReaders($container); $this->processClassGenerators($container); $this->processAnnotationReaders($container); + $this->processAttributeReaders($container); } /** @@ -142,7 +151,7 @@ private function loadFactory(ContainerBuilder $container) */ private function loadArgumentResolverFactory(ContainerBuilder $container) { - $definition = new Definition('Behat\Behat\Context\Argument\CompositeFactory'); + $definition = new Definition('Behat\Behat\Context\Argument\CompositeArgumentResolverFactory'); $container->setDefinition(self::AGGREGATE_RESOLVER_FACTORY_ID, $definition); } @@ -158,7 +167,7 @@ private function loadEnvironmentHandler(ContainerBuilder $container) new Reference(self::AGGREGATE_RESOLVER_FACTORY_ID) )); $definition->addTag(EnvironmentExtension::HANDLER_TAG, array('priority' => 50)); - $container->setDefinition(self::getEnvironmentHandlerId(), $definition); + $container->setDefinition(self::ENVIRONMENT_HANDLER_ID, $definition); } /** @@ -170,7 +179,7 @@ private function loadEnvironmentReader(ContainerBuilder $container) { $definition = new Definition('Behat\Behat\Context\Environment\Reader\ContextEnvironmentReader'); $definition->addTag(EnvironmentExtension::READER_TAG, array('priority' => 50)); - $container->setDefinition(self::getEnvironmentReaderId(), $definition); + $container->setDefinition(self::ENVIRONMENT_READER_ID, $definition); } /** @@ -185,7 +194,7 @@ private function loadSuiteSetup(ContainerBuilder $container) new Reference(FilesystemExtension::LOGGER_ID) )); $definition->addTag(SuiteExtension::SETUP_TAG, array('priority' => 20)); - $container->setDefinition(self::getSuiteSetupId(), $definition); + $container->setDefinition(self::SUITE_SETUP_ID, $definition); } /** @@ -248,15 +257,57 @@ private function loadDefaultClassGenerators(ContainerBuilder $container) */ private function loadDefaultContextReaders(ContainerBuilder $container) { - $definition = new Definition('Behat\Behat\Context\Reader\AnnotatedContextReader'); - $container->setDefinition(self::getAnnotatedContextReaderId(), $definition); + $this->loadAnnotatedContextReader($container); + + $this->loadAttributedContextReader($container); + + $this->loadTranslatableContextReader($container); + } + + /** + * Loads AnnotatedContextReader + * + * @param ContainerBuilder $container + */ + private function loadAnnotatedContextReader(ContainerBuilder $container) + { + $definition = new Definition('Behat\Behat\Context\Reader\AnnotatedContextReader', array( + new Reference(self::DOC_BLOCK_HELPER_ID) + )); + $container->setDefinition(self::ANNOTATED_CONTEXT_READER_ID, $definition); + + $definition = new Definition('Behat\Behat\Context\Reader\ContextReaderCachedPerContext', array( + new Reference(self::ANNOTATED_CONTEXT_READER_ID) + )); + $definition->addTag(self::READER_TAG, array('priority' => 50)); + $container->setDefinition(self::ANNOTATED_CONTEXT_READER_ID . '.cached', $definition); + } + + /** + * Loads AttributedContextReader + * + * @param ContainerBuilder $container + */ + private function loadAttributedContextReader(ContainerBuilder $container) + { + $definition = new Definition('Behat\Behat\Context\Reader\AttributeContextReader'); + $container->setDefinition(self::ATTRIBUTED_CONTEXT_READER_ID, $definition); $definition = new Definition('Behat\Behat\Context\Reader\ContextReaderCachedPerContext', array( - new Reference(self::getAnnotatedContextReaderId()) + new Reference(self::ATTRIBUTED_CONTEXT_READER_ID) )); $definition->addTag(self::READER_TAG, array('priority' => 50)); - $container->setDefinition(self::getAnnotatedContextReaderId() . '.cached', $definition); + $container->setDefinition(self::ATTRIBUTED_CONTEXT_READER_ID . '.cached', $definition); + return $definition; + } + /** + * Loads TranslatableContextReader + * + * @param ContainerBuilder $container + */ + private function loadTranslatableContextReader(ContainerBuilder $container) + { $definition = new Definition('Behat\Behat\Context\Reader\TranslatableContextReader', array( new Reference(TranslatorExtension::TRANSLATOR_ID) )); @@ -269,6 +320,18 @@ private function loadDefaultContextReaders(ContainerBuilder $container) $container->setDefinition(self::READER_TAG . '.translatable.cached', $definition); } + /** + * Loads DocBlockHelper + * + * @param ContainerBuilder $container + */ + private function loadDocblockHelper(ContainerBuilder $container) + { + $definition = new Definition('Behat\Behat\Context\Annotation\DocBlockHelper'); + + $container->setDefinition(self::DOC_BLOCK_HELPER_ID, $definition); + } + /** * Processes all class resolvers. * @@ -277,7 +340,7 @@ private function loadDefaultContextReaders(ContainerBuilder $container) private function processClassResolvers(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, self::CLASS_RESOLVER_TAG); - $definition = $container->getDefinition(self::getEnvironmentHandlerId()); + $definition = $container->getDefinition(self::ENVIRONMENT_HANDLER_ID); foreach ($references as $reference) { $definition->addMethodCall('registerClassResolver', array($reference)); @@ -337,7 +400,7 @@ private function processContextInitializers(ContainerBuilder $container) private function processContextReaders(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, self::READER_TAG); - $definition = $container->getDefinition(self::getEnvironmentReaderId()); + $definition = $container->getDefinition(self::ENVIRONMENT_READER_ID); foreach ($references as $reference) { $definition->addMethodCall('registerContextReader', array($reference)); @@ -352,7 +415,7 @@ private function processContextReaders(ContainerBuilder $container) private function processClassGenerators(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, self::CLASS_GENERATOR_TAG); - $definition = $container->getDefinition(self::getSuiteSetupId()); + $definition = $container->getDefinition(self::SUITE_SETUP_ID); foreach ($references as $reference) { $definition->addMethodCall('registerClassGenerator', array($reference)); @@ -367,7 +430,7 @@ private function processClassGenerators(ContainerBuilder $container) private function processAnnotationReaders(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, self::ANNOTATION_READER_TAG); - $definition = $container->getDefinition(self::getAnnotatedContextReaderId()); + $definition = $container->getDefinition(self::ANNOTATED_CONTEXT_READER_ID); foreach ($references as $reference) { $definition->addMethodCall('registerAnnotationReader', array($reference)); @@ -375,42 +438,17 @@ private function processAnnotationReaders(ContainerBuilder $container) } /** - * Returns context environment handler service id. + * Processes all attribute readers. * - * @return string - */ - private static function getEnvironmentHandlerId() - { - return EnvironmentExtension::HANDLER_TAG . '.context'; - } - - /** - * Returns context environment reader id. - * - * @return string - */ - private static function getEnvironmentReaderId() - { - return EnvironmentExtension::READER_TAG . '.context'; - } - - /** - * Returns context suite setup id. - * - * @return string + * @param ContainerBuilder $container */ - private static function getSuiteSetupId() + private function processAttributeReaders(ContainerBuilder $container) { - return SuiteExtension::SETUP_TAG . '.suite_with_contexts'; - } + $references = $this->processor->findAndSortTaggedServices($container, self::ATTRIBUTE_READER_TAG); + $definition = $container->getDefinition(self::ATTRIBUTED_CONTEXT_READER_ID); - /** - * Returns annotated context reader id. - * - * @return string - */ - private static function getAnnotatedContextReaderId() - { - return self::READER_TAG . '.annotated'; + foreach ($references as $reference) { + $definition->addMethodCall('registerAttributeReader', array($reference)); + } } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Appender/ContextSnippetAppender.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Appender/ContextSnippetAppender.php index 5a881f94b..456809bc2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Appender/ContextSnippetAppender.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Appender/ContextSnippetAppender.php @@ -25,7 +25,7 @@ final class ContextSnippetAppender implements SnippetAppender /** * @const PendingException class */ - const PENDING_EXCEPTION_CLASS = 'Behat\Behat\Tester\Exception\PendingException'; + public const PENDING_EXCEPTION_CLASS = 'Behat\Behat\Tester\Exception\PendingException'; /** * @var FilesystemLogger @@ -81,7 +81,7 @@ public function appendSnippet(AggregateSnippet $snippet) * @param string $class * @param string $contextFileContent * - * @return Boolean + * @return bool */ private function isClassImported($class, $contextFileContent) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php index edb2227f5..2373220a7 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php @@ -324,7 +324,7 @@ private function getMethodNameNotProposedEarlier($contextClass, $stepPattern, $n */ private function getAlreadyProposedMethods($contextClass) { - return isset(self::$proposedMethods[$contextClass]) ? self::$proposedMethods[$contextClass] : array(); + return self::$proposedMethods[$contextClass] ?? array(); } /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Suite/Setup/SuiteWithContextsSetup.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Suite/Setup/SuiteWithContextsSetup.php index 0bc41db7a..9fc219d3a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Suite/Setup/SuiteWithContextsSetup.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Context/Suite/Setup/SuiteWithContextsSetup.php @@ -16,7 +16,7 @@ use Behat\Testwork\Suite\Exception\SuiteConfigurationException; use Behat\Testwork\Suite\Setup\SuiteSetup; use Behat\Testwork\Suite\Suite; -use Symfony\Component\ClassLoader\ClassLoader; +use Composer\Autoload\ClassLoader; /** * Generates classes for all contexts in the suite using autoloader. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Context/Attribute/DefinitionAttributeReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Context/Attribute/DefinitionAttributeReader.php new file mode 100644 index 000000000..c37300f77 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Context/Attribute/DefinitionAttributeReader.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Definition\Context\Attribute; + +use Behat\Behat\Context\Annotation\DocBlockHelper; +use Behat\Behat\Context\Attribute\AttributeReader; +use Behat\Step\Definition; +use Behat\Step\Given; +use Behat\Step\Then; +use Behat\Step\When; +use ReflectionMethod; + +/** + * Reads definition Attributes from the context class. + * + * @author Konstantin Kudryashov + */ +final class DefinitionAttributeReader implements AttributeReader +{ + /** + * @var string[] + */ + private static $classes = array( + Given::class => 'Behat\Behat\Definition\Call\Given', + When::class => 'Behat\Behat\Definition\Call\When', + Then::class => 'Behat\Behat\Definition\Call\Then', + ); + + /** + * @var DocBlockHelper + */ + private $docBlockHelper; + + /** + * Initializes reader. + * + * @param DocBlockHelper $docBlockHelper + */ + public function __construct(DocBlockHelper $docBlockHelper) + { + $this->docBlockHelper = $docBlockHelper; + } + + /** + * @{inheritdoc} + */ + public function readCallees(string $contextClass, ReflectionMethod $method) + { + if (\PHP_MAJOR_VERSION < 8) { + return []; + } + + $attributes = $method->getAttributes(Definition::class, \ReflectionAttribute::IS_INSTANCEOF); + + $callees = []; + foreach ($attributes as $attribute) { + $class = self::$classes[$attribute->getName()]; + $callable = array($contextClass, $method->getName()); + $description = null; + if ($docBlock = $method->getDocComment()) { + $description = $this->docBlockHelper->extractDescription($docBlock); + } + + $callees[] = new $class($attribute->newInstance()->pattern, $callable, $description); + } + + return $callees; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/PatternTransformer.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/PatternTransformer.php index 6af5bacd6..adda52b66 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/PatternTransformer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/PatternTransformer.php @@ -26,6 +26,11 @@ final class PatternTransformer */ private $policies = array(); + /** + * @var string[] + */ + private $patternToRegexpCache = array(); + /** * Registers pattern policy. * @@ -34,6 +39,7 @@ final class PatternTransformer public function registerPatternPolicy(PatternPolicy $policy) { $this->policies[] = $policy; + $this->patternToRegexpCache = array(); } /** @@ -67,6 +73,22 @@ public function generatePattern($type, $stepText) * @throws UnknownPatternException */ public function transformPatternToRegex($pattern) + { + if (!isset($this->patternToRegexpCache[$pattern])) { + $this->patternToRegexpCache[$pattern] = $this->transformPatternToRegexWithSupportedPolicy($pattern); + } + + return $this->patternToRegexpCache[$pattern]; + } + + /** + * @param string $pattern + * + * @return string + * + * @throws UnknownPatternException + */ + private function transformPatternToRegexWithSupportedPolicy($pattern) { foreach ($this->policies as $policy) { if ($policy->supportsPattern($pattern)) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/PatternPolicy.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/PatternPolicy.php index 247624170..cd3daac5c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/PatternPolicy.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/PatternPolicy.php @@ -27,7 +27,7 @@ interface PatternPolicy * * @param string $type * - * @return Boolean + * @return bool */ public function supportsPatternType($type); @@ -45,7 +45,7 @@ public function generatePattern($stepText); * * @param string $pattern * - * @return Boolean + * @return bool */ public function supportsPattern($pattern); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/RegexPatternPolicy.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/RegexPatternPolicy.php index 6ea50a277..76498b91b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/RegexPatternPolicy.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/RegexPatternPolicy.php @@ -65,7 +65,7 @@ public function transformPatternToRegex($pattern) { if (false === @preg_match($pattern, 'anything')) { $error = error_get_last(); - $errorMessage = isset($error['message']) ? $error['message'] : ''; + $errorMessage = $error['message'] ?? ''; throw new InvalidPatternException(sprintf('The regex `%s` is invalid: %s', $pattern, $errorMessage)); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/TurnipPatternPolicy.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/TurnipPatternPolicy.php index c0732680f..aa37f9e62 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/TurnipPatternPolicy.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Pattern/Policy/TurnipPatternPolicy.php @@ -21,11 +21,11 @@ */ final class TurnipPatternPolicy implements PatternPolicy { - const TOKEN_REGEX = "[\"']?(?P<%s>(?<=\")[^\"]*(?=\")|(?<=')[^']*(?=')|\-?[\w\.\,]+)['\"]?"; + public const TOKEN_REGEX = "[\"']?(?P<%s>(?<=\")[^\"]*(?=\")|(?<=')[^']*(?=')|\-?[\w\.\,]+)['\"]?"; - const PLACEHOLDER_REGEXP = "/\\\:(\w+)/"; - const OPTIONAL_WORD_REGEXP = '/(\s)?\\\\\(([^\\\]+)\\\\\)(\s)?/'; - const ALTERNATIVE_WORD_REGEXP = '/(\w+)\\\\\/(\w+)/'; + public const PLACEHOLDER_REGEXP = "/\\\:(\w+)/"; + public const OPTIONAL_WORD_REGEXP = '/(\s)?\\\\\(([^\\\]+)\\\\\)(\s)?/'; + public const ALTERNATIVE_WORD_REGEXP = '/(\w+)\\\\\/(\w+)/'; /** * @var string[] @@ -100,7 +100,7 @@ private function createTransformedRegex($pattern) $regex = $this->replaceTurnipOptionalEndingWithRegex($regex); $regex = $this->replaceTurnipAlternativeWordsWithRegex($regex); - return '/^' . $regex . '$/i'; + return '/^' . $regex . '$/iu'; } /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionInformationPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionInformationPrinter.php index 4c074ea45..d690982ee 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionInformationPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionInformationPrinter.php @@ -131,6 +131,17 @@ private function extractFooter(Suite $suite, Definition $definition) ) ); + if ($this->isVerbose()) { + $lines[] = strtr( + '{space}| on `{filepath}[{start}:{end}]`', array( + '{space}' => str_pad('', mb_strlen($suite->getName(), 'utf8') + 1), + '{filepath}' => $definition->getReflection()->getFileName(), + '{start}' => $definition->getReflection()->getStartLine(), + '{end}' => $definition->getReflection()->getEndLine() + ) + ); + } + return $lines; } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionPrinter.php index 08b8f92ac..bd0546f41 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Printer/ConsoleDefinitionPrinter.php @@ -110,4 +110,14 @@ final protected function translateDefinition(Suite $suite, Definition $definitio { return $this->translator->translateDefinition($suite, $definition); } + + /** + * Returns whether verbosity is verbose (-v). + * + * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise + */ + final protected function isVerbose() + { + return $this->output->isVerbose(); + } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/SearchResult.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/SearchResult.php index 158f63557..cdb4191ad 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/SearchResult.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/SearchResult.php @@ -47,7 +47,7 @@ public function __construct(Definition $definition = null, $matchedText = null, /** * Checks if result contains a match. * - * @return Boolean + * @return bool */ public function hasMatch() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/ServiceContainer/DefinitionExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/ServiceContainer/DefinitionExtension.php index cce43415b..e408f5358 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/ServiceContainer/DefinitionExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/ServiceContainer/DefinitionExtension.php @@ -35,17 +35,18 @@ final class DefinitionExtension implements Extension /* * Available services */ - const FINDER_ID = 'definition.finder'; - const REPOSITORY_ID = 'definition.repository'; - const PATTERN_TRANSFORMER_ID = 'definition.pattern_transformer'; - const WRITER_ID = 'definition.writer'; - const DEFINITION_TRANSLATOR_ID = 'definition.translator'; + public const FINDER_ID = 'definition.finder'; + public const REPOSITORY_ID = 'definition.repository'; + public const PATTERN_TRANSFORMER_ID = 'definition.pattern_transformer'; + public const WRITER_ID = 'definition.writer'; + public const DEFINITION_TRANSLATOR_ID = 'definition.translator'; /* * Available extension points */ - const SEARCH_ENGINE_TAG = 'definition.search_engine'; - const PATTERN_POLICY_TAG = 'definition.pattern_policy'; + public const SEARCH_ENGINE_TAG = 'definition.search_engine'; + public const PATTERN_POLICY_TAG = 'definition.pattern_policy'; + public const DOC_BLOCK_HELPER_ID = 'definition.doc_block_helper'; /** * @var ServiceProcessor @@ -97,8 +98,10 @@ public function load(ContainerBuilder $container, array $config) $this->loadDefaultSearchEngines($container); $this->loadDefaultPatternPolicies($container); $this->loadAnnotationReader($container); + $this->loadAttributeReader($container); $this->loadDefinitionPrinters($container); $this->loadController($container); + $this->loadDocblockHelper($container); } /** @@ -217,6 +220,20 @@ private function loadAnnotationReader(ContainerBuilder $container) $container->setDefinition(ContextExtension::ANNOTATION_READER_TAG . '.definition', $definition); } + /** + * Loads definition Attribute reader. + * + * @param ContainerBuilder $container + */ + private function loadAttributeReader(ContainerBuilder $container) + { + $definition = new Definition('\Behat\Behat\Definition\Context\Attribute\DefinitionAttributeReader', array( + new Reference(self::DOC_BLOCK_HELPER_ID) + )); + $definition->addTag(ContextExtension::ATTRIBUTE_READER_TAG, array('priority' => 50)); + $container->setDefinition(ContextExtension::ATTRIBUTE_READER_TAG . '.definition', $definition); + } + /** * Loads definition printers. * @@ -258,6 +275,18 @@ private function loadController(ContainerBuilder $container) $container->setDefinition(CliExtension::CONTROLLER_TAG . '.available_definitions', $definition); } + /** + * Loads DocBlockHelper + * + * @param ContainerBuilder $container + */ + private function loadDocblockHelper(ContainerBuilder $container) + { + $definition = new Definition('Behat\Behat\Context\Annotation\DocBlockHelper'); + + $container->setDefinition(self::DOC_BLOCK_HELPER_ID, $definition); + } + /** * Processes all search engines in the container. * diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/DefinitionTranslator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/DefinitionTranslator.php index 3192a3d8e..105d61c50 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/DefinitionTranslator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/DefinitionTranslator.php @@ -12,7 +12,6 @@ use Behat\Behat\Definition\Definition; use Behat\Testwork\Suite\Suite; -use Symfony\Component\Translation\TranslatorInterface; /** * Translates definitions using translator component. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/Translator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/Translator.php new file mode 100644 index 000000000..97cdb4071 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Definition/Translator/Translator.php @@ -0,0 +1,7 @@ +eventDispatcher->dispatch(SuiteTested::AFTER, new AfterSuiteAborted($event->getEnvironment())); - $this->eventDispatcher->dispatch(ExerciseCompleted::AFTER, new AfterExerciseAborted()); + $this->eventDispatcher->dispatch(new AfterSuiteAborted($event->getEnvironment()), SuiteTested::AFTER); + $this->eventDispatcher->dispatch(new AfterExerciseAborted(), ExerciseCompleted::AFTER); exit(1); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepSetup.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepSetup.php index cf9b6c5f5..a2256f0c1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepSetup.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepSetup.php @@ -86,7 +86,7 @@ public function getSetup() /** * Checks if step call, setup or teardown produced any output (stdOut or exception). * - * @return Boolean + * @return bool */ public function hasOutput() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepTested.php index 37b497bfa..d5829b77a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/AfterStepTested.php @@ -111,7 +111,7 @@ public function getTeardown() /** * Checks if step call, setup or teardown produced any output (stdOut or exception). * - * @return Boolean + * @return bool */ public function hasOutput() { @@ -121,7 +121,7 @@ public function hasOutput() /** * Checks if step teardown has output. * - * @return Boolean + * @return bool */ private function teardownHasOutput() { @@ -131,7 +131,7 @@ private function teardownHasOutput() /** * Checks if result has produced exception. * - * @return Boolean + * @return bool */ private function resultHasException() { @@ -141,7 +141,7 @@ private function resultHasException() /** * Checks if result is executed and call result has produced exception or stdOut. * - * @return Boolean + * @return bool */ private function resultCallHasOutput() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BackgroundTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BackgroundTested.php index f67f855c2..dd43ae066 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BackgroundTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BackgroundTested.php @@ -21,10 +21,10 @@ */ abstract class BackgroundTested extends LifecycleEvent implements ScenarioLikeTested { - const BEFORE = 'tester.background_tested.before'; - const AFTER_SETUP = 'tester.background_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.background_tested.before_teardown'; - const AFTER = 'tester.background_tested.after'; + public const BEFORE = 'tester.background_tested.before'; + public const AFTER_SETUP = 'tester.background_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.background_tested.before_teardown'; + public const AFTER = 'tester.background_tested.after'; /** * Returns background node. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BeforeStepTeardown.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BeforeStepTeardown.php index 6d9b9c6e9..89f626fc5 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BeforeStepTeardown.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/BeforeStepTeardown.php @@ -93,7 +93,7 @@ public function getTestResult() /** * Checks if step call produced any output (stdOut or exception). * - * @return Boolean + * @return bool */ public function hasOutput() { @@ -103,7 +103,7 @@ public function hasOutput() /** * Checks if result has produced exception. * - * @return Boolean + * @return bool */ private function resultHasException() { @@ -113,7 +113,7 @@ private function resultHasException() /** * Checks if result is executed and call result has produced exception or stdOut. * - * @return Boolean + * @return bool */ private function resultCallHasOutput() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ExampleTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ExampleTested.php index 1bb1a4206..9a107670e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ExampleTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ExampleTested.php @@ -17,8 +17,8 @@ */ interface ExampleTested { - const BEFORE = 'tester.example_tested.before'; - const AFTER_SETUP = 'tester.example_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.example_tested.before_teardown'; - const AFTER = 'tester.example_tested.after'; + public const BEFORE = 'tester.example_tested.before'; + public const AFTER_SETUP = 'tester.example_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.example_tested.before_teardown'; + public const AFTER = 'tester.example_tested.after'; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/FeatureTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/FeatureTested.php index f29c31c1a..a75d81ff2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/FeatureTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/FeatureTested.php @@ -21,10 +21,10 @@ */ abstract class FeatureTested extends LifecycleEvent implements GherkinNodeTested { - const BEFORE = 'tester.feature_tested.before'; - const AFTER_SETUP = 'tester.feature_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.feature_tested.before_teardown'; - const AFTER = 'tester.feature_tested.after'; + public const BEFORE = 'tester.feature_tested.before'; + public const AFTER_SETUP = 'tester.feature_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.feature_tested.before_teardown'; + public const AFTER = 'tester.feature_tested.after'; /** * Returns feature. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/OutlineTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/OutlineTested.php index 149fc4ff7..557e429d2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/OutlineTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/OutlineTested.php @@ -22,10 +22,10 @@ */ abstract class OutlineTested extends LifecycleEvent implements GherkinNodeTested { - const BEFORE = 'tester.outline_tested.before'; - const AFTER_SETUP = 'tester.outline_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.outline_tested.before_teardown'; - const AFTER = 'tester.outline_tested.after'; + public const BEFORE = 'tester.outline_tested.before'; + public const AFTER_SETUP = 'tester.outline_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.outline_tested.before_teardown'; + public const AFTER = 'tester.outline_tested.after'; /** * Returns feature. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ScenarioTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ScenarioTested.php index 2251b4a27..6a27ae5d6 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ScenarioTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/ScenarioTested.php @@ -19,10 +19,10 @@ */ abstract class ScenarioTested extends LifecycleEvent implements ScenarioLikeTested { - const BEFORE = 'tester.scenario_tested.before'; - const AFTER_SETUP = 'tester.scenario_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.scenario_tested.before_teardown'; - const AFTER = 'tester.scenario_tested.after'; + public const BEFORE = 'tester.scenario_tested.before'; + public const AFTER_SETUP = 'tester.scenario_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.scenario_tested.before_teardown'; + public const AFTER = 'tester.scenario_tested.after'; /** * {@inheritdoc} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/StepTested.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/StepTested.php index 7053d0d55..9df91fbb1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/StepTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Event/StepTested.php @@ -21,10 +21,10 @@ */ abstract class StepTested extends LifecycleEvent implements GherkinNodeTested { - const BEFORE = 'tester.step_tested.before'; - const AFTER_SETUP = 'tester.step_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.step_tested.before_teardown'; - const AFTER = 'tester.step_tested.after'; + public const BEFORE = 'tester.step_tested.before'; + public const AFTER_SETUP = 'tester.step_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.step_tested.before_teardown'; + public const AFTER = 'tester.step_tested.after'; /** * Returns feature. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/ServiceContainer/EventDispatcherExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/ServiceContainer/EventDispatcherExtension.php index 252cd03f5..0aa06952c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/ServiceContainer/EventDispatcherExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/ServiceContainer/EventDispatcherExtension.php @@ -156,20 +156,18 @@ protected function loadEventDispatchingStepTester(ContainerBuilder $container) } /** - * Loads ticking step tester. + * This method used in the past to load the TickingStepTester to work around + * a bug with the scope of declare(ticks) in PHP < 7.1. Since we don't + * support those PHP versions anymore loading the TickingStepTester is + * no longer needed. This method is left here to prevent breaking BC. + * + * @todo Remove this method in next major + * + * @deprecated * * @param ContainerBuilder $container */ protected function loadTickingStepTester(ContainerBuilder $container) { - if (!function_exists('pcntl_signal')) { - return; - } - - $definition = new Definition('Behat\Behat\EventDispatcher\Tester\TickingStepTester', array( - new Reference(TesterExtension::STEP_TESTER_ID) - )); - $definition->addTag(TesterExtension::STEP_TESTER_WRAPPER_TAG, array('priority' => 9999)); - $container->setDefinition(TesterExtension::STEP_TESTER_WRAPPER_TAG . '.ticking', $definition); } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingBackgroundTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingBackgroundTester.php index d947df55d..cb5d32d32 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingBackgroundTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingBackgroundTester.php @@ -18,6 +18,7 @@ use Behat\Behat\Tester\BackgroundTester; use Behat\Gherkin\Node\FeatureNode; use Behat\Testwork\Environment\Environment; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Tester\Result\TestResult; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -55,12 +56,14 @@ public function __construct(BackgroundTester $baseTester, EventDispatcherInterfa public function setUp(Environment $env, FeatureNode $feature, $skip) { $event = new BeforeBackgroundTested($env, $feature, $feature->getBackground()); - $this->eventDispatcher->dispatch($event::BEFORE, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE); $setup = $this->baseTester->setUp($env, $feature, $skip); $event = new AfterBackgroundSetup($env, $feature, $feature->getBackground(), $setup); - $this->eventDispatcher->dispatch($event::AFTER_SETUP, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER_SETUP); return $setup; } @@ -79,12 +82,14 @@ public function test(Environment $env, FeatureNode $feature, $skip) public function tearDown(Environment $env, FeatureNode $feature, $skip, TestResult $result) { $event = new BeforeBackgroundTeardown($env, $feature, $feature->getBackground(), $result); - $this->eventDispatcher->dispatch(BackgroundTested::BEFORE_TEARDOWN, $event); + + $this->eventDispatcher->dispatch($event, BackgroundTested::BEFORE_TEARDOWN); $teardown = $this->baseTester->tearDown($env, $feature, $skip, $result); $event = new AfterBackgroundTested($env, $feature, $feature->getBackground(), $result, $teardown); - $this->eventDispatcher->dispatch(BackgroundTested::AFTER, $event); + + $this->eventDispatcher->dispatch($event, BackgroundTested::AFTER); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingFeatureTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingFeatureTester.php index 2bd0ad5dd..a0ce588f6 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingFeatureTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingFeatureTester.php @@ -15,6 +15,7 @@ use Behat\Behat\EventDispatcher\Event\BeforeFeatureTeardown; use Behat\Behat\EventDispatcher\Event\BeforeFeatureTested; use Behat\Testwork\Environment\Environment; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Tester\Result\TestResult; use Behat\Testwork\Tester\SpecificationTester; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -53,12 +54,14 @@ public function __construct(SpecificationTester $baseTester, EventDispatcherInte public function setUp(Environment $env, $feature, $skip) { $event = new BeforeFeatureTested($env, $feature); - $this->eventDispatcher->dispatch($event::BEFORE, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE); $setup = $this->baseTester->setUp($env, $feature, $skip); $event = new AfterFeatureSetup($env, $feature, $setup); - $this->eventDispatcher->dispatch($event::AFTER_SETUP, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER_SETUP); return $setup; } @@ -77,12 +80,14 @@ public function test(Environment $env, $feature, $skip) public function tearDown(Environment $env, $feature, $skip, TestResult $result) { $event = new BeforeFeatureTeardown($env, $feature, $result); - $this->eventDispatcher->dispatch($event::BEFORE_TEARDOWN, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE_TEARDOWN); $teardown = $this->baseTester->tearDown($env, $feature, $skip, $result); $event = new AfterFeatureTested($env, $feature, $result, $teardown); - $this->eventDispatcher->dispatch($event::AFTER, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingOutlineTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingOutlineTester.php index 13d257cef..132f584fb 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingOutlineTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingOutlineTester.php @@ -18,6 +18,7 @@ use Behat\Gherkin\Node\FeatureNode; use Behat\Gherkin\Node\OutlineNode; use Behat\Testwork\Environment\Environment; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Tester\Result\TestResult; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -55,12 +56,14 @@ public function __construct(OutlineTester $baseTester, EventDispatcherInterface public function setUp(Environment $env, FeatureNode $feature, OutlineNode $outline, $skip) { $event = new BeforeOutlineTested($env, $feature, $outline); - $this->eventDispatcher->dispatch($event::BEFORE, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE); $setup = $this->baseTester->setUp($env, $feature, $outline, $skip); $event = new AfterOutlineSetup($env, $feature, $outline, $setup); - $this->eventDispatcher->dispatch($event::AFTER_SETUP, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER_SETUP); return $setup; } @@ -79,12 +82,14 @@ public function test(Environment $env, FeatureNode $feature, OutlineNode $outlin public function tearDown(Environment $env, FeatureNode $feature, OutlineNode $outline, $skip, TestResult $result) { $event = new BeforeOutlineTeardown($env, $feature, $outline, $result); - $this->eventDispatcher->dispatch($event::BEFORE_TEARDOWN, $event); + + $this->eventDispatcher->dispatch( $event,$event::BEFORE_TEARDOWN); $teardown = $this->baseTester->tearDown($env, $feature, $outline, $skip, $result); $event = new AfterOutlineTested($env, $feature, $outline, $result, $teardown); - $this->eventDispatcher->dispatch($event::AFTER, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingScenarioTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingScenarioTester.php index 44eb13120..882be52ed 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingScenarioTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingScenarioTester.php @@ -18,6 +18,7 @@ use Behat\Gherkin\Node\FeatureNode; use Behat\Gherkin\Node\ScenarioInterface as Scenario; use Behat\Testwork\Environment\Environment; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Tester\Result\TestResult; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -85,12 +86,14 @@ public function __construct( public function setUp(Environment $env, FeatureNode $feature, Scenario $scenario, $skip) { $event = new BeforeScenarioTested($env, $feature, $scenario); - $this->eventDispatcher->dispatch($this->beforeEventName, $event); + + $this->eventDispatcher->dispatch($event, $this->beforeEventName); $setup = $this->baseTester->setUp($env, $feature, $scenario, $skip); $event = new AfterScenarioSetup($env, $feature, $scenario, $setup); - $this->eventDispatcher->dispatch($this->afterSetupEventName, $event); + + $this->eventDispatcher->dispatch($event, $this->afterSetupEventName); return $setup; } @@ -109,12 +112,14 @@ public function test(Environment $env, FeatureNode $feature, Scenario $scenario, public function tearDown(Environment $env, FeatureNode $feature, Scenario $scenario, $skip, TestResult $result) { $event = new BeforeScenarioTeardown($env, $feature, $scenario, $result); - $this->eventDispatcher->dispatch($this->beforeTeardownEventName, $event); + + $this->eventDispatcher->dispatch($event, $this->beforeTeardownEventName); $teardown = $this->baseTester->tearDown($env, $feature, $scenario, $skip, $result); $event = new AfterScenarioTested($env, $feature, $scenario, $result, $teardown); - $this->eventDispatcher->dispatch($this->afterEventName, $event); + + $this->eventDispatcher->dispatch($event, $this->afterEventName); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingStepTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingStepTester.php index 6bf3c46b2..1daafadce 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingStepTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingStepTester.php @@ -19,6 +19,7 @@ use Behat\Gherkin\Node\FeatureNode; use Behat\Gherkin\Node\StepNode; use Behat\Testwork\Environment\Environment; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -55,12 +56,14 @@ public function __construct(StepTester $baseTester, EventDispatcherInterface $ev public function setUp(Environment $env, FeatureNode $feature, StepNode $step, $skip) { $event = new BeforeStepTested($env, $feature, $step); - $this->eventDispatcher->dispatch($event::BEFORE, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE); $setup = $this->baseTester->setUp($env, $feature, $step, $skip); $event = new AfterStepSetup($env, $feature, $step, $setup); - $this->eventDispatcher->dispatch($event::AFTER_SETUP, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER_SETUP); return $setup; } @@ -79,12 +82,14 @@ public function test(Environment $env, FeatureNode $feature, StepNode $step, $sk public function tearDown(Environment $env, FeatureNode $feature, StepNode $step, $skip, StepResult $result) { $event = new BeforeStepTeardown($env, $feature, $step, $result); - $this->eventDispatcher->dispatch($event::BEFORE_TEARDOWN, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE_TEARDOWN); $teardown = $this->baseTester->tearDown($env, $feature, $step, $skip, $result); $event = new AfterStepTested($env, $feature, $step, $result, $teardown); - $this->eventDispatcher->dispatch($event::AFTER, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/TickingStepTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/TickingStepTester.php index 401725495..dcbdf4c24 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/TickingStepTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/TickingStepTester.php @@ -21,6 +21,11 @@ * to handle an interupt (on PHP7) * * @see Behat\Testwork\EventDispatcher\Cli\SigintController + * + * @deprecated Since the way signals are handled changed to use pcntl_signal_dispatch + * this class is no longer needed. + * + * @todo Remove this class in the next major version * * @author Peter Mitchell */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Cli/SyntaxController.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Cli/SyntaxController.php index a3e3f6cfc..62acc12a6 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Cli/SyntaxController.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Cli/SyntaxController.php @@ -10,6 +10,7 @@ namespace Behat\Behat\Gherkin\Cli; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Behat\Gherkin\Keywords\KeywordsDumper; use Behat\Testwork\Cli\Controller; use Symfony\Component\Console\Command\Command; @@ -17,7 +18,6 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Translation\TranslatorInterface; /** * Prints example of the feature to present all available syntax keywords. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/ServiceContainer/GherkinExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/ServiceContainer/GherkinExtension.php index 2b33cd569..60aef8e60 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/ServiceContainer/GherkinExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/ServiceContainer/GherkinExtension.php @@ -35,14 +35,14 @@ final class GherkinExtension implements Extension /* * Available services */ - const MANAGER_ID = 'gherkin'; - const KEYWORDS_DUMPER_ID = 'gherkin.keywords_dumper'; - const KEYWORDS_ID = 'gherkin.keywords'; + public const MANAGER_ID = 'gherkin'; + public const KEYWORDS_DUMPER_ID = 'gherkin.keywords_dumper'; + public const KEYWORDS_ID = 'gherkin.keywords'; /* * Available extension points */ - const LOADER_TAG = 'gherkin.loader'; + public const LOADER_TAG = 'gherkin.loader'; /** * @var ServiceProcessor @@ -225,6 +225,7 @@ private function loadDefaultLoaders(ContainerBuilder $container, $cachePath) } $definition->addMethodCall('setCache', array($cacheDefinition)); + $definition->addMethodCall('setBasePath', array('%paths.base%')); $definition->addTag(self::LOADER_TAG, array('priority' => 50)); $container->setDefinition('gherkin.loader.gherkin_file', $definition); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/LazyFeatureIterator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/LazyFeatureIterator.php index 61dd9bdb2..1883a0c6b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/LazyFeatureIterator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/LazyFeatureIterator.php @@ -84,7 +84,7 @@ public function getSuite() /** * {@inheritdoc} */ - public function rewind() + public function rewind(): void { $this->position = 0; $this->moveToNextAvailableFeature(); @@ -93,7 +93,7 @@ public function rewind() /** * {@inheritdoc} */ - public function next() + public function next(): void { $this->moveToNextAvailableFeature(); } @@ -101,7 +101,7 @@ public function next() /** * {@inheritdoc} */ - public function valid() + public function valid(): bool { return null !== $this->currentFeature; } @@ -109,7 +109,7 @@ public function valid() /** * {@inheritdoc} */ - public function key() + public function key(): int { return $this->position; } @@ -117,7 +117,7 @@ public function key() /** * {@inheritdoc} */ - public function current() + public function current(): FeatureNode { return $this->currentFeature; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemRerunScenariosListLocator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemRerunScenariosListLocator.php index d5ac5b3a3..8636d3279 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemRerunScenariosListLocator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemRerunScenariosListLocator.php @@ -51,7 +51,7 @@ public function getLocatorExamples() */ public function locateSpecifications(Suite $suite, $locator) { - if (!is_file($locator) || 'rerun' !== pathinfo($locator, PATHINFO_EXTENSION)) { + if (null === $locator || !is_file($locator) || 'rerun' !== pathinfo($locator, PATHINFO_EXTENSION)) { return new NoSpecificationsIterator($suite); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemScenariosListLocator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemScenariosListLocator.php index 59141df80..ccacedd0f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemScenariosListLocator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Specification/Locator/FilesystemScenariosListLocator.php @@ -51,7 +51,7 @@ public function getLocatorExamples() */ public function locateSpecifications(Suite $suite, $locator) { - if (!is_file($locator) || 'scenarios' !== pathinfo($locator, PATHINFO_EXTENSION)) { + if (null === $locator || !is_file($locator) || 'scenarios' !== pathinfo($locator, PATHINFO_EXTENSION)) { return new NoSpecificationsIterator($suite); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Suite/Setup/SuiteWithPathsSetup.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Suite/Setup/SuiteWithPathsSetup.php index 65c7c8f73..f50249f6e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Suite/Setup/SuiteWithPathsSetup.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Gherkin/Suite/Setup/SuiteWithPathsSetup.php @@ -97,7 +97,7 @@ private function locatePath($path) * * @param string $file A file path * - * @return Boolean + * @return bool */ private function isAbsolutePath($file) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/AutowiringResolver.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/AutowiringResolver.php new file mode 100644 index 000000000..11dde13b9 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/AutowiringResolver.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer\Argument; + +use Behat\Behat\Context\Argument\ArgumentResolver; +use Behat\Behat\HelperContainer\ArgumentAutowirer; +use Psr\Container\ContainerInterface; +use ReflectionClass; + +/** + * Resolves arguments that weren't resolved before by autowiring. + * + * @see ContextFactory + * + * @author Konstantin Kudryashov + */ +final class AutowiringResolver implements ArgumentResolver +{ + /** + * @var ArgumentAutowirer + */ + private $autowirer; + + /** + * Initialises resolver. + * + * @param ContainerInterface $container + */ + public function __construct(ContainerInterface $container) + { + $this->autowirer = new ArgumentAutowirer($container); + } + + /** + * {@inheritdoc} + */ + public function resolveArguments(ReflectionClass $classReflection, array $arguments) + { + if ($constructor = $classReflection->getConstructor()) { + return $this->autowirer->autowireArguments($constructor, $arguments); + } + + return $arguments; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolver.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolver.php index e8d23474d..7e2191a0f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolver.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolver.php @@ -11,7 +11,8 @@ namespace Behat\Behat\HelperContainer\Argument; use Behat\Behat\Context\Argument\ArgumentResolver; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; use ReflectionClass; /** @@ -40,6 +41,8 @@ public function __construct(ContainerInterface $container) /** * {@inheritdoc} + * + * @throws ContainerExceptionInterface */ public function resolveArguments(ReflectionClass $classReflection, array $arguments) { @@ -55,10 +58,12 @@ public function resolveArguments(ReflectionClass $classReflection, array $argume * @param mixed $value * * @return mixed + * + * @throws ContainerExceptionInterface */ private function resolveArgument($value) { - if ('@' === mb_substr($value, 0, 1)) { + if (is_string($value) && 0 === mb_strpos($value, '@')) { return $this->container->get(mb_substr($value, 1)); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolverFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolverFactory.php index e5dbfadc5..8820288c5 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolverFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Argument/ServicesResolverFactory.php @@ -10,13 +10,17 @@ namespace Behat\Behat\HelperContainer\Argument; +use Behat\Behat\Context\Argument\ArgumentResolver; +use Behat\Behat\HelperContainer\Environment\ServiceContainerEnvironment; +use Behat\Behat\Context\Argument\ArgumentResolverFactory; use Behat\Behat\Context\Argument\SuiteScopedResolverFactory; use Behat\Behat\HelperContainer\BuiltInServiceContainer; use Behat\Behat\HelperContainer\Exception\WrongContainerClassException; use Behat\Behat\HelperContainer\Exception\WrongServicesConfigurationException; use Behat\Behat\HelperContainer\ServiceContainer\HelperContainerExtension; +use Behat\Testwork\Environment\Environment; use Behat\Testwork\Suite\Suite; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; use Symfony\Component\DependencyInjection\TaggedContainerInterface; /** @@ -26,7 +30,7 @@ * * @author Konstantin Kudryashov */ -final class ServicesResolverFactory implements SuiteScopedResolverFactory +final class ServicesResolverFactory implements SuiteScopedResolverFactory, ArgumentResolverFactory { /** * @var TaggedContainerInterface @@ -45,16 +49,50 @@ public function __construct(TaggedContainerInterface $container) /** * {@inheritdoc} + * + * @deprecated as part of SuiteScopedResolverFactory deprecation. Would be removed in 4.0 + * + * @throws WrongServicesConfigurationException + * @throws WrongContainerClassException */ public function generateArgumentResolvers(Suite $suite) { + @trigger_error( + 'SuiteScopedResolverFactory::generateArgumentResolvers() was deprecated and will be removed in 4.0', + E_USER_DEPRECATED + ); + + if (!$suite->hasSetting('services')) { + return array(); + } + + $container = $this->createContainer($suite->getSetting('services')); + + return $this->createResolvers($container, false); + } + + /** + * {@inheritdoc} + * + * @throws WrongServicesConfigurationException + * @throws WrongContainerClassException + */ + public function createArgumentResolvers(Environment $environment) + { + $suite = $environment->getSuite(); + if (!$suite->hasSetting('services')) { return array(); } $container = $this->createContainer($suite->getSetting('services')); + $autowire = $suite->hasSetting('autowire') && $suite->getSetting('autowire'); + + if ($environment instanceof ServiceContainerEnvironment) { + $environment->setServiceContainer($container); + } - return array($this->createArgumentResolver($container)); + return $this->createResolvers($container, $autowire); } /** @@ -63,6 +101,8 @@ public function generateArgumentResolvers(Suite $suite) * @param string $settings * * @return mixed + * + * @throws WrongServicesConfigurationException */ private function createContainer($settings) { @@ -85,10 +125,12 @@ private function createContainer($settings) * @param string $settings * * @return mixed + * + * @throws WrongServicesConfigurationException */ private function createContainerFromString($settings) { - if ('@' === mb_substr($settings, 0, 1)) { + if (0 === mb_strpos($settings, '@')) { return $this->loadContainerFromContainer(mb_substr($settings, 1)); } @@ -113,12 +155,14 @@ private function createContainerFromArray(array $settings) * @param string $name * * @return mixed + * + * @throws WrongServicesConfigurationException */ private function loadContainerFromContainer($name) { $services = $this->container->findTaggedServiceIds(HelperContainerExtension::HELPER_CONTAINER_TAG); - if (!in_array($name, array_keys($services))) { + if (!array_key_exists($name, $services)) { throw new WrongServicesConfigurationException( sprintf('Service container `@%s` was not found.', $name) ); @@ -138,7 +182,7 @@ private function createContainerFromClassSpec($classSpec) { $constructor = explode('::', $classSpec); - if (2 == count($constructor)) { + if (2 === count($constructor)) { return call_user_func($constructor); } @@ -149,21 +193,28 @@ private function createContainerFromClassSpec($classSpec) * Checks if container implements the correct interface and creates resolver using it. * * @param mixed $container + * @param bool $autowire * - * @return ServicesResolver + * @return ArgumentResolver[] + * + * @throws WrongContainerClassException */ - private function createArgumentResolver($container) + private function createResolvers($container, $autowire) { if (!$container instanceof ContainerInterface) { throw new WrongContainerClassException( sprintf( - 'Service container is expected to implement `Interop\Container\ContainerInterface`, but `%s` does not.', + 'Service container is expected to implement `Psr\Container\ContainerInterface`, but `%s` does not.', get_class($container) ), get_class($container) ); } - return new ServicesResolver($container); + if ($autowire) { + return array(new ServicesResolver($container), new AutowiringResolver($container)); + } + + return array(new ServicesResolver($container)); } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ArgumentAutowirer.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ArgumentAutowirer.php new file mode 100644 index 000000000..0fbbe210c --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ArgumentAutowirer.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer; + +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface as PsrContainerInterface; +use ReflectionClass; +use ReflectionFunctionAbstract; +use ReflectionNamedType; +use ReflectionParameter; +use ReflectionException; + +/** + * Automatically wires arguments of a given function from inside the container by using type-hints. + * + * @author Konstantin Kudryashov + */ +final class ArgumentAutowirer +{ + /** + * @var PsrContainerInterface + */ + private $container; + + /** + * Initialises wirer. + * + * @param PsrContainerInterface $container + */ + public function __construct(PsrContainerInterface $container) + { + $this->container = $container; + } + + /** + * Autowires given arguments using provided container. + * + * @param ReflectionFunctionAbstract $reflection + * @param array $arguments + * + * @return array + * + * @throws ContainerExceptionInterface if unset argument typehint can not be resolved from container + */ + public function autowireArguments(ReflectionFunctionAbstract $reflection, array $arguments) + { + $newArguments = $arguments; + foreach ($reflection->getParameters() as $index => $parameter) { + if ($this->isArgumentWireable($newArguments, $index, $parameter)) { + $newArguments[$index] = $this->container->get($this->getClassFromParameter($parameter)); + } + } + + return $newArguments; + } + + /** + * Checks if given argument is wireable. + * + * Argument is wireable if it was not previously set and it has a class type-hint. + * + * @param array $arguments + * @param integer $index + * @param ReflectionParameter $parameter + * + * @return bool + */ + private function isArgumentWireable(array $arguments, $index, ReflectionParameter $parameter) + { + if (isset($arguments[$index]) || array_key_exists($index, $arguments)) { + return false; + } + + if (isset($arguments[$parameter->getName()]) || array_key_exists($parameter->getName(), $arguments)) { + return false; + } + + return (bool) $this->getClassFromParameter($parameter); + } + + private function getClassFromParameter(ReflectionParameter $parameter) : ?string + { + if (!($type = $parameter->getType()) || !($type instanceof ReflectionNamedType)) { + return null; + } + + try { + $typeString = $type->getName(); + + if ($typeString == 'self') { + return $parameter->getDeclaringClass()->getName(); + } + elseif ($typeString == 'parent') { + return $parameter->getDeclaringClass()->getParentClass()->getName(); + } + + // will throw if not valid class + new ReflectionClass($typeString); + + return $typeString; + + } catch (ReflectionException $e) { + return null; + } + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/BuiltInServiceContainer.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/BuiltInServiceContainer.php index 71795ee84..0f131cbc0 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/BuiltInServiceContainer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/BuiltInServiceContainer.php @@ -12,7 +12,6 @@ use Behat\Behat\HelperContainer\Exception\ServiceNotFoundException; use Behat\Behat\HelperContainer\Exception\WrongServicesConfigurationException; -use Interop\Container\ContainerInterface; use ReflectionClass; use ReflectionMethod; @@ -47,7 +46,7 @@ public function __construct(array $schema) */ public function has($id) { - return isset($this->schema[$id]); + return array_key_exists($id, $this->schema); } /** @@ -62,7 +61,7 @@ public function get($id) ); } - return $this->instances[$id] = isset($this->instances[$id]) ? $this->instances[$id] : $this->createInstance($id); + return $this->instances[$id] = $this->instances[$id] ?? $this->createInstance($id); } /** @@ -99,6 +98,10 @@ private function getAndValidateServiceSchema($id) { $schema = $this->schema[$id]; + if (null === $schema) { + $schema = array('class' => $id); + } + if (is_string($schema)) { $schema = array('class' => $schema); } @@ -120,10 +123,7 @@ private function getAndValidateServiceSchema($id) private function getAndValidateClass($id, array $schema) { if (!isset($schema['class'])) { - throw new WrongServicesConfigurationException(sprintf( - 'All services of the built-in `services` must have `class` option set, but `%s` does not.', - $id - )); + $schema['class'] = $id; } return $schema['class']; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Call/Filter/ServicesResolver.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Call/Filter/ServicesResolver.php new file mode 100644 index 000000000..0c70fef71 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Call/Filter/ServicesResolver.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer\Call\Filter; + +use Behat\Behat\Definition\Definition; +use Behat\Behat\HelperContainer\Environment\ServiceContainerEnvironment; +use Behat\Behat\Definition\Call\DefinitionCall; +use Behat\Behat\HelperContainer\ArgumentAutowirer; +use Behat\Behat\HelperContainer\Exception\UnsupportedCallException; +use Behat\Behat\Transformation\Call\TransformationCall; +use Behat\Behat\Transformation\Transformation; +use Behat\Testwork\Call\Call; +use Behat\Testwork\Call\Filter\CallFilter; +use Behat\Testwork\Environment\Call\EnvironmentCall; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; + +/** + * Dynamically resolves call arguments using the service container. + * + * @author Konstantin Kudryashov + */ +final class ServicesResolver implements CallFilter +{ + /** + * {@inheritdoc} + */ + public function supportsCall(Call $call) + { + return ($call instanceof DefinitionCall || $call instanceof TransformationCall) + && $call->getEnvironment() instanceof ServiceContainerEnvironment; + } + + /** + * Filters a call and returns a new one. + * + * @param Call $call + * + * @return Call + * + * @throws UnsupportedCallException + * @throws ContainerExceptionInterface + */ + public function filterCall(Call $call) + { + if ($container = $this->getContainer($call)) { + $autowirer = new ArgumentAutowirer($container); + $newArguments = $autowirer->autowireArguments($call->getCallee()->getReflection(), $call->getArguments()); + + return $this->repackageCallIfNewArguments($call, $newArguments); + } + + return $call; + } + + /** + * Gets container from the call. + * + * @param Call $call + * + * @return null|ContainerInterface + * + * @throws UnsupportedCallException if given call is not EnvironmentCall or environment is not ServiceContainerEnvironment + */ + private function getContainer(Call $call) + { + if (!$call instanceof EnvironmentCall) { + throw new UnsupportedCallException(sprintf( + 'ServicesResolver can not filter `%s` call.', + get_class($call) + ), $call); + } + + $environment = $call->getEnvironment(); + + if (!$environment instanceof ServiceContainerEnvironment) { + throw new UnsupportedCallException(sprintf( + 'ServicesResolver can not filter `%s` call.', + get_class($call) + ), $call); + } + + return $environment->getServiceContainer(); + } + + /** + * Repackages old calls with new arguments, but only if two differ. + * + * @param Call $call + * @param array $arguments + * + * @return Call + * + * @throws UnsupportedCallException if given call is not DefinitionCall or TransformationCall + */ + private function repackageCallIfNewArguments(Call $call, array $arguments) + { + if ($arguments === $call->getArguments()) { + return $call; + } + + return $this->repackageCallWithNewArguments($call, $arguments); + } + + /** + * Repackages old calls with new arguments. + * + * @param Call $call + * @param array $newArguments + * + * @return DefinitionCall|TransformationCall + * + * @throws UnsupportedCallException + */ + private function repackageCallWithNewArguments(Call $call, array $newArguments) + { + if ($call instanceof DefinitionCall) { + return $this->repackageDefinitionCall($call, $newArguments); + } + + if ($call instanceof TransformationCall) { + return $this->repackageTransformationCall($call, $newArguments); + } + + throw new UnsupportedCallException( + sprintf( + 'ServicesResolver can not filter `%s` call.', + get_class($call) + ), $call + ); + } + + /** + * Repackages definition call with new arguments. + * + * @param DefinitionCall $call + * @param array $newArguments + * + * @return DefinitionCall + * + * @throws UnsupportedCallException + */ + private function repackageDefinitionCall(DefinitionCall $call, array $newArguments) + { + $definition = $call->getCallee(); + + if (!$definition instanceof Definition) { + throw new UnsupportedCallException( + sprintf( + 'Something is wrong in callee associated with `%s` call.', + get_class($call) + ), $call + ); + } + + return new DefinitionCall( + $call->getEnvironment(), + $call->getFeature(), + $call->getStep(), + $definition, + $newArguments, + $call->getErrorReportingLevel() + ); + } + + /** + * Repackages transformation call with new arguments. + * + * @param TransformationCall $call + * @param array $newArguments + * + * @return TransformationCall + * + * @throws UnsupportedCallException + */ + private function repackageTransformationCall(TransformationCall $call, array $newArguments) + { + $transformation = $call->getCallee(); + + if (!$transformation instanceof Transformation) { + throw new UnsupportedCallException( + sprintf( + 'Something is wrong in callee associated with `%s` call.', + get_class($call) + ), $call + ); + } + + return new TransformationCall( + $call->getEnvironment(), + $call->getDefinition(), + $transformation, + $newArguments + ); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ContainerInterface.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ContainerInterface.php new file mode 100644 index 000000000..75fb43fa8 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ContainerInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer; + +class_alias( + interface_exists('Interop\\Container\\ContainerInterface') + ? 'Interop\\Container\\ContainerInterface' + : 'Psr\\Container\\ContainerInterface', + 'Behat\\Behat\\HelperContainer\\ContainerInterface' +); + +if (false) { + /** + * @internal + */ + interface ContainerInterface + { + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Environment/ServiceContainerEnvironment.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Environment/ServiceContainerEnvironment.php new file mode 100644 index 000000000..8f58ba623 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Environment/ServiceContainerEnvironment.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer\Environment; + +use Behat\Testwork\Environment\Environment; +use Psr\Container\ContainerInterface; + +/** + * Represents test environment based on a service locator pattern. + * + * @see ContextEnvironmentHandler + * + * @author Konstantin Kudryashov + */ +interface ServiceContainerEnvironment extends Environment +{ + /** + * Sets/unsets service container for the environment. + * + * @param ContainerInterface|null $container + */ + public function setServiceContainer(ContainerInterface $container = null); + + /** + * Returns environment service container if set. + * + * @return null|ContainerInterface + */ + public function getServiceContainer(); +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ContainerException.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ContainerException.php new file mode 100644 index 000000000..fd5543ad0 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ContainerException.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer\Exception; + +class_alias( + interface_exists('Interop\\Container\\Exception\\ContainerException') + ? 'Interop\\Container\\Exception\\ContainerException' + : 'Psr\\Container\\ContainerExceptionInterface', + 'Behat\\Behat\\HelperContainer\\Exception\\ContainerException' +); + +if (false) { + /** + * @internal + */ + interface ContainerException + { + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/HelperContainerException.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/HelperContainerException.php index 04ab35f96..d61919225 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/HelperContainerException.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/HelperContainerException.php @@ -11,7 +11,6 @@ namespace Behat\Behat\HelperContainer\Exception; use Behat\Testwork\Environment\Exception\EnvironmentException; -use Interop\Container\Exception\ContainerException; /** * All HelperContainer exceptions implement this interface. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/NotFoundException.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/NotFoundException.php new file mode 100644 index 000000000..739e5181f --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/NotFoundException.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer\Exception; + +class_alias( + interface_exists('Interop\\Container\\Exception\\NotFoundException') + ? 'Interop\\Container\\Exception\\NotFoundException' + : 'Psr\\Container\\NotFoundExceptionInterface', + 'Behat\\Behat\\HelperContainer\\Exception\\NotFoundException' +); + +if (false) { + /** + * @internal + */ + interface NotFoundException + { + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ServiceNotFoundException.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ServiceNotFoundException.php index 02bd601b7..a82b63ed0 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ServiceNotFoundException.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/ServiceNotFoundException.php @@ -10,7 +10,6 @@ namespace Behat\Behat\HelperContainer\Exception; -use Interop\Container\Exception\NotFoundException; use InvalidArgumentException; /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/UnsupportedCallException.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/UnsupportedCallException.php new file mode 100644 index 000000000..7f16ca82f --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/Exception/UnsupportedCallException.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\HelperContainer\Exception; + +use Behat\Testwork\Call\Call; +use InvalidArgumentException; + +/** + * Represents an exception caused by an attempt to filter an unsupported call. + * + * @author Konstantin Kudryashov + */ +final class UnsupportedCallException extends InvalidArgumentException implements HelperContainerException +{ + /** + * @var Call + */ + private $call; + + /** + * Initializes exception. + * + * @param string $message + * @param Call $call + */ + public function __construct($message, Call $call) + { + parent::__construct($message); + + $this->call = $call; + } + + /** + * Returns a call that caused exception. + * + * @return Call + */ + public function getCall() + { + return $this->call; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ServiceContainer/HelperContainerExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ServiceContainer/HelperContainerExtension.php index 09ff59400..399ffff09 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ServiceContainer/HelperContainerExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/HelperContainer/ServiceContainer/HelperContainerExtension.php @@ -12,12 +12,14 @@ use Behat\Behat\Context\ServiceContainer\ContextExtension; use Behat\Behat\HelperContainer\Exception\WrongServicesConfigurationException; +use Behat\Testwork\Call\ServiceContainer\CallExtension; use Behat\Testwork\ServiceContainer\Extension; use Behat\Testwork\ServiceContainer\ExtensionManager; use Behat\Testwork\ServiceContainer\ServiceProcessor; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; /** * Behat helper container extension. @@ -31,7 +33,7 @@ final class HelperContainerExtension implements Extension /* * Available extension points */ - const HELPER_CONTAINER_TAG = 'helper_container.container'; + public const HELPER_CONTAINER_TAG = 'helper_container.container'; /** * @var ServiceProcessor @@ -75,9 +77,15 @@ public function configure(ArrayNodeDefinition $builder) */ public function load(ContainerBuilder $container, array $config) { - $definition = new Definition('Behat\Behat\HelperContainer\Argument\ServicesResolverFactory', array($container)); + $definition = new Definition('Behat\Behat\HelperContainer\Argument\ServicesResolverFactory', array( + new Reference('service_container') + )); $definition->addTag(ContextExtension::SUITE_SCOPED_RESOLVER_FACTORY_TAG, array('priority' => 0)); $container->setDefinition(ContextExtension::SUITE_SCOPED_RESOLVER_FACTORY_TAG . '.helper_container', $definition); + + $definition = new Definition('Behat\Behat\HelperContainer\Call\Filter\ServicesResolver'); + $definition->addTag(CallExtension::CALL_FILTER_TAG, array('priority' => 0)); + $container->setDefinition(CallExtension::CALL_FILTER_TAG . '.helper_container', $definition); } /** @@ -88,31 +96,11 @@ public function process(ContainerBuilder $container) $references = $this->processor->findAndSortTaggedServices($container, self::HELPER_CONTAINER_TAG); foreach ($references as $reference) { - if ($this->isDefinitionShared($container->getDefinition((string) $reference))) { + if ($container->getDefinition((string) $reference)->isShared()) { throw new WrongServicesConfigurationException(sprintf( 'Container services must not be configured as shared, but `@%s` is.', $reference )); } } } - - /** - * Checks if provided definition is shared. - * - * @param Definition $definition - * - * @return bool - * - * @todo Remove after upgrading to Symfony 2.8+ - */ - private function isDefinitionShared(Definition $definition) - { - if (method_exists($definition, 'isShared')) { - return $definition->isShared(); - } else if (method_exists($definition, 'getScope')) { - return $definition->getScope() !== ContainerBuilder::SCOPE_PROTOTYPE; - } - - return false; - } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeFeatureHook.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeFeatureHook.php index b3d4fb24b..9bc9a554f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeFeatureHook.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeFeatureHook.php @@ -68,7 +68,7 @@ public function filterMatches(HookScope $scope) * @param FeatureNode $feature * @param string $filterString * - * @return Boolean + * @return bool */ private function isMatch(FeatureNode $feature, $filterString) { @@ -89,7 +89,7 @@ private function isMatch(FeatureNode $feature, $filterString) * @param FeatureNode $feature * @param string $filterString * - * @return Boolean + * @return bool */ private function isMatchTagFilter(FeatureNode $feature, $filterString) { @@ -104,7 +104,7 @@ private function isMatchTagFilter(FeatureNode $feature, $filterString) * @param FeatureNode $feature * @param string $filterString * - * @return Boolean + * @return bool */ private function isMatchNameFilter(FeatureNode $feature, $filterString) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeScenarioHook.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeScenarioHook.php index 56e25f999..429fafef2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeScenarioHook.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeScenarioHook.php @@ -48,7 +48,7 @@ public function filterMatches(HookScope $scope) * @param ScenarioInterface $scenario * @param string $filterString * - * @return Boolean + * @return bool */ protected function isMatch(FeatureNode $feature, ScenarioInterface $scenario, $filterString) { @@ -70,16 +70,12 @@ protected function isMatch(FeatureNode $feature, ScenarioInterface $scenario, $f * @param ScenarioInterface $scenario * @param string $filterString * - * @return Boolean + * @return bool */ protected function isMatchTagFilter(FeatureNode $feature, ScenarioInterface $scenario, $filterString) { $filter = new TagFilter($filterString); - if ($filter->isFeatureMatch($feature)) { - return true; - } - return $filter->isScenarioMatch($feature, $scenario); } @@ -89,7 +85,7 @@ protected function isMatchTagFilter(FeatureNode $feature, ScenarioInterface $sce * @param ScenarioInterface $scenario * @param string $filterString * - * @return Boolean + * @return bool */ protected function isMatchNameFilter(ScenarioInterface $scenario, $filterString) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeStepHook.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeStepHook.php index a1326c3cf..401e3ccdf 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeStepHook.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Call/RuntimeStepHook.php @@ -55,7 +55,7 @@ public function filterMatches(HookScope $scope) * @param StepNode $step * @param string $filterString * - * @return Boolean + * @return bool */ private function isStepMatch(StepNode $step, $filterString) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Annotation/HookAnnotationReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Annotation/HookAnnotationReader.php index 86a95cca5..663245ee0 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Annotation/HookAnnotationReader.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Annotation/HookAnnotationReader.php @@ -57,7 +57,7 @@ public function readCallee($contextClass, ReflectionMethod $method, $docLine, $d $type = strtolower($match[1]); $class = self::$classes[$type]; - $pattern = isset($match[2]) ? $match[2] : null; + $pattern = $match[2] ?? null; $callable = array($contextClass, $method->getName()); return new $class($pattern, $callable, $description); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Attribute/HookAttributeReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Attribute/HookAttributeReader.php new file mode 100644 index 000000000..f49dd93dc --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Context/Attribute/HookAttributeReader.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Hook\Context\Attribute; + +use Behat\Behat\Context\Annotation\DocBlockHelper; +use Behat\Behat\Context\Attribute\AttributeReader; +use Behat\Hook\AfterFeature; +use Behat\Hook\AfterScenario; +use Behat\Hook\AfterStep; +use Behat\Hook\BeforeFeature; +use Behat\Hook\BeforeScenario; +use Behat\Hook\BeforeStep; +use Behat\Hook\Hook; +use ReflectionMethod; + +final class HookAttributeReader implements AttributeReader +{ + /** + * @var string[] + */ + private const KNOWN_ATTRIBUTES = array( + AfterFeature::class => 'Behat\Behat\Hook\Call\AfterFeature', + AfterScenario::class => 'Behat\Behat\Hook\Call\AfterScenario', + AfterStep::class => 'Behat\Behat\Hook\Call\AfterStep', + BeforeFeature::class => 'Behat\Behat\Hook\Call\BeforeFeature', + BeforeScenario::class => 'Behat\Behat\Hook\Call\BeforeScenario', + BeforeStep::class => 'Behat\Behat\Hook\Call\BeforeStep', + ); + + /** + * @var DocBlockHelper + */ + private $docBlockHelper; + + /** + * Initializes reader. + * + * @param DocBlockHelper $docBlockHelper + */ + public function __construct(DocBlockHelper $docBlockHelper) + { + $this->docBlockHelper = $docBlockHelper; + } + + /** + * @{inheritdoc} + */ + public function readCallees(string $contextClass, ReflectionMethod $method) + { + if (\PHP_MAJOR_VERSION < 8) { + return []; + } + + $attributes = $method->getAttributes(Hook::class, \ReflectionAttribute::IS_INSTANCEOF); + + $callees = []; + foreach ($attributes as $attribute) { + $class = self::KNOWN_ATTRIBUTES[$attribute->getName()]; + $callable = array($contextClass, $method->getName()); + $description = null; + if ($docBlock = $method->getDocComment()) { + $description = $this->docBlockHelper->extractDescription($docBlock); + } + + $callees[] = new $class($attribute->newInstance()->filterString, $callable, $description); + } + + return $callees; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/FeatureScope.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/FeatureScope.php index fe87469de..6b27b8271 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/FeatureScope.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/FeatureScope.php @@ -20,8 +20,8 @@ */ interface FeatureScope extends HookScope { - const BEFORE = 'feature.before'; - const AFTER = 'feature.after'; + public const BEFORE = 'feature.before'; + public const AFTER = 'feature.after'; /** * Returns scope feature. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/ScenarioScope.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/ScenarioScope.php index c5238bc79..c7dbe513a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/ScenarioScope.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/ScenarioScope.php @@ -21,8 +21,8 @@ */ interface ScenarioScope extends HookScope { - const BEFORE = 'scenario.before'; - const AFTER = 'scenario.after'; + public const BEFORE = 'scenario.before'; + public const AFTER = 'scenario.after'; /** * Returns scope feature. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/StepScope.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/StepScope.php index c07a79b02..449154dd0 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/StepScope.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/Scope/StepScope.php @@ -21,8 +21,8 @@ */ interface StepScope extends HookScope { - const BEFORE = 'step.before'; - const AFTER = 'step.after'; + public const BEFORE = 'step.before'; + public const AFTER = 'step.after'; /** * Returns scope feature. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/ServiceContainer/HookExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/ServiceContainer/HookExtension.php index 76d4be9be..97742c392 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/ServiceContainer/HookExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Hook/ServiceContainer/HookExtension.php @@ -11,6 +11,7 @@ namespace Behat\Behat\Hook\ServiceContainer; use Behat\Behat\Context\ServiceContainer\ContextExtension; +use Behat\Behat\Definition\ServiceContainer\DefinitionExtension; use Behat\Behat\Tester\ServiceContainer\TesterExtension; use Behat\Testwork\Hook\ServiceContainer\HookExtension as BaseExtension; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -32,6 +33,7 @@ public function load(ContainerBuilder $container, array $config) parent::load($container, $config); $this->loadAnnotationReader($container); + $this->loadAttributeReader($container); } /** @@ -85,4 +87,18 @@ private function loadAnnotationReader(ContainerBuilder $container) $definition->addTag(ContextExtension::ANNOTATION_READER_TAG, array('priority' => 50)); $container->setDefinition(ContextExtension::ANNOTATION_READER_TAG . '.hook', $definition); } + + /** + * Loads hook attribute reader. + * + * @param ContainerBuilder $container + */ + private function loadAttributeReader(ContainerBuilder $container) + { + $definition = new Definition('\Behat\Behat\Hook\Context\Attribute\HookAttributeReader', array( + new Reference(DefinitionExtension::DOC_BLOCK_HELPER_ID) + )); + $definition->addTag(ContextExtension::ATTRIBUTE_READER_TAG, array('priority' => 50)); + $container->setDefinition(ContextExtension::ATTRIBUTE_READER_TAG . '.hook', $definition); + } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/FeatureListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/FeatureListener.php index 13bc0a415..9487ecde2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/FeatureListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/FeatureListener.php @@ -15,9 +15,9 @@ use Behat\Behat\EventDispatcher\Event\FeatureTested; use Behat\Behat\Output\Node\Printer\FeaturePrinter; use Behat\Behat\Output\Node\Printer\SetupPrinter; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens to feature events and calls appropriate printers. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineListener.php index bacb12f3a..85b602b7b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineListener.php @@ -22,9 +22,9 @@ use Behat\Behat\Output\Node\Printer\SetupPrinter; use Behat\Behat\Output\Node\Printer\StepPrinter; use Behat\Gherkin\Node\ExampleNode; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens to expanded outline events and calls appropriate printers. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineTableListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineTableListener.php index fa77da454..09a179bc1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineTableListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/OutlineTableListener.php @@ -24,10 +24,10 @@ use Behat\Behat\Output\Node\Printer\SetupPrinter; use Behat\Behat\Tester\Result\StepResult; use Behat\Gherkin\Node\OutlineNode; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; use Behat\Testwork\Tester\Setup\Setup; -use Symfony\Component\EventDispatcher\Event; /** * Listens to outline table events and calls appropriate printers. @@ -61,7 +61,7 @@ final class OutlineTableListener implements EventListener */ private $exampleSetup; /** - * @var Boolean + * @var bool */ private $headerPrinted = false; /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/ScenarioNodeListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/ScenarioNodeListener.php index 77ac28e70..bcc9f206a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/ScenarioNodeListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/ScenarioNodeListener.php @@ -13,11 +13,11 @@ use Behat\Behat\EventDispatcher\Event\ScenarioLikeTested; use Behat\Behat\Output\Node\Printer\ScenarioPrinter; use Behat\Behat\Output\Node\Printer\SetupPrinter; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\Event\AfterSetup; use Behat\Testwork\EventDispatcher\Event\AfterTested; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens to scenario events and calls appropriate printers (header/footer). diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/StepListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/StepListener.php index 0d62a56ef..dda597b72 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/StepListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/StepListener.php @@ -18,9 +18,9 @@ use Behat\Behat\Output\Node\Printer\SetupPrinter; use Behat\Behat\Output\Node\Printer\StepPrinter; use Behat\Gherkin\Node\ScenarioLikeInterface; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens to step events and call appropriate printers. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/SuiteListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/SuiteListener.php index 8e116e06d..71ee608fe 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/SuiteListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/AST/SuiteListener.php @@ -11,11 +11,11 @@ namespace Behat\Behat\Output\Node\EventListener\AST; use Behat\Behat\Output\Node\Printer\SetupPrinter; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\Event\AfterSuiteSetup; use Behat\Testwork\EventDispatcher\Event\AfterSuiteTested; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Behat suite listener. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FireOnlySiblingsListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FireOnlySiblingsListener.php index 7e5788554..243b200d8 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FireOnlySiblingsListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FireOnlySiblingsListener.php @@ -10,9 +10,9 @@ namespace Behat\Behat\Output\Node\EventListener\Flow; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Behat fire only siblings listener. @@ -37,7 +37,7 @@ class FireOnlySiblingsListener implements EventListener */ private $descendant; /** - * @var Boolean + * @var bool */ private $inContext = false; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FirstBackgroundFiresFirstListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FirstBackgroundFiresFirstListener.php index f9bb95f64..78983627a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FirstBackgroundFiresFirstListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/FirstBackgroundFiresFirstListener.php @@ -15,9 +15,9 @@ use Behat\Behat\EventDispatcher\Event\FeatureTested; use Behat\Behat\EventDispatcher\Event\OutlineTested; use Behat\Behat\EventDispatcher\Event\ScenarioTested; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Behat first background fires first listener. @@ -34,7 +34,7 @@ class FirstBackgroundFiresFirstListener implements EventListener */ private $descendant; /** - * @var Boolean + * @var bool */ private $firstBackgroundEnded = false; /** @@ -103,7 +103,7 @@ private function markFirstBackgroundPrintedAfterBackground($eventName) * * @param Event $event * - * @return Boolean + * @return bool */ private function isEventDelayedUntilFirstBackgroundPrinted(Event $event) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/OnlyFirstBackgroundFiresListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/OnlyFirstBackgroundFiresListener.php index ad3679c8f..9bb73822e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/OnlyFirstBackgroundFiresListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Flow/OnlyFirstBackgroundFiresListener.php @@ -14,9 +14,9 @@ use Behat\Behat\EventDispatcher\Event\AfterStepTested; use Behat\Behat\EventDispatcher\Event\BackgroundTested; use Behat\Behat\EventDispatcher\Event\FeatureTested; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Behat only first background fires listener. @@ -36,15 +36,15 @@ class OnlyFirstBackgroundFiresListener implements EventListener */ private $descendant; /** - * @var Boolean + * @var bool */ private $firstBackgroundEnded = false; /** - * @var Boolean + * @var bool */ private $inBackground = false; /** - * @var Boolean + * @var bool */ private $stepSetupHadOutput = false; @@ -125,7 +125,7 @@ private function markFirstBackgroundPrintedAfterBackground($eventName) * * @param Event $event * - * @return Boolean + * @return bool */ private function isSkippableEvent(Event $event) { @@ -141,7 +141,7 @@ private function isSkippableEvent(Event $event) * * @param Event $event * - * @return Boolean + * @return bool */ private function isNonFailingConsequentBackgroundStep(Event $event) { @@ -157,7 +157,7 @@ private function isNonFailingConsequentBackgroundStep(Event $event) * * @param Event $event * - * @return Boolean + * @return bool */ private function isStepEventWithOutput(Event $event) { @@ -169,7 +169,7 @@ private function isStepEventWithOutput(Event $event) * * @param Event $event * - * @return Boolean + * @return bool */ private function isBeforeStepEventWithOutput(Event $event) { @@ -187,7 +187,7 @@ private function isBeforeStepEventWithOutput(Event $event) * * @param Event $event * - * @return Boolean + * @return bool */ private function isAfterStepWithOutput(Event $event) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitDurationListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitDurationListener.php new file mode 100644 index 000000000..12604d5b7 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitDurationListener.php @@ -0,0 +1,103 @@ +captureBeforeScenarioEvent($event); + $this->captureBeforeFeatureTested($event); + $this->captureAfterScenarioEvent($event); + $this->captureAfterFeatureEvent($event); + } + + public function getDuration(ScenarioLikeInterface $scenario) + { + $key = $this->getHash($scenario); + return array_key_exists($key, $this->resultStore) ? $this->resultStore[$key] : ''; + } + + public function getFeatureDuration(FeatureNode $feature) + { + $key = $this->getHash($feature); + return array_key_exists($key, $this->featureResultStore) ? $this->featureResultStore[$key] : ''; + } + + private function captureBeforeFeatureTested(Event $event) + { + if (!$event instanceof BeforeFeatureTested) { + return; + } + + $this->featureTimerStore[$this->getHash($event->getFeature())] = $this->startTimer(); + } + + private function captureBeforeScenarioEvent(Event $event) + { + if (!$event instanceof BeforeScenarioTested) { + return; + } + + $this->scenarioTimerStore[$this->getHash($event->getScenario())] = $this->startTimer(); + } + + private function captureAfterScenarioEvent(Event $event) + { + if (!$event instanceof AfterScenarioTested) { + return; + } + + $key = $this->getHash($event->getScenario()); + $timer = $this->scenarioTimerStore[$key]; + if ($timer instanceof Timer) { + $timer->stop(); + $this->resultStore[$key] = round($timer->getTime()); + } + } + + private function captureAfterFeatureEvent(Event $event) + { + if (!$event instanceof AfterFeatureTested) { + return; + } + + $key = $this->getHash($event->getFeature()); + $timer = $this->featureTimerStore[$key]; + if ($timer instanceof Timer) { + $timer->stop(); + $this->featureResultStore[$key] = round($timer->getTime()); + } + } + + private function getHash(KeywordNodeInterface $node) + { + return spl_object_hash($node); + } + + /** @return Timer */ + private function startTimer() + { + $timer = new Timer(); + $timer->start(); + + return $timer; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitFeatureElementListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitFeatureElementListener.php index 465efb6ab..031accdaa 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitFeatureElementListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitFeatureElementListener.php @@ -22,10 +22,10 @@ use Behat\Behat\Output\Node\Printer\SetupPrinter; use Behat\Behat\Output\Node\Printer\StepPrinter; use Behat\Gherkin\Node\FeatureNode; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\Event\AfterSetup; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens to feature, scenario and step events and calls appropriate printers. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitOutlineStoreListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitOutlineStoreListener.php index 4458ebb73..530a6ca18 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitOutlineStoreListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/JUnit/JUnitOutlineStoreListener.php @@ -14,11 +14,11 @@ use Behat\Behat\Output\Node\Printer\SuitePrinter; use Behat\Gherkin\Node\ExampleNode; use Behat\Gherkin\Node\OutlineNode; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\Event\AfterSuiteTested; use Behat\Testwork\EventDispatcher\Event\BeforeSuiteTested; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens for Outline events store the current one diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/HookStatsListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/HookStatsListener.php index a8d6007b9..a4c9b779b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/HookStatsListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/HookStatsListener.php @@ -13,6 +13,7 @@ use Behat\Behat\Output\Statistics\HookStat; use Behat\Behat\Output\Statistics\Statistics; use Behat\Testwork\Call\CallResult; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\Event\AfterSetup; use Behat\Testwork\EventDispatcher\Event\AfterTested; use Behat\Testwork\Exception\ExceptionPresenter; @@ -20,7 +21,6 @@ use Behat\Testwork\Hook\Tester\Setup\HookedTeardown; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens and records hook stats. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/ScenarioStatsListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/ScenarioStatsListener.php index f83a38f78..ce94b2b2f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/ScenarioStatsListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/ScenarioStatsListener.php @@ -15,9 +15,9 @@ use Behat\Behat\EventDispatcher\Event\BeforeFeatureTested; use Behat\Behat\Output\Statistics\ScenarioStat; use Behat\Behat\Output\Statistics\Statistics; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Listens and records scenario events to the statistics. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StatisticsListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StatisticsListener.php index 1845e1b64..763eb76ad 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StatisticsListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StatisticsListener.php @@ -12,10 +12,10 @@ use Behat\Behat\Output\Node\Printer\StatisticsPrinter; use Behat\Behat\Output\Statistics\Statistics; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\Event\ExerciseCompleted; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Collects general suite stats such as time and memory during its execution and prints it afterwards. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StepStatsListener.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StepStatsListener.php index 2f84d052b..2e4a2f7ba 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StepStatsListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/EventListener/Statistics/StepStatsListener.php @@ -17,16 +17,15 @@ use Behat\Behat\EventDispatcher\Event\ScenarioTested; use Behat\Behat\Output\Statistics\StepStatV2; use Behat\Behat\Output\Statistics\Statistics; -use Behat\Behat\Output\Statistics\StepStat; use Behat\Behat\Tester\Exception\PendingException; use Behat\Behat\Tester\Result\ExecutedStepResult; use Behat\Behat\Tester\Result\StepResult; +use Behat\Testwork\Event\Event; use Behat\Testwork\Exception\ExceptionPresenter; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; use Behat\Testwork\Tester\Result\ExceptionResult; use Exception; -use Symfony\Component\EventDispatcher\Event; /** * Listens and records step events to statistics. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/CounterPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/CounterPrinter.php index ff567dba3..92954c342 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/CounterPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/CounterPrinter.php @@ -10,9 +10,9 @@ namespace Behat\Behat\Output\Node\Printer; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Behat\Behat\Output\Node\Printer\Helper\ResultToStringConverter; use Behat\Testwork\Output\Printer\OutputPrinter; -use Symfony\Component\Translation\TranslatorInterface; /** * Behat counter printer. @@ -64,12 +64,12 @@ public function printCounters(OutputPrinter $printer, $intro, array $stats) $style = $this->resultConverter->convertResultCodeToString($resultCode); $transId = $style . '_count'; - $message = $this->translator->transChoice($transId, $count, array('%1%' => $count), 'output'); + $message = $this->translator->trans($transId, array('%count%' => $count), 'output'); $detailedStats[] = sprintf('{+%s}%s{-%s}', $style, $message, $style); } - $message = $this->translator->transChoice($intro, $totalCount, array('%1%' => $totalCount), 'output'); + $message = $this->translator->trans($intro, array('%count%' => $totalCount), 'output'); $printer->write($message); if (count($detailedStats)) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Helper/WidthCalculator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Helper/WidthCalculator.php index bef188128..c7a1fa96d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Helper/WidthCalculator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Helper/WidthCalculator.php @@ -79,7 +79,7 @@ public function calculateScenarioHeaderWidth(Scenario $scenario, $indentation) $header = sprintf('%s%s', $indentText, $scenario->getTitle()); } else { $title = $scenario->getTitle(); - $lines = explode("\n", $title); + $lines = explode("\n", $title ?? ''); $header = sprintf('%s%s: %s', $indentText, $scenario->getKeyword(), array_shift($lines)); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitFeaturePrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitFeaturePrinter.php index 6b6e87184..651c4a650 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitFeaturePrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitFeaturePrinter.php @@ -10,6 +10,7 @@ namespace Behat\Behat\Output\Node\Printer\JUnit; +use Behat\Behat\Output\Node\EventListener\JUnit\JUnitDurationListener; use Behat\Behat\Output\Node\Printer\FeaturePrinter; use Behat\Behat\Output\Statistics\PhaseStatistics; use Behat\Behat\Tester\Result\StepResult; @@ -30,9 +31,15 @@ final class JUnitFeaturePrinter implements FeaturePrinter */ private $statistics; - public function __construct(PhaseStatistics $statistics) + /** + * @var JUnitDurationListener|null + */ + private $durationListener; + + public function __construct(PhaseStatistics $statistics, JUnitDurationListener $durationListener = null) { $this->statistics = $statistics; + $this->durationListener = $durationListener; } /** @@ -57,6 +64,7 @@ public function printHeader(Formatter $formatter, FeatureNode $feature) 'skipped' => $stats[TestResult::SKIPPED], 'failures' => $stats[TestResult::FAILED], 'errors' => $stats[TestResult::PENDING] + $stats[StepResult::UNDEFINED], + 'time' => $this->durationListener ? $this->durationListener->getFeatureDuration($feature) : '', )); $this->statistics->reset(); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitScenarioPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitScenarioPrinter.php index cf61eaab9..0ca46e987 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitScenarioPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/JUnit/JUnitScenarioPrinter.php @@ -11,6 +11,7 @@ namespace Behat\Behat\Output\Node\Printer\JUnit; use Behat\Behat\Output\Node\EventListener\JUnit\JUnitOutlineStoreListener; +use Behat\Behat\Output\Node\EventListener\JUnit\JUnitDurationListener; use Behat\Behat\Output\Node\Printer\Helper\ResultToStringConverter; use Behat\Gherkin\Node\ExampleNode; use Behat\Gherkin\Node\FeatureNode; @@ -47,10 +48,16 @@ final class JUnitScenarioPrinter */ private $outlineStepCount; - public function __construct(ResultToStringConverter $resultConverter, JUnitOutlineStoreListener $outlineListener) + /** + * @var JUnitDurationListener|null + */ + private $durationListener; + + public function __construct(ResultToStringConverter $resultConverter, JUnitOutlineStoreListener $outlineListener, JUnitDurationListener $durationListener = null) { $this->resultConverter = $resultConverter; $this->outlineStoreListener = $outlineListener; + $this->durationListener = $durationListener; } /** @@ -71,7 +78,9 @@ public function printOpenTag(Formatter $formatter, FeatureNode $feature, Scenari $outputPrinter->addTestcase(array( 'name' => $name, - 'status' => $this->resultConverter->convertResultToString($result) + 'classname' => $feature->getTitle(), + 'status' => $this->resultConverter->convertResultToString($result), + 'time' => $this->durationListener ? $this->durationListener->getDuration($scenario) : '' )); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/ListPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/ListPrinter.php index 31e8388ee..8f7193649 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/ListPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/ListPrinter.php @@ -10,6 +10,7 @@ namespace Behat\Behat\Output\Node\Printer; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Behat\Behat\Output\Node\Printer\Helper\ResultToStringConverter; use Behat\Behat\Output\Statistics\HookStat; use Behat\Behat\Output\Statistics\ScenarioStat; @@ -18,7 +19,6 @@ use Behat\Testwork\Exception\ExceptionPresenter; use Behat\Testwork\Output\Printer\OutputPrinter; use Behat\Testwork\Tester\Result\TestResult; -use Symfony\Component\Translation\TranslatorInterface; /** * Behat list printer. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyScenarioPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyScenarioPrinter.php index 378a99118..28f3adccc 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyScenarioPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyScenarioPrinter.php @@ -110,7 +110,7 @@ private function printKeyword(OutputPrinter $printer, $keyword) */ private function printTitle(OutputPrinter $printer, $longTitle) { - $description = explode("\n", $longTitle); + $description = explode("\n", $longTitle ?? ''); $title = array_shift($description); if ('' !== $title) { @@ -126,7 +126,7 @@ private function printTitle(OutputPrinter $printer, $longTitle) */ private function printDescription(OutputPrinter $printer, $longTitle) { - $lines = explode("\n", $longTitle); + $lines = explode("\n", $longTitle ?? ''); array_shift($lines); foreach ($lines as $line) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySetupPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySetupPrinter.php index 46bcba3a3..5475f51fe 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySetupPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySetupPrinter.php @@ -56,8 +56,8 @@ final class PrettySetupPrinter implements SetupPrinter * @param ResultToStringConverter $resultConverter * @param ExceptionPresenter $exceptionPresenter * @param integer $indentation - * @param Boolean $newlineBefore - * @param Boolean $newlineAfter + * @param bool $newlineBefore + * @param bool $newlineAfter */ public function __construct( ResultToStringConverter $resultConverter, diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySkippedStepPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySkippedStepPrinter.php index 718afd3b9..b8c8d464a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySkippedStepPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettySkippedStepPrinter.php @@ -129,7 +129,7 @@ private function printArguments(Formatter $formatter, array $arguments) * Returns argument string for provided argument. * * @param ArgumentInterface $argument - * @param Boolean $collapse + * @param bool $collapse * * @return string */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyStepPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyStepPrinter.php index 8d47da1ec..ee2e5d09e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyStepPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Node/Printer/Pretty/PrettyStepPrinter.php @@ -180,7 +180,7 @@ private function printException(OutputPrinter $printer, StepResult $result) * Returns argument string for provided argument. * * @param ArgumentInterface $argument - * @param Boolean $collapse + * @param bool $collapse * * @return string */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Printer/Formatter/ConsoleFormatter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Printer/Formatter/ConsoleFormatter.php index 9b403a68e..57d3ef38b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Printer/Formatter/ConsoleFormatter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/Printer/Formatter/ConsoleFormatter.php @@ -19,7 +19,7 @@ */ final class ConsoleFormatter extends BaseOutputFormatter { - const CUSTOM_PATTERN = '/{\+([a-z-_]+)}(.*?){\-\\1}/si'; + public const CUSTOM_PATTERN = '/{\+([a-z-_]+)}(.*?){\-\\1}/si'; /** * Formats a message according to the given styles. @@ -28,7 +28,7 @@ final class ConsoleFormatter extends BaseOutputFormatter * * @return string The styled message */ - public function format($message) + public function format($message): string { return preg_replace_callback(self::CUSTOM_PATTERN, array($this, 'replaceStyle'), $message); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/JUnitFormatterFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/JUnitFormatterFactory.php index a9d289516..c28d2cda5 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/JUnitFormatterFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/JUnitFormatterFactory.php @@ -27,8 +27,8 @@ final class JUnitFormatterFactory implements FormatterFactory /* * Available services */ - const ROOT_LISTENER_ID = 'output.node.listener.junit'; - const RESULT_TO_STRING_CONVERTER_ID = 'output.node.printer.result_to_string'; + public const ROOT_LISTENER_ID = 'output.node.listener.junit'; + public const RESULT_TO_STRING_CONVERTER_ID = 'output.node.printer.result_to_string'; /** * {@inheritdoc} @@ -73,12 +73,14 @@ private function loadCorePrinters(ContainerBuilder $container) $definition = new Definition('Behat\Behat\Output\Node\Printer\JUnit\JUnitFeaturePrinter', array( new Reference('output.junit.statistics'), + new Reference('output.node.listener.junit.duration') )); $container->setDefinition('output.node.printer.junit.feature', $definition); $definition = new Definition('Behat\Behat\Output\Node\Printer\JUnit\JUnitScenarioPrinter', array( new Reference(self::RESULT_TO_STRING_CONVERTER_ID), new Reference('output.node.listener.junit.outline'), + new Reference('output.node.listener.junit.duration') )); $container->setDefinition('output.node.printer.junit.scenario', $definition); @@ -109,9 +111,15 @@ private function loadRootNodeListener(ContainerBuilder $container) ); $container->setDefinition('output.node.listener.junit.outline', $definition); + $definition = new Definition( + 'Behat\Behat\Output\Node\EventListener\JUnit\JUnitDurationListener' + ); + + $container->setDefinition('output.node.listener.junit.duration', $definition); $definition = new Definition('Behat\Testwork\Output\Node\EventListener\ChainEventListener', array( array( + new Reference('output.node.listener.junit.duration'), new Reference('output.node.listener.junit.outline'), new Definition('Behat\Behat\Output\Node\EventListener\JUnit\JUnitFeatureElementListener', array( new Reference('output.node.printer.junit.feature'), diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/PrettyFormatterFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/PrettyFormatterFactory.php index 261b509a8..b2d266025 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/PrettyFormatterFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/PrettyFormatterFactory.php @@ -38,13 +38,13 @@ class PrettyFormatterFactory implements FormatterFactory /* * Available services */ - const ROOT_LISTENER_ID = 'output.node.listener.pretty'; - const RESULT_TO_STRING_CONVERTER_ID = 'output.node.printer.result_to_string'; + public const ROOT_LISTENER_ID = 'output.node.listener.pretty'; + public const RESULT_TO_STRING_CONVERTER_ID = 'output.node.printer.result_to_string'; /* * Available extension points */ - const ROOT_LISTENER_WRAPPER_TAG = 'output.node.listener.pretty.wrapper'; + public const ROOT_LISTENER_WRAPPER_TAG = 'output.node.listener.pretty.wrapper'; /** * Initializes extension. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/ProgressFormatterFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/ProgressFormatterFactory.php index e19acdc05..3e02154da 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/ProgressFormatterFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Output/ServiceContainer/Formatter/ProgressFormatterFactory.php @@ -34,13 +34,13 @@ class ProgressFormatterFactory implements FormatterFactory /* * Available services */ - const ROOT_LISTENER_ID = 'output.node.listener.progress'; - const RESULT_TO_STRING_CONVERTER_ID = 'output.node.printer.result_to_string'; + public const ROOT_LISTENER_ID = 'output.node.listener.progress'; + public const RESULT_TO_STRING_CONVERTER_ID = 'output.node.printer.result_to_string'; /* * Available extension points */ - const ROOT_LISTENER_WRAPPER_TAG = 'output.node.listener.progress.wrapper'; + public const ROOT_LISTENER_WRAPPER_TAG = 'output.node.listener.progress.wrapper'; /** * Initializes extension. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/AggregateSnippet.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/AggregateSnippet.php index cbadc0566..c63e8f1d7 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/AggregateSnippet.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/AggregateSnippet.php @@ -112,9 +112,8 @@ public function getUsedClasses() } return array_unique( - call_user_func_array( - 'array_merge', - array_map( + array_merge( + ...array_map( function (Snippet $snippet) { if (!$snippet instanceof ContextSnippet) { return array(); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Appender/SnippetAppender.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Appender/SnippetAppender.php index 041b8e6e5..863d88fb3 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Appender/SnippetAppender.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Appender/SnippetAppender.php @@ -27,7 +27,7 @@ interface SnippetAppender * * @param AggregateSnippet $snippet * - * @return Boolean + * @return bool */ public function supportsSnippet(AggregateSnippet $snippet); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Generator/SnippetGenerator.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Generator/SnippetGenerator.php index ca7d73937..09bbd1164 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Generator/SnippetGenerator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Generator/SnippetGenerator.php @@ -30,7 +30,7 @@ interface SnippetGenerator * @param Environment $environment * @param StepNode $step * - * @return Boolean + * @return bool */ public function supportsEnvironmentAndStep(Environment $environment, StepNode $step); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Printer/ConsoleSnippetPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Printer/ConsoleSnippetPrinter.php index 7a5b36e74..4112b695d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Printer/ConsoleSnippetPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/Printer/ConsoleSnippetPrinter.php @@ -10,11 +10,11 @@ namespace Behat\Behat\Snippet\Printer; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Behat\Behat\Snippet\AggregateSnippet; use Behat\Gherkin\Node\StepNode; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Translation\TranslatorInterface; /** * Behat console-based snippet printer. @@ -57,7 +57,7 @@ public function __construct(OutputInterface $output, TranslatorInterface $transl */ public function printSnippets($targetName, array $snippets) { - $message = $this->translator->trans('snippet_proposal_title', array('%1%' => $targetName), 'output'); + $message = $this->translator->trans('snippet_proposal_title', array('%count%' => $targetName), 'output'); $this->output->writeln('--- ' . $message . PHP_EOL); @@ -74,7 +74,7 @@ public function printSnippets($targetName, array $snippets) */ public function printUndefinedSteps($suiteName, array $steps) { - $message = $this->translator->trans('snippet_missing_title', array('%1%' => $suiteName), 'output'); + $message = $this->translator->trans('snippet_missing_title', array('%count%' => $suiteName), 'output'); $this->output->writeln('--- ' . $message . PHP_EOL); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/ServiceContainer/SnippetExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/ServiceContainer/SnippetExtension.php index 992c3716d..6c68821af 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/ServiceContainer/SnippetExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/ServiceContainer/SnippetExtension.php @@ -31,14 +31,14 @@ class SnippetExtension implements Extension /* * Available services */ - const REGISTRY_ID = 'snippet.registry'; - const WRITER_ID = 'snippet.writer'; + public const REGISTRY_ID = 'snippet.registry'; + public const WRITER_ID = 'snippet.writer'; /* * Available extension points */ - const GENERATOR_TAG = 'snippet.generator'; - const APPENDER_TAG = 'snippet.appender'; + public const GENERATOR_TAG = 'snippet.generator'; + public const APPENDER_TAG = 'snippet.appender'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/SnippetRegistry.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/SnippetRegistry.php index 0724c3e40..7b9257263 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/SnippetRegistry.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Snippet/SnippetRegistry.php @@ -34,7 +34,7 @@ final class SnippetRegistry implements SnippetRepository */ private $snippets = array(); /** - * @var Boolean + * @var bool */ private $snippetsGenerated = false; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/BackgroundTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/BackgroundTester.php index 8d70b57d8..10086c76c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/BackgroundTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/BackgroundTester.php @@ -28,7 +28,7 @@ interface BackgroundTester * * @param Environment $env * @param FeatureNode $feature - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -39,7 +39,7 @@ public function setUp(Environment $env, FeatureNode $feature, $skip); * * @param Environment $env * @param FeatureNode $feature - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -50,7 +50,7 @@ public function test(Environment $env, FeatureNode $feature, $skip); * * @param Environment $env * @param FeatureNode $feature - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/OutlineTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/OutlineTester.php index 863aba38c..43b45e62f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/OutlineTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/OutlineTester.php @@ -30,7 +30,7 @@ interface OutlineTester * @param Environment $env * @param FeatureNode $feature * @param OutlineNode $outline - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -42,7 +42,7 @@ public function setUp(Environment $env, FeatureNode $feature, OutlineNode $outli * @param Environment $env * @param FeatureNode $feature * @param OutlineNode $outline - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -54,7 +54,7 @@ public function test(Environment $env, FeatureNode $feature, OutlineNode $outlin * @param Environment $env * @param FeatureNode $feature * @param OutlineNode $outline - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Result/StepResult.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Result/StepResult.php index 0a898c996..87cec353d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Result/StepResult.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Result/StepResult.php @@ -19,5 +19,5 @@ */ interface StepResult extends TestResult { - const UNDEFINED = 30; + public const UNDEFINED = 30; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeScenarioTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeScenarioTester.php index 7ab11a0be..08b8ca951 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeScenarioTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeScenarioTester.php @@ -91,7 +91,7 @@ public function tearDown(Environment $env, FeatureNode $feature, Scenario $scena * * @param Environment $env * @param FeatureNode $feature - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeStepTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeStepTester.php index 7031eeca7..46959f6de 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeStepTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeStepTester.php @@ -107,7 +107,7 @@ private function searchDefinition(Environment $env, FeatureNode $feature, StepNo * @param FeatureNode $feature * @param StepNode $step * @param SearchResult $search - * @param Boolean $skip + * @param bool $skip * * @return StepResult */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ScenarioTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ScenarioTester.php index 2fd249489..601e4f67a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ScenarioTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ScenarioTester.php @@ -30,7 +30,7 @@ interface ScenarioTester * @param Environment $env * @param FeatureNode $feature * @param Scenario $scenario - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -42,7 +42,7 @@ public function setUp(Environment $env, FeatureNode $feature, Scenario $scenario * @param Environment $env * @param FeatureNode $feature * @param Scenario $scenario - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -54,7 +54,7 @@ public function test(Environment $env, FeatureNode $feature, Scenario $scenario, * @param Environment $env * @param FeatureNode $feature * @param Scenario $scenario - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ServiceContainer/TesterExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ServiceContainer/TesterExtension.php index 394ee9317..e8ba129d1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ServiceContainer/TesterExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/ServiceContainer/TesterExtension.php @@ -33,20 +33,20 @@ class TesterExtension extends BaseExtension /* * Available services */ - const SCENARIO_TESTER_ID = 'tester.scenario'; - const OUTLINE_TESTER_ID = 'tester.outline'; - const EXAMPLE_TESTER_ID = 'tester.example'; - const BACKGROUND_TESTER_ID = 'tester.background'; - const STEP_TESTER_ID = 'tester.step'; + public const SCENARIO_TESTER_ID = 'tester.scenario'; + public const OUTLINE_TESTER_ID = 'tester.outline'; + public const EXAMPLE_TESTER_ID = 'tester.example'; + public const BACKGROUND_TESTER_ID = 'tester.background'; + public const STEP_TESTER_ID = 'tester.step'; /** * Available extension points */ - const SCENARIO_TESTER_WRAPPER_TAG = 'tester.scenario.wrapper'; - const OUTLINE_TESTER_WRAPPER_TAG = 'tester.outline.wrapper'; - const EXAMPLE_TESTER_WRAPPER_TAG = 'tester.example.wrapper'; - const BACKGROUND_TESTER_WRAPPER_TAG = 'tester.background.wrapper'; - const STEP_TESTER_WRAPPER_TAG = 'tester.step.wrapper'; + public const SCENARIO_TESTER_WRAPPER_TAG = 'tester.scenario.wrapper'; + public const OUTLINE_TESTER_WRAPPER_TAG = 'tester.outline.wrapper'; + public const EXAMPLE_TESTER_WRAPPER_TAG = 'tester.example.wrapper'; + public const BACKGROUND_TESTER_WRAPPER_TAG = 'tester.background.wrapper'; + public const STEP_TESTER_WRAPPER_TAG = 'tester.step.wrapper'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepContainerTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepContainerTester.php index 543e133d2..d7364a9bc 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepContainerTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepContainerTester.php @@ -45,7 +45,7 @@ public function __construct(StepTester $stepTester) * @param Environment $env * @param FeatureNode $feature * @param StepContainerInterface $container - * @param Boolean $skip + * @param bool $skip * * @return TestResult[] */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepTester.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepTester.php index d5c995ceb..94d735778 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Tester/StepTester.php @@ -30,7 +30,7 @@ interface StepTester * @param Environment $env * @param FeatureNode $feature * @param StepNode $step - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -42,7 +42,7 @@ public function setUp(Environment $env, FeatureNode $feature, StepNode $step, $s * @param Environment $env * @param FeatureNode $feature * @param StepNode $step - * @param Boolean $skip + * @param bool $skip * * @return StepResult */ @@ -54,7 +54,7 @@ public function test(Environment $env, FeatureNode $feature, StepNode $step, $sk * @param Environment $env * @param FeatureNode $feature * @param StepNode $step - * @param Boolean $skip + * @param bool $skip * @param StepResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Context/Annotation/TransformationAnnotationReader.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Context/Annotation/TransformationAnnotationReader.php index a32ac5cca..680d19685 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Context/Annotation/TransformationAnnotationReader.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Context/Annotation/TransformationAnnotationReader.php @@ -64,18 +64,13 @@ public function readCallee($contextClass, ReflectionMethod $method, $docLine, $d */ private function simpleTransformations() { - $transformations = array(); - $transformations[] = 'Behat\Behat\Transformation\Transformation\RowBasedTableTransformation'; - $transformations[] = 'Behat\Behat\Transformation\Transformation\ColumnBasedTableTransformation'; - $transformations[] = 'Behat\Behat\Transformation\Transformation\TableRowTransformation'; - - if (PHP_VERSION_ID >= 70000) { - $transformations[] = 'Behat\Behat\Transformation\Transformation\TokenNameAndReturnTypeTransformation'; - $transformations[] = 'Behat\Behat\Transformation\Transformation\ReturnTypeTransformation'; - } - - $transformations[] = 'Behat\Behat\Transformation\Transformation\TokenNameTransformation'; - - return $transformations; + return array( + 'Behat\Behat\Transformation\Transformation\RowBasedTableTransformation', + 'Behat\Behat\Transformation\Transformation\ColumnBasedTableTransformation', + 'Behat\Behat\Transformation\Transformation\TableRowTransformation', + 'Behat\Behat\Transformation\Transformation\TokenNameAndReturnTypeTransformation', + 'Behat\Behat\Transformation\Transformation\ReturnTypeTransformation', + 'Behat\Behat\Transformation\Transformation\TokenNameTransformation' + ); } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/ServiceContainer/TransformationExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/ServiceContainer/TransformationExtension.php index f322ee926..9c767b90e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/ServiceContainer/TransformationExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/ServiceContainer/TransformationExtension.php @@ -33,12 +33,14 @@ class TransformationExtension implements Extension /* * Available services */ - const REPOSITORY_ID = 'transformation.repository'; + public const REPOSITORY_ID = 'transformation.repository'; /* * Available extension points */ - const ARGUMENT_TRANSFORMER_TAG = 'transformation.argument_transformer'; + public const ARGUMENT_TRANSFORMER_TAG = 'transformation.argument_transformer'; + + protected const DEFINITION_ARGUMENT_TRANSFORMER_ID = CallExtension::CALL_FILTER_TAG . '.definition_argument_transformer'; /** * @var ServiceProcessor @@ -105,7 +107,7 @@ protected function loadDefinitionArgumentsTransformer(ContainerBuilder $containe { $definition = new Definition('Behat\Behat\Transformation\Call\Filter\DefinitionArgumentsTransformer'); $definition->addTag(CallExtension::CALL_FILTER_TAG, array('priority' => 200)); - $container->setDefinition($this->getDefinitionArgumentTransformerId(), $definition); + $container->setDefinition(self::DEFINITION_ARGUMENT_TRANSFORMER_ID, $definition); } /** @@ -158,7 +160,7 @@ protected function loadRepository(ContainerBuilder $container) protected function processArgumentsTransformers(ContainerBuilder $container) { $references = $this->processor->findAndSortTaggedServices($container, self::ARGUMENT_TRANSFORMER_TAG); - $definition = $container->getDefinition($this->getDefinitionArgumentTransformerId()); + $definition = $container->getDefinition(self::DEFINITION_ARGUMENT_TRANSFORMER_ID); foreach ($references as $reference) { $definition->addMethodCall('registerArgumentTransformer', array($reference)); @@ -169,9 +171,13 @@ protected function processArgumentsTransformers(ContainerBuilder $container) * Returns definition argument transformer service id. * * @return string + * + * @deprecated Use DEFINITION_ARGUMENT_TRANSFORMER_ID constant instead + * + * @todo Remove method in next major version */ protected function getDefinitionArgumentTransformerId() { - return CallExtension::CALL_FILTER_TAG . '.definition_argument_transformer'; + return self::DEFINITION_ARGUMENT_TRANSFORMER_ID; } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/SimpleArgumentTransformation.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/SimpleArgumentTransformation.php index 77cdeefb2..16530c3f7 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/SimpleArgumentTransformation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/SimpleArgumentTransformation.php @@ -45,7 +45,7 @@ public function getPriority(); * @param integer|string $argumentIndex * @param mixed $argumentValue * - * @return Boolean + * @return bool */ public function supportsDefinitionAndArgument(DefinitionCall $definitionCall, $argumentIndex, $argumentValue); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ColumnBasedTableTransformation.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ColumnBasedTableTransformation.php index 1c8cc0ec3..c26df56d2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ColumnBasedTableTransformation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ColumnBasedTableTransformation.php @@ -25,7 +25,7 @@ */ final class ColumnBasedTableTransformation extends RuntimeCallee implements SimpleArgumentTransformation { - const PATTERN_REGEX = '/^table\:(?:\*|[[:print:]]+)$/'; + public const PATTERN_REGEX = '/^table\:(?:\*|[[:print:]]+)$/'; /** * @var string diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ReturnTypeTransformation.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ReturnTypeTransformation.php index ecdc613a1..dd2e9c08c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ReturnTypeTransformation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/ReturnTypeTransformation.php @@ -16,9 +16,12 @@ use Behat\Testwork\Call\CallCenter; use Behat\Testwork\Call\RuntimeCallee; use Closure; +use ReflectionClass; +use ReflectionException; use ReflectionFunctionAbstract; use ReflectionMethod; use ReflectionParameter; +use ReflectionNamedType; /** * By-type object transformation. @@ -126,10 +129,15 @@ static private function getReturnClass(ReflectionFunctionAbstract $reflection) { $type = $reflection->getReturnType(); - if (null === $type || $type->isBuiltin()) { + // Skip ReflectionUnionType as they can't be relied on for a transform + if (null === $type || !($type instanceof \ReflectionNamedType) || $type->isBuiltin()) { return null; } + if ($type instanceof ReflectionNamedType) { + return $type->getName(); + } + return (string) $type; } @@ -147,9 +155,14 @@ private function getParameterClassNameByIndex(DefinitionCall $definitionCall, $a array_filter($this->getCallParameters($definitionCall), $this->hasIndex($argumentIndex) ), - $this->isClass() + $this->getClassReflection() ); - return count($parameters) ? current($parameters)->getClass()->getName() : null; + + if (count($parameters) == 0) { + return null; + } + + return ($this->getClassReflection())(current($parameters))->getName(); } /** @@ -209,10 +222,22 @@ private function hasPosition($index) * * @return Closure */ - private function isClass() + private function getClassReflection() : closure { - return function (ReflectionParameter $parameter) { - return $parameter->getClass(); + return function (ReflectionParameter $parameter) : ?ReflectionClass + { + $t = $parameter->getType(); + + if ($t instanceof ReflectionNamedType) { + try { + return new ReflectionClass($t->getName()); + } + catch (ReflectionException $t) { + return null; + } + } + + return null; }; } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/RowBasedTableTransformation.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/RowBasedTableTransformation.php index 61389834d..a5b1c5123 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/RowBasedTableTransformation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/RowBasedTableTransformation.php @@ -26,7 +26,7 @@ */ final class RowBasedTableTransformation extends RuntimeCallee implements SimpleArgumentTransformation { - const PATTERN_REGEX = '/^rowtable\:[[:print:]]+$/'; + public const PATTERN_REGEX = '/^rowtable\:[[:print:]]+$/'; /** * @var string diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TableRowTransformation.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TableRowTransformation.php index 571f1f0cd..1a71c0b7b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TableRowTransformation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TableRowTransformation.php @@ -25,7 +25,7 @@ */ final class TableRowTransformation extends RuntimeCallee implements SimpleArgumentTransformation { - const PATTERN_REGEX = '/^row\:[[:print:]]+$/'; + public const PATTERN_REGEX = '/^row\:[[:print:]]+$/'; /** * @var string diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TokenNameTransformation.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TokenNameTransformation.php index cd4f4ab0b..a64d86e64 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TokenNameTransformation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformation/TokenNameTransformation.php @@ -24,7 +24,7 @@ */ final class TokenNameTransformation extends RuntimeCallee implements SimpleArgumentTransformation { - const PATTERN_REGEX = '/^\:\w+$/'; + public const PATTERN_REGEX = '/^\:\w+$/'; /** * @var string diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/ArgumentTransformer.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/ArgumentTransformer.php index 4773960e0..4457d838b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/ArgumentTransformer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/ArgumentTransformer.php @@ -26,7 +26,7 @@ interface ArgumentTransformer * @param integer|string $argumentIndex * @param mixed $argumentValue * - * @return Boolean + * @return bool */ public function supportsDefinitionAndArgument(DefinitionCall $definitionCall, $argumentIndex, $argumentValue); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/RepositoryArgumentTransformer.php b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/RepositoryArgumentTransformer.php index 795f42fae..4bdca2378 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/RepositoryArgumentTransformer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Behat/Transformation/Transformer/RepositoryArgumentTransformer.php @@ -12,6 +12,7 @@ use Behat\Behat\Definition\Call\DefinitionCall; use Behat\Behat\Definition\Pattern\PatternTransformer; +use Behat\Behat\Definition\Translator\TranslatorInterface; use Behat\Behat\Transformation\SimpleArgumentTransformation; use Behat\Behat\Transformation\Transformation\PatternTransformation; use Behat\Behat\Transformation\RegexGenerator; @@ -19,7 +20,6 @@ use Behat\Behat\Transformation\TransformationRepository; use Behat\Gherkin\Node\ArgumentInterface; use Behat\Testwork\Call\CallCenter; -use Symfony\Component\Translation\TranslatorInterface; /** * Argument transformer based on transformations repository. @@ -115,11 +115,7 @@ public function generateRegex($suiteName, $pattern, $language) private function applySimpleTransformations(array $transformations, DefinitionCall $definitionCall, $index, $value) { usort($transformations, function (SimpleArgumentTransformation $t1, SimpleArgumentTransformation $t2) { - if ($t1->getPriority() == $t2->getPriority()) { - return 0; - } - - return ($t1->getPriority() > $t2->getPriority()) ? -1 : 1; + return $t2->getPriority() <=> $t1->getPriority(); }); $newValue = $value; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterFeature.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterFeature.php new file mode 100644 index 000000000..05c818009 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterFeature.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Represents an Attribute for AfterFeature hook + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class AfterFeature implements Hook +{ + /** + * @var string + */ + public $filterString; + + public function __construct($filterString = null) + { + $this->filterString = $filterString; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterScenario.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterScenario.php new file mode 100644 index 000000000..feb480cc1 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterScenario.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Represents an Attribute for AfterScenario hook + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class AfterScenario implements Hook +{ + /** + * @var string + */ + public $filterString; + + public function __construct($filterString = null) + { + $this->filterString = $filterString; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterStep.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterStep.php new file mode 100644 index 000000000..8a27ff646 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/AfterStep.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Represents an Attribute for AfterStep hook + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class AfterStep implements Hook +{ + /** + * @var string + */ + public $filterString; + + public function __construct($filterString = null) + { + $this->filterString = $filterString; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeFeature.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeFeature.php new file mode 100644 index 000000000..401ae2e75 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeFeature.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Represents an Attribute for BeforeFeature hook + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class BeforeFeature implements Hook +{ + /** + * @var string + */ + public $filterString; + + public function __construct($filterString = null) + { + $this->filterString = $filterString; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeScenario.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeScenario.php new file mode 100644 index 000000000..d1d0bb7ef --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeScenario.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Represents an Attribute for BeforeScenario hook + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class BeforeScenario implements Hook +{ + /** + * @var string + */ + public $filterString; + + public function __construct($filterString = null) + { + $this->filterString = $filterString; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeStep.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeStep.php new file mode 100644 index 000000000..1c7097143 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/BeforeStep.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Represents an Attribute for BeforeStep hook + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class BeforeStep implements Hook +{ + /** + * @var string + */ + public $filterString; + + public function __construct($filterString = null) + { + $this->filterString = $filterString; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Hook/Hook.php b/tests/integration/vendor/behat/behat/src/Behat/Hook/Hook.php new file mode 100644 index 000000000..8c6dc7d8b --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Hook/Hook.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Hook; + +/** + * Marker interface for all Attributes regarding + * Hooks + * + * @internal Only meant as marker, not as an extension point + */ +interface Hook +{ +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Step/Definition.php b/tests/integration/vendor/behat/behat/src/Behat/Step/Definition.php new file mode 100644 index 000000000..29578aebd --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Step/Definition.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Step; + +/** + * Marker interface for all Attributes regarding + * Call definitions + * + * @internal Only meant as marker, not as an extension point + */ +interface Definition +{ +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Step/Given.php b/tests/integration/vendor/behat/behat/src/Behat/Step/Given.php new file mode 100644 index 000000000..6e07bf1a2 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Step/Given.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Step; + +/** + * Represents an Attribute for Given steps + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Given implements Definition +{ + /** + * @var string + */ + public $pattern; + + public function __construct($pattern = null) + { + $this->pattern = $pattern; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Step/Then.php b/tests/integration/vendor/behat/behat/src/Behat/Step/Then.php new file mode 100644 index 000000000..a3b9923c7 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Step/Then.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Step; + +/** + * Represents an Attribute for Then steps + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class Then implements Definition +{ + /** + * @var string + */ + public $pattern; + + public function __construct($pattern = null) + { + $this->pattern = $pattern; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Step/When.php b/tests/integration/vendor/behat/behat/src/Behat/Step/When.php new file mode 100644 index 000000000..d6727e4c2 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Step/When.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Step; + +/** + * Represents an Attribute for When steps + */ +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +final class When implements Definition +{ + /** + * @var string + */ + public $pattern; + + public function __construct($pattern = null) + { + $this->pattern = $pattern; + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/MixedArgumentOrganiser.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/MixedArgumentOrganiser.php index 88ace2243..49694e298 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/MixedArgumentOrganiser.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/MixedArgumentOrganiser.php @@ -10,10 +10,10 @@ namespace Behat\Testwork\Argument; -use Behat\Testwork\Argument\Exception\UnknownParameterValueException; use ReflectionFunctionAbstract; -use ReflectionMethod; +use ReflectionClass; use ReflectionParameter; +use ReflectionException; /** * Organises function arguments using its reflection. @@ -34,12 +34,7 @@ final class MixedArgumentOrganiser implements ArgumentOrganiser */ public function organiseArguments(ReflectionFunctionAbstract $function, array $arguments) { - $parameters = $function->getParameters(); - $arguments = $this->prepareArguments($parameters, $arguments); - - $this->validateArguments($function, $parameters, $arguments); - - return $arguments; + return $this->prepareArguments($function->getParameters(), $arguments); } /** @@ -58,7 +53,7 @@ private function prepareArguments(array $parameters, array $arguments) $arguments = $this->prepareNamedArguments($parameters, $named) + - $typehinted + + $this->prepareTypehintedArguments($parameters, $typehinted) + $this->prepareNumberedArguments($parameters, $numbered) + $this->prepareDefaultArguments($parameters); @@ -88,8 +83,8 @@ function (ReflectionParameter $parameter) { foreach ($arguments as $key => $val) { if ($this->isStringKeyAndExistsInParameters($key, $parameterNames)) { $namedArguments[$key] = $val; - } elseif ($num = $this->getParameterNumberWithTypehintingValue($parameters, $val)) { - $typehintedArguments[$num] = $val; + } elseif ($this->isParameterTypehintedInArgumentList($parameters, $val)) { + $typehintedArguments[] = $val; } else { $numberedArguments[] = $val; } @@ -104,7 +99,7 @@ function (ReflectionParameter $parameter) { * @param mixed $argumentKey * @param string[] $parameterNames * - * @return Boolean + * @return bool */ private function isStringKeyAndExistsInParameters($argumentKey, $parameterNames) { @@ -112,41 +107,40 @@ private function isStringKeyAndExistsInParameters($argumentKey, $parameterNames) } /** - * Tries to find a parameter number, which typehints provided value. + * Check if a given value is typehinted in the argument list. * - * @param ReflectionParameter[] $parameters - * @param mixed $value + * @param ReflectionParameter[] $parameters + * @param mixed $value * - * @return null|integer + * @return bool */ - private function getParameterNumberWithTypehintingValue(array $parameters, $value) + private function isParameterTypehintedInArgumentList(array $parameters, $value) { if (!is_object($value)) { - return null; + return false; } - foreach ($parameters as $num => $parameter) { + foreach ($parameters as $parameter) { if ($this->isValueMatchesTypehintedParameter($value, $parameter)) { - return $num; + return true; } } - return null; + return false; } /** * Checks if value matches typehint of provided parameter. - * - * @param object $value - * @param ReflectionParameter $parameter - * - * @return Boolean */ - private function isValueMatchesTypehintedParameter($value, ReflectionParameter $parameter) + private function isValueMatchesTypehintedParameter($value, ReflectionParameter $parameter) : bool { - $typehintRefl = $parameter->getClass(); + foreach($this->getReflectionClassesFromParameter($parameter) as $typehintRefl) { + if($typehintRefl->isInstance($value)) { + return true; + } + } - return $typehintRefl && $typehintRefl->isInstance($value); + return false; } /** @@ -173,6 +167,198 @@ private function prepareNamedArguments(array $parameters, array $namedArguments) return $arguments; } + /** + * Captures argument values for typehinted arguments based on the given candidates. + * + * This method attempts to match up the best fitting arguments to each constructor argument. + * + * This case specifically fixes the issue where a constructor asks for a parent and child class, + * as separate arguments, but both arguments could satisfy the first argument, + * so they would both be passed in (overwriting each other). + * + * This will ensure that the children (exact class matches) are mapped first, and then other dependencies + * are mapped sequentially (to arguments which they are an `instanceof`). + * + * As such, this requires two passes of the $parameters array to ensure it is mapped as accurately as possible. + * + * @param ReflectionParameter[] $parameters Reflection Parameters (constructor argument requirements) + * @param mixed[] $typehintedArguments Resolved arguments + * + * @return mixed[] Ordered list of arguments, index is the constructor argument position, value is what will be injected + */ + private function prepareTypehintedArguments(array $parameters, array $typehintedArguments) + { + $arguments = array(); + + $candidates = $typehintedArguments; + + $this->applyPredicateToTypehintedArguments( + $parameters, + $candidates, + $arguments, + array($this, 'classMatchingPredicateForTypehintedArguments') + ); + + // This iteration maps up everything else, providing the argument is an instanceof the parameter. + $this->applyPredicateToTypehintedArguments( + $parameters, + $candidates, + $arguments, + array($this, 'isInstancePredicateForTypehintedArguments') + ); + + return $arguments; + } + + /** + * Filtered out superfluous parameters for matching up typehinted arguments. + * + * @param ReflectionParameter[] $parameters Constructor Arguments + * @return ReflectionParameter[] Filtered $parameters + */ + private function filterApplicableTypehintedParameters(array $parameters) : array + { + return array_filter($parameters, + function($parameter, $num) { + return !$this->isArgumentDefined($num) + && $this->getReflectionClassesFromParameter($parameter); + }, + ARRAY_FILTER_USE_BOTH + ); + + } + + /** + * @return ReflectionClass[] + */ + private function getReflectionClassesFromParameter(\ReflectionParameter $parameter): array + { + $classes = []; + + if (!$parameter->hasType()) { + return $classes; + } + + $type = $parameter->getType(); + + if ($type instanceof \ReflectionNamedType) { + $types = [$type]; + } + elseif ($parameter->getType() instanceof \ReflectionUnionType) { + $types = $type->getTypes(); + } + else { + $types = []; + } + + foreach ($types as $type) { + + // ReflectionUnionType::getTypes is only documented as returning ReflectionType[] + if (!$type instanceof \ReflectionNamedType) { + continue; + } + + $typeString = $type->getName(); + + if ($typeString == 'self') { + $typeString = $parameter->getDeclaringClass(); + } elseif ($typeString == 'parent') { + $typeString = $parameter->getDeclaringClass()->getParentClass(); + } + + try { + $classes[] = new ReflectionClass($typeString); + } catch (ReflectionException $e) { + continue; + } + } + + return $classes; + } + + /** + * Applies a predicate for each candidate when matching up typehinted arguments. + * This passes through to another loop of the candidates in @matchParameterToCandidateUsingPredicate, + * because this method is "too complex" with two loops... + * + * @param ReflectionParameter[] $parameters Reflection Parameters (constructor argument requirements) + * @param mixed[] &$candidates Resolved arguments + * @param mixed[] &$arguments Argument mapping + * @param callable $predicate Callable predicate to apply to each candidate + * @return void + */ + private function applyPredicateToTypehintedArguments( + array $parameters, + array &$candidates, + array &$arguments, + $predicate + ) { + $filtered = $this->filterApplicableTypehintedParameters($parameters); + + foreach ($filtered as $num => $parameter) { + $this->matchParameterToCandidateUsingPredicate($parameter, $candidates, $arguments, $predicate); + } + } + + /** + * Applies a predicate for each candidate when matching up typehinted arguments. + * This helps to avoid repetition when looping them, as multiple passes are needed over the parameters / candidates. + * + * @param ReflectionParameter $parameter Reflection Parameter (constructor argument requirements) + * @param mixed[] &$candidates Resolved arguments + * @param mixed[] &$arguments Argument mapping + * @param callable $predicate Callable predicate to apply to each candidate + * @return bool Returns true if a candidate has been matched to the given parameter, otherwise false + */ + public function matchParameterToCandidateUsingPredicate( + ReflectionParameter $parameter, + array &$candidates, + array &$arguments, + $predicate + ) { + foreach ($candidates as $candidateIndex => $candidate) { + foreach($this->getReflectionClassesFromParameter($parameter) as $class) { + if ($predicate($class, $candidate)) { + $num = $parameter->getPosition(); + + $arguments[$num] = $candidate; + + $this->markArgumentDefined($num); + + unset($candidates[$candidateIndex]); + + return true; + } + } + } + + return false; + } + + /** + * Typehinted argument predicate to check if the argument and parameter classes match equally. + * + * @param ReflectionClass $reflectionClass Typehinted argument + * @param mixed $candidate Resolved argument + * @return bool + */ + private function classMatchingPredicateForTypehintedArguments(ReflectionClass $reflectionClass, $candidate) + { + return $reflectionClass->getName() === get_class($candidate); + } + + /** + * Typehinted argument predicate to check if the argument is an instance of the parameter. + * + * @param ReflectionClass $reflectionClass Typehinted argument + * @param mixed $candidate Resolved argument + * @return bool + */ + private function isInstancePredicateForTypehintedArguments(ReflectionClass $reflectionClass, $candidate) + { + return $reflectionClass->isInstance($candidate); + } + /** * Captures argument values for undefined arguments based on their respective numbers. * @@ -250,55 +436,6 @@ private function reorderArguments(array $parameters, array $arguments) return $orderedArguments; } - /** - * Validates that all arguments are in place, throws exception otherwise. - * - * @param ReflectionFunctionAbstract $function - * @param ReflectionParameter[] $parameters - * @param mixed[] $arguments - * - * @throws UnknownParameterValueException - */ - private function validateArguments( - ReflectionFunctionAbstract $function, - array $parameters, - array $arguments - ) { - foreach ($parameters as $num => $parameter) { - $name = $parameter->getName(); - - if (array_key_exists($num, $arguments) || array_key_exists($name, $arguments)) { - continue; - } - - throw new UnknownParameterValueException(sprintf( - 'Can not find a matching value for an argument `$%s` of the method `%s`.', - $name, - $this->getFunctionPath($function) - )); - } - } - - /** - * Returns function path for a provided reflection. - * - * @param ReflectionFunctionAbstract $function - * - * @return string - */ - private function getFunctionPath(ReflectionFunctionAbstract $function) - { - if ($function instanceof ReflectionMethod) { - return sprintf( - '%s::%s()', - $function->getDeclaringClass()->getName(), - $function->getName() - ); - } - - return sprintf('%s()', $function->getName()); - } - /** * Marks arguments at all positions as undefined. * @@ -324,7 +461,7 @@ private function markArgumentDefined($position) * * @param integer $position * - * @return Boolean + * @return bool */ private function isArgumentDefined($position) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/PregMatchArgumentOrganiser.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/PregMatchArgumentOrganiser.php index 2fb053585..8a03b884f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/PregMatchArgumentOrganiser.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/PregMatchArgumentOrganiser.php @@ -81,7 +81,7 @@ private function cleanupMatchDuplicates(array $match) * @param integer $keyIndex * @param mixed[] $keys * - * @return Boolean + * @return bool */ private function isKeyAStringAndNexOneIsAnInteger($keyIndex, array $keys) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/ServiceContainer/ArgumentExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/ServiceContainer/ArgumentExtension.php index 021966440..88172a979 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/ServiceContainer/ArgumentExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/ServiceContainer/ArgumentExtension.php @@ -18,7 +18,7 @@ use Symfony\Component\DependencyInjection\Reference; /** - * Enables argument organises for the Testwork. + * Enables argument organisers for Testwork. * * @author Konstantin Kudryashov */ @@ -27,9 +27,9 @@ final class ArgumentExtension implements Extension /* * Available services */ - const MIXED_ARGUMENT_ORGANISER_ID = 'argument.mixed_organiser'; - const PREG_MATCH_ARGUMENT_ORGANISER_ID = 'argument.preg_match_organiser'; - const CONSTRUCTOR_ARGUMENT_ORGANISER_ID = 'argument.constructor_organiser'; + public const MIXED_ARGUMENT_ORGANISER_ID = 'argument.mixed_organiser'; + public const PREG_MATCH_ARGUMENT_ORGANISER_ID = 'argument.preg_match_organiser'; + public const CONSTRUCTOR_ARGUMENT_ORGANISER_ID = 'argument.constructor_organiser'; /** * {@inheritdoc} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/Validator.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/Validator.php new file mode 100644 index 000000000..3d47e7e5a --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Argument/Validator.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Testwork\Argument; + +use Behat\Testwork\Argument\Exception\UnknownParameterValueException; +use ReflectionFunctionAbstract; +use ReflectionMethod; +use ReflectionParameter; + +/** + * Validates function arguments. + * + * @author Konstantin Kudryashov + */ +final class Validator +{ + /** + * Validates that all arguments are in place, throws exception otherwise. + * + * @param ReflectionFunctionAbstract $function + * @param mixed[] $arguments + * + * @throws UnknownParameterValueException + */ + public function validateArguments(ReflectionFunctionAbstract $function, array $arguments) + { + foreach ($function->getParameters() as $num => $parameter) { + $this->validateArgument($parameter, $num, $arguments); + } + } + + /** + * Validates given argument. + * + * @param ReflectionParameter $parameter + * @param integer $parameterIndex + * @param array $givenArguments + */ + private function validateArgument(ReflectionParameter $parameter, $parameterIndex, array $givenArguments) + { + if ($parameter->isDefaultValueAvailable()) { + return; + } + + if (array_key_exists($parameterIndex, $givenArguments)) { + return; + } + + if (array_key_exists($parameter->getName(), $givenArguments)) { + return; + } + + throw new UnknownParameterValueException(sprintf( + 'Can not find a matching value for an argument `$%s` of the method `%s`.', + $parameter->getName(), + $this->getFunctionPath($parameter->getDeclaringFunction()) + )); + } + + /** + * Returns function path for a provided reflection. + * + * @param ReflectionFunctionAbstract $function + * + * @return string + */ + private function getFunctionPath(ReflectionFunctionAbstract $function) + { + if ($function instanceof ReflectionMethod) { + return sprintf( + '%s::%s()', + $function->getDeclaringClass()->getName(), + $function->getName() + ); + } + + return sprintf('%s()', $function->getName()); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/Cli/AutoloaderController.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/Cli/AutoloaderController.php index 991eb412c..70ebe94af 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/Cli/AutoloaderController.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/Cli/AutoloaderController.php @@ -11,7 +11,7 @@ namespace Behat\Testwork\Autoloader\Cli; use Behat\Testwork\Cli\Controller; -use Symfony\Component\ClassLoader\ClassLoader; +use Composer\Autoload\ClassLoader; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/ServiceContainer/AutoloaderExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/ServiceContainer/AutoloaderExtension.php index 2c5f021cd..3908e0f15 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/ServiceContainer/AutoloaderExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Autoloader/ServiceContainer/AutoloaderExtension.php @@ -28,7 +28,7 @@ final class AutoloaderExtension implements Extension /* * Available services */ - const CLASS_LOADER_ID = 'class_loader'; + public const CLASS_LOADER_ID = 'class_loader'; /** * @var array @@ -108,7 +108,7 @@ public function process(ContainerBuilder $container) */ private function loadAutoloader(ContainerBuilder $container) { - $definition = new Definition('Symfony\Component\ClassLoader\ClassLoader'); + $definition = new Definition('Composer\Autoload\ClassLoader'); $container->setDefinition(self::CLASS_LOADER_ID, $definition); } @@ -146,6 +146,10 @@ private function setLoaderPrefixes(ContainerBuilder $container, array $prefixes) private function processLoaderPrefixes(ContainerBuilder $container) { $loaderDefinition = $container->getDefinition(self::CLASS_LOADER_ID); - $loaderDefinition->addMethodCall('addPrefixes', array($container->getParameter('class_loader.prefixes'))); + $prefixes = $container->getParameter('class_loader.prefixes'); + + foreach ($prefixes as $prefix => $path) { + $loaderDefinition->addMethodCall('add', array($prefix, $path)); + } } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php index 448367b2d..2798acf68 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php @@ -94,8 +94,6 @@ public function makeCall(Call $call) { try { return $this->filterResult($this->handleCall($this->filterCall($call))); - } catch (Exception $exception) { - return new CallResult($call, null, $this->handleException($exception), null); } catch (Throwable $exception) { return new CallResult($call, null, $this->handleException($exception), null); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResult.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResult.php index ca3c52837..4ee808b82 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResult.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResult.php @@ -75,7 +75,10 @@ public function getReturn() /** * Check if call thrown exception. * - * @return Boolean + * @psalm-assert-if-true Exception $this->exception + * @psalm-assert-if-true Exception $this->getException() + * + * @return bool */ public function hasException() { @@ -95,7 +98,7 @@ public function getException() /** * Checks if call produced stdOut. * - * @return Boolean + * @return bool */ public function hasStdOut() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResults.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResults.php index f79ea0c9e..6bc3e8b11 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResults.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/CallResults.php @@ -12,7 +12,6 @@ use ArrayIterator; use Countable; -use Iterator; use IteratorAggregate; /** @@ -53,7 +52,7 @@ public static function merge(CallResults $first, CallResults $second) /** * Checks if any call in collection throws an exception. * - * @return Boolean + * @return bool */ public function hasExceptions() { @@ -69,7 +68,7 @@ public function hasExceptions() /** * Checks if any call in collection produces an output. * - * @return Boolean + * @return bool */ public function hasStdOuts() { @@ -87,7 +86,7 @@ public function hasStdOuts() * * @return integer */ - public function count() + public function count(): int { return count($this->results); } @@ -95,9 +94,9 @@ public function count() /** * Returns collection iterator. * - * @return Iterator + * @return ArrayIterator */ - public function getIterator() + public function getIterator(): ArrayIterator { return new ArrayIterator($this->results); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Callee.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Callee.php index c03e77649..17ed0f5cc 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Callee.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Callee.php @@ -36,14 +36,14 @@ public function getDescription(); /** * Returns true if callee is a method, false otherwise. * - * @return Boolean + * @return bool */ public function isAMethod(); /** * Returns true if callee is an instance (non-static) method, false otherwise. * - * @return Boolean + * @return bool */ public function isAnInstanceMethod(); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Exception/CallErrorException.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Exception/CallErrorException.php index 423abf38f..1622f0fe2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Exception/CallErrorException.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Exception/CallErrorException.php @@ -42,7 +42,7 @@ public function __construct($level, $message, $file, $line) parent::__construct( sprintf( '%s: %s in %s line %d', - isset($this->levels[$level]) ? $this->levels[$level] : $level, + $this->levels[$level] ?? $level, $message, $file, $line diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/CallFilter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/CallFilter.php index 5426c160a..1519823db 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/CallFilter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/CallFilter.php @@ -27,7 +27,7 @@ interface CallFilter * * @param Call $call * - * @return Boolean + * @return bool */ public function supportsCall(Call $call); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/ResultFilter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/ResultFilter.php index ac3ebe000..02267029a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/ResultFilter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Filter/ResultFilter.php @@ -27,7 +27,7 @@ interface ResultFilter * * @param CallResult $result * - * @return Boolean + * @return bool */ public function supportsResult(CallResult $result); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/CallHandler.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/CallHandler.php index e553d2a76..93966130d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/CallHandler.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/CallHandler.php @@ -28,7 +28,7 @@ interface CallHandler * * @param Call $call * - * @return Boolean + * @return bool */ public function supportsCall(Call $call); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/ClassNotFoundHandler.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/ClassNotFoundHandler.php index 8d0f488fd..6ddeee21e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/ClassNotFoundHandler.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/ClassNotFoundHandler.php @@ -22,7 +22,7 @@ */ abstract class ClassNotFoundHandler implements ExceptionHandler { - const PATTERN = "/^Class '([^']+)' not found$/"; + public const PATTERN = "/^Class (?:'|\")([^'\"]+)(?:'|\") not found$/"; /** * {@inheritdoc} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/MethodNotFoundHandler.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/MethodNotFoundHandler.php index 3ca680dae..f35890999 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/MethodNotFoundHandler.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/Exception/MethodNotFoundHandler.php @@ -22,7 +22,7 @@ */ abstract class MethodNotFoundHandler implements ExceptionHandler { - const PATTERN = '/^Call to undefined method ([^:]+)::([^\)]+)\(\)$/'; + public const PATTERN = '/^Call to undefined method ([^:]+)::([^\)]+)\(\)$/'; /** * {@inheritdoc} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php index 09267bbd4..9f51cd06c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php @@ -10,6 +10,7 @@ namespace Behat\Testwork\Call\Handler; +use Behat\Testwork\Argument\Validator; use Behat\Testwork\Call\Call; use Behat\Testwork\Call\CallResult; use Behat\Testwork\Call\Exception\CallErrorException; @@ -26,11 +27,14 @@ final class RuntimeCallHandler implements CallHandler * @var integer */ private $errorReportingLevel; - /** * @var bool */ private $obStarted = false; + /** + * @var Validator + */ + private $validator; /** * Initializes executor. @@ -40,6 +44,7 @@ final class RuntimeCallHandler implements CallHandler public function __construct($errorReportingLevel = E_ALL) { $this->errorReportingLevel = $errorReportingLevel; + $this->validator = new Validator(); } /** @@ -72,7 +77,7 @@ public function handleCall(Call $call) * @param string $file * @param integer $line * - * @return Boolean + * @return bool * * @throws CallErrorException */ @@ -94,13 +99,15 @@ public function handleError($level, $message, $file, $line) */ private function executeCall(Call $call) { + $reflection = $call->getCallee()->getReflection(); $callable = $call->getBoundCallable(); $arguments = $call->getArguments(); - $return = $exception = null; try { - $return = call_user_func_array($callable, $arguments); + $arguments = array_values($arguments); + $this->validator->validateArguments($reflection, $arguments); + $return = $callable(...$arguments); } catch (Exception $caught) { $exception = $caught; } @@ -148,7 +155,7 @@ private function stopErrorAndOutputBuffering() * * @param integer $level * - * @return Boolean + * @return bool */ private function errorLevelIsNotReportable($level) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/RuntimeCallee.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/RuntimeCallee.php index c764629d2..fa232f836 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/RuntimeCallee.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/RuntimeCallee.php @@ -112,7 +112,7 @@ public function getReflection() /** * Returns true if callee is a method, false otherwise. * - * @return Boolean + * @return bool */ public function isAMethod() { @@ -122,7 +122,7 @@ public function isAMethod() /** * Returns true if callee is an instance (non-static) method, false otherwise. * - * @return Boolean + * @return bool */ public function isAnInstanceMethod() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/ServiceContainer/CallExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/ServiceContainer/CallExtension.php index b9df7ea22..81bf25825 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/ServiceContainer/CallExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Call/ServiceContainer/CallExtension.php @@ -27,15 +27,15 @@ final class CallExtension implements Extension /* * Available services */ - const CALL_CENTER_ID = 'call.center'; + public const CALL_CENTER_ID = 'call.center'; /* * Available extension points */ - const CALL_FILTER_TAG = 'call.call_filter'; - const CALL_HANDLER_TAG = 'call.call_handler'; - const RESULT_FILTER_TAG = 'call.result_filter'; - const EXCEPTION_HANDLER_TAG = 'call.exception_handler'; + public const CALL_FILTER_TAG = 'call.call_filter'; + public const CALL_HANDLER_TAG = 'call.call_handler'; + public const RESULT_FILTER_TAG = 'call.result_filter'; + public const EXCEPTION_HANDLER_TAG = 'call.exception_handler'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/Application.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/Application.php index 08eca8711..006b3f1b7 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/Application.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/Application.php @@ -63,7 +63,7 @@ public function __construct($name, $version, ConfigurationLoader $configLoader, * * @return InputDefinition An InputDefinition instance */ - public function getDefaultInputDefinition() + public function getDefaultInputDefinition(): InputDefinition { return new InputDefinition(array( new InputOption('--profile', '-p', InputOption::VALUE_REQUIRED, 'Specify config profile to use.'), @@ -75,8 +75,8 @@ public function getDefaultInputDefinition() ), new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'), new InputOption('--config-reference', null, InputOption::VALUE_NONE, 'Display the configuration reference.'), - new InputOption('--debug', null, InputOption::VALUE_NONE, 'Provide debuggin information about current environment.'), - new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this behat version.'), + new InputOption('--debug', null, InputOption::VALUE_NONE, 'Provide debugging information about current environment.'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display version.'), new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'), new InputOption( '--colors', null, InputOption::VALUE_NONE, @@ -124,7 +124,7 @@ public function doRun(InputInterface $input, OutputInterface $output) return parent::doRun($input, $output); } - protected function getDefaultCommands() + protected function getDefaultCommands(): array { $commands = parent::getDefaultCommands(); @@ -210,7 +210,7 @@ private function getBasePath() * * @return string The command name */ - protected function getCommandName(InputInterface $input) + protected function getCommandName(InputInterface $input): string { if ($input->hasParameterOption(array('--config-reference'))) { return 'dump-reference'; diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DebugCommand.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DebugCommand.php index d888cb90b..d24ffd5d0 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DebugCommand.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DebugCommand.php @@ -76,5 +76,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln(sprintf(' extensions loaded: %s', count($debug['extensions_list']) ? implode(', ', $debug['extensions_list']) : 'none')); $output->writeln(''); + + return 0; } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DumpReferenceCommand.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DumpReferenceCommand.php index d6b57d442..e4242bbb5 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DumpReferenceCommand.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/DumpReferenceCommand.php @@ -47,15 +47,11 @@ public function __construct(ExtensionManager $extensionManager) */ protected function execute(InputInterface $input, OutputInterface $output) { - if (class_exists('Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper')) { - $dumper = new YamlReferenceDumper(); - } else { - // Support Symfony Config 2.3 - $dumper = new ReferenceDumper(); - } - + $dumper = new YamlReferenceDumper(); $configTree = new ConfigurationTree(); $output->writeln($dumper->dumpNode($configTree->getConfigTree($this->extensionManager->getExtensions()))); + + return 0; } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/ServiceContainer/CliExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/ServiceContainer/CliExtension.php index 4b9fba847..faca4ce61 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/ServiceContainer/CliExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Cli/ServiceContainer/CliExtension.php @@ -27,14 +27,14 @@ final class CliExtension implements Extension /* * Available services */ - const COMMAND_ID = 'cli.command'; - const INPUT_ID = 'cli.input'; - const OUTPUT_ID = 'cli.output'; + public const COMMAND_ID = 'cli.command'; + public const INPUT_ID = 'cli.input'; + public const OUTPUT_ID = 'cli.output'; /* * Available extension points */ - const CONTROLLER_TAG = 'cli.controller'; + public const CONTROLLER_TAG = 'cli.controller'; /** * @var ServiceProcessor @@ -81,6 +81,7 @@ public function configure(ArrayNodeDefinition $builder) public function load(ContainerBuilder $container, array $config) { $this->loadCommand($container); + $this->loadSyntheticServices($container); } /** @@ -99,9 +100,16 @@ public function process(ContainerBuilder $container) protected function loadCommand(ContainerBuilder $container) { $definition = new Definition('Behat\Testwork\Cli\Command', array('%cli.command.name%', array())); + $definition->setPublic(true); $container->setDefinition(self::COMMAND_ID, $definition); } + protected function loadSyntheticServices(ContainerBuilder $container) + { + $container->register(self::INPUT_ID)->setSynthetic(true)->setPublic(true); + $container->register(self::OUTPUT_ID)->setSynthetic(true)->setPublic(true); + } + /** * Processes all controllers in container. * diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Handler/EnvironmentHandler.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Handler/EnvironmentHandler.php index f2b6b9820..1b4358919 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Handler/EnvironmentHandler.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Handler/EnvironmentHandler.php @@ -28,7 +28,7 @@ interface EnvironmentHandler * * @param Suite $suite * - * @return Boolean + * @return bool */ public function supportsSuite(Suite $suite); @@ -47,7 +47,7 @@ public function buildEnvironment(Suite $suite); * @param Environment $environment * @param mixed $testSubject * - * @return Boolean + * @return bool */ public function supportsEnvironmentAndSubject(Environment $environment, $testSubject = null); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Reader/EnvironmentReader.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Reader/EnvironmentReader.php index dcf66fde8..39cd46724 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Reader/EnvironmentReader.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/Reader/EnvironmentReader.php @@ -28,7 +28,7 @@ interface EnvironmentReader * * @param Environment $environment * - * @return Boolean + * @return bool */ public function supportsEnvironment(Environment $environment); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/ServiceContainer/EnvironmentExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/ServiceContainer/EnvironmentExtension.php index e06f058c7..389b36143 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/ServiceContainer/EnvironmentExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Environment/ServiceContainer/EnvironmentExtension.php @@ -29,13 +29,13 @@ final class EnvironmentExtension implements Extension /* * Available services */ - const MANAGER_ID = 'environment.manager'; + public const MANAGER_ID = 'environment.manager'; /* * Available extension points */ - const HANDLER_TAG = 'environment.handler'; - const READER_TAG = 'environment.reader'; + public const HANDLER_TAG = 'environment.handler'; + public const READER_TAG = 'environment.reader'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Event/Event.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Event/Event.php new file mode 100644 index 000000000..61c65cac3 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Event/Event.php @@ -0,0 +1,9 @@ +eventDispatcher->dispatch(ExerciseCompleted::AFTER, new AfterExerciseAborted()); + $this->eventDispatcher->dispatch(new AfterExerciseAborted(), ExerciseCompleted::AFTER); exit(1); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/ExerciseCompleted.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/ExerciseCompleted.php index 026592bdc..4a88dfbff 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/ExerciseCompleted.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/ExerciseCompleted.php @@ -10,8 +10,9 @@ namespace Behat\Testwork\EventDispatcher\Event; +use Behat\Testwork\Event\Event; use Behat\Testwork\Specification\SpecificationIterator; -use Symfony\Component\EventDispatcher\Event; + /** * Represents an exercise event. @@ -20,10 +21,10 @@ */ abstract class ExerciseCompleted extends Event { - const BEFORE = 'tester.exercise_completed.before'; - const AFTER_SETUP = 'tester.exercise_completed.after_setup'; - const BEFORE_TEARDOWN = 'tester.exercise_completed.before_teardown'; - const AFTER = 'tester.exercise_completed.after'; + public const BEFORE = 'tester.exercise_completed.before'; + public const AFTER_SETUP = 'tester.exercise_completed.after_setup'; + public const BEFORE_TEARDOWN = 'tester.exercise_completed.before_teardown'; + public const AFTER = 'tester.exercise_completed.after'; /** * Returns specification iterators. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/LifecycleEvent.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/LifecycleEvent.php index 50191b463..b2867f020 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/LifecycleEvent.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/LifecycleEvent.php @@ -11,8 +11,8 @@ namespace Behat\Testwork\EventDispatcher\Event; use Behat\Testwork\Environment\Environment; +use Behat\Testwork\Event\Event; use Behat\Testwork\Suite\Suite; -use Symfony\Component\EventDispatcher\Event; /** * Represents an event which holds references to current suite and environment. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/SuiteTested.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/SuiteTested.php index 2ded66a28..de56d077b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/SuiteTested.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Event/SuiteTested.php @@ -19,10 +19,10 @@ */ abstract class SuiteTested extends LifecycleEvent { - const BEFORE = 'tester.suite_tested.before'; - const AFTER_SETUP = 'tester.suite_tested.after_setup'; - const BEFORE_TEARDOWN = 'tester.suite_tested.before_teardown'; - const AFTER = 'tester.suite_tested.after'; + public const BEFORE = 'tester.suite_tested.before'; + public const AFTER_SETUP = 'tester.suite_tested.after_setup'; + public const BEFORE_TEARDOWN = 'tester.suite_tested.before_teardown'; + public const AFTER = 'tester.suite_tested.after'; /** * Returns specification iterator. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/ServiceContainer/EventDispatcherExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/ServiceContainer/EventDispatcherExtension.php index 72376dac5..502740a5f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/ServiceContainer/EventDispatcherExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/ServiceContainer/EventDispatcherExtension.php @@ -30,12 +30,12 @@ class EventDispatcherExtension implements Extension /* * Available services */ - const DISPATCHER_ID = 'event_dispatcher'; + public const DISPATCHER_ID = 'event_dispatcher'; /* * Available extension points */ - const SUBSCRIBER_TAG = 'event_dispatcher.subscriber'; + public const SUBSCRIBER_TAG = 'event_dispatcher.subscriber'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingExercise.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingExercise.php index 10f34196e..9c319761d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingExercise.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingExercise.php @@ -14,6 +14,7 @@ use Behat\Testwork\EventDispatcher\Event\AfterExerciseSetup; use Behat\Testwork\EventDispatcher\Event\BeforeExerciseCompleted; use Behat\Testwork\EventDispatcher\Event\BeforeExerciseTeardown; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Tester\Exercise; use Behat\Testwork\Tester\Result\TestResult; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -52,12 +53,14 @@ public function __construct(Exercise $baseExercise, EventDispatcherInterface $ev public function setUp(array $iterators, $skip) { $event = new BeforeExerciseCompleted($iterators); - $this->eventDispatcher->dispatch($event::BEFORE, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE); $setup = $this->baseExercise->setUp($iterators, $skip); $event = new AfterExerciseSetup($iterators, $setup); - $this->eventDispatcher->dispatch($event::AFTER_SETUP, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER_SETUP); return $setup; } @@ -76,12 +79,14 @@ public function test(array $iterators, $skip = false) public function tearDown(array $iterators, $skip, TestResult $result) { $event = new BeforeExerciseTeardown($iterators, $result); - $this->eventDispatcher->dispatch($event::BEFORE_TEARDOWN, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE_TEARDOWN); $teardown = $this->baseExercise->tearDown($iterators, $skip, $result); $event = new AfterExerciseCompleted($iterators, $result, $teardown); - $this->eventDispatcher->dispatch($event::AFTER, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingSuiteTester.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingSuiteTester.php index 84b2d07c0..acfef275a 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingSuiteTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingSuiteTester.php @@ -15,6 +15,7 @@ use Behat\Testwork\EventDispatcher\Event\AfterSuiteTested; use Behat\Testwork\EventDispatcher\Event\BeforeSuiteTeardown; use Behat\Testwork\EventDispatcher\Event\BeforeSuiteTested; +use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Specification\SpecificationIterator; use Behat\Testwork\Tester\Result\TestResult; use Behat\Testwork\Tester\SuiteTester; @@ -54,12 +55,14 @@ public function __construct(SuiteTester $baseTester, EventDispatcherInterface $e public function setUp(Environment $env, SpecificationIterator $iterator, $skip) { $event = new BeforeSuiteTested($env, $iterator); - $this->eventDispatcher->dispatch($event::BEFORE, $event); + + $this->eventDispatcher->dispatch($event, $event::BEFORE); $setup = $this->baseTester->setUp($env, $iterator, $skip); $event = new AfterSuiteSetup($env, $iterator, $setup); - $this->eventDispatcher->dispatch($event::AFTER_SETUP, $event); + + $this->eventDispatcher->dispatch($event, $event::AFTER_SETUP); return $setup; } @@ -78,12 +81,13 @@ public function test(Environment $env, SpecificationIterator $iterator, $skip = public function tearDown(Environment $env, SpecificationIterator $iterator, $skip, TestResult $result) { $event = new BeforeSuiteTeardown($env, $iterator, $result); - $this->eventDispatcher->dispatch($event::BEFORE_TEARDOWN, $event); + $this->eventDispatcher->dispatch( $event, $event::BEFORE_TEARDOWN); $teardown = $this->baseTester->tearDown($env, $iterator, $skip, $result); $event = new AfterSuiteTested($env, $iterator, $result, $teardown); - $this->eventDispatcher->dispatch($event::AFTER, $event); + + $this->eventDispatcher->dispatch( $event, $event::AFTER); return $teardown; } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcher.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcher.php index c1200f300..cf96c5d78 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcher.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcher.php @@ -10,8 +10,8 @@ namespace Behat\Testwork\EventDispatcher; -use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Contracts\EventDispatcher\Event; /** * Extends Symfony2 event dispatcher with catch-all listeners. @@ -20,40 +20,47 @@ */ final class TestworkEventDispatcher extends EventDispatcher { - const BEFORE_ALL_EVENTS = '*~'; - const AFTER_ALL_EVENTS = '~*'; + public const BEFORE_ALL_EVENTS = '*~'; + public const AFTER_ALL_EVENTS = '~*'; + public const DISPATCHER_VERSION = 2; /** * {@inheritdoc} + * + * @param string|null $eventName */ - public function dispatch($eventName, Event $event = null) + public function getListeners($eventName = null): array { - if (null === $event) { - $event = new Event(); + if (null === $eventName || self::BEFORE_ALL_EVENTS === $eventName) { + return parent::getListeners($eventName); } - if (method_exists($event, 'setName')) { - $event->setName($eventName); - } + return array_merge( + parent::getListeners(self::BEFORE_ALL_EVENTS), + parent::getListeners($eventName), + parent::getListeners(self::AFTER_ALL_EVENTS) + ); + } - $this->doDispatch($this->getListeners($eventName), $eventName, $event); + public function dispatch($event, $eventName = null): object + { + if (is_object($event)) { + return $this->bcAwareDispatch($event, $eventName); + } - return $event; + return $this->bcAwareDispatch($eventName, $event); } - /** - * {@inheritdoc} - */ - public function getListeners($eventName = null) + private function bcAwareDispatch(object $event, $eventName) { - if (null == $eventName || self::BEFORE_ALL_EVENTS === $eventName) { - return parent::getListeners($eventName); + if (null === $event) { + $event = new Event(); } - return array_merge( - parent::getListeners(self::BEFORE_ALL_EVENTS), - parent::getListeners($eventName), - parent::getListeners(self::AFTER_ALL_EVENTS) - ); + if (method_exists($event, 'setName')) { + $event->setName($eventName); + } + + return parent::dispatch($event, $eventName); } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcherSymfony5.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcherSymfony5.php new file mode 100644 index 000000000..d82c500dd --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcherSymfony5.php @@ -0,0 +1,68 @@ +=5.0) event dispatcher with catch-all listeners. + * + * This is magically aliased to TestworkEventDispatcher by the code in TestworkEventDispatcher.php + * if the new symfony interface is detected. + * + * @deprecated Do not reference this class directly, use TestworkEventDispatcher + */ +final class TestworkEventDispatcherSymfony5 extends EventDispatcher +{ + public const BEFORE_ALL_EVENTS = '*~'; + public const AFTER_ALL_EVENTS = '~*'; + public const DISPATCHER_VERSION = 2; + + /** + * {@inheritdoc} + */ + public function dispatch($event, string $eventName = null): object + { + trigger_error( + 'Class "\Behat\Testwork\EventDispatcher\TestworkEventDispatcherSymfony5" is deprecated ' . + 'and should not be relied upon anymore. Use "Behat\Testwork\EventDispatcher\TestworkEventDispatcher" ' . + 'instead', + E_USER_DEPRECATED + ); + if (null === $event) { + $event = new \Symfony\Contracts\EventDispatcher\Event(); + } + if (method_exists($event, 'setName')) { + $event->setName($eventName); + } + + $this->callListeners($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + trigger_error( + 'Class "\Behat\Testwork\EventDispatcher\TestworkEventDispatcherSymfony5" is deprecated ' . + 'and should not be relied upon anymore. Use "Behat\Testwork\EventDispatcher\TestworkEventDispatcher" ' . + 'instead', + E_USER_DEPRECATED + ); + + if (null == $eventName || self::BEFORE_ALL_EVENTS === $eventName) { + return parent::getListeners($eventName); + } + + return array_merge( + parent::getListeners(self::BEFORE_ALL_EVENTS), + parent::getListeners($eventName), + parent::getListeners(self::AFTER_ALL_EVENTS) + ); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcherSymfonyLegacy.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcherSymfonyLegacy.php new file mode 100644 index 000000000..3a41ba6d5 --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/TestworkEventDispatcherSymfonyLegacy.php @@ -0,0 +1,71 @@ +setName($eventName); + } + /** @scrutinizer ignore-call */ + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + trigger_error( + 'Class "\Behat\Testwork\EventDispatcher\TestworkEventDispatcherSymfonyLegacy" is deprecated ' . + 'and should not be relied upon anymore. Use "Behat\Testwork\EventDispatcher\TestworkEventDispatcher" ' . + 'instead', + E_USER_DEPRECATED + ); + + if (null == $eventName || self::BEFORE_ALL_EVENTS === $eventName) { + return parent::getListeners($eventName); + } + + return array_merge( + parent::getListeners(self::BEFORE_ALL_EVENTS), + parent::getListeners($eventName), + parent::getListeners(self::AFTER_ALL_EVENTS) + ); + } +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ExceptionPresenter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ExceptionPresenter.php index fce59bbf6..9ad0d14c2 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ExceptionPresenter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ExceptionPresenter.php @@ -13,6 +13,7 @@ use Behat\Testwork\Exception\Stringer\ExceptionStringer; use Behat\Testwork\Output\Printer\OutputPrinter; use Exception; +use Symfony\Component\Console\Output\OutputInterface; /** * Presents exceptions as strings using registered stringers. @@ -93,13 +94,45 @@ public function presentException(Exception $exception, $verbosity = null) } } - if (OutputPrinter::VERBOSITY_VERY_VERBOSE <= $verbosity) { + if (OutputInterface::VERBOSITY_VERY_VERBOSE <= $verbosity) { + if (OutputInterface::VERBOSITY_DEBUG > $verbosity) { + $exception = $this->removeBehatCallsFromTrace($exception); + } + return $this->relativizePaths(trim($exception)); } return trim($this->relativizePaths($exception->getMessage()) . ' (' . get_class($exception) . ')'); } + private function removeBehatCallsFromTrace(Exception $exception) + { + $traceOutput = ''; + foreach ($exception->getTrace() as $i => $trace) { + if (isset($trace['file']) && false !== strpos(str_replace('\\', '/', $trace['file']), 'Behat/Testwork/Call/Handler/RuntimeCallHandler')) { + break; + } + + $traceOutput .= sprintf( + '#%d %s: %s()'.PHP_EOL, + $i, + isset($trace['file']) ? $trace['file'].'('.$trace['line'].')' : '[internal function]', + isset($trace['class']) ? $trace['class'].$trace['type'].$trace['function'] : $trace['function'] + ); + } + + return sprintf( + "%s: %s in %s:%d%sStack trace:%s%s", + get_class($exception), + $exception->getMessage(), + $exception->getFile(), + $exception->getLine(), + PHP_EOL, + PHP_EOL, + $traceOutput + ); + } + /** * Relativizes absolute paths in the text. * diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ServiceContainer/ExceptionExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ServiceContainer/ExceptionExtension.php index ac89715e5..92ae6afee 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ServiceContainer/ExceptionExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/ServiceContainer/ExceptionExtension.php @@ -30,12 +30,12 @@ final class ExceptionExtension implements Extension /* * Available services */ - const PRESENTER_ID = 'exception.presenter'; + public const PRESENTER_ID = 'exception.presenter'; /* * Available extension points */ - const STRINGER_TAG = 'exception.stringer'; + public const STRINGER_TAG = 'exception.stringer'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/ExceptionStringer.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/ExceptionStringer.php index eab706186..5f8de153c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/ExceptionStringer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/ExceptionStringer.php @@ -27,7 +27,7 @@ interface ExceptionStringer * * @param Exception $exception * - * @return Boolean + * @return bool */ public function supportsException(Exception $exception); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/PHPUnitExceptionStringer.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/PHPUnitExceptionStringer.php index 3534924f8..0b06299ef 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/PHPUnitExceptionStringer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Exception/Stringer/PHPUnitExceptionStringer.php @@ -11,8 +11,6 @@ namespace Behat\Testwork\Exception\Stringer; use Exception; -use PHPUnit_Framework_Exception; -use PHPUnit_Framework_TestFailure; /** * Strings PHPUnit assertion exceptions. @@ -28,7 +26,8 @@ final class PHPUnitExceptionStringer implements ExceptionStringer */ public function supportsException(Exception $exception) { - return $exception instanceof PHPUnit_Framework_Exception; + return $exception instanceof \PHPUnit_Framework_Exception + || $exception instanceof \PHPUnit\Framework\Exception; } /** @@ -36,9 +35,13 @@ public function supportsException(Exception $exception) */ public function stringException(Exception $exception, $verbosity) { + if (!class_exists('PHPUnit\\Framework\\TestFailure')) { + return trim(\PHPUnit_Framework_TestFailure::exceptionToString($exception)); + } + // PHPUnit assertion exceptions do not include expected / observed info in their // messages, but expect the test listeners to format that info like the following // (see e.g. PHPUnit_TextUI_ResultPrinter::printDefectTrace) - return trim(PHPUnit_Framework_TestFailure::exceptionToString($exception)); + return trim(\PHPUnit\Framework\TestFailure::exceptionToString($exception)); } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Filesystem/ServiceContainer/FilesystemExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Filesystem/ServiceContainer/FilesystemExtension.php index 4fdc537ca..0d668ff11 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Filesystem/ServiceContainer/FilesystemExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Filesystem/ServiceContainer/FilesystemExtension.php @@ -28,7 +28,7 @@ final class FilesystemExtension implements Extension /* * Available services */ - const LOGGER_ID = 'filesystem.logger'; + public const LOGGER_ID = 'filesystem.logger'; /** * {@inheritdoc} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Call/RuntimeSuiteHook.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Call/RuntimeSuiteHook.php index 237bc7615..44b7f6573 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Call/RuntimeSuiteHook.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Call/RuntimeSuiteHook.php @@ -70,7 +70,7 @@ public function filterMatches(HookScope $scope) * @param Suite $suite * @param string $filterString * - * @return Boolean + * @return bool */ private function isSuiteMatch(Suite $suite, $filterString) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/FilterableHook.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/FilterableHook.php index 48ece6aba..a241ff816 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/FilterableHook.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/FilterableHook.php @@ -24,7 +24,7 @@ interface FilterableHook extends Hook * * @param HookScope $scope * - * @return Boolean + * @return bool */ public function filterMatches(HookScope $scope); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Scope/SuiteScope.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Scope/SuiteScope.php index 6cc308f00..659a8f6cd 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Scope/SuiteScope.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/Scope/SuiteScope.php @@ -19,8 +19,8 @@ */ interface SuiteScope extends HookScope { - const BEFORE = 'suite.before'; - const AFTER = 'suite.after'; + public const BEFORE = 'suite.before'; + public const AFTER = 'suite.after'; /** * Returns specification iterator. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/ServiceContainer/HookExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/ServiceContainer/HookExtension.php index fee6de4d4..9697c98e5 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/ServiceContainer/HookExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Hook/ServiceContainer/HookExtension.php @@ -30,8 +30,8 @@ class HookExtension implements Extension /* * Available services */ - const DISPATCHER_ID = 'hook.dispatcher'; - const REPOSITORY_ID = 'hook.repository'; + public const DISPATCHER_ID = 'hook.dispatcher'; + public const REPOSITORY_ID = 'hook.repository'; /** * {@inheritdoc} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/OrderedExercise.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/OrderedExercise.php index 1cc3055a6..aba9ba894 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/OrderedExercise.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/OrderedExercise.php @@ -58,7 +58,7 @@ public function __construct(Exercise $decoratedExercise) * Sets up exercise for a test. * * @param SpecificationIterator[] $iterators - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -71,7 +71,7 @@ public function setUp(array $iterators, $skip) * Tests suites specifications. * * @param SpecificationIterator[] $iterators - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -84,7 +84,7 @@ public function test(array $iterators, $skip) * Tears down exercise after a test. * * @param SpecificationIterator[] $iterators - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/Orderer/RandomOrderer.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/Orderer/RandomOrderer.php index 551e44796..8dc9c9e0e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/Orderer/RandomOrderer.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/Orderer/RandomOrderer.php @@ -11,6 +11,7 @@ namespace Behat\Testwork\Ordering\Orderer; use Behat\Testwork\Specification\SpecificationArrayIterator; +use Behat\Testwork\Specification\SpecificationIterator; /** * Prioritises Suites and Features into random order diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/ServiceContainer/OrderingExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/ServiceContainer/OrderingExtension.php index 2a9d5a7fa..329227330 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/ServiceContainer/OrderingExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Ordering/ServiceContainer/OrderingExtension.php @@ -28,7 +28,7 @@ */ final class OrderingExtension implements Extension { - const ORDERER_TAG = 'tester.orderer'; + public const ORDERER_TAG = 'tester.orderer'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Cli/OutputController.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Cli/OutputController.php index ae886519b..bf8f94488 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Cli/OutputController.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Cli/OutputController.php @@ -153,7 +153,7 @@ protected function locateOutputPath($path) * * @param array $formats * @param array $outputs - * @param Boolean $decorated + * @param bool $decorated */ private function configureOutputs(array $formats, array $outputs, $decorated = false) { @@ -184,7 +184,7 @@ private function configureOutputs(array $formats, array $outputs, $decorated = f * * @param string $outputId * - * @return Boolean + * @return bool */ private function isStandardOutput($outputId) { @@ -196,7 +196,7 @@ private function isStandardOutput($outputId) * * @param string $file A file path * - * @return Boolean + * @return bool */ private function isAbsolutePath($file) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Exception/MissingExtensionException.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Exception/MissingExtensionException.php new file mode 100644 index 000000000..a54cdbd9f --- /dev/null +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Exception/MissingExtensionException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Testwork\Output\Exception; + +use RuntimeException; + +/** + * Represents an exception thrown when a required extension is missing. + * + * @author Konstantin Kudryashov + */ +class MissingExtensionException extends RuntimeException implements PrinterException +{ +} diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/ChainEventListener.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/ChainEventListener.php index 0c76bac39..2fb8d44cb 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/ChainEventListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/ChainEventListener.php @@ -11,10 +11,10 @@ namespace Behat\Testwork\Output\Node\EventListener; use ArrayIterator; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Countable; use IteratorAggregate; -use Symfony\Component\EventDispatcher\Event; /** * Used to compose formatter event listeners. @@ -51,7 +51,7 @@ public function listenEvent(Formatter $formatter, Event $event, $eventName) /** * {@inheritdoc} */ - public function count() + public function count(): int { return count($this->listeners); } @@ -59,7 +59,7 @@ public function count() /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): ArrayIterator { return new ArrayIterator($this->listeners); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/EventListener.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/EventListener.php index 6fbc078ff..2faaef957 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/EventListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/EventListener.php @@ -10,8 +10,8 @@ namespace Behat\Testwork\Output\Node\EventListener; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; -use Symfony\Component\EventDispatcher\Event; /** * Used to define formatter event listeners. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/Flow/FireOnlyIfFormatterParameterListener.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/Flow/FireOnlyIfFormatterParameterListener.php index 0df21eb3a..3c917189b 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/Flow/FireOnlyIfFormatterParameterListener.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Node/EventListener/Flow/FireOnlyIfFormatterParameterListener.php @@ -10,9 +10,9 @@ namespace Behat\Testwork\Output\Node\EventListener\Flow; +use Behat\Testwork\Event\Event; use Behat\Testwork\Output\Formatter; use Behat\Testwork\Output\Node\EventListener\EventListener; -use Symfony\Component\EventDispatcher\Event; /** * Catches all events, but proxies them only if formatter has specific parameter set to a specific value. diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/NodeEventListeningFormatter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/NodeEventListeningFormatter.php index 3894d49fa..4ecdd2085 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/NodeEventListeningFormatter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/NodeEventListeningFormatter.php @@ -10,10 +10,10 @@ namespace Behat\Testwork\Output; +use Behat\Testwork\Event\Event; use Behat\Testwork\EventDispatcher\TestworkEventDispatcher; use Behat\Testwork\Output\Node\EventListener\EventListener; use Behat\Testwork\Output\Printer\OutputPrinter; -use Symfony\Component\EventDispatcher\Event; /** * Formatter built around the idea of event delegation and composition. @@ -79,7 +79,13 @@ public static function getSubscribedEvents() */ public function listenEvent(Event $event, $eventName = null) { - $eventName = $eventName ?: $event->getName(); + if (null === $eventName) { + if (method_exists($event, 'getName')) { + $eventName = $event->getName(); + } else { + $eventName = get_class($event); + } + } $this->listener->listenEvent($this, $event, $eventName); } @@ -121,6 +127,6 @@ public function setParameter($name, $value) */ public function getParameter($name) { - return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + return $this->parameters[$name] ?? null; } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/OutputManager.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/OutputManager.php index cdf776018..ede7b574f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/OutputManager.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/OutputManager.php @@ -58,7 +58,7 @@ public function registerFormatter(Formatter $formatter) * * @param string $name * - * @return Boolean + * @return bool */ public function isFormatterRegistered($name) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/Factory/OutputFactory.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/Factory/OutputFactory.php index 20f47d35a..e65f7f152 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/Factory/OutputFactory.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/Factory/OutputFactory.php @@ -18,10 +18,10 @@ */ abstract class OutputFactory { - const VERBOSITY_NORMAL = 1; - const VERBOSITY_VERBOSE = 2; - const VERBOSITY_VERY_VERBOSE = 3; - const VERBOSITY_DEBUG = 4; + public const VERBOSITY_NORMAL = 1; + public const VERBOSITY_VERBOSE = 2; + public const VERBOSITY_VERY_VERBOSE = 3; + public const VERBOSITY_DEBUG = 4; /** * @var null|string @@ -32,7 +32,7 @@ abstract class OutputFactory */ private $outputStyles = array(); /** - * @var null|Boolean + * @var null|bool */ private $outputDecorated = null; /** @@ -83,7 +83,7 @@ public function getOutputStyles() /** * Forces output to be decorated. * - * @param Boolean $decorated + * @param bool $decorated */ public function setOutputDecorated($decorated) { @@ -93,7 +93,7 @@ public function setOutputDecorated($decorated) /** * Returns output decoration status. * - * @return null|Boolean + * @return null|bool */ public function isOutputDecorated() { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/JUnitOutputPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/JUnitOutputPrinter.php index 7577e111c..f415b96a1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/JUnitOutputPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/JUnitOutputPrinter.php @@ -10,6 +10,7 @@ namespace Behat\Testwork\Output\Printer; +use Behat\Testwork\Output\Exception\MissingExtensionException; use Behat\Testwork\Output\Printer\Factory\FilesystemOutputFactory; use Symfony\Component\Console\Output\OutputInterface; @@ -22,8 +23,8 @@ */ final class JUnitOutputPrinter extends StreamOutputPrinter { - const XML_VERSION = '1.0'; - const XML_ENCODING = 'UTF-8'; + public const XML_VERSION = '1.0'; + public const XML_ENCODING = 'UTF-8'; /** * @var \DOMDocument @@ -57,6 +58,10 @@ public function __construct(FilesystemOutputFactory $outputFactory) */ public function createNewFile($name, array $testsuitesAttributes = array()) { + // This requires the DOM extension to be enabled. + if (!extension_loaded('dom')) { + throw new MissingExtensionException('The PHP DOM extension is required to generate JUnit reports.'); + } $this->setFileName(strtolower(trim(preg_replace('/[^[:alnum:]_]+/', '_', $name), '_'))); $this->domDocument = new \DOMDocument(self::XML_VERSION, self::XML_ENCODING); @@ -102,7 +107,7 @@ public function addTestcase(array $testcaseAttributes = array()) */ public function addTestcaseChild($nodeName, array $nodeAttributes = array(), $nodeValue = null) { - $childNode = $this->domDocument->createElement($nodeName, $nodeValue); + $childNode = $this->domDocument->createElement($nodeName, $nodeValue ?? ''); $this->currentTestcase->appendChild($childNode); $this->addAttributesToNode($childNode, $nodeAttributes); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/OutputPrinter.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/OutputPrinter.php index 91292d39c..6ef907439 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/OutputPrinter.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/Printer/OutputPrinter.php @@ -20,19 +20,19 @@ interface OutputPrinter /** * @deprecated since 3.1, to be removed in 4.0 */ - const VERBOSITY_NORMAL = 1; + public const VERBOSITY_NORMAL = 1; /** * @deprecated since 3.1, to be removed in 4.0 */ - const VERBOSITY_VERBOSE = 2; + public const VERBOSITY_VERBOSE = 2; /** * @deprecated since 3.1, to be removed in 4.0 */ - const VERBOSITY_VERY_VERBOSE = 3; + public const VERBOSITY_VERY_VERBOSE = 3; /** * @deprecated since 3.1, to be removed in 4.0 */ - const VERBOSITY_DEBUG = 4; + public const VERBOSITY_DEBUG = 4; /** * Sets output path. @@ -69,14 +69,14 @@ public function getOutputStyles(); /** * Forces output to be decorated. * - * @param Boolean $decorated + * @param bool $decorated */ public function setOutputDecorated($decorated); /** * Returns output decoration status. * - * @return null|Boolean + * @return null|bool * * @deprecated since 3.1, to be removed in 4.0 */ diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/ServiceContainer/OutputExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/ServiceContainer/OutputExtension.php index f49babe97..dff83fde1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/ServiceContainer/OutputExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Output/ServiceContainer/OutputExtension.php @@ -31,12 +31,12 @@ final class OutputExtension implements Extension /* * Available services */ - const MANAGER_ID = 'output.manager'; + public const MANAGER_ID = 'output.manager'; /* * Available extension points */ - const FORMATTER_TAG = 'output.formatter'; + public const FORMATTER_TAG = 'output.formatter'; /** * @var string diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationLoader.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationLoader.php index 33521c10d..dfc02eb4d 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationLoader.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationLoader.php @@ -29,7 +29,7 @@ final class ConfigurationLoader */ private $environmentVariable; /** - * @var Boolean + * @var bool */ private $profileFound; /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationTree.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationTree.php index d713cbc9b..f5b215fab 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationTree.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/Configuration/ConfigurationTree.php @@ -11,6 +11,7 @@ namespace Behat\Testwork\ServiceContainer\Configuration; use Behat\Testwork\ServiceContainer\Extension; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\NodeInterface; @@ -30,13 +31,14 @@ final class ConfigurationTree */ public function getConfigTree(array $extensions) { - $tree = new TreeBuilder(); - $root = $tree->root('testwork'); + $treeBuilder = new TreeBuilder('testwork'); + /** @var ArrayNodeDefinition $rootNode */ + $rootNode = $treeBuilder->getRootNode(); foreach ($extensions as $extension) { - $extension->configure($root->children()->arrayNode($extension->getConfigKey())); + $extension->configure($rootNode->children()->arrayNode($extension->getConfigKey())); } - return $tree->buildTree(); + return $treeBuilder->buildTree(); } } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/ExtensionManager.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/ExtensionManager.php index 7e2d955dc..ad49484c8 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/ExtensionManager.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/ServiceContainer/ExtensionManager.php @@ -81,11 +81,11 @@ public function activateExtension($locator) * * @param string $key * - * @return Extension + * @return Extension|null */ public function getExtension($key) { - return isset($this->extensions[$key]) ? $this->extensions[$key] : null; + return $this->extensions[$key] ?? null; } /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/GroupedSpecificationIterator.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/GroupedSpecificationIterator.php index bac556d05..8975040fc 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/GroupedSpecificationIterator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/GroupedSpecificationIterator.php @@ -77,7 +77,7 @@ public function getSuite() /** * {@inheritdoc} */ - public function rewind() + public function rewind(): void { $this->position = 0; while (isset($this->iterators[$this->position])) { @@ -93,7 +93,7 @@ public function rewind() /** * {@inheritdoc} */ - public function next() + public function next(): void { if (!isset($this->iterators[$this->position])) { return; @@ -114,7 +114,7 @@ public function next() /** * {@inheritdoc} */ - public function valid() + public function valid(): bool { return isset($this->iterators[$this->position]) && $this->iterators[$this->position]->valid(); } @@ -122,6 +122,7 @@ public function valid() /** * {@inheritdoc} */ + #[\ReturnTypeWillChange] public function current() { return $this->iterators[$this->position]->current(); @@ -130,7 +131,7 @@ public function current() /** * {@inheritdoc} */ - public function key() + public function key(): int { return $this->position + $this->iterators[$this->position]->key(); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/ServiceContainer/SpecificationExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/ServiceContainer/SpecificationExtension.php index 03ca52de7..d369d458c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/ServiceContainer/SpecificationExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Specification/ServiceContainer/SpecificationExtension.php @@ -27,12 +27,12 @@ final class SpecificationExtension implements Extension /* * Available services */ - const FINDER_ID = 'specifications.finder'; + public const FINDER_ID = 'specifications.finder'; /* * Available extension points */ - const LOCATOR_TAG = 'specifications.locator'; + public const LOCATOR_TAG = 'specifications.locator'; /** * @var ServiceProcessor diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Generator/SuiteGenerator.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Generator/SuiteGenerator.php index 876d6b600..5d8934683 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Generator/SuiteGenerator.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Generator/SuiteGenerator.php @@ -28,7 +28,7 @@ interface SuiteGenerator * @param string $type * @param array $settings * - * @return Boolean + * @return bool */ public function supportsTypeAndSettings($type, array $settings); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/GenericSuite.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/GenericSuite.php index 766cffe5b..1c6702b61 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/GenericSuite.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/GenericSuite.php @@ -65,7 +65,7 @@ public function getSettings() * * @param string $key * - * @return Boolean + * @return bool */ public function hasSetting($key) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/ServiceContainer/SuiteExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/ServiceContainer/SuiteExtension.php index 79fbcda84..804cb92e4 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/ServiceContainer/SuiteExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/ServiceContainer/SuiteExtension.php @@ -29,14 +29,14 @@ final class SuiteExtension implements Extension /* * Available services */ - const REGISTRY_ID = 'suite.registry'; - const BOOTSTRAPPER_ID = 'suite.bootstrapper'; + public const REGISTRY_ID = 'suite.registry'; + public const BOOTSTRAPPER_ID = 'suite.bootstrapper'; /* * Available extension points */ - const GENERATOR_TAG = 'suite.generator'; - const SETUP_TAG = 'suite.setup'; + public const GENERATOR_TAG = 'suite.generator'; + public const SETUP_TAG = 'suite.setup'; /** * @var ServiceProcessor @@ -89,9 +89,7 @@ public function configure(ArrayNodeDefinition $builder) return is_array($suite) && count($suite); }) ->then(function ($suite) { - $suite['settings'] = isset($suite['settings']) - ? $suite['settings'] - : array(); + $suite['settings'] = $suite['settings'] ?? array(); foreach ($suite as $key => $val) { $suiteKeys = array('enabled', 'type', 'settings'); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Setup/SuiteSetup.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Setup/SuiteSetup.php index 616562434..75eef7c13 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Setup/SuiteSetup.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Setup/SuiteSetup.php @@ -27,7 +27,7 @@ interface SuiteSetup * * @param Suite $suite * - * @return Boolean + * @return bool */ public function supportsSuite(Suite $suite); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Suite.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Suite.php index 782f8fd7c..e26c1617e 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Suite.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/Suite.php @@ -36,7 +36,7 @@ public function getSettings(); * * @param string $key * - * @return Boolean + * @return bool */ public function hasSetting($key); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/SuiteRegistry.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/SuiteRegistry.php index 5ad98fdcb..b5f90df46 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/SuiteRegistry.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Suite/SuiteRegistry.php @@ -23,7 +23,7 @@ final class SuiteRegistry implements SuiteRepository { /** - * @var Boolean + * @var bool */ private $suitesGenerated = false; /** diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php index b3d2eac5b..2fc5642e1 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php @@ -52,7 +52,7 @@ final class ExerciseController implements Controller */ private $resultInterpreter; /** - * @var Boolean + * @var bool */ private $skip; @@ -63,7 +63,7 @@ final class ExerciseController implements Controller * @param SpecificationFinder $specificationFinder * @param Exercise $exercise * @param ResultInterpreter $resultInterpreter - * @param Boolean $skip + * @param bool $skip */ public function __construct( SuiteRepository $suiteRepository, diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/StrictController.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/StrictController.php index 1e610be21..697b881fc 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/StrictController.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/StrictController.php @@ -30,7 +30,7 @@ final class StrictController implements Controller */ private $resultInterpreter; /** - * @var Boolean + * @var bool */ private $strict; @@ -38,7 +38,7 @@ final class StrictController implements Controller * Initializes controller. * * @param ResultInterpreter $resultInterpreter - * @param Boolean $strict + * @param bool $strict */ public function __construct(ResultInterpreter $resultInterpreter, $strict = false) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Exercise.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Exercise.php index 6a19c1efb..e63883d8c 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Exercise.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Exercise.php @@ -26,7 +26,7 @@ interface Exercise * Sets up exercise for a test. * * @param SpecificationIterator[] $iterators - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -36,7 +36,7 @@ public function setUp(array $iterators, $skip); * Tests suites specifications. * * @param SpecificationIterator[] $iterators - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -46,7 +46,7 @@ public function test(array $iterators, $skip); * Tears down exercise after a test. * * @param SpecificationIterator[] $iterators - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/ExceptionResult.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/ExceptionResult.php index f30edcf19..1b724f5c3 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/ExceptionResult.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/ExceptionResult.php @@ -22,7 +22,7 @@ interface ExceptionResult extends TestResult /** * Checks that the test result has exception. * - * @return Boolean + * @return bool */ public function hasException(); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/Interpretation/ResultInterpretation.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/Interpretation/ResultInterpretation.php index 555e75fa8..cc9250915 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/Interpretation/ResultInterpretation.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/Interpretation/ResultInterpretation.php @@ -27,7 +27,7 @@ interface ResultInterpretation * * @param TestResult $result * - * @return Boolean + * @return bool */ public function isFailure(TestResult $result); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResult.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResult.php index 9d557965d..7c3df6ffe 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResult.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResult.php @@ -17,15 +17,15 @@ */ interface TestResult { - const PASSED = 0; - const SKIPPED = 10; - const PENDING = 20; - const FAILED = 99; + public const PASSED = 0; + public const SKIPPED = 10; + public const PENDING = 20; + public const FAILED = 99; /** * Checks that test has passed. * - * @return Boolean + * @return bool */ public function isPassed(); diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResults.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResults.php index 5102f3747..cf8e4aa52 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResults.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Result/TestResults.php @@ -21,7 +21,7 @@ */ final class TestResults implements TestResult, Countable, IteratorAggregate { - const NO_TESTS = -100; + public const NO_TESTS = -100; /** * @var TestResult[] @@ -62,7 +62,7 @@ public function getResultCode() /** * {@inheritdoc} */ - public function count() + public function count(): int { return count($this->results); } @@ -70,7 +70,7 @@ public function count() /** * {@inheritdoc} */ - public function getIterator() + public function getIterator(): ArrayIterator { return new ArrayIterator($this->results); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/ServiceContainer/TesterExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/ServiceContainer/TesterExtension.php index 55bcfa9da..8892992ce 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/ServiceContainer/TesterExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/ServiceContainer/TesterExtension.php @@ -32,18 +32,18 @@ abstract class TesterExtension implements Extension /* * Available services */ - const EXERCISE_ID = 'tester.exercise'; - const SUITE_TESTER_ID = 'tester.suite'; - const SPECIFICATION_TESTER_ID = 'tester.specification'; - const RESULT_INTERPRETER_ID = 'tester.result.interpreter'; + public const EXERCISE_ID = 'tester.exercise'; + public const SUITE_TESTER_ID = 'tester.suite'; + public const SPECIFICATION_TESTER_ID = 'tester.specification'; + public const RESULT_INTERPRETER_ID = 'tester.result.interpreter'; /** * Available extension points */ - const EXERCISE_WRAPPER_TAG = 'tester.exercise.wrapper'; - const SUITE_TESTER_WRAPPER_TAG = 'tester.suite.wrapper'; - const SPECIFICATION_TESTER_WRAPPER_TAG = 'tester.specification.wrapper'; - const RESULT_INTERPRETATION_TAG = 'test.result.interpretation'; + public const EXERCISE_WRAPPER_TAG = 'tester.exercise.wrapper'; + public const SUITE_TESTER_WRAPPER_TAG = 'tester.suite.wrapper'; + public const SPECIFICATION_TESTER_WRAPPER_TAG = 'tester.specification.wrapper'; + public const RESULT_INTERPRETATION_TAG = 'test.result.interpretation'; /** * @var ServiceProcessor @@ -123,7 +123,7 @@ public function process(ContainerBuilder $container) * Loads exercise cli controllers. * * @param ContainerBuilder $container - * @param Boolean $skip + * @param bool $skip */ protected function loadExerciseController(ContainerBuilder $container, $skip = false) { @@ -142,7 +142,7 @@ protected function loadExerciseController(ContainerBuilder $container, $skip = f * Loads exercise cli controllers. * * @param ContainerBuilder $container - * @param Boolean $strict + * @param bool $strict */ protected function loadStrictController(ContainerBuilder $container, $strict = false) { diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Setup.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Setup.php index e904ebb35..5f6fd45b7 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Setup.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Setup.php @@ -20,14 +20,14 @@ interface Setup /** * Returns true if fixtures have been handled successfully. * - * @return Boolean + * @return bool */ public function isSuccessful(); /** * Checks if setup has produced any output. * - * @return Boolean + * @return bool */ public function hasOutput(); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Teardown.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Teardown.php index fbb445b17..7ff253168 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Teardown.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/Setup/Teardown.php @@ -20,14 +20,14 @@ interface Teardown /** * Returns true if fixtures have been handled successfully. * - * @return Boolean + * @return bool */ public function isSuccessful(); /** * Checks if tear down has produced any output. * - * @return Boolean + * @return bool */ public function hasOutput(); } diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SpecificationTester.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SpecificationTester.php index 9dc106a22..b6e06ff41 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SpecificationTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SpecificationTester.php @@ -27,7 +27,7 @@ interface SpecificationTester * * @param Environment $env * @param mixed $spec - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -38,7 +38,7 @@ public function setUp(Environment $env, $spec, $skip); * * @param Environment $env * @param mixed $spec - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -49,7 +49,7 @@ public function test(Environment $env, $spec, $skip); * * @param Environment $env * @param mixed $spec - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SuiteTester.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SuiteTester.php index f139f1a14..61dffad7f 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SuiteTester.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Tester/SuiteTester.php @@ -28,7 +28,7 @@ interface SuiteTester * * @param Environment $env * @param SpecificationIterator $iterator - * @param Boolean $skip + * @param bool $skip * * @return Setup */ @@ -39,7 +39,7 @@ public function setUp(Environment $env, SpecificationIterator $iterator, $skip); * * @param Environment $env * @param SpecificationIterator $iterator - * @param Boolean $skip + * @param bool $skip * * @return TestResult */ @@ -50,7 +50,7 @@ public function test(Environment $env, SpecificationIterator $iterator, $skip); * * @param Environment $env * @param SpecificationIterator $iterator - * @param Boolean $skip + * @param bool $skip * @param TestResult $result * * @return Teardown diff --git a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Translator/ServiceContainer/TranslatorExtension.php b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Translator/ServiceContainer/TranslatorExtension.php index 39de237cf..5fc53f0b9 100644 --- a/tests/integration/vendor/behat/behat/src/Behat/Testwork/Translator/ServiceContainer/TranslatorExtension.php +++ b/tests/integration/vendor/behat/behat/src/Behat/Testwork/Translator/ServiceContainer/TranslatorExtension.php @@ -28,7 +28,7 @@ final class TranslatorExtension implements Extension /* * Available services */ - const TRANSLATOR_ID = 'translator'; + public const TRANSLATOR_ID = 'translator'; /** * {@inheritdoc} @@ -91,7 +91,7 @@ public function process(ContainerBuilder $container) */ private function loadTranslator(ContainerBuilder $container, $locale, $fallbackLocale) { - $definition = new Definition('Symfony\Component\Translation\Translator', array($locale)); + $definition = new Definition('Behat\Behat\Definition\Translator\Translator', array($locale)); $container->setDefinition(self::TRANSLATOR_ID, $definition); $definition->addMethodCall('setFallbackLocales', array(array($fallbackLocale))); diff --git a/tests/integration/vendor/behat/gherkin/.github/workflows/build.yml b/tests/integration/vendor/behat/gherkin/.github/workflows/build.yml new file mode 100644 index 000000000..93d5d1d11 --- /dev/null +++ b/tests/integration/vendor/behat/gherkin/.github/workflows/build.yml @@ -0,0 +1,47 @@ +name: Build + +on: + push: + branches: [master] + pull_request: + release: + types: [created] + +jobs: + tests: + runs-on: ubuntu-latest + name: Build and test + strategy: + fail-fast: false + matrix: + php: [7.2, 7.3, 7.4, 8.0, 8.1] + composer-flags: [ "" ] + symfony-version: [ "" ] + include: + - php: 7.2 + symfony-version: '3.*' + - php: 7.3 + symfony-version: '4.*' + - php: 7.4 + symfony-version: '5.*' + - php: 8.0 + symfony-version: '5.*' + + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + + - name: Update Symfony version + if: matrix.symfony-version != '' + run: composer require --no-update "symfony/symfony:${{ matrix.symfony-version }}" + + - name: Install dependencies + run: composer update ${{ matrix.composer-flags }} + + - name: Run tests (phpunit) + run: ./vendor/bin/phpunit diff --git a/tests/integration/vendor/behat/gherkin/.github/workflows/update.yml b/tests/integration/vendor/behat/gherkin/.github/workflows/update.yml new file mode 100644 index 000000000..0ba32d3f9 --- /dev/null +++ b/tests/integration/vendor/behat/gherkin/.github/workflows/update.yml @@ -0,0 +1,49 @@ +name: Update Cucumber + +on: + schedule: + - cron: '0 7 * * *' + +jobs: + cucumber-update: + runs-on: ubuntu-latest + name: Upstream cucumber update + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + + - uses: actions/checkout@v2 + + - name: Install dependencies + run: composer update + + - name: Update cucumber tag + id: cucumber + run: bin/update_cucumber + + - name: Re-install dependencies + run: composer update + if: steps.cucumber.outputs.cucumber_version + + - name: Update translations + run: bin/update_i18n + if: steps.cucumber.outputs.cucumber_version + + - name: Find changelog + id: changelog + run: bin/cucumber_changelog ${{ steps.cucumber.outputs.cucumber_version }} + if: steps.cucumber.outputs.cucumber_version + + - name: Open a PR + uses: peter-evans/create-pull-request@v3 + if: steps.cucumber.outputs.cucumber_version + with: + commit-message: Automatic Cucumber tag update to ${{ steps.cucumber.outputs.cucumber_version }} + branch: cucumber-update-${{ steps.cucumber.outputs.cucumber_version }} + delete-branch: true + title: Cucumber update ${{ steps.cucumber.outputs.cucumber_version }} + body: ${{ steps.changelog.outputs.changelog }} + base: 'master' diff --git a/tests/integration/vendor/behat/gherkin/.gitignore b/tests/integration/vendor/behat/gherkin/.gitignore deleted file mode 100644 index 33b7c6582..000000000 --- a/tests/integration/vendor/behat/gherkin/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.tgz -vendor -composer.phar -composer.lock diff --git a/tests/integration/vendor/behat/gherkin/.travis.yml b/tests/integration/vendor/behat/gherkin/.travis.yml deleted file mode 100644 index 97b6ce67f..000000000 --- a/tests/integration/vendor/behat/gherkin/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: php - -sudo: false - -cache: - directories: - - $HOME/.composer/cache/files - -php: [5.3, 5.4, 5.5, 5.6, 7.0, hhvm] - -matrix: - include: - - php: 5.6 - env: SYMFONY_VERSION='2.3.*' - - php: 5.6 - env: SYMFONY_VERSION='2.7.*' - - php: 5.6 - env: SYMFONY_VERSION='2.8.*' - - php: 7.0 - env: SYMFONY_VERSION='3.0.*' - -before_install: - - composer self-update - - if [ "$SYMFONY_VERSION" != "" ]; then composer require --no-update symfony/yaml=$SYMFONY_VERSION; fi; - -install: - - composer install - -script: vendor/bin/phpunit -v --coverage-clover=coverage.clover - -after_script: - # don't upload coverage on PHP 7 and HHVM as it cannot be generated. We don't want to tell Scrutinizer that the coverage generation failed. - - if [[ "7.0" != "$TRAVIS_PHP_VERSION" && "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/tests/integration/vendor/behat/gherkin/CHANGES.md b/tests/integration/vendor/behat/gherkin/CHANGES.md index f2c1043c3..4533536ce 100644 --- a/tests/integration/vendor/behat/gherkin/CHANGES.md +++ b/tests/integration/vendor/behat/gherkin/CHANGES.md @@ -1,3 +1,78 @@ +4.9.0 / 2021-10-12 +================== + +* Simplify the boolean condition for the tag matching by @stof in https://github.com/Behat/Gherkin/pull/219 +* Remove symfony phpunit bridge by @ciaranmcnulty in https://github.com/Behat/Gherkin/pull/220 +* Ignore the bin folder in archives by @stof in https://github.com/Behat/Gherkin/pull/226 +* Cast table node exceptions into ParserExceptions when throwing by @ciaranmcnulty in https://github.com/Behat/Gherkin/pull/216 +* Cucumber changelog in PRs and using correct hash by @ciaranmcnulty in https://github.com/Behat/Gherkin/pull/225 +* Support alternative docstrings format (```) by @ciaranmcnulty in https://github.com/Behat/Gherkin/pull/214 +* Fix DocBlocks (Boolean -> bool) by @simonhammes in https://github.com/Behat/Gherkin/pull/237 +* Tag parsing by @ciaranmcnulty in https://github.com/Behat/Gherkin/pull/215 +* Remove test - cucumber added an example with Rule which is not supported by @ciaranmcnulty in https://github.com/Behat/Gherkin/pull/239 +* Add PHP 8.1 support by @javer in https://github.com/Behat/Gherkin/pull/242 +* Fix main branch alias version by @mvorisek in https://github.com/Behat/Gherkin/pull/244 + +4.8.0 / 2021-02-04 +================== + +* Drop support for PHP before version 7.2 + +4.7.3 / 2021-02-04 +================== + +* Refactored comments parsing to avoid Maximum function nesting level errors + +4.7.2 / 2021-02-03 +================== + +* Issue where Scenario Outline title was not populated into Examples +* Updated translations from cucumber 16.0.0 + +4.7.1 / 2021-01-26 +================== + +* Issue parsing comments before scenarios when following an Examples table + +4.7.0 / 2021-01-24 +================== + + * Provides better messages for TableNode construct errors + * Now allows single character steps + * Supports multiple Example Tables with tags + +4.6.2 / 2020-03-17 +================== + + * Fixed issues due to incorrect cache key + +4.6.1 / 2020-02-27 +================== + + * Fix AZ translations + * Correctly filter features, now that the base path is correctly set + +4.6.0 / 2019-01-16 +================== + + * Updated translations (including 'Example' as synonym for 'Scenario' in `en`) + +4.5.1 / 2017-08-30 +================== + + * Fix regression in `PathsFilter` + +4.5.0 / 2017-08-30 +================== + + * Sync i18n with Cucumber Gherkin + * Drop support for HHVM tests on Travis + * Add `TableNode::fromList()` method (thanks @TravisCarden) + * Add `ExampleNode::getOutlineTitle()` method (thanks @duxet) + * Use realpath, so the feature receives the cwd prefixed (thanks @glennunipro) + * Explicitly handle non-two-dimensional arrays in TableNode (thanks @TravisCarden) + * Fix to line/linefilter scenario runs which take relative paths to files (thanks @generalconsensus) + 4.4.5 / 2016-10-30 ================== diff --git a/tests/integration/vendor/behat/gherkin/CONTRIBUTING.md b/tests/integration/vendor/behat/gherkin/CONTRIBUTING.md index 5e8d97b7f..6869f76df 100644 --- a/tests/integration/vendor/behat/gherkin/CONTRIBUTING.md +++ b/tests/integration/vendor/behat/gherkin/CONTRIBUTING.md @@ -23,7 +23,7 @@ Contributing to Gherkin Translations Gherkin supports →40 different languages and you could add more! You might notice `i18n.php` file in the root of the library. This file is downloaded and **autogenerated** -from original [cucumber/gherkin translations](https://github.com/cucumber/gherkin/blob/master/lib/gherkin/i18n.json). +from original [cucumber/gherkin translations](https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json). So, in order to fix/update/add some translation, you should send Pull Request to the `cucumber/gherkin` repository. `Behat\Gherkin` will redownload/regenerate translations from there before each release. diff --git a/tests/integration/vendor/behat/gherkin/README.md b/tests/integration/vendor/behat/gherkin/README.md index e7034df92..0e263845a 100644 --- a/tests/integration/vendor/behat/gherkin/README.md +++ b/tests/integration/vendor/behat/gherkin/README.md @@ -4,10 +4,6 @@ Behat Gherkin Parser This is the php Gherkin parser for Behat. It comes bundled with more than 40 native languages (see `i18n.php`) support & clean architecture. -[![Build Status](https://travis-ci.org/Behat/Gherkin.svg?branch=master)](https://travis-ci.org/Behat/Gherkin) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Behat/Gherkin/badges/quality-score.png?s=04d9d0237c89f4c45a94ba5304234db861dfd036)](https://scrutinizer-ci.com/g/Behat/Gherkin/) -[![Code Coverage](https://scrutinizer-ci.com/g/Behat/Gherkin/badges/coverage.png?s=204ca44800469d295b73d18c91011cb9d2dc99fc)](https://scrutinizer-ci.com/g/Behat/Gherkin/) - Useful Links ------------ diff --git a/tests/integration/vendor/behat/gherkin/bin/update_i18n b/tests/integration/vendor/behat/gherkin/bin/update_i18n deleted file mode 100755 index cef40686a..000000000 --- a/tests/integration/vendor/behat/gherkin/bin/update_i18n +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env php - $keywords) { - $langMessages = array(); - - foreach ($keywords as $type => $words) { - if (!is_array($words)) { - $words = array($words); - } - - if ('scenarioOutline' === $type) { - $type = 'scenario_outline'; - } - - if (in_array($type, array('given', 'when', 'then', 'and', 'but'))) { - $formattedKeywords = array(); - - foreach ($words as $word) { - $formattedWord = trim($word); - - if ($formattedWord === $word) { - $formattedWord = $formattedWord.'<'; // Convert the keywords to the syntax used by Gherkin 2, which is expected by our Lexer. - } - - $formattedKeywords[] = $formattedWord; - } - - $words = $formattedKeywords; - } - - usort($words, function($type1, $type2) { - return mb_strlen($type2, 'utf8') - mb_strlen($type1, 'utf8'); - }); - - $langMessages[$type] = implode('|', $words); - } - - // ensure that the order of keys is consistent between updates - ksort($langMessages); - - $array[$lang] = $langMessages; -} - -// ensure that the languages are sorted to avoid useless diffs between updates. We keep the English first though as it is the reference. -$enData = $array['en']; -unset($array['en']); -ksort($array); -$array = array_merge(array('en' => $enData), $array); -$arrayString = var_export($array, true); - -file_put_contents(__DIR__.'/../i18n.php', <<=5.3.1" + "php": "~7.2|~8.0" }, "require-dev": { - "symfony/yaml": "~2.3|~3", - "symfony/phpunit-bridge": "~2.7|~3", - "phpunit/phpunit": "~4.5|~5" + "symfony/yaml": "~3|~4|~5", + "phpunit/phpunit": "~8|~9", + "cucumber/cucumber": "dev-gherkin-22.0.0" }, "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" + "symfony/yaml": "If you want to parse features, represented in YAML files" }, "autoload": { "psr-0": { - "Behat\\Gherkin": "src/" + "Behat\\Gherkin": "src/" } }, @@ -41,7 +41,27 @@ "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.x-dev" } - } + }, + + "repositories": [ + { + "type": "package", + "package": { + "name": "cucumber/cucumber", + "version": "dev-gherkin-22.0.0", + "source": { + "type": "git", + "url": "https://github.com/cucumber/cucumber.git", + "reference": "379280b6b98ee9a1bf79444e7c75e23265ccb3e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cucumber/cucumber/zipball/379280b6b98ee9a1bf79444e7c75e23265ccb3e0", + "reference": "379280b6b98ee9a1bf79444e7c75e23265ccb3e0" + } + } + } + ] } diff --git a/tests/integration/vendor/behat/gherkin/i18n.php b/tests/integration/vendor/behat/gherkin/i18n.php index ea71bad59..7f7c2e135 100644 --- a/tests/integration/vendor/behat/gherkin/i18n.php +++ b/tests/integration/vendor/behat/gherkin/i18n.php @@ -5,7 +5,7 @@ * * This file is automatically generated by `bin/update_i18n`. * Actual Gherkin translations live in cucumber/gherkin repo: - * https://raw.github.com/cucumber/gherkin/master/gherkin-languages.json + * https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json * Please send your translation changes there. */ @@ -16,11 +16,12 @@ 'background' => 'Background', 'but' => 'But|*', 'examples' => 'Scenarios|Examples', - 'feature' => 'Business Need|Feature|Ability', + 'feature' => 'Business Need|Ability|Feature', 'given' => 'Given|*', 'name' => 'English', 'native' => 'English', - 'scenario' => 'Scenario', + 'rule' => 'Rule', + 'scenario' => 'Scenario|Example', 'scenario_outline' => 'Scenario Template|Scenario Outline', 'then' => 'Then|*', 'when' => 'When|*', @@ -35,7 +36,8 @@ 'given' => 'Gegewe|*', 'name' => 'Afrikaans', 'native' => 'Afrikaans', - 'scenario' => 'Situasie', + 'rule' => 'Regel', + 'scenario' => 'Voorbeeld|Situasie', 'scenario_outline' => 'Situasie Uiteensetting', 'then' => 'Dan|*', 'when' => 'Wanneer|*', @@ -50,10 +52,27 @@ 'given' => 'Դիցուք|*', 'name' => 'Armenian', 'native' => 'հայերեն', - 'scenario' => 'Սցենար', + 'rule' => 'Rule', + 'scenario' => 'Սցենար|Օրինակ', 'scenario_outline' => 'Սցենարի կառուցվացքը', 'then' => 'Ապա|*', - 'when' => 'Երբ|Եթե|*', + 'when' => 'Եթե|Երբ|*', + ), + 'an' => + array ( + 'and' => '*|E|Y', + 'background' => 'Antecedents', + 'but' => 'Pero|*', + 'examples' => 'Eixemplos', + 'feature' => 'Caracteristica', + 'given' => 'Dadas|Dada|Daus|Dau|*', + 'name' => 'Aragonese', + 'native' => 'Aragonés', + 'rule' => 'Rule', + 'scenario' => 'Eixemplo|Caso', + 'scenario_outline' => 'Esquema del caso', + 'then' => 'Antonces|Alavez|Allora|*', + 'when' => 'Cuan|*', ), 'ar' => array ( @@ -65,7 +84,8 @@ 'given' => 'بفرض|*', 'name' => 'Arabic', 'native' => 'العربية', - 'scenario' => 'سيناريو', + 'rule' => 'Rule', + 'scenario' => 'سيناريو|مثال', 'scenario_outline' => 'سيناريو مخطط', 'then' => 'اذاً|ثم|*', 'when' => 'عندما|متى|*', @@ -77,10 +97,11 @@ 'but' => 'Peru|*', 'examples' => 'Exemplos', 'feature' => 'Carauterística', - 'given' => 'Dada|Daos|Daes|Dáu|*', + 'given' => 'Dada|Daes|Daos|Dáu|*', 'name' => 'Asturian', 'native' => 'asturianu', - 'scenario' => 'Casu', + 'rule' => 'Rule', + 'scenario' => 'Exemplo|Casu', 'scenario_outline' => 'Esbozu del casu', 'then' => 'Entós|*', 'when' => 'Cuando|*', @@ -95,7 +116,8 @@ 'given' => 'Tutaq ki|Verilir|*', 'name' => 'Azerbaijani', 'native' => 'Azərbaycanca', - 'scenario' => 'Ssenari', + 'rule' => 'Rule', + 'scenario' => 'Ssenari|Nümunə', 'scenario_outline' => 'Ssenarinin strukturu', 'then' => 'O halda|*', 'when' => 'Nə vaxt ki|Əgər|*', @@ -110,7 +132,8 @@ 'given' => 'Дадено|*', 'name' => 'Bulgarian', 'native' => 'български', - 'scenario' => 'Сценарий', + 'rule' => 'Rule', + 'scenario' => 'Сценарий|Пример', 'scenario_outline' => 'Рамка на сценарий', 'then' => 'То|*', 'when' => 'Когато|*', @@ -125,14 +148,15 @@ 'given' => 'Diberi|Bagi|*', 'name' => 'Malay', 'native' => 'Bahasa Melayu', - 'scenario' => 'Senario|Situasi|Keadaan', - 'scenario_outline' => 'Garis Panduan Senario|Kerangka Senario|Kerangka Situasi|Kerangka Keadaan', + 'rule' => 'Rule', + 'scenario' => 'Keadaan|Senario|Situasi', + 'scenario_outline' => 'Garis Panduan Senario|Kerangka Keadaan|Kerangka Senario|Kerangka Situasi', 'then' => 'Kemudian|Maka|*', 'when' => 'Apabila|*', ), 'bs' => array ( - 'and' => '*|I|A', + 'and' => '*|A|I', 'background' => 'Pozadina', 'but' => 'Ali|*', 'examples' => 'Primjeri', @@ -140,7 +164,8 @@ 'given' => 'Dato|*', 'name' => 'Bosnian', 'native' => 'Bosanski', - 'scenario' => 'Scenariju|Scenario', + 'rule' => 'Rule', + 'scenario' => 'Scenariju|Scenario|Primjer', 'scenario_outline' => 'Scenario-outline|Scenariju-obris', 'then' => 'Zatim|*', 'when' => 'Kada|*', @@ -152,10 +177,11 @@ 'but' => 'Però|*', 'examples' => 'Exemples', 'feature' => 'Característica|Funcionalitat', - 'given' => 'Donada|Donat|Atesa|Atès|*', + 'given' => 'Donada|Atesa|Donat|Atès|*', 'name' => 'Catalan', 'native' => 'català', - 'scenario' => 'Escenari', + 'rule' => 'Rule', + 'scenario' => 'Escenari|Exemple', 'scenario_outline' => 'Esquema de l\'escenari', 'then' => 'Aleshores|Cal|*', 'when' => 'Quan|*', @@ -170,7 +196,8 @@ 'given' => 'Za předpokladu|Pokud|*', 'name' => 'Czech', 'native' => 'Česky', - 'scenario' => 'Scénář', + 'rule' => 'Pravidlo', + 'scenario' => 'Příklad|Scénář', 'scenario_outline' => 'Osnova scénáře|Náčrt Scénáře', 'then' => 'Pak|*', 'when' => 'Když|*', @@ -185,7 +212,8 @@ 'given' => 'Anrhegedig a|*', 'name' => 'Welsh', 'native' => 'Cymraeg', - 'scenario' => 'Scenario', + 'rule' => 'Rule', + 'scenario' => 'Enghraifft|Scenario', 'scenario_outline' => 'Scenario Amlinellol', 'then' => 'Yna|*', 'when' => 'Pryd|*', @@ -200,7 +228,8 @@ 'given' => 'Givet|*', 'name' => 'Danish', 'native' => 'dansk', - 'scenario' => 'Scenarie', + 'rule' => 'Rule', + 'scenario' => 'Eksempel|Scenarie', 'scenario_outline' => 'Abstrakt Scenario', 'then' => 'Så|*', 'when' => 'Når|*', @@ -208,15 +237,16 @@ 'de' => array ( 'and' => 'Und|*', - 'background' => 'Grundlage', + 'background' => 'Voraussetzungen|Vorbedingungen|Hintergrund|Grundlage', 'but' => 'Aber|*', 'examples' => 'Beispiele', - 'feature' => 'Funktionalität', + 'feature' => 'Funktionalität|Funktion', 'given' => 'Gegeben seien|Gegeben sei|Angenommen|*', 'name' => 'German', 'native' => 'Deutsch', - 'scenario' => 'Szenario', - 'scenario_outline' => 'Szenariogrundriss', + 'rule' => 'Regel|Rule', + 'scenario' => 'Beispiel|Szenario', + 'scenario_outline' => 'Szenariogrundriss|Szenarien', 'then' => 'Dann|*', 'when' => 'Wenn|*', ), @@ -230,8 +260,9 @@ 'given' => 'Δεδομένου|*', 'name' => 'Greek', 'native' => 'Ελληνικά', - 'scenario' => 'Σενάριο', - 'scenario_outline' => 'Περιγραφή Σεναρίου', + 'rule' => 'Rule', + 'scenario' => 'Παράδειγμα|Σενάριο', + 'scenario_outline' => 'Περίγραμμα Σεναρίου|Περιγραφή Σεναρίου', 'then' => 'Τότε|*', 'when' => 'Όταν|*', ), @@ -245,7 +276,8 @@ 'given' => '😐<|*', 'name' => 'Emoji', 'native' => '😀', - 'scenario' => '📕', + 'rule' => 'Rule', + 'scenario' => '📕|🥒', 'scenario_outline' => '📖', 'then' => '🙏<|*', 'when' => '🎬<|*', @@ -260,6 +292,7 @@ 'given' => 'Youse know when youse got|Givun|*', 'name' => 'Scouse', 'native' => 'Scouse', + 'rule' => 'Rule', 'scenario' => 'The thing of it is', 'scenario_outline' => 'Wharrimean is', 'then' => 'Den youse gotta|Dun|*', @@ -275,6 +308,7 @@ 'given' => 'Y\'know|*', 'name' => 'Australian', 'native' => 'Australian', + 'rule' => 'Rule', 'scenario' => 'Awww, look mate', 'scenario_outline' => 'Reckon it\'s like', 'then' => 'But at the end of the day I reckon|*', @@ -290,6 +324,7 @@ 'given' => 'I CAN HAZ|*', 'name' => 'LOLCAT', 'native' => 'LOLCAT', + 'rule' => 'Rule', 'scenario' => 'MISHUN', 'scenario_outline' => 'MISHUN SRSLY', 'then' => 'DEN|*', @@ -300,15 +335,16 @@ 'and' => 'Ond|*|7', 'background' => 'Aer|Ær', 'but' => 'Ac|*', - 'examples' => 'Se the|Se þe|Se ðe', + 'examples' => 'Se the|Se ðe|Se þe', 'feature' => 'Hwaet|Hwæt', - 'given' => 'Thurh|Þurh|Ðurh|*', + 'given' => 'Thurh|Ðurh|Þurh|*', 'name' => 'Old English', 'native' => 'Englisc', + 'rule' => 'Rule', 'scenario' => 'Swa', 'scenario_outline' => 'Swa hwaer swa|Swa hwær swa', - 'then' => 'Tha the|Þa þe|Ða ðe|Tha|Þa|Ða|*', - 'when' => 'Tha|Þa|Ða|*', + 'then' => 'Tha the|Ða ðe|Þa þe|Tha|Ða|Þa|*', + 'when' => 'Tha|Ða|Þa|*', ), 'en-pirate' => array ( @@ -320,11 +356,28 @@ 'given' => 'Gangway!|*', 'name' => 'Pirate', 'native' => 'Pirate', + 'rule' => 'Rule', 'scenario' => 'Heave to', 'scenario_outline' => 'Shiver me timbers', 'then' => 'Let go and haul|*', 'when' => 'Blimey!|*', ), + 'en-tx' => + array ( + 'and' => 'Come hell or high water', + 'background' => 'Lemme tell y\'all a story', + 'but' => 'Well now hold on, I\'ll you what', + 'examples' => 'Now that\'s a story longer than a cattle drive in July', + 'feature' => 'This ain’t my first rodeo|All gussied up', + 'given' => 'All git out|Fixin\' to', + 'name' => 'Texas', + 'native' => 'Texas', + 'rule' => 'Rule ', + 'scenario' => 'All hat and no cattle', + 'scenario_outline' => 'Busy as a hound in flea season|Serious as a snake bite', + 'then' => 'There’s no tree but bears some fruit', + 'when' => 'Quick out of the chute', + ), 'eo' => array ( 'and' => 'Kaj|*', @@ -335,22 +388,24 @@ 'given' => 'Donitaĵo|Komence|*', 'name' => 'Esperanto', 'native' => 'Esperanto', - 'scenario' => 'Scenaro|Kazo', + 'rule' => 'Rule', + 'scenario' => 'Ekzemplo|Scenaro|Kazo', 'scenario_outline' => 'Konturo de la scenaro|Kazo-skizo|Skizo', 'then' => 'Do|*', 'when' => 'Se|*', ), 'es' => array ( - 'and' => '*|Y|E', + 'and' => '*|E|Y', 'background' => 'Antecedentes', 'but' => 'Pero|*', 'examples' => 'Ejemplos', - 'feature' => 'Característica', - 'given' => 'Dados|Dadas|Dada|Dado|*', + 'feature' => 'Necesidad del negocio|Característica|Requisito', + 'given' => 'Dadas|Dados|Dada|Dado|*', 'name' => 'Spanish', 'native' => 'español', - 'scenario' => 'Escenario', + 'rule' => 'Regla de negocio|Regla', + 'scenario' => 'Escenario|Ejemplo', 'scenario_outline' => 'Esquema del escenario', 'then' => 'Entonces|*', 'when' => 'Cuando|*', @@ -365,8 +420,9 @@ 'given' => 'Eeldades|*', 'name' => 'Estonian', 'native' => 'eesti keel', - 'scenario' => 'Stsenaarium', - 'scenario_outline' => 'Raamstsenaarium', + 'rule' => 'Reegel', + 'scenario' => 'Stsenaarium|Juhtum', + 'scenario_outline' => 'Raamstsenaarium|Raamjuhtum', 'then' => 'Siis|*', 'when' => 'Kui|*', ), @@ -380,7 +436,8 @@ 'given' => 'با فرض|*', 'name' => 'Persian', 'native' => 'فارسی', - 'scenario' => 'سناریو', + 'rule' => 'Rule', + 'scenario' => 'سناریو|مثال', 'scenario_outline' => 'الگوی سناریو', 'then' => 'آنگاه|*', 'when' => 'هنگامی|*', @@ -395,6 +452,7 @@ 'given' => 'Oletetaan|*', 'name' => 'Finnish', 'native' => 'suomi', + 'rule' => 'Rule', 'scenario' => 'Tapaus', 'scenario_outline' => 'Tapausaihio', 'then' => 'Niin|*', @@ -407,12 +465,13 @@ 'but' => 'Mais qu\'<|Mais que|Mais|*', 'examples' => 'Exemples', 'feature' => 'Fonctionnalité', - 'given' => 'Etant donné qu\'<|Étant donné qu\'<|Etant donné que|Étant donné que|Etant données|Étant données|Etant donnée|Etant donnés|Étant donnée|Étant donnés|Etant donné|Étant donné|Soit|*', + 'given' => 'Etant donné qu\'<|Étant donné qu\'<|Etant donné que|Étant donné que|Etant données|Étant données|Etant donnée|Etant donnés|Sachant qu\'<|Étant donnée|Étant donnés|Etant donné|Sachant que|Étant donné|Sachant|Soit|*', 'name' => 'French', 'native' => 'français', - 'scenario' => 'Scénario', - 'scenario_outline' => 'Plan du scénario|Plan du Scénario', - 'then' => 'Alors|*', + 'rule' => 'Règle', + 'scenario' => 'Scénario|Exemple', + 'scenario_outline' => 'Plan du Scénario|Plan du scénario', + 'then' => 'Alors|Donc|*', 'when' => 'Lorsqu\'<|Lorsque|Quand|*', ), 'ga' => @@ -425,7 +484,8 @@ 'given' => 'Cuir i gcás nach<|Cuir i gcás gur<|Cuir i gcás nár<|Cuir i gcás go<|*', 'name' => 'Irish', 'native' => 'Gaeilge', - 'scenario' => 'Cás', + 'rule' => 'Rule', + 'scenario' => 'Sampla|Cás', 'scenario_outline' => 'Cás Achomair', 'then' => 'Ansin<|*', 'when' => 'Nuair nach<|Nuair nár<|Nuair ba<|Nuair a<|*', @@ -440,7 +500,8 @@ 'given' => 'આપેલ છે|*', 'name' => 'Gujarati', 'native' => 'ગુજરાતી', - 'scenario' => 'સ્થિતિ', + 'rule' => 'Rule', + 'scenario' => 'ઉદાહરણ|સ્થિતિ', 'scenario_outline' => 'પરિદ્દશ્ય રૂપરેખા|પરિદ્દશ્ય ઢાંચો', 'then' => 'પછી|*', 'when' => 'ક્યારે|*', @@ -449,13 +510,14 @@ array ( 'and' => '*|E', 'background' => 'Contexto', - 'but' => 'Pero|Mais|*', + 'but' => 'Mais|Pero|*', 'examples' => 'Exemplos', 'feature' => 'Característica', - 'given' => 'Dados|Dadas|Dada|Dado|*', + 'given' => 'Dadas|Dados|Dada|Dado|*', 'name' => 'Galician', 'native' => 'galego', - 'scenario' => 'Escenario', + 'rule' => 'Rule', + 'scenario' => 'Escenario|Exemplo', 'scenario_outline' => 'Esbozo do escenario', 'then' => 'Entón|Logo|*', 'when' => 'Cando|*', @@ -470,7 +532,8 @@ 'given' => 'בהינתן|*', 'name' => 'Hebrew', 'native' => 'עברית', - 'scenario' => 'תרחיש', + 'rule' => 'כלל', + 'scenario' => 'דוגמא|תרחיש', 'scenario_outline' => 'תבנית תרחיש', 'then' => 'אזי|אז|*', 'when' => 'כאשר|*', @@ -479,12 +542,13 @@ array ( 'and' => 'तथा|और|*', 'background' => 'पृष्ठभूमि', - 'but' => 'परन्तु|किन्तु|पर|*', + 'but' => 'किन्तु|परन्तु|पर|*', 'examples' => 'उदाहरण', 'feature' => 'रूप लेख', - 'given' => 'चूंकि|यदि|अगर|*', + 'given' => 'चूंकि|अगर|यदि|*', 'name' => 'Hindi', 'native' => 'हिंदी', + 'rule' => 'नियम', 'scenario' => 'परिदृश्य', 'scenario_outline' => 'परिदृश्य रूपरेखा', 'then' => 'तदा|तब|*', @@ -497,10 +561,11 @@ 'but' => 'Ali|*', 'examples' => 'Scenariji|Primjeri', 'feature' => 'Mogucnost|Mogućnost|Osobina', - 'given' => 'Zadani|Zadano|Zadan|*', + 'given' => 'Ukoliko|Zadani|Zadano|Zadan|*', 'name' => 'Croatian', 'native' => 'hrvatski', - 'scenario' => 'Scenarij', + 'rule' => 'Rule', + 'scenario' => 'Scenarij|Primjer', 'scenario_outline' => 'Koncept|Skica', 'then' => 'Onda|*', 'when' => 'Kada|Kad|*', @@ -508,15 +573,16 @@ 'ht' => array ( 'and' => 'Epi|Ak|*|E', - 'background' => 'Kontèks|Istorik', + 'background' => 'Istorik|Kontèks', 'but' => 'Men|*', 'examples' => 'Egzanp', - 'feature' => 'Karakteristik|Fonksyonalite|Mak', - 'given' => 'Sipoze ke|Sipoze Ke|Sipoze|*', + 'feature' => 'Fonksyonalite|Karakteristik|Mak', + 'given' => 'Sipoze Ke|Sipoze ke|Sipoze|*', 'name' => 'Creole', 'native' => 'kreyòl', + 'rule' => 'Rule', 'scenario' => 'Senaryo', - 'scenario_outline' => 'Senaryo deskripsyon|Senaryo Deskripsyon|Dyagram senaryo|Dyagram Senaryo|Plan senaryo|Plan Senaryo', + 'scenario_outline' => 'Senaryo Deskripsyon|Senaryo deskripsyon|Dyagram Senaryo|Dyagram senaryo|Plan Senaryo|Plan senaryo', 'then' => 'Le sa a|Lè sa a|*', 'when' => 'Le|Lè|*', ), @@ -530,7 +596,8 @@ 'given' => 'Amennyiben|Adott|*', 'name' => 'Hungarian', 'native' => 'magyar', - 'scenario' => 'Forgatókönyv', + 'rule' => 'Szabály', + 'scenario' => 'Forgatókönyv|Példa', 'scenario_outline' => 'Forgatókönyv vázlat', 'then' => 'Akkor|*', 'when' => 'Amikor|Majd|Ha|*', @@ -538,16 +605,17 @@ 'id' => array ( 'and' => 'Dan|*', - 'background' => 'Dasar', - 'but' => 'Tapi|*', - 'examples' => 'Contoh', + 'background' => 'Latar Belakang|Dasar', + 'but' => 'Tetapi|Tapi|*', + 'examples' => 'Contoh|Misal', 'feature' => 'Fitur', - 'given' => 'Dengan|*', + 'given' => 'Diasumsikan|Diketahui|Dengan|Bila|Jika|*', 'name' => 'Indonesian', 'native' => 'Bahasa Indonesia', + 'rule' => 'Aturan|Rule', 'scenario' => 'Skenario', - 'scenario_outline' => 'Skenario konsep', - 'then' => 'Maka|*', + 'scenario_outline' => 'Garis-Besar Skenario|Skenario konsep', + 'then' => 'Kemudian|Maka|*', 'when' => 'Ketika|*', ), 'is' => @@ -560,6 +628,7 @@ 'given' => 'Ef|*', 'name' => 'Icelandic', 'native' => 'Íslenska', + 'rule' => 'Rule', 'scenario' => 'Atburðarás', 'scenario_outline' => 'Lýsing Atburðarásar|Lýsing Dæma', 'then' => 'Þá|*', @@ -571,11 +640,12 @@ 'background' => 'Contesto', 'but' => 'Ma|*', 'examples' => 'Esempi', - 'feature' => 'Funzionalità', - 'given' => 'Data|Dato|Dati|Date|*', + 'feature' => 'Esigenza di Business|Funzionalità|Abilità', + 'given' => 'Data|Date|Dati|Dato|*', 'name' => 'Italian', 'native' => 'italiano', - 'scenario' => 'Scenario', + 'rule' => 'Regola', + 'scenario' => 'Scenario|Esempio', 'scenario_outline' => 'Schema dello scenario', 'then' => 'Allora|*', 'when' => 'Quando|*', @@ -590,6 +660,7 @@ 'given' => '前提<|*', 'name' => 'Japanese', 'native' => '日本語', + 'rule' => 'Rule', 'scenario' => 'シナリオ', 'scenario_outline' => 'シナリオアウトライン|シナリオテンプレート|シナリオテンプレ|テンプレ', 'then' => 'ならば<|*', @@ -605,10 +676,27 @@ 'given' => 'Nalikaning|Nalika|*', 'name' => 'Javanese', 'native' => 'Basa Jawa', + 'rule' => 'Rule', 'scenario' => 'Skenario', 'scenario_outline' => 'Konsep skenario', 'then' => 'Banjur|Njuk|*', - 'when' => 'Menawa|Manawa|*', + 'when' => 'Manawa|Menawa|*', + ), + 'ka' => + array ( + 'and' => 'და<|*', + 'background' => 'კონტექსტი', + 'but' => 'მაგ­რამ<|*', + 'examples' => 'მაგალითები', + 'feature' => 'თვისება', + 'given' => 'მოცემული<|*', + 'name' => 'Georgian', + 'native' => 'ქართველი', + 'rule' => 'Rule', + 'scenario' => 'მაგალითად|სცენარის', + 'scenario_outline' => 'სცენარის ნიმუში', + 'then' => 'მაშინ<|*', + 'when' => 'როდესაც<|*', ), 'kn' => array ( @@ -620,7 +708,8 @@ 'given' => 'ನೀಡಿದ|*', 'name' => 'Kannada', 'native' => 'ಕನ್ನಡ', - 'scenario' => 'ಕಥಾಸಾರಾಂಶ', + 'rule' => 'Rule', + 'scenario' => 'ಕಥಾಸಾರಾಂಶ|ಉದಾಹರಣೆ', 'scenario_outline' => 'ವಿವರಣೆ', 'then' => 'ನಂತರ|*', 'when' => 'ಸ್ಥಿತಿಯನ್ನು|*', @@ -635,6 +724,7 @@ 'given' => '먼저<|조건<|*', 'name' => 'Korean', 'native' => '한국어', + 'rule' => 'Rule', 'scenario' => '시나리오', 'scenario_outline' => '시나리오 개요', 'then' => '그러면<|*', @@ -650,7 +740,8 @@ 'given' => 'Duota|*', 'name' => 'Lithuanian', 'native' => 'lietuvių kalba', - 'scenario' => 'Scenarijus', + 'rule' => 'Rule', + 'scenario' => 'Scenarijus|Pavyzdys', 'scenario_outline' => 'Scenarijaus šablonas', 'then' => 'Tada|*', 'when' => 'Kai|*', @@ -665,7 +756,8 @@ 'given' => 'ugeholl|*', 'name' => 'Luxemburgish', 'native' => 'Lëtzebuergesch', - 'scenario' => 'Szenario', + 'rule' => 'Rule', + 'scenario' => 'Beispill|Szenario', 'scenario_outline' => 'Plang vum Szenario', 'then' => 'dann|*', 'when' => 'wann|*', @@ -675,16 +767,49 @@ 'and' => 'Un|*', 'background' => 'Konteksts|Situācija', 'but' => 'Bet|*', - 'examples' => 'Piemēri|Paraugs', + 'examples' => 'Paraugs|Piemēri', 'feature' => 'Funkcionalitāte|Fīča', 'given' => 'Kad|*', 'name' => 'Latvian', 'native' => 'latviešu', - 'scenario' => 'Scenārijs', + 'rule' => 'Rule', + 'scenario' => 'Scenārijs|Piemērs', 'scenario_outline' => 'Scenārijs pēc parauga', 'then' => 'Tad|*', 'when' => 'Ja|*', ), + 'mk-Cyrl' => + array ( + 'and' => '*|И', + 'background' => 'Контекст|Содржина', + 'but' => 'Но|*', + 'examples' => 'Сценарија|Примери', + 'feature' => 'Бизнис потреба|Функционалност|Можност', + 'given' => 'Дадена|Дадено|*', + 'name' => 'Macedonian', + 'native' => 'Македонски', + 'rule' => 'Rule', + 'scenario' => 'На пример|Сценарио|Пример', + 'scenario_outline' => 'Преглед на сценарија|Концепт|Скица', + 'then' => 'Тогаш|*', + 'when' => 'Кога|*', + ), + 'mk-Latn' => + array ( + 'and' => '*|I', + 'background' => 'Sodrzhina|Kontekst', + 'but' => 'No|*', + 'examples' => 'Scenaria|Primeri', + 'feature' => 'Biznis potreba|Funkcionalnost|Mozhnost', + 'given' => 'Dadena|Dadeno|*', + 'name' => 'Macedonian (Latin)', + 'native' => 'Makedonski (Latinica)', + 'rule' => 'Rule', + 'scenario' => 'Na primer|Scenario', + 'scenario_outline' => 'Pregled na scenarija|Koncept|Skica', + 'then' => 'Togash|*', + 'when' => 'Koga|*', + ), 'mn' => array ( 'and' => 'Тэгээд|Мөн|*', @@ -695,11 +820,44 @@ 'given' => 'Өгөгдсөн нь|Анх|*', 'name' => 'Mongolian', 'native' => 'монгол', + 'rule' => 'Rule', 'scenario' => 'Сценар', 'scenario_outline' => 'Сценарын төлөвлөгөө', 'then' => 'Үүний дараа|Тэгэхэд|*', 'when' => 'Хэрэв|*', ), + 'mr' => + array ( + 'and' => 'तसेच|आणि|*', + 'background' => 'पार्श्वभूमी', + 'but' => 'परंतु|पण|*', + 'examples' => 'उदाहरण', + 'feature' => 'वैशिष्ट्य|सुविधा', + 'given' => 'दिलेल्या प्रमाणे|जर<|*', + 'name' => 'Marathi', + 'native' => 'मराठी', + 'rule' => 'नियम', + 'scenario' => 'परिदृश्य', + 'scenario_outline' => 'परिदृश्य रूपरेखा', + 'then' => 'तेव्हा|मग|*', + 'when' => 'जेव्हा|*', + ), + 'ne' => + array ( + 'and' => 'अनी|*|र', + 'background' => 'पृष्ठभूमी', + 'but' => 'तर|*', + 'examples' => 'उदाहरणहरु|उदाहरण', + 'feature' => 'विशेषता|सुविधा', + 'given' => 'दिइएको|दिएको|यदि|*', + 'name' => 'Nepali', + 'native' => 'नेपाली', + 'rule' => 'नियम', + 'scenario' => 'परिदृश्य', + 'scenario_outline' => 'परिदृश्य रूपरेखा', + 'then' => 'त्यसपछि|अनी|*', + 'when' => 'जब|*', + ), 'nl' => array ( 'and' => 'En|*', @@ -710,10 +868,11 @@ 'given' => 'Gegeven|Stel|*', 'name' => 'Dutch', 'native' => 'Nederlands', - 'scenario' => 'Scenario', + 'rule' => 'Rule', + 'scenario' => 'Voorbeeld|Scenario', 'scenario_outline' => 'Abstract Scenario', 'then' => 'Dan|*', - 'when' => 'Als|*', + 'when' => 'Wanneer|Als|*', ), 'no' => array ( @@ -725,7 +884,8 @@ 'given' => 'Gitt|*', 'name' => 'Norwegian', 'native' => 'norsk', - 'scenario' => 'Scenario', + 'rule' => 'Regel', + 'scenario' => 'Eksempel|Scenario', 'scenario_outline' => 'Abstrakt Scenario|Scenariomal', 'then' => 'Så|*', 'when' => 'Når|*', @@ -740,7 +900,8 @@ 'given' => 'ਜਿਵੇਂ ਕਿ|ਜੇਕਰ|*', 'name' => 'Panjabi', 'native' => 'ਪੰਜਾਬੀ', - 'scenario' => 'ਪਟਕਥਾ', + 'rule' => 'Rule', + 'scenario' => 'ਉਦਾਹਰਨ|ਪਟਕਥਾ', 'scenario_outline' => 'ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ|ਪਟਕਥਾ ਢਾਂਚਾ', 'then' => 'ਤਦ|*', 'when' => 'ਜਦੋਂ|*', @@ -755,7 +916,8 @@ 'given' => 'Zakładając, że|Zakładając|Mając|*', 'name' => 'Polish', 'native' => 'polski', - 'scenario' => 'Scenariusz', + 'rule' => 'Reguła|Zasada', + 'scenario' => 'Scenariusz|Przykład', 'scenario_outline' => 'Szablon scenariusza', 'then' => 'Wtedy|*', 'when' => 'Jeżeli|Jeśli|Kiedy|Gdy|*', @@ -765,44 +927,47 @@ 'and' => '*|E', 'background' => 'Cenario de Fundo|Cenário de Fundo|Contexto|Fundo', 'but' => 'Mas|*', - 'examples' => 'Exemplos|Cenários|Cenarios', - 'feature' => 'Funcionalidade|Característica|Caracteristica', - 'given' => 'Dados|Dadas|Dada|Dado|*', + 'examples' => 'Cenarios|Cenários|Exemplos', + 'feature' => 'Caracteristica|Característica|Funcionalidade', + 'given' => 'Dadas|Dados|Dada|Dado|*', 'name' => 'Portuguese', 'native' => 'português', - 'scenario' => 'Cenário|Cenario', - 'scenario_outline' => 'Delineação do Cenário|Delineacao do Cenario|Esquema do Cenário|Esquema do Cenario', + 'rule' => 'Regra', + 'scenario' => 'Cenario|Cenário|Exemplo', + 'scenario_outline' => 'Delineacao do Cenario|Delineação do Cenário|Esquema do Cenario|Esquema do Cenário', 'then' => 'Entao|Então|*', 'when' => 'Quando|*', ), 'ro' => array ( - 'and' => 'Și|Si|Şi|*', + 'and' => 'Si|Şi|Și|*', 'background' => 'Context', 'but' => 'Dar|*', 'examples' => 'Exemple', - 'feature' => 'Functionalitate|Funcționalitate|Funcţionalitate', - 'given' => 'Date fiind|Dati fiind|Dați fiind|Daţi fiind|Dat fiind|*', + 'feature' => 'Functionalitate|Funcţionalitate|Funcționalitate', + 'given' => 'Dată fiind<|Date fiind|Dati fiind|Daţi fiind|Dați fiind|Dat fiind|*', 'name' => 'Romanian', 'native' => 'română', - 'scenario' => 'Scenariu', + 'rule' => 'Rule', + 'scenario' => 'Scenariu|Exemplu', 'scenario_outline' => 'Structura scenariu|Structură scenariu', 'then' => 'Atunci|*', - 'when' => 'Când|Cand|*', + 'when' => 'Cand|Când|*', ), 'ru' => array ( 'and' => 'К тому же|Также|*|И', 'background' => 'Предыстория|Контекст', - 'but' => 'Но|*|А', + 'but' => 'Иначе|Но|*|А', 'examples' => 'Примеры', 'feature' => 'Функциональность|Функционал|Свойство|Функция', 'given' => 'Допустим|Пусть|Дано|*', 'name' => 'Russian', 'native' => 'русский', - 'scenario' => 'Сценарий', - 'scenario_outline' => 'Структура сценария', - 'then' => 'Тогда|То|*', + 'rule' => 'Правило', + 'scenario' => 'Сценарий|Пример', + 'scenario_outline' => 'Структура сценария|Шаблон сценария', + 'then' => 'Затем|Тогда|То|*', 'when' => 'Когда|Если|*', ), 'sk' => @@ -815,8 +980,9 @@ 'given' => 'Za predpokladu|Pokiaľ|*', 'name' => 'Slovak', 'native' => 'Slovensky', - 'scenario' => 'Scenár', - 'scenario_outline' => 'Osnova Scenára|Náčrt Scenáru|Náčrt Scenára', + 'rule' => 'Rule', + 'scenario' => 'Príklad|Scenár', + 'scenario_outline' => 'Osnova Scenára|Náčrt Scenára|Náčrt Scenáru', 'then' => 'Potom|Tak|*', 'when' => 'Keď|Ak|*', ), @@ -826,14 +992,15 @@ 'background' => 'Kontekst|Osnova|Ozadje', 'but' => 'Vendar|Ampak|Toda', 'examples' => 'Scenariji|Primeri', - 'feature' => 'Funkcionalnost|Značilnost|Funkcija|Možnosti|Moznosti|Lastnost', - 'given' => 'Privzeto|Zaradi|Podano|Dano', + 'feature' => 'Funkcionalnost|Značilnost|Funkcija|Lastnost|Moznosti|Možnosti', + 'given' => 'Privzeto|Podano|Zaradi|Dano', 'name' => 'Slovenian', 'native' => 'Slovenski', + 'rule' => 'Rule', 'scenario' => 'Scenarij|Primer', 'scenario_outline' => 'Struktura scenarija|Oris scenarija|Koncept|Osnutek|Skica', 'then' => 'Takrat|Potem|Nato', - 'when' => 'Kadar|Ko|Ce|Če', + 'when' => 'Kadar|Ce|Ko|Če', ), 'sr-Cyrl' => array ( @@ -842,10 +1009,11 @@ 'but' => 'Али|*', 'examples' => 'Сценарији|Примери', 'feature' => 'Функционалност|Могућност|Особина', - 'given' => 'За дате|За дато|За дати|*', + 'given' => 'За дате|За дати|За дато|*', 'name' => 'Serbian', 'native' => 'Српски', - 'scenario' => 'Сценарио|Пример', + 'rule' => 'Правило', + 'scenario' => 'Сценарио|Пример|Пример', 'scenario_outline' => 'Структура сценарија|Концепт|Скица', 'then' => 'Онда|*', 'when' => 'Када|Кад|*', @@ -856,10 +1024,11 @@ 'background' => 'Kontekst|Pozadina|Osnova', 'but' => 'Ali|*', 'examples' => 'Scenariji|Primeri', - 'feature' => 'Funkcionalnost|Mogućnost|Mogucnost|Osobina', - 'given' => 'Za date|Za dato|Za dati|*', + 'feature' => 'Funkcionalnost|Mogucnost|Mogućnost|Osobina', + 'given' => 'Za date|Za dati|Za dato|*', 'name' => 'Serbian (Latin)', 'native' => 'Srpski (Latinica)', + 'rule' => 'Pravilo', 'scenario' => 'Scenario|Primer', 'scenario_outline' => 'Struktura scenarija|Koncept|Skica', 'then' => 'Onda|*', @@ -875,6 +1044,7 @@ 'given' => 'Givet|*', 'name' => 'Swedish', 'native' => 'Svenska', + 'rule' => 'Regel', 'scenario' => 'Scenario', 'scenario_outline' => 'Abstrakt Scenario|Scenariomall', 'then' => 'Så|*', @@ -885,16 +1055,33 @@ 'and' => 'மற்றும்|மேலும்|*', 'background' => 'பின்னணி', 'but' => 'ஆனால்|*', - 'examples' => 'எடுத்துக்காட்டுகள்| நிலைமைகளில்|காட்சிகள்', + 'examples' => 'எடுத்துக்காட்டுகள்|நிலைமைகளில்|காட்சிகள்', 'feature' => 'வணிக தேவை|அம்சம்|திறன்', 'given' => 'கொடுக்கப்பட்ட|*', 'name' => 'Tamil', 'native' => 'தமிழ்', - 'scenario' => 'காட்சி', + 'rule' => 'Rule', + 'scenario' => 'உதாரணமாக|காட்சி', 'scenario_outline' => 'காட்சி வார்ப்புரு|காட்சி சுருக்கம்', 'then' => 'அப்பொழுது|*', 'when' => 'எப்போது|*', ), + 'te' => + array ( + 'and' => 'మరియు|*', + 'background' => 'నేపథ్యం', + 'but' => 'కాని|*', + 'examples' => 'ఉదాహరణలు', + 'feature' => 'గుణము', + 'given' => 'చెప్పబడినది|*', + 'name' => 'Telugu', + 'native' => 'తెలుగు', + 'rule' => 'Rule', + 'scenario' => 'సన్నివేశం|ఉదాహరణ', + 'scenario_outline' => 'కథనం', + 'then' => 'అప్పుడు|*', + 'when' => 'ఈ పరిస్థితిలో|*', + ), 'th' => array ( 'and' => 'และ|*', @@ -905,26 +1092,12 @@ 'given' => 'กำหนดให้|*', 'name' => 'Thai', 'native' => 'ไทย', + 'rule' => 'Rule', 'scenario' => 'เหตุการณ์', 'scenario_outline' => 'โครงสร้างของเหตุการณ์|สรุปเหตุการณ์', 'then' => 'ดังนั้น|*', 'when' => 'เมื่อ|*', ), - 'tl' => - array ( - 'and' => 'మరియు|*', - 'background' => 'నేపథ్యం', - 'but' => 'కాని|*', - 'examples' => 'ఉదాహరణలు', - 'feature' => 'గుణము', - 'given' => 'చెప్పబడినది|*', - 'name' => 'Telugu', - 'native' => 'తెలుగు', - 'scenario' => 'సన్నివేశం', - 'scenario_outline' => 'కథనం', - 'then' => 'అప్పుడు|*', - 'when' => 'ఈ పరిస్థితిలో|*', - ), 'tlh' => array ( 'and' => 'latlh|\'ej|*', @@ -935,6 +1108,7 @@ 'given' => 'DaH ghu\' bejlu\'|ghu\' noblu\'|*', 'name' => 'Klingon', 'native' => 'tlhIngan', + 'rule' => 'Rule', 'scenario' => 'lut', 'scenario_outline' => 'lut chovnatlh', 'then' => 'vaj|*', @@ -950,7 +1124,8 @@ 'given' => 'Diyelim ki|*', 'name' => 'Turkish', 'native' => 'Türkçe', - 'scenario' => 'Senaryo', + 'rule' => 'Kural', + 'scenario' => 'Senaryo|Örnek', 'scenario_outline' => 'Senaryo taslağı', 'then' => 'O zaman|*', 'when' => 'Eğer ki|*', @@ -960,11 +1135,12 @@ 'and' => 'Һәм|Вә|*', 'background' => 'Кереш', 'but' => 'Ләкин|Әмма|*', - 'examples' => 'Үрнәкләр|Мисаллар', + 'examples' => 'Мисаллар|Үрнәкләр', 'feature' => 'Үзенчәлеклелек|Мөмкинлек', 'given' => 'Әйтик|*', 'name' => 'Tatar', 'native' => 'Татарча', + 'rule' => 'Rule', 'scenario' => 'Сценарий', 'scenario_outline' => 'Сценарийның төзелеше', 'then' => 'Нәтиҗәдә|*', @@ -980,7 +1156,8 @@ 'given' => 'Припустимо, що|Припустимо|Нехай|Дано|*', 'name' => 'Ukrainian', 'native' => 'Українська', - 'scenario' => 'Сценарій', + 'rule' => 'Rule', + 'scenario' => 'Сценарій|Приклад', 'scenario_outline' => 'Структура сценарію', 'then' => 'Тоді|То|*', 'when' => 'Коли|Якщо|*', @@ -991,10 +1168,11 @@ 'background' => 'پس منظر', 'but' => 'لیکن|*', 'examples' => 'مثالیں', - 'feature' => 'کاروبار کی ضرورت|صلاحیت|خصوصیت', + 'feature' => 'کاروبار کی ضرورت|خصوصیت|صلاحیت', 'given' => 'فرض کیا|بالفرض|اگر|*', 'name' => 'Urdu', 'native' => 'اردو', + 'rule' => 'Rule', 'scenario' => 'منظرنامہ', 'scenario_outline' => 'منظر نامے کا خاکہ', 'then' => 'پھر|تب|*', @@ -1010,6 +1188,7 @@ 'given' => 'Агар|*', 'name' => 'Uzbek', 'native' => 'Узбекча', + 'rule' => 'Rule', 'scenario' => 'Сценарий', 'scenario_outline' => 'Сценарий структураси', 'then' => 'Унда|*', @@ -1025,6 +1204,7 @@ 'given' => 'Biết|Cho|*', 'name' => 'Vietnamese', 'native' => 'Tiếng Việt', + 'rule' => 'Rule', 'scenario' => 'Tình huống|Kịch bản', 'scenario_outline' => 'Khung tình huống|Khung kịch bản', 'then' => 'Thì|*', @@ -1032,31 +1212,33 @@ ), 'zh-CN' => array ( - 'and' => '并且<|而且<|同时<|*', + 'and' => '同时<|并且<|而且<|*', 'background' => '背景', 'but' => '但是<|*', 'examples' => '例子', 'feature' => '功能', - 'given' => '假设<|假如<|假定<|*', + 'given' => '假如<|假定<|假设<|*', 'name' => 'Chinese simplified', 'native' => '简体中文', - 'scenario' => '场景|剧本', - 'scenario_outline' => '场景大纲|剧本大纲', + 'rule' => 'Rule', + 'scenario' => '剧本|场景', + 'scenario_outline' => '剧本大纲|场景大纲', 'then' => '那么<|*', 'when' => '当<|*', ), 'zh-TW' => array ( - 'and' => '並且<|而且<|同時<|*', + 'and' => '並且<|同時<|而且<|*', 'background' => '背景', 'but' => '但是<|*', 'examples' => '例子', 'feature' => '功能', - 'given' => '假設<|假如<|假定<|*', + 'given' => '假如<|假定<|假設<|*', 'name' => 'Chinese traditional', 'native' => '繁體中文', - 'scenario' => '場景|劇本', - 'scenario_outline' => '場景大綱|劇本大綱', + 'rule' => 'Rule', + 'scenario' => '劇本|場景', + 'scenario_outline' => '劇本大綱|場景大綱', 'then' => '那麼<|*', 'when' => '當<|*', ), diff --git a/tests/integration/vendor/behat/gherkin/package.xml.tpl b/tests/integration/vendor/behat/gherkin/package.xml.tpl deleted file mode 100644 index 1714236c7..000000000 --- a/tests/integration/vendor/behat/gherkin/package.xml.tpl +++ /dev/null @@ -1,71 +0,0 @@ - - - gherkin - pear.behat.org - Behat\Gherkin is a BDD DSL for PHP - - Behat\Gherkin is an open source behavior driven development DSL for php 5.3. - - - Konstantin Kudryashov - everzet - ever.zet@gmail.com - yes - - ##CURRENT_DATE## - - ##GHERKIN_VERSION## - 1.0.0 - - - ##STABILITY## - ##STABILITY## - - MIT - - - - - - ##SOURCE_FILES## - - - - - - - - - - - - - - - - - - - 5.3.1 - - - 1.4.0 - - - pcre - - - simplexml - - - xml - - - mbstring - - - - - diff --git a/tests/integration/vendor/behat/gherkin/phpdoc.ini.dist b/tests/integration/vendor/behat/gherkin/phpdoc.ini.dist deleted file mode 100644 index f983946e2..000000000 --- a/tests/integration/vendor/behat/gherkin/phpdoc.ini.dist +++ /dev/null @@ -1,14 +0,0 @@ -files = "*.php" -ignore = "CVS, .svn, .git, _compiled" -source_path = "./src" -doclet = standard -overview = readme.html -package_comment_dir = ./ -public = on -d = "api" -default_package = "Behat Gherkin" -windowtitle = "Behat Gherkin" -doctitle = "Behat Gherkin: PHP 5.3 Gherkin parser" -header = "Behat Gherkin" -footer = "Behat Gherkin" -tree = on diff --git a/tests/integration/vendor/behat/gherkin/phpunit.xml.dist b/tests/integration/vendor/behat/gherkin/phpunit.xml.dist deleted file mode 100644 index 24fc8d87a..000000000 --- a/tests/integration/vendor/behat/gherkin/phpunit.xml.dist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - ./tests/Behat/Gherkin/ - - - - - - ./src/Behat/Gherkin/ - - - diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php index 87d4a472e..d54397830 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php @@ -25,7 +25,7 @@ interface CacheInterface * @param string $path Feature path * @param integer $timestamp The last time feature was updated * - * @return Boolean + * @return bool */ public function isFresh($path, $timestamp); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.php index 17a206ac7..44707c2da 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.php @@ -50,7 +50,7 @@ public function __construct($path) * @param string $path Feature path * @param integer $timestamp The last time feature was updated * - * @return Boolean + * @return bool */ public function isFresh($path, $timestamp) { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php index e404f3048..12d56ffa2 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php @@ -29,7 +29,7 @@ class MemoryCache implements CacheInterface * @param string $path Feature path * @param integer $timestamp The last time feature was updated * - * @return Boolean + * @return bool */ public function isFresh($path, $timestamp) { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php index 8a0ebd566..82addd029 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php @@ -26,7 +26,7 @@ interface ComplexFilterInterface extends FeatureFilterInterface * @param FeatureNode $feature Feature node instance * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(FeatureNode $feature, ScenarioInterface $scenario); } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php index a7c43eccb..6994b318e 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php @@ -24,7 +24,7 @@ interface FeatureFilterInterface * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php index 4a531a0dd..469cb3cff 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php @@ -24,7 +24,7 @@ interface FilterInterface extends FeatureFilterInterface * * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(ScenarioInterface $scenario); } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php index 455e9ac35..9cf6b3042 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php @@ -39,7 +39,7 @@ public function __construct($filterLine) * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { @@ -51,7 +51,7 @@ public function isFeatureMatch(FeatureNode $feature) * * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(ScenarioInterface $scenario) { @@ -83,24 +83,26 @@ public function filterFeature(FeatureNode $feature) } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { - $table = $scenario->getExampleTable()->getTable(); - $lines = array_keys($table); - - if (in_array($this->filterLine, $lines)) { - $filteredTable = array($lines[0] => $table[$lines[0]]); - - if ($lines[0] !== $this->filterLine) { - $filteredTable[$this->filterLine] = $table[$this->filterLine]; + foreach ($scenario->getExampleTables() as $exampleTable) { + $table = $exampleTable->getTable(); + $lines = array_keys($table); + + if (in_array($this->filterLine, $lines)) { + $filteredTable = array($lines[0] => $table[$lines[0]]); + + if ($lines[0] !== $this->filterLine) { + $filteredTable[$this->filterLine] = $table[$this->filterLine]; + } + + $scenario = new OutlineNode( + $scenario->getTitle(), + $scenario->getTags(), + $scenario->getSteps(), + array(new ExampleTableNode($filteredTable, $exampleTable->getKeyword(), $exampleTable->getTags())), + $scenario->getKeyword(), + $scenario->getLine() + ); } - - $scenario = new OutlineNode( - $scenario->getTitle(), - $scenario->getTags(), - $scenario->getSteps(), - new ExampleTableNode($filteredTable, $scenario->getExampleTable()->getKeyword()), - $scenario->getKeyword(), - $scenario->getLine() - ); } } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php index b8062bed5..e73d12d87 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php @@ -46,7 +46,7 @@ public function __construct($filterMinLine, $filterMaxLine) * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { @@ -59,7 +59,7 @@ public function isFeatureMatch(FeatureNode $feature) * * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(ScenarioInterface $scenario) { @@ -94,15 +94,24 @@ public function filterFeature(FeatureNode $feature) } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { - $table = $scenario->getExampleTable()->getTable(); - $lines = array_keys($table); + // first accumulate examples and then create scenario + $exampleTableNodes = array(); - $filteredTable = array($lines[0] => $table[$lines[0]]); - unset($table[$lines[0]]); + foreach ($scenario->getExampleTables() as $exampleTable) { + $table = $exampleTable->getTable(); + $lines = array_keys($table); - foreach ($table as $line => $row) { - if ($this->filterMinLine <= $line && $this->filterMaxLine >= $line) { - $filteredTable[$line] = $row; + $filteredTable = array($lines[0] => $table[$lines[0]]); + unset($table[$lines[0]]); + + foreach ($table as $line => $row) { + if ($this->filterMinLine <= $line && $this->filterMaxLine >= $line) { + $filteredTable[$line] = $row; + } + } + + if (count($filteredTable) > 1) { + $exampleTableNodes[] = new ExampleTableNode($filteredTable, $exampleTable->getKeyword(), $exampleTable->getTags()); } } @@ -110,7 +119,7 @@ public function filterFeature(FeatureNode $feature) $scenario->getTitle(), $scenario->getTags(), $scenario->getSteps(), - new ExampleTableNode($filteredTable, $scenario->getExampleTable()->getKeyword()), + $exampleTableNodes, $scenario->getKeyword(), $scenario->getLine() ); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php index 82ae0e582..80974d1fc 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php @@ -37,10 +37,14 @@ public function __construct($filterString) * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { + if (null === $feature->getTitle()) { + return false; + } + if ('/' === $this->filterString[0]) { return 1 === preg_match($this->filterString, $feature->getTitle()); } @@ -53,10 +57,14 @@ public function isFeatureMatch(FeatureNode $feature) * * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(ScenarioInterface $scenario) { + if (null === $scenario->getTitle()) { + return false; + } + if ('/' === $this->filterString[0] && 1 === preg_match($this->filterString, $scenario->getTitle())) { return true; } elseif (false !== mb_strpos($scenario->getTitle(), $this->filterString, 0, 'utf8')) { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php index 61126e569..eb5ae48d4 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php @@ -40,11 +40,11 @@ public function __construct($regex) * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { - return 1 === preg_match($this->regex, $feature->getDescription()); + return 1 === preg_match($this->regex, $feature->getDescription() ?? ''); } /** @@ -52,7 +52,7 @@ public function isFeatureMatch(FeatureNode $feature) * * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(ScenarioInterface $scenario) { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php index 5dac3dc9d..f92859774 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php @@ -45,12 +45,12 @@ function ($realpath) { * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { foreach ($this->filterPaths as $path) { - if (0 === strpos($feature->getFile(), $path)) { + if (0 === strpos(realpath($feature->getFile()), $path)) { return true; } } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.php index 19e9377a9..0ad3a29a5 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.php @@ -42,11 +42,11 @@ public function __construct($role) * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { - return 1 === preg_match($this->pattern, $feature->getDescription()); + return 1 === preg_match($this->pattern, $feature->getDescription() ?? ''); } /** diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php index fed6c1afd..579ed4e8d 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php @@ -11,6 +11,7 @@ namespace Behat\Gherkin\Filter; use Behat\Gherkin\Node\FeatureNode; +use Behat\Gherkin\Node\OutlineNode; use Behat\Gherkin\Node\ScenarioInterface; /** @@ -30,6 +31,64 @@ class TagFilter extends ComplexFilter public function __construct($filterString) { $this->filterString = trim($filterString); + + if(preg_match('/\s/u', $this->filterString)) { + trigger_error( + "Tags with whitespace are deprecated and may be removed in a future version", + E_USER_DEPRECATED + ); + } + } + + /** + * Filters feature according to the filter. + * + * @param FeatureNode $feature + * + * @return FeatureNode + */ + public function filterFeature(FeatureNode $feature) + { + $scenarios = array(); + foreach ($feature->getScenarios() as $scenario) { + if (!$this->isScenarioMatch($feature, $scenario)) { + continue; + } + + if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { + + $exampleTables = array(); + + foreach ($scenario->getExampleTables() as $exampleTable) { + if ($this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags(), $exampleTable->getTags()))) { + $exampleTables[] = $exampleTable; + } + } + + $scenario = new OutlineNode( + $scenario->getTitle(), + $scenario->getTags(), + $scenario->getSteps(), + $exampleTables, + $scenario->getKeyword(), + $scenario->getLine() + ); + } + + $scenarios[] = $scenario; + } + + return new FeatureNode( + $feature->getTitle(), + $feature->getDescription(), + $feature->getTags(), + $feature->getBackground(), + $scenarios, + $feature->getKeyword(), + $feature->getLanguage(), + $feature->getFile(), + $feature->getLine() + ); } /** @@ -37,7 +96,7 @@ public function __construct($filterString) * * @param FeatureNode $feature Feature instance * - * @return Boolean + * @return bool */ public function isFeatureMatch(FeatureNode $feature) { @@ -47,13 +106,23 @@ public function isFeatureMatch(FeatureNode $feature) /** * Checks if scenario or outline matches specified filter. * - * @param FeatureNode $feature Feature node instance + * @param FeatureNode $feature Feature node instance * @param ScenarioInterface $scenario Scenario or Outline node instance * - * @return Boolean + * @return bool */ public function isScenarioMatch(FeatureNode $feature, ScenarioInterface $scenario) { + if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { + foreach ($scenario->getExampleTables() as $example) { + if ($this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags(), $example->getTags()))) { + return true; + } + } + + return false; + } + return $this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags())); } @@ -62,7 +131,7 @@ public function isScenarioMatch(FeatureNode $feature, ScenarioInterface $scenari * * @param string[] $tags * - * @return Boolean + * @return bool */ protected function isTagsMatchCondition($tags) { @@ -82,7 +151,7 @@ protected function isTagsMatchCondition($tags) } } - $satisfies = (false !== $satisfiesComma && $satisfies && $satisfiesComma) || false; + $satisfies = $satisfiesComma && $satisfies; } return $satisfies; diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php index e457cd873..c3cc5c0e8 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php @@ -23,7 +23,7 @@ */ class Gherkin { - const VERSION = '4.4-dev'; + const VERSION = '4.8.0'; /** * @var LoaderInterface[] diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php index 35b9b821a..2aa6b3086 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php @@ -31,7 +31,7 @@ * 'background' => 'Предыстория', * 'scenario' => 'Сценарий', * 'scenario_outline' => 'Структура сценария', - * 'examples' => 'Значения', + * 'examples' => 'Примеры', * 'given' => 'Допустим', * 'when' => 'Если', * 'then' => 'То', diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php index fcd9769ba..3254a3a34 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php @@ -34,7 +34,7 @@ public function __construct(KeywordsInterface $keywords) /** * Sets keywords mapper function. * - * Callable should accept 2 arguments (array $keywords and Boolean $isShort) + * Callable should accept 2 arguments (array $keywords and bool $isShort) * * @param callable $mapper Mapper function */ @@ -46,8 +46,8 @@ public function setKeywordsDumperFunction($mapper) /** * Defaults keywords dumper. * - * @param array $keywords Keywords list - * @param Boolean $isShort Is short version + * @param array $keywords Keywords list + * @param bool $isShort Is short version * * @return string */ @@ -63,9 +63,9 @@ public function dumpKeywords(array $keywords, $isShort) /** * Dumps keyworded feature into string. * - * @param string $language Keywords language - * @param Boolean $short Dump short version - * @param bool $excludeAsterisk + * @param string $language Keywords language + * @param bool $short Dump short version + * @param bool $excludeAsterisk * * @return string|array String for short version and array of features for extended */ @@ -98,7 +98,7 @@ public function dump($language, $short = true, $excludeAsterisk = false) * Dumps feature example. * * @param string $keyword Item keyword - * @param Boolean $short Dump short version? + * @param bool $short Dump short version? * * @return string */ @@ -153,8 +153,8 @@ protected function dumpFeature($keyword, $short = true, $excludeAsterisk = false /** * Dumps background example. * - * @param string $keyword Item keyword - * @param Boolean $short Dump short version? + * @param string $keyword Item keyword + * @param bool $short Dump short version? * * @return string */ @@ -187,8 +187,8 @@ protected function dumpBackground($keyword, $short = true, $excludeAsterisk = fa /** * Dumps scenario example. * - * @param string $keyword Item keyword - * @param Boolean $short Dump short version? + * @param string $keyword Item keyword + * @param bool $short Dump short version? * * @return string */ @@ -245,8 +245,8 @@ protected function dumpScenario($keyword, $short = true, $excludeAsterisk = fals /** * Dumps outline example. * - * @param string $keyword Item keyword - * @param Boolean $short Dump short version? + * @param string $keyword Item keyword + * @param bool $short Dump short version? * * @return string */ @@ -318,9 +318,9 @@ protected function dumpOutline($keyword, $short = true, $excludeAsterisk = false /** * Dumps step example. * - * @param string $keywords Item keyword - * @param string $text Step text - * @param Boolean $short Dump short version? + * @param string $keywords Item keyword + * @param string $text Step text + * @param bool $short Dump short version? * * @return string */ diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Lexer.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Lexer.php index 446d462c9..1f3b3c40d 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Lexer.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Lexer.php @@ -38,6 +38,7 @@ class Lexer private $featureStarted = false; private $allowMultilineArguments = false; private $allowSteps = false; + private $pyStringDelimiter = null; /** * Initializes lexer. @@ -121,7 +122,7 @@ public function deferToken(array $token) } /** - * Predicts for number of tokens. + * Predicts the upcoming token without passing over it. * * @return array */ @@ -134,6 +135,16 @@ public function predictToken() return $this->stashedToken; } + /** + * Skips over the currently-predicted token, if any. + * + * @return void + */ + public function skipPredictedToken() + { + $this->stashedToken = null; + } + /** * Constructs token with specified parameters. * @@ -169,6 +180,15 @@ protected function consumeLine() $this->trimmedLine = null; } + /** + * Consumes first part of line from input without incrementing the line number + */ + protected function consumeLineUntil(int $trimmedOffset) + { + $this->line = mb_substr(ltrim($this->line), $trimmedOffset, null, 'utf-8'); + $this->trimmedLine = null; + } + /** * Returns trimmed version of line. * @@ -399,7 +419,7 @@ protected function scanStep() } $keywords = $this->getKeywords('Step'); - if (!preg_match('/^\s*(' . $keywords . ')([^\s].+)/u', $this->line, $matches)) { + if (!preg_match('/^\s*(' . $keywords . ')([^\s].*)/u', $this->line, $matches)) { return null; } @@ -425,13 +445,25 @@ protected function scanPyStringOp() return null; } - if (false === ($pos = mb_strpos($this->line, '"""', 0, 'utf8'))) { + if(!preg_match('/^\s*(?"""|```)/u', $this->line, $matches, PREG_OFFSET_CAPTURE)) { return null; } + ['delimiter' => [0 => $delimiter, 1 => $indent]] = $matches; + + if ($this->inPyString) { + if ($this->pyStringDelimiter !== $delimiter) { + return null; + } + $this->pyStringDelimiter = null; + } + else { + $this->pyStringDelimiter= $delimiter; + } + $this->inPyString = !$this->inPyString; $token = $this->takeToken('PyStringOp'); - $this->pyStringSwallow = $pos; + $this->pyStringSwallow = $indent; $this->consumeLine(); @@ -451,7 +483,7 @@ protected function scanPyStringContent() $token = $this->scanText(); // swallow trailing spaces - $token['value'] = preg_replace('/^\s{0,' . $this->pyStringSwallow . '}/u', '', $token['value']); + $token['value'] = preg_replace('/^\s{0,' . $this->pyStringSwallow . '}/u', '', $token['value'] ?? ''); return $token; } @@ -492,16 +524,23 @@ protected function scanTableRow() protected function scanTags() { $line = $this->getTrimmedLine(); + if (!isset($line[0]) || '@' !== $line[0]) { return null; } + if(preg_match('/^(?.*)\s+#.*$/', $line, $matches)) { + ['line' => $line] = $matches; + $this->consumeLineUntil(mb_strlen($line, 'utf-8')); + } else { + $this->consumeLine(); + } + $token = $this->takeToken('Tag'); $tags = explode('@', mb_substr($line, 1, mb_strlen($line, 'utf8') - 1, 'utf8')); $tags = array_map('trim', $tags); $token['tags'] = $tags; - $this->consumeLine(); return $token; } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php index 3492d6e6a..145bed9d6 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php @@ -31,7 +31,7 @@ class ArrayLoader implements LoaderInterface * * @param mixed $resource Resource to load * - * @return Boolean + * @return bool */ public function supports($resource) { @@ -179,7 +179,15 @@ protected function loadOutlineHash(array $hash, $line = 0) $examplesKeyword = 'Examples'; } - $examples = new ExampleTableNode($hash['examples'], $examplesKeyword); + $exHash = $hash['examples']; + $examples = array(); + + if ($this->examplesAreInArray($exHash)) { + $examples = $this->processExamplesArray($exHash, $examplesKeyword, $examples); + } else { + // examples as a single table - we create an array with the only one element + $examples[] = new ExampleTableNode($exHash, $examplesKeyword);; + } return new OutlineNode($hash['title'], $hash['tags'], $steps, $examples, $hash['keyword'], $hash['line']); } @@ -266,4 +274,39 @@ protected function loadPyStringHash(array $hash, $line = 0) return new PyStringNode($strings, $line); } + + /** + * Checks if examples node is an array + * @param $exHash object hash + * @return bool + */ + private function examplesAreInArray($exHash) + { + return isset($exHash[0]); + } + + /** + * Processes cases when examples are in the form of array of arrays + * OR in the form of array of objects + * + * @param $exHash array hash + * @param $examplesKeyword string + * @param $examples array + * @return array + */ + private function processExamplesArray($exHash, $examplesKeyword, $examples) + { + for ($i = 0; $i < count($exHash); $i++) { + if (isset($exHash[$i]['table'])) { + // we have examples as objects, hence there could be tags + $exHashTags = isset($exHash[$i]['tags']) ? $exHash[$i]['tags'] : array(); + $examples[] = new ExampleTableNode($exHash[$i]['table'], $examplesKeyword, $exHashTags); + } else { + // we have examples as arrays + $examples[] = new ExampleTableNode($exHash[$i], $examplesKeyword); + } + } + + return $examples; + } } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.php new file mode 100644 index 000000000..9fe763bcf --- /dev/null +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.php @@ -0,0 +1,193 @@ +findRelativePath($path); $content = file_get_contents($path); - $feature = $this->parser->parse($content, $filename); + $feature = $this->parser->parse($content, $path); return $feature; } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php index 861332ccc..322ce6c96 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php @@ -24,7 +24,7 @@ interface LoaderInterface * * @param mixed $resource Resource to load * - * @return Boolean + * @return bool */ public function supports($resource); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php index 0c268fd3a..88193aa35 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php @@ -32,7 +32,7 @@ public function __construct() * * @param mixed $path Resource to load * - * @return Boolean + * @return bool */ public function supports($path) { @@ -54,9 +54,8 @@ public function load($path) $hash = Yaml::parse(file_get_contents($path)); $features = $this->loader->load($hash); - $filename = $this->findRelativePath($path); - return array_map(function (FeatureNode $feature) use ($filename) { + return array_map(function (FeatureNode $feature) use ($path) { return new FeatureNode( $feature->getTitle(), $feature->getDescription(), @@ -65,7 +64,7 @@ public function load($path) $feature->getScenarios(), $feature->getKeyword(), $feature->getLanguage(), - $filename, + $path, $feature->getLine() ); }, $features); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php index fb1edb4b2..61044866c 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php @@ -73,7 +73,7 @@ public function getTitle() /** * Checks if background has steps. * - * @return Boolean + * @return bool */ public function hasSteps() { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php index b4beaba48..59d9705de 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php @@ -41,23 +41,29 @@ class ExampleNode implements ScenarioInterface * @var null|StepNode[] */ private $steps; + /** + * @var string + */ + private $outlineTitle; /** * Initializes outline. * - * @param string $title - * @param string[] $tags - * @param StepNode[] $outlineSteps - * @param string[] $tokens - * @param integer $line + * @param string $title + * @param string[] $tags + * @param StepNode[] $outlineSteps + * @param string[] $tokens + * @param integer $line + * @param string|null $outlineTitle */ - public function __construct($title, array $tags, $outlineSteps, array $tokens, $line) + public function __construct($title, array $tags, $outlineSteps, array $tokens, $line, $outlineTitle = null) { $this->title = $title; $this->tags = $tags; $this->outlineSteps = $outlineSteps; $this->tokens = $tokens; $this->line = $line; + $this->outlineTitle = $outlineTitle; } /** @@ -95,7 +101,7 @@ public function getTitle() * * @param string $tag * - * @return Boolean + * @return bool */ public function hasTag($tag) { @@ -105,7 +111,7 @@ public function hasTag($tag) /** * Checks if outline has tags (both inherited from feature and own). * - * @return Boolean + * @return bool */ public function hasTags() { @@ -125,7 +131,7 @@ public function getTags() /** * Checks if outline has steps. * - * @return Boolean + * @return bool */ public function hasSteps() { @@ -162,6 +168,16 @@ public function getLine() return $this->line; } + /** + * Returns outline title. + * + * @return string + */ + public function getOutlineTitle() + { + return $this->outlineTitle; + } + /** * Creates steps for this example from abstract outline steps. * diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php index 805e659ef..917535117 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php @@ -17,6 +17,11 @@ */ class ExampleTableNode extends TableNode { + /** + * @var string[] + */ + private $tags; + /** * @var string */ @@ -25,12 +30,14 @@ class ExampleTableNode extends TableNode /** * Initializes example table. * - * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] + * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] * @param string $keyword + * @param string[] $tags */ - public function __construct(array $table, $keyword) + public function __construct(array $table, $keyword, array $tags = array()) { $this->keyword = $keyword; + $this->tags = $tags; parent::__construct($table); } @@ -45,6 +52,15 @@ public function getNodeType() return 'ExampleTable'; } + /** + * Returns attached tags + * @return \string[] + */ + public function getTags() + { + return $this->tags; + } + /** * Returns example table keyword. * diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php index 641365996..d53b97c67 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php @@ -64,7 +64,7 @@ class FeatureNode implements KeywordNodeInterface, TaggedNodeInterface * @param ScenarioInterface[] $scenarios * @param string $keyword * @param string $language - * @param null|string $file + * @param null|string $file The absolute path to the feature file. * @param integer $line */ public function __construct( @@ -78,6 +78,10 @@ public function __construct( $file, $line ) { + // Verify that the feature file is an absolute path. + if (!empty($file) && !$this->isAbsolutePath($file)) { + throw new \InvalidArgumentException('The file should be an absolute path.'); + } $this->title = $title; $this->description = $description; $this->tags = $tags; @@ -112,7 +116,7 @@ public function getTitle() /** * Checks if feature has a description. * - * @return Boolean + * @return bool */ public function hasDescription() { @@ -134,7 +138,7 @@ public function getDescription() * * @param string $tag * - * @return Boolean + * @return bool */ public function hasTag($tag) { @@ -144,7 +148,7 @@ public function hasTag($tag) /** * Checks if feature has tags. * - * @return Boolean + * @return bool */ public function hasTags() { @@ -164,7 +168,7 @@ public function getTags() /** * Checks if feature has background. * - * @return Boolean + * @return bool */ public function hasBackground() { @@ -184,7 +188,7 @@ public function getBackground() /** * Checks if feature has scenarios. * - * @return Boolean + * @return bool */ public function hasScenarios() { @@ -222,7 +226,7 @@ public function getLanguage() } /** - * Returns feature file. + * Returns feature file as an absolute path. * * @return null|string */ @@ -240,4 +244,28 @@ public function getLine() { return $this->line; } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + * + * @see https://github.com/symfony/filesystem/blob/master/Filesystem.php + */ + protected function isAbsolutePath($file) + { + if (null === $file) { + @trigger_error(sprintf('Calling "%s()" with a null in the $file argument is deprecated since Symfony 4.4.', __METHOD__), E_USER_DEPRECATED); + } + + return strspn($file, '/\\', 0, 1) + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && strspn($file, '/\\', 2, 1) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ; + } } diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.php index 083b411fc..8e68b25ae 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.php @@ -30,9 +30,9 @@ class OutlineNode implements ScenarioInterface */ private $steps; /** - * @var ExampleTableNode + * @var ExampleTableNode|ExampleTableNode[] */ - private $table; + private $tables; /** * @var string */ @@ -52,7 +52,7 @@ class OutlineNode implements ScenarioInterface * @param null|string $title * @param string[] $tags * @param StepNode[] $steps - * @param ExampleTableNode $table + * @param ExampleTableNode|ExampleTableNode[] $tables * @param string $keyword * @param integer $line */ @@ -60,16 +60,20 @@ public function __construct( $title, array $tags, array $steps, - ExampleTableNode $table, + $tables, $keyword, $line ) { $this->title = $title; $this->tags = $tags; $this->steps = $steps; - $this->table = $table; $this->keyword = $keyword; $this->line = $line; + if (!is_array($tables)) { + $this->tables = array($tables); + } else { + $this->tables = $tables; + } } /** @@ -97,7 +101,7 @@ public function getTitle() * * @param string $tag * - * @return Boolean + * @return bool */ public function hasTag($tag) { @@ -107,7 +111,7 @@ public function hasTag($tag) /** * Checks if outline has tags (both inherited from feature and own). * - * @return Boolean + * @return bool */ public function hasTags() { @@ -127,7 +131,7 @@ public function getTags() /** * Checks if outline has steps. * - * @return Boolean + * @return bool */ public function hasSteps() { @@ -147,31 +151,52 @@ public function getSteps() /** * Checks if outline has examples. * - * @return Boolean + * @return bool */ public function hasExamples() { - return 0 < count($this->table->getColumnsHash()); + return 0 < count($this->tables); } /** - * Returns examples table. + * Builds and returns examples table for the outline. + * + * WARNING: it returns a merged table with tags lost. * + * @deprecated use getExampleTables instead * @return ExampleTableNode */ public function getExampleTable() { - return $this->table; + $table = array(); + foreach ($this->tables[0]->getTable() as $k => $v) { + $table[$k] = $v; + } + + /** @var ExampleTableNode $exampleTableNode */ + $exampleTableNode = new ExampleTableNode($table, $this->tables[0]->getKeyword()); + for ($i = 1; $i < count($this->tables); $i++) { + $exampleTableNode->mergeRowsFromTable($this->tables[$i]); + } + return $exampleTableNode; } /** * Returns list of examples for the outline. - * * @return ExampleNode[] */ public function getExamples() { - return $this->examples = $this->examples ? : $this->createExamples(); + return $this->examples = $this->examples ?: $this->createExamples(); + } + + /** + * Returns examples tables array for the outline. + * @return ExampleTableNode[] + */ + public function getExampleTables() + { + return $this->tables; } /** @@ -202,14 +227,18 @@ public function getLine() protected function createExamples() { $examples = array(); - foreach ($this->table->getColumnsHash() as $rowNum => $row) { - $examples[] = new ExampleNode( - $this->table->getRowAsString($rowNum + 1), - $this->tags, - $this->getSteps(), - $row, - $this->table->getRowLine($rowNum + 1) - ); + + foreach ($this->getExampleTables() as $exampleTable) { + foreach ($exampleTable->getColumnsHash() as $rowNum => $row) { + $examples[] = new ExampleNode( + $exampleTable->getRowAsString($rowNum + 1), + array_merge($this->tags, $exampleTable->getTags()), + $this->getSteps(), + $row, + $exampleTable->getRowLine($rowNum + 1), + $this->getTitle() + ); + } } return $examples; diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php index 58267e1e2..277ab9689 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php @@ -81,7 +81,7 @@ public function getTitle() * * @param string $tag * - * @return Boolean + * @return bool */ public function hasTag($tag) { @@ -91,7 +91,7 @@ public function hasTag($tag) /** * Checks if scenario has tags (both inherited from feature and own). * - * @return Boolean + * @return bool */ public function hasTags() { @@ -111,7 +111,7 @@ public function getTags() /** * Checks if scenario has steps. * - * @return Boolean + * @return bool */ public function hasSteps() { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php index 2a5c73d9e..ebec3c4be 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php @@ -20,7 +20,7 @@ interface StepContainerInterface extends NodeInterface /** * Checks if container has steps. * - * @return Boolean + * @return bool */ public function hasSteps(); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php index 82eaef490..77113bba5 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php @@ -123,7 +123,7 @@ public function getText() /** * Checks if step has arguments. * - * @return Boolean + * @return bool */ public function hasArguments() { diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php index 9e5d17407..4099f640a 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php @@ -14,6 +14,7 @@ use Behat\Gherkin\Exception\NodeException; use Iterator; use IteratorAggregate; +use ReturnTypeWillChange; /** * Represents Gherkin Table argument. @@ -35,21 +36,35 @@ class TableNode implements ArgumentInterface, IteratorAggregate * Initializes table. * * @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] - * - * @throws NodeException If the number of columns is not the same in each row + * + * @throws NodeException If the given table is invalid */ public function __construct(array $table) { $this->table = $table; $columnCount = null; - foreach ($this->getRows() as $row) { + foreach ($this->getRows() as $ridx => $row) { + + if (!is_array($row)) { + throw new NodeException(sprintf( + "Table row '%s' is expected to be array, got %s", + $ridx, + gettype($row) + )); + } + if ($columnCount === null) { $columnCount = count($row); } if (count($row) !== $columnCount) { - throw new NodeException('Table does not have same number of columns in every row.'); + throw new NodeException(sprintf( + "Table row '%s' is expected to have %s columns, got %s", + $ridx, + $columnCount, + count($row) + )); } foreach ($row as $column => $string) { @@ -57,11 +72,41 @@ public function __construct(array $table) $this->maxLineLength[$column] = 0; } + if (!is_scalar($string)) { + throw new NodeException(sprintf( + "Table cell at row '%s', col '%s' is expected to be scalar, got %s", + $ridx, + $column, + gettype($string) + )); + } + $this->maxLineLength[$column] = max($this->maxLineLength[$column], mb_strlen($string, 'utf8')); } } } + /** + * Creates a table from a given list. + * + * @param array $list One-dimensional array + * + * @return TableNode + * + * @throws NodeException If the given list is not a one-dimensional array + */ + public static function fromList(array $list) + { + if (count($list) !== count($list, COUNT_RECURSIVE)) { + throw new NodeException('List is not a one-dimensional array.'); + } + + array_walk($list, function (&$item) { + $item = array($item); + }); + return new self($list); + } + /** * Returns node type. * @@ -289,11 +334,36 @@ public function __toString() * * @return Iterator */ + #[ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->getHash()); } + /** + * Obtains and adds rows from another table to the current table. + * The second table should have the same structure as the current one. + * @param TableNode $node + * + * @deprecated remove together with OutlineNode::getExampleTable + */ + public function mergeRowsFromTable(TableNode $node) + { + // check structure + if ($this->getRow(0) !== $node->getRow(0)) { + throw new NodeException("Tables have different structure. Cannot merge one into another"); + } + + $firstLine = $node->getLine(); + foreach ($node->getTable() as $line => $value) { + if ($line === $firstLine) { + continue; + } + + $this->table[$line] = $value; + } + } + /** * Pads string right. * diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php index cce6bcafd..4afa6d1d8 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php @@ -22,14 +22,14 @@ interface TaggedNodeInterface extends NodeInterface * * @param string $tag * - * @return Boolean + * @return bool */ public function hasTag($tag); /** * Checks if node has tags (both inherited from feature and own). * - * @return Boolean + * @return bool */ public function hasTags(); diff --git a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Parser.php b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Parser.php index 5cc85424b..cdfe44505 100644 --- a/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Parser.php +++ b/tests/integration/vendor/behat/gherkin/src/Behat/Gherkin/Parser.php @@ -11,6 +11,7 @@ namespace Behat\Gherkin; use Behat\Gherkin\Exception\LexerException; +use Behat\Gherkin\Exception\NodeException; use Behat\Gherkin\Exception\ParserException; use Behat\Gherkin\Node\BackgroundNode; use Behat\Gherkin\Node\ExampleTableNode; @@ -39,6 +40,8 @@ class Parser private $tags = array(); private $languageSpecifierLine; + private $passedNodesStack = array(); + /** * Initializes parser. * @@ -181,7 +184,15 @@ protected function predictTokenType() */ protected function parseExpression() { - switch ($type = $this->predictTokenType()) { + $type = $this->predictTokenType(); + + while ($type === 'Comment') { + $this->expectTokenType('Comment'); + + $type = $this->predictTokenType(); + } + + switch ($type) { case 'Feature': return $this->parseFeature(); case 'Background': @@ -204,8 +215,6 @@ protected function parseExpression() return $this->parseNewline(); case 'Tag': return $this->parseTags(); - case 'Comment': - return $this->parseComment(); case 'Language': return $this->parseLanguage(); case 'EOS': @@ -226,7 +235,7 @@ protected function parseFeature() { $token = $this->expectTokenType('Feature'); - $title = trim($token['value']) ?: null; + $title = trim($token['value'] ?? ''); $description = null; $tags = $this->popTags(); $background = null; @@ -236,6 +245,8 @@ protected function parseFeature() $file = $this->file; $line = $token['line']; + array_push($this->passedNodesStack, 'Feature'); + // Parse description, background, scenarios & outlines while ('EOS' !== $this->predictTokenType()) { $node = $this->parseExpression(); @@ -277,7 +288,7 @@ protected function parseFeature() return new FeatureNode( rtrim($title) ?: null, - rtrim($description) ?: null, + rtrim($description ?? '') ?: null, $tags, $background, $scenarios, @@ -299,7 +310,7 @@ protected function parseBackground() { $token = $this->expectTokenType('Background'); - $title = trim($token['value']); + $title = trim($token['value'] ?? ''); $keyword = $token['keyword']; $line = $token['line']; @@ -364,11 +375,13 @@ protected function parseScenario() { $token = $this->expectTokenType('Scenario'); - $title = trim($token['value']); + $title = trim($token['value'] ?? ''); $tags = $this->popTags(); $keyword = $token['keyword']; $line = $token['line']; + array_push($this->passedNodesStack, 'Scenario'); + // Parse description and steps $steps = array(); while (in_array($this->predictTokenType(), array('Step', 'Newline', 'Text', 'Comment'))) { @@ -407,6 +420,8 @@ protected function parseScenario() } } + array_pop($this->passedNodesStack); + return new ScenarioNode(rtrim($title) ?: null, $tags, $steps, $keyword, $line); } @@ -421,15 +436,25 @@ protected function parseOutline() { $token = $this->expectTokenType('Outline'); - $title = trim($token['value']); + $title = trim($token['value'] ?? ''); $tags = $this->popTags(); $keyword = $token['keyword']; - $examples = null; + + /** @var ExampleTableNode $examples */ + $examples = array(); $line = $token['line']; // Parse description, steps and examples $steps = array(); - while (in_array($this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment'))) { + + array_push($this->passedNodesStack, 'Outline'); + + while (in_array($nextTokenType = $this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment', 'Tag'))) { + if ($nextTokenType === 'Comment') { + $this->lexer->skipPredictedToken(); + continue; + } + $node = $this->parseExpression(); if ($node instanceof StepNode) { @@ -438,7 +463,8 @@ protected function parseOutline() } if ($node instanceof ExampleTableNode) { - $examples = $node; + $examples[] = $node; + continue; } @@ -470,7 +496,7 @@ protected function parseOutline() } } - if (null === $examples) { + if (empty($examples)) { throw new ParserException(sprintf( 'Outline should have examples table, but got none for outline "%s" on line: %d%s', rtrim($title), @@ -496,6 +522,8 @@ protected function parseStep() $text = trim($token['text']); $line = $token['line']; + array_push($this->passedNodesStack, 'Step'); + $arguments = array(); while (in_array($predicted = $this->predictTokenType(), array('PyStringOp', 'TableRow', 'Newline', 'Comment'))) { if ('Comment' === $predicted || 'Newline' === $predicted) { @@ -510,6 +538,8 @@ protected function parseStep() } } + array_pop($this->passedNodesStack); + return new StepNode($keyword, $text, $arguments, $line, $keywordType); } @@ -520,11 +550,15 @@ protected function parseStep() */ protected function parseExamples() { - $token = $this->expectTokenType('Examples'); + $keyword = ($this->expectTokenType('Examples'))['keyword']; + $tags = empty($this->tags) ? array() : $this->popTags(); + $table = $this->parseTableRows(); - $keyword = $token['keyword']; - - return new ExampleTableNode($this->parseTableRows(), $keyword); + try { + return new ExampleTableNode($table, $keyword, $tags); + } catch(NodeException $e) { + $this->rethrowNodeException($e); + } } /** @@ -534,7 +568,13 @@ protected function parseExamples() */ protected function parseTable() { - return new TableNode($this->parseTableRows()); + $table = $this->parseTableRows(); + + try { + return new TableNode($table); + } catch(NodeException $e) { + $this->rethrowNodeException($e); + } } /** @@ -568,9 +608,30 @@ protected function parsePyString() protected function parseTags() { $token = $this->expectTokenType('Tag'); + + $this->guardTags($token['tags']); + $this->tags = array_merge($this->tags, $token['tags']); - return $this->parseExpression(); + $possibleTransitions = array( + 'Outline' => array( + 'Examples', + 'Step' + ) + ); + + $currentType = '-1'; + // check if that is ok to go inside: + if (!empty($this->passedNodesStack)) { + $currentType = $this->passedNodesStack[count($this->passedNodesStack) - 1]; + } + + $nextType = $this->predictTokenType(); + if (!isset($possibleTransitions[$currentType]) || in_array($nextType, $possibleTransitions[$currentType])) { + return $this->parseExpression(); + } + + return "\n"; } /** @@ -586,6 +647,20 @@ protected function popTags() return $tags; } + /** + * Checks the tags fit the required format + * + * @param string[] $tags + */ + protected function guardTags(array $tags) + { + foreach ($tags as $tag) { + if (preg_match('/\s/', $tag)) { + trigger_error('Whitespace in tags is deprecated, found "$tag"', E_USER_DEPRECATED); + } + } + } + /** * Parses next text line & returns it. * @@ -610,18 +685,6 @@ protected function parseNewline() return "\n"; } - /** - * Parses next comment token & returns it's string content. - * - * @return BackgroundNode|FeatureNode|OutlineNode|ScenarioNode|StepNode|TableNode|string - */ - protected function parseComment() - { - $this->expectTokenType('Comment'); - - return $this->parseExpression(); - } - /** * Parses language block and updates lexer configuration based on it. * @@ -696,4 +759,13 @@ private function normalizeStepNodeKeywordType(StepNode $node, array $steps = arr } return $node; } + + private function rethrowNodeException(NodeException $e): void + { + throw new ParserException( + $e->getMessage() . ($this->file ? ' in file ' . $this->file : ''), + 0, + $e + ); + } } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Cache/FileCacheTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Cache/FileCacheTest.php deleted file mode 100644 index 2f0b7c950..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Cache/FileCacheTest.php +++ /dev/null @@ -1,75 +0,0 @@ -assertFalse($this->cache->isFresh('unexisting', time() + 100)); - } - - public function testIsFreshOnFreshFile() - { - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, null); - - $this->cache->write('some_path', $feature); - - $this->assertFalse($this->cache->isFresh('some_path', time() + 100)); - } - - public function testIsFreshOnOutdated() - { - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, null); - - $this->cache->write('some_path', $feature); - - $this->assertTrue($this->cache->isFresh('some_path', time() - 100)); - } - - public function testCacheAndRead() - { - $scenarios = array(new ScenarioNode('Some scenario', array(), array(), null, null)); - $feature = new FeatureNode('Some feature', 'some description', array(), null, $scenarios, null, null, null, null); - - $this->cache->write('some_feature', $feature); - $featureRead = $this->cache->read('some_feature'); - - $this->assertEquals($feature, $featureRead); - } - - public function testBrokenCacheRead() - { - $this->setExpectedException('Behat\Gherkin\Exception\CacheException'); - - touch($this->path . '/v' . Gherkin::VERSION . '/' . md5('broken_feature') . '.feature.cache'); - $this->cache->read('broken_feature'); - } - - public function testUnwriteableCacheDir() - { - $this->setExpectedException('Behat\Gherkin\Exception\CacheException'); - - new FileCache('/dev/null/gherkin-test'); - } - - protected function setUp() - { - $this->cache = new FileCache($this->path = sys_get_temp_dir() . '/gherkin-test'); - } - - protected function tearDown() - { - foreach (glob($this->path . '/*.feature.cache') as $file) { - unlink((string) $file); - } - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Cache/MemoryCacheTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Cache/MemoryCacheTest.php deleted file mode 100644 index a0bb6c86f..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Cache/MemoryCacheTest.php +++ /dev/null @@ -1,51 +0,0 @@ -assertFalse($this->cache->isFresh('unexisting', time() + 100)); - } - - public function testIsFreshOnFreshFile() - { - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, null); - - $this->cache->write('some_path', $feature); - - $this->assertFalse($this->cache->isFresh('some_path', time() + 100)); - } - - public function testIsFreshOnOutdated() - { - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, null); - - $this->cache->write('some_path', $feature); - - $this->assertTrue($this->cache->isFresh('some_path', time() - 100)); - } - - public function testCacheAndRead() - { - $scenarios = array(new ScenarioNode('Some scenario', array(), array(), null, null)); - $feature = new FeatureNode('Some feature', 'some description', array(), null, $scenarios, null, null, null, null); - - $this->cache->write('some_feature', $feature); - $featureRead = $this->cache->read('some_feature'); - - $this->assertEquals($feature, $featureRead); - } - - protected function setUp() - { - $this->cache = new MemoryCache(); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/FilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/FilterTest.php deleted file mode 100644 index 994e6564a..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/FilterTest.php +++ /dev/null @@ -1,64 +0,0 @@ - array( - 'feature' => 'Feature', - 'background' => 'Background', - 'scenario' => 'Scenario', - 'scenario_outline' => 'Scenario Outline|Scenario Template', - 'examples' => 'Examples|Scenarios', - 'given' => 'Given', - 'when' => 'When', - 'then' => 'Then', - 'and' => 'And', - 'but' => 'But' - ) - )) - ) - ); - } - - protected function getGherkinFeature() - { - return << occurs - Then should be visible - - Examples: - | action | outcome | - | act#1 | out#1 | - | act#2 | out#2 | - | act#3 | out#3 | -GHERKIN; - } - - protected function getParsedFeature() - { - return $this->getParser()->parse($this->getGherkinFeature()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/Fixtures/full/file2 b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/Fixtures/full/file2 deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/Fixtures/full_path/file1 b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/Fixtures/full_path/file1 deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/LineFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/LineFilterTest.php deleted file mode 100644 index 846a719de..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/LineFilterTest.php +++ /dev/null @@ -1,103 +0,0 @@ -assertTrue($filter->isFeatureMatch($feature)); - - $filter = new LineFilter(2); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new LineFilter(3); - $this->assertFalse($filter->isFeatureMatch($feature)); - } - - public function testIsScenarioMatchFilter() - { - $scenario = new ScenarioNode(null, array(), array(), null, 2); - - $filter = new LineFilter(2); - $this->assertTrue($filter->isScenarioMatch($scenario)); - - $filter = new LineFilter(1); - $this->assertFalse($filter->isScenarioMatch($scenario)); - - $filter = new LineFilter(5); - $this->assertFalse($filter->isScenarioMatch($scenario)); - - $outline = new OutlineNode(null, array(), array(), new ExampleTableNode(array(), null), null, 20); - - $filter = new LineFilter(5); - $this->assertFalse($filter->isScenarioMatch($outline)); - - $filter = new LineFilter(20); - $this->assertTrue($filter->isScenarioMatch($outline)); - } - - public function testFilterFeatureScenario() - { - $filter = new LineFilter(2); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#1', $scenarios[0]->getTitle()); - - $filter = new LineFilter(7); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#2', $scenarios[0]->getTitle()); - - $filter = new LineFilter(5); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(0, $scenarios = $feature->getScenarios()); - } - - public function testFilterFeatureOutline() - { - $filter = new LineFilter(13); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(4, $scenarios[0]->getExampleTable()->getRows()); - - $filter = new LineFilter(19); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(2, $scenarios[0]->getExampleTable()->getRows()); - $this->assertSame(array( - array('action', 'outcome'), - array('act#1', 'out#1'), - ), $scenarios[0]->getExampleTable()->getRows()); - - $filter = new LineFilter(21); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(2, $scenarios[0]->getExampleTable()->getRows()); - $this->assertSame(array( - array('action', 'outcome'), - array('act#3', 'out#3'), - ), $scenarios[0]->getExampleTable()->getRows()); - - $filter = new LineFilter(18); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(1, $scenarios[0]->getExampleTable()->getRows()); - $this->assertSame(array( - array('action', 'outcome'), - ), $scenarios[0]->getExampleTable()->getRows()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php deleted file mode 100644 index fb8abe1ce..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/LineRangeFilterTest.php +++ /dev/null @@ -1,101 +0,0 @@ -assertSame($expected, $filter->isFeatureMatch($feature)); - } - - public function scenarioLineRangeProvider() - { - return array( - array('1', '2', 1), - array('1', '*', 2), - array('2', '2', 1), - array('2', '*', 2), - array('3', '3', 1), - array('3', '*', 1), - array('1', '1', 0), - array('4', '4', 0), - array('4', '*', 0) - ); - } - - /** - * @dataProvider scenarioLineRangeProvider - */ - public function testIsScenarioMatchFilter($filterMinLine, $filterMaxLine, $expectedNumberOfMatches) - { - $scenario = new ScenarioNode(null, array(), array(), null, 2); - $outline = new OutlineNode(null, array(), array(), new ExampleTableNode(array(), null), null, 3); - - $filter = new LineRangeFilter($filterMinLine, $filterMaxLine); - $this->assertEquals( - $expectedNumberOfMatches, - intval($filter->isScenarioMatch($scenario)) + intval($filter->isScenarioMatch($outline)) - ); - } - - public function testFilterFeatureScenario() - { - $filter = new LineRangeFilter(1, 3); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#1', $scenarios[0]->getTitle()); - - $filter = new LineRangeFilter(5, 9); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#2', $scenarios[0]->getTitle()); - - $filter = new LineRangeFilter(5, 6); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(0, $scenarios = $feature->getScenarios()); - } - - public function testFilterFeatureOutline() - { - $filter = new LineRangeFilter(12, 14); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(1, $scenarios[0]->getExampleTable()->getRows()); - - $filter = new LineRangeFilter(15, 20); - $feature = $filter->filterFeature($this->getParsedFeature()); - $this->assertCount(1, $scenarios = $feature->getScenarios()); - $this->assertSame('Scenario#3', $scenarios[0]->getTitle()); - $this->assertCount(3, $scenarios[0]->getExampleTable()->getRows()); - $this->assertSame(array( - array('action', 'outcome'), - array('act#1', 'out#1'), - array('act#2', 'out#2'), - ), $scenarios[0]->getExampleTable()->getRows()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/NameFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/NameFilterTest.php deleted file mode 100644 index 24914ecf1..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/NameFilterTest.php +++ /dev/null @@ -1,79 +0,0 @@ -assertSame($feature, $filter->filterFeature($feature)); - - $scenarios = array( - new ScenarioNode('scenario1', array(), array(), null, 2), - $matchedScenario = new ScenarioNode('scenario2', array(), array(), null, 4) - ); - $feature = new FeatureNode('feature1', null, array(), null, $scenarios, null, null, null, 1); - $filter = new NameFilter('scenario2'); - $filteredFeature = $filter->filterFeature($feature); - - $this->assertSame(array($matchedScenario), $filteredFeature->getScenarios()); - } - - public function testIsFeatureMatchFilter() - { - $feature = new FeatureNode('random feature title', null, array(), null, array(), null, null, null, 1); - - $filter = new NameFilter('feature1'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode('feature1', null, array(), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode('feature1 title', null, array(), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode('some feature1 title', null, array(), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode('some feature title', null, array(), null, array(), null, null, null, 1); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new NameFilter('/fea.ure/'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode('some feaSure title', null, array(), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode('some feture title', null, array(), null, array(), null, null, null, 1); - $this->assertFalse($filter->isFeatureMatch($feature)); - } - - public function testIsScenarioMatchFilter() - { - $filter = new NameFilter('scenario1'); - - $scenario = new ScenarioNode('UNKNOWN', array(), array(), null, 2); - $this->assertFalse($filter->isScenarioMatch($scenario)); - - $scenario = new ScenarioNode('scenario1', array(), array(), null, 2); - $this->assertTrue($filter->isScenarioMatch($scenario)); - - $scenario = new ScenarioNode('scenario1 title', array(), array(), null, 2); - $this->assertTrue($filter->isScenarioMatch($scenario)); - - $scenario = new ScenarioNode('some scenario title', array(), array(), null, 2); - $this->assertFalse($filter->isScenarioMatch($scenario)); - - $filter = new NameFilter('/sce.ario/'); - $this->assertTrue($filter->isScenarioMatch($scenario)); - - $filter = new NameFilter('/scen.rio/'); - $this->assertTrue($filter->isScenarioMatch($scenario)); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/NarrativeFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/NarrativeFilterTest.php deleted file mode 100644 index 6a50e1eb0..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/NarrativeFilterTest.php +++ /dev/null @@ -1,34 +0,0 @@ -assertFalse($filter->isFeatureMatch($feature)); - - $filter = new NarrativeFilter('/as (?:a|an) french user/i'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new NarrativeFilter('/french .*/'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new NarrativeFilter('/^french/'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new NarrativeFilter('/user$/'); - $this->assertFalse($filter->isFeatureMatch($feature)); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/PathsFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/PathsFilterTest.php deleted file mode 100644 index b53e18dc7..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/PathsFilterTest.php +++ /dev/null @@ -1,48 +0,0 @@ -assertTrue($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array('/abc', '/def', dirname(__DIR__))); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array('/abc', '/def', __DIR__)); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array('/abc', __DIR__, '/def')); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array('/abc', '/def', '/wrong/path')); - $this->assertFalse($filter->isFeatureMatch($feature)); - } - - public function testItDoesNotMatchPartialPaths() - { - $fixtures = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR; - - $feature = new FeatureNode(null, null, array(), null, array(), null, null, $fixtures . 'full_path' . DIRECTORY_SEPARATOR . 'file1', 1); - - $filter = new PathsFilter(array($fixtures . 'full')); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array($fixtures . 'full' . DIRECTORY_SEPARATOR)); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array($fixtures . 'full_path' . DIRECTORY_SEPARATOR)); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new PathsFilter(array($fixtures . 'full_path')); - $this->assertTrue($filter->isFeatureMatch($feature)); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/RoleFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/RoleFilterTest.php deleted file mode 100644 index 8e8af3e0d..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/RoleFilterTest.php +++ /dev/null @@ -1,73 +0,0 @@ -assertTrue($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('french *'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('french'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('user'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('*user'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('French User'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, 1); - $filter = new RoleFilter('French User'); - $this->assertFalse($filter->isFeatureMatch($feature)); - } - - public function testFeatureRolePrefixedWithAn() - { - $description = <<assertTrue($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('american *'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('american'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('user'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('*user'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new RoleFilter('American User'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, 1); - $filter = new RoleFilter('American User'); - $this->assertFalse($filter->isFeatureMatch($feature)); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/TagFilterTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/TagFilterTest.php deleted file mode 100644 index 5c6c9ab28..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Filter/TagFilterTest.php +++ /dev/null @@ -1,144 +0,0 @@ -assertEquals($feature, $filter->filterFeature($feature)); - - $scenarios = array( - new ScenarioNode(null, array(), array(), null, 2), - $matchedScenario = new ScenarioNode(null, array('wip'), array(), null, 4) - ); - $feature = new FeatureNode(null, null, array(), null, $scenarios, null, null, null, 1); - $filteredFeature = $filter->filterFeature($feature); - - $this->assertSame(array($matchedScenario), $filteredFeature->getScenarios()); - - $filter = new TagFilter('~@wip'); - $scenarios = array( - $matchedScenario = new ScenarioNode(null, array(), array(), null, 2), - new ScenarioNode(null, array('wip'), array(), null, 4) - ); - $feature = new FeatureNode(null, null, array(), null, $scenarios, null, null, null, 1); - $filteredFeature = $filter->filterFeature($feature); - - $this->assertSame(array($matchedScenario), $filteredFeature->getScenarios()); - } - - public function testIsFeatureMatchFilter() - { - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, 1); - - $filter = new TagFilter('@wip'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('wip'), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new TagFilter('~@done'); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('wip', 'done'), null, array(), null, null, null, 1); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('tag1', 'tag2', 'tag3'), null, array(), null, null, null, 1); - $filter = new TagFilter('@tag5,@tag4,@tag6'); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array( - 'tag1', - 'tag2', - 'tag3', - 'tag5' - ), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new TagFilter('@wip&&@vip'); - $feature = new FeatureNode(null, null, array('wip', 'done'), null, array(), null, null, null, 1); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('wip', 'done', 'vip'), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $filter = new TagFilter('@wip,@vip&&@user'); - $feature = new FeatureNode(null, null, array('wip'), null, array(), null, null, null, 1); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('vip'), null, array(), null, null, null, 1); - $this->assertFalse($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('wip', 'user'), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - - $feature = new FeatureNode(null, null, array('vip', 'user'), null, array(), null, null, null, 1); - $this->assertTrue($filter->isFeatureMatch($feature)); - } - - public function testIsScenarioMatchFilter() - { - $feature = new FeatureNode(null, null, array('feature-tag'), null, array(), null, null, null, 1); - $scenario = new ScenarioNode(null, array(), array(), null, 2); - - $filter = new TagFilter('@wip'); - $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); - - $filter = new TagFilter('~@done'); - $this->assertTrue($filter->isScenarioMatch($feature, $scenario)); - - $scenario = new ScenarioNode(null, array( - 'tag1', - 'tag2', - 'tag3' - ), array(), null, 2); - $filter = new TagFilter('@tag5,@tag4,@tag6'); - $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); - - $scenario = new ScenarioNode(null, array( - 'tag1', - 'tag2', - 'tag3', - 'tag5' - ), array(), null, 2); - $this->assertTrue($filter->isScenarioMatch($feature, $scenario)); - - $filter = new TagFilter('@wip&&@vip'); - $scenario = new ScenarioNode(null, array('wip', 'not-done'), array(), null, 2); - $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); - - $scenario = new ScenarioNode(null, array( - 'wip', - 'not-done', - 'vip' - ), array(), null, 2); - $this->assertTrue($filter->isScenarioMatch($feature, $scenario)); - - $filter = new TagFilter('@wip,@vip&&@user'); - $scenario = new ScenarioNode(null, array( - 'wip' - ), array(), null, 2); - $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); - - $scenario = new ScenarioNode(null, array('vip'), array(), null, 2); - $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); - - $scenario = new ScenarioNode(null, array('wip', 'user'), array(), null, 2); - $this->assertTrue($filter->isScenarioMatch($feature, $scenario)); - - $filter = new TagFilter('@feature-tag&&@user'); - $scenario = new ScenarioNode(null, array('wip', 'user'), array(), null, 2); - $this->assertTrue($filter->isScenarioMatch($feature, $scenario)); - - $filter = new TagFilter('@feature-tag&&@user'); - $scenario = new ScenarioNode(null, array('wip'), array(), null, 2); - $this->assertFalse($filter->isScenarioMatch($feature, $scenario)); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/directories/phps/some_file.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/directories/phps/some_file.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/addition.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/addition.yml deleted file mode 100644 index 7627fe614..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/addition.yml +++ /dev/null @@ -1,29 +0,0 @@ -feature: - title: Addition - language: en - line: 2 - description: |- - In order to avoid silly mistakes - As a math idiot - I want to be told the sum of two numbers - - scenarios: - - - type: scenario - title: Add two numbers - line: 7 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'I have entered 11 into the calculator', line: 8 } - - { keyword_type: 'Given', type: 'And', text: 'I have entered 12 into the calculator', line: 9 } - - { keyword_type: 'When', type: 'When', text: 'I press add', line: 10 } - - { keyword_type: 'Then', type: 'Then', text: 'the result should be 23 on the screen', line: 11 } - - - - type: scenario - title: Div two numbers - line: 13 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'I have entered 10 into the calculator', line: 14 } - - { keyword_type: 'Given', type: 'And', text: 'I have entered 2 into the calculator', line: 15 } - - { keyword_type: 'When', type: 'When', text: 'I press div', line: 16 } - - { keyword_type: 'Then', type: 'Then', text: 'the result should be 5 on the screen', line: 17 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/background.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/background.yml deleted file mode 100644 index a7b1a7b25..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/background.yml +++ /dev/null @@ -1,18 +0,0 @@ -feature: - title: Feature with background - language: en - line: 1 - description: ~ - - background: - line: 3 - steps: - - { keyword_type: Given, type: Given, text: a passing step, line: 4 } - - scenarios: - - - type: scenario - title: ~ - line: 6 - steps: - - { keyword_type: Given, type: Given, text: a failing step, line: 7 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/background_title.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/background_title.yml deleted file mode 100644 index f69277d74..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/background_title.yml +++ /dev/null @@ -1,26 +0,0 @@ -feature: - title: Feature with titled background - language: en - line: 1 - description: ~ - - background: - line: 3 - title: |- - Some Background - title with - couple - of - | continuous | - """ - strings - steps: - - { keyword_type: Given, type: Given, text: a passing step, line: 10 } - - scenarios: - - - type: scenario - title: ~ - line: 12 - steps: - - { keyword_type: Given, type: Given, text: a failing step, line: 13 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/big_pystring.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/big_pystring.yml deleted file mode 100644 index 31089fd5d..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/big_pystring.yml +++ /dev/null @@ -1,18 +0,0 @@ -feature: - line: 1 - title: Big PyString - - scenarios: - - - type: scenario - line: 2 - steps: - - - keyword_type: Then - type: Then - text: it should fail with: - line: 3 - arguments: - - - type: pystring - text: "\n# language: ru\n\nUUUUUU\n\n2 scenarios (2 undefined)\n6 steps (6 undefined)\n\nYou can implement step definitions for undefined steps with these snippets:\n\n$steps->Given('/^I have entered (\\d+)$/', function($world, $arg1) {\n throw new \\Everzet\\Behat\\Exception\\Pending();\n});\n\n$steps->Then('/^I must have (\\d+)$/', function($world, $arg1) {\n throw new \\Everzet\\Behat\\Exception\\Pending();\n});\n\n$steps->Then('/^String must be \\'([^\\']*)\\'$/', function($world, $arg1) {\n throw new \\Everzet\\Behat\\Exception\\Pending();\n});" diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/clean_tags.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/clean_tags.yml deleted file mode 100644 index 014c8d38a..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/clean_tags.yml +++ /dev/null @@ -1,20 +0,0 @@ -feature: - title: Feature N4 - line: 1 - description: ~ - - scenarios: - - - type: scenario - tags: [normal] - line: 4 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'Some normal step N41', line: 5 } - - { keyword_type: 'Given', type: 'And', text: 'Some fast step N42', line: 6 } - - - - type: scenario - tags: [fast] - line: 9 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'Some slow step N43', line: 10 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/commented_out.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/commented_out.yml deleted file mode 100644 index a8a2c5159..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/commented_out.yml +++ /dev/null @@ -1,10 +0,0 @@ -feature: - title: Fibonacci - language: en - line: 1 - description: |- - In order to calculate super fast fibonacci series - As a pythonista - I want to use Python for that - - scenarios: ~ diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/comments.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/comments.yml deleted file mode 100644 index 2ae46cbc1..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/comments.yml +++ /dev/null @@ -1,21 +0,0 @@ -feature: - title: Using the Console Formatter - language: en - line: 3 - description: |- - In order to verify this error # comment - - - - I want to run this feature using the progress format#comment - So that it can be fixed - - scenarios: - - - type: scenario - title: "A normal feature #comment in scenario title" - line: 19 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'I have a pending step #comment', line: 21 } - - { keyword_type: 'When', type: 'When', text: 'I run this feature with the progress format #comment', line: 24 } - - { keyword_type: 'Then', type: 'Then', text: "I should get a no method error for 'backtrace_line'", line: 31 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/complex_descriptions.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/complex_descriptions.yml deleted file mode 100644 index d0d65dc27..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/complex_descriptions.yml +++ /dev/null @@ -1,26 +0,0 @@ -feature: - title: Complex descriptions - language: en - line: 7 - description: |- - Some description with - | table | row| - - and - - """ - """ - - scenarios: - - - type: scenario - title: |- - Some - | complex | description | - - """ - hell yeah - """ - line: 16 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'one two three', line: 22 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty.yml deleted file mode 100644 index e13f36391..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty.yml +++ /dev/null @@ -1,7 +0,0 @@ -feature: - title: Some feature - language: en - line: 1 - description: ~ - - scenarios: [] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenario.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenario.yml deleted file mode 100644 index 43e7201d5..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenario.yml +++ /dev/null @@ -1,13 +0,0 @@ -feature: - title: Cucumber command line - language: en - line: 1 - description: |- - In order to write better software - Developers should be able to execute requirements as tests - - scenarios: - - - type: scenario - title: Pending Scenario at the end of a file with whitespace after it - line: 6 diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenario_without_linefeed.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenario_without_linefeed.yml deleted file mode 100644 index 43e7201d5..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenario_without_linefeed.yml +++ /dev/null @@ -1,13 +0,0 @@ -feature: - title: Cucumber command line - language: en - line: 1 - description: |- - In order to write better software - Developers should be able to execute requirements as tests - - scenarios: - - - type: scenario - title: Pending Scenario at the end of a file with whitespace after it - line: 6 diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenarios.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenarios.yml deleted file mode 100644 index 35a144ba0..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/empty_scenarios.yml +++ /dev/null @@ -1,30 +0,0 @@ -feature: - title: Math - language: en - line: 1 - description: |- - In order to avoid silly mistakes - As a math idiot - I want to be told the calculation of two numbers - - scenarios: - - - type: scenario - title: Add two numbers - line: 6 - steps: [] - - - type: scenario - title: Div two numbers - line: 7 - steps: [] - - - type: scenario - title: Multiply two numbers - line: 9 - steps: [] - - - type: scenario - title: Sub two numbers - line: 12 - steps: [] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/fibonacci.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/fibonacci.yml deleted file mode 100644 index c450a8b10..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/fibonacci.yml +++ /dev/null @@ -1,27 +0,0 @@ -feature: - title: Fibonacci - language: en - line: 1 - description: |- - In order to calculate super fast fibonacci series - As a pythonista - I want to use Python for that - - scenarios: - - - type: outline - title: Series - line: 6 - steps: - - { keyword_type: 'When', type: 'When', text: 'I ask python to calculate fibonacci up to ', line: 7 } - - { keyword_type: 'Then', type: 'Then', text: 'it should give me ', line: 8 } - - examples: - 11: [ n , series ] - 12: [ 1 , '[]' ] - 13: [ 2 , '[1, 1]' ] - 14: [ 3 , '[1, 1, 2]' ] - 15: [ 4 , '[1, 1, 2, 3]' ] - 16: [ 6 , '[1, 1, 2, 3, 5]' ] - 17: [ 9 , '[1, 1, 2, 3, 5, 8]' ] - 18: [ 100 , '[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]' ] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/hashes_in_quotes.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/hashes_in_quotes.yml deleted file mode 100644 index 55abe3b22..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/hashes_in_quotes.yml +++ /dev/null @@ -1,29 +0,0 @@ -feature: - title: "Some '#quoted' string" - language: en - line: 2 - description: |- - In order to avoid silly mistakes - As a "#math" idiot - I want to be told the sum of two numbers - - scenarios: - - - type: scenario - title: Add two numbers - line: 7 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'I have entered 11 into the calculator', line: 8 } - - { keyword_type: 'Given', type: 'And', text: 'I have entered 12 into the calculator', line: 9 } - - { keyword_type: 'When', type: 'When', text: 'I press "#add"', line: 10 } - - { keyword_type: 'Then', type: 'Then', text: 'the result should be 23 on the screen', line: 11 } - - - - type: scenario - title: 'Div "#two" numbers # as' - line: 13 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'I have entered 10 into the calculator', line: 14 } - - { keyword_type: 'Given', type: 'And', text: 'I have entered # 2 into the calculator', line: 15 } - - { keyword_type: 'When', type: 'When', text: 'I press div', line: 16 } - - { keyword_type: 'Then', type: 'Then', text: 'the result should be 5 on the screen', line: 17 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/issue_13.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/issue_13.yml deleted file mode 100644 index 091103c2f..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/issue_13.yml +++ /dev/null @@ -1,49 +0,0 @@ -feature: - title: test pystring - description: second line - language: en - line: 1 - - scenarios: - - - type: scenario - title: |- - testing py string in scenario - second line - line: 4 - steps: - - - keyword_type: Given - type: Given - text: the pystring is - line: 7 - arguments: - - - type: pystring - text: |- - Test store name - Denmark, Kolding - 6000 - - - - type: outline - title: |- - testing py string in scenario outline - second line - line: 14 - steps: - - - keyword_type: Given - type: Given - text: the pystring is - line: 17 - arguments: - arguments: - - - type: pystring - text: |- - Test store name - Denmark, Kolding - 6000 - examples: - 24: [''] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml deleted file mode 100644 index d56aa4412..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ja_addition.yml +++ /dev/null @@ -1,21 +0,0 @@ -feature: - title: '加算' - keyword: 'フィーチャ' - language: ja - line: 2 - description: |- - バカな間違いを避けるために - 数学オンチとして - 2つの数の合計を知りたい - - scenarios: - - - type: scenario - keyword: 'シナリオ' - title: '2つの数の加算について' - line: 7 - steps: - - { keyword_type: 'Given', type: '前提', type: '前提', text: '50 を入力', line: 8 } - - { keyword_type: 'Given', type: 'かつ', type: 'かつ', text: '70 を入力', line: 9 } - - { keyword_type: 'When', type: 'もし', type: 'もし', text: 'add ボタンを押した', line: 10 } - - { keyword_type: 'Then', type: 'ならば', type: 'ならば', text: '結果は 120 を表示', line: 11 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/long_title_feature.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/long_title_feature.yml deleted file mode 100644 index 7e30cf54d..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/long_title_feature.yml +++ /dev/null @@ -1,13 +0,0 @@ -feature: - title: https://rspec.lighthouseapp.com/projects/16211/tickets/246-distorted-console-output-for-slightly-complicated-step-regexp-match - language: en - line: 1 - description: ~ - - scenarios: - - - type: scenario - title: See "No Record(s) Found" for Zero Existing - line: 3 - steps: - - { keyword_type: Given, type: Given, text: no public holiday exists in the system, line: 4 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiline_name.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiline_name.yml deleted file mode 100644 index b41bc58d6..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiline_name.yml +++ /dev/null @@ -1,44 +0,0 @@ -feature: - title: multiline - language: en - line: 1 - description: ~ - - background: - line: 3 - steps: - - { keyword_type: Given, type: Given, text: passing without a table, line: 4 } - - scenarios: - - - type: scenario - title: |- - I'm a multiline name - which goes on and on and on for three lines - yawn - line: 6 - steps: - - { keyword_type: Given, type: Given, text: passing without a table, line: 9 } - - - - type: outline - title: |- - I'm a multiline name - which goes on and on and on for three lines - yawn - line: 11 - steps: - - { keyword_type: Given, type: 'Given', text: ' without a table', line: 14 } - examples: - 16: [state] - 17: [passing] - - - - type: outline - title: name - line: 19 - steps: - - { keyword_type: Given, type: 'Given', text: ' without a table', line: 20 } - examples: - 22: [state] - 23: [passing] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiline_name_with_newlines.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiline_name_with_newlines.yml deleted file mode 100644 index d7aebc06c..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiline_name_with_newlines.yml +++ /dev/null @@ -1,65 +0,0 @@ -feature: - title: multiline - language: en - line: 1 - description: |- - - Feature description - - With etc. - - background: - line: 8 - steps: - - { keyword_type: Given, type: Given, text: passing without a table, line: 11 } - - scenarios: - - - type: scenario - title: |- - - I'm a multiline name - which goes on and on and on for three lines - yawn - line: 13 - steps: - - { keyword_type: Given, type: Given, text: passing without a table, line: 19 } - - - - type: outline - title: |- - I'm a multiline name - - which goes on and on and on for three lines - - yawn - line: 21 - steps: - - { keyword_type: 'Given', type: 'Given', text: ' without a table', line: 28 } - examples: - 34: [state] - 35: [passing] - - - - type: outline - title: name - line: 37 - steps: - - { keyword_type: 'Given', type: 'Given', text: ' without a table', line: 40 } - examples: - 45: [state] - 46: [passing] - - - type: outline - title: |- - - - I'm a multiline name - which goes on and on and on for three lines - yawn - line: 48 - steps: - - { keyword_type: 'Given', type: 'Given', text: ' without a table', line: 55 } - examples: - 60: [state] - 61: [passing] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiplepystrings.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiplepystrings.yml deleted file mode 100644 index 6c73bba01..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/multiplepystrings.yml +++ /dev/null @@ -1,47 +0,0 @@ -feature: - title: A multiple py string feature - line: 1 - language: en - description: ~ - - scenarios: - - - type: scenario - title: ~ - line: 3 - steps: - - - keyword_type: When - type: When - text: I enter a string - line: 4 - arguments: - - - type: pystring - text: |- - - - a string - with something - be - a - u - ti - ful - - - - keyword_type: Then - type: Then - text: String must be - line: 15 - arguments: - - - type: pystring - text: |- - - - a string - with something - be - a - u - ti - ful diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/outline_with_spaces.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/outline_with_spaces.yml deleted file mode 100644 index c6e897725..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/outline_with_spaces.yml +++ /dev/null @@ -1,33 +0,0 @@ -feature: - title: Login - language: en - line: 1 - description: |- - To ensure the safety of the application - A regular user of the system - Must authenticate before using the app - - scenarios: - - - type: outline - title: Failed Login - line: 7 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'the user "known_user"', line: 8 } - - { keyword_type: 'When', type: 'When', text: 'I go to the main page', line: 10 } - - { keyword_type: 'Then', type: 'Then', text: 'I should see the login form', line: 11 } - - { keyword_type: 'When', type: 'When', text: 'I fill in "login" with ""', line: 13 } - - { keyword_type: 'When', type: 'And', text: 'I fill in "password" with ""', line: 14 } - - { keyword_type: 'When', type: 'And', text: 'I press "Log In"', line: 15 } - - { keyword_type: 'Then', type: 'Then', text: 'the login request should fail', line: 16 } - - { keyword_type: 'Then', type: 'And', text: 'I should see the error message "Login or Password incorrect"', line: 17 } - examples: - 20: [login, password] - 21: ['', ''] - 22: [unknown_user, ''] - 23: [known_user, ''] - 24: ['', wrong_password] - 25: ['', known_userpass] - 26: [unknown_user, wrong_password] - 27: [unknown_user, known_userpass] - 28: [known_user, wrong_password] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/outline_with_step_table.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/outline_with_step_table.yml deleted file mode 100644 index 8c59ff132..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/outline_with_step_table.yml +++ /dev/null @@ -1,29 +0,0 @@ -feature: - title: Unsubstituted argument placeholder - language: en - line: 1 - description: ~ - - scenarios: - - - type: outline - title: 'See Annual Leave Details (as Management & Human Resource)' - line: 3 - steps: - - - keyword_type: Given - type: Given - text: the following users exist in the system - line: 4 - arguments: - - - type: table - rows: - 5: [ name, email, role_assignments, group_memberships ] - 6: [ Jane, jane@fmail.com, , Sales (manager) ] - 7: [ Max, max@fmail.com, '', Sales (member) ] - 8: [ Carol, carol@fmail.com, '', Sales (member) ] - 9: [ Cat, cat@fmail.com, '', '' ] - examples: - 12: [ role ] - 13: [ HUMAN RESOURCE ] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/pystring.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/pystring.yml deleted file mode 100644 index bc21a9a95..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/pystring.yml +++ /dev/null @@ -1,22 +0,0 @@ -feature: - title: A py string feature - language: en - line: 1 - description: ~ - - scenarios: - - - type: scenario - title: ~ - line: 3 - steps: - - - keyword_type: Then - type: Then - text: I should see - line: 4 - arguments: - - - type: pystring - swallow: 6 - text: "a string with #something" diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_addition.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_addition.yml deleted file mode 100644 index 9c0db4aa2..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_addition.yml +++ /dev/null @@ -1,21 +0,0 @@ -feature: - title: Сложение чисел - keyword: Функционал - language: ru - line: 2 - description: |- - Чтобы не складывать в уме - Все, у кого с этим туго - Хотят автоматическое сложение целых чисел - - scenarios: - - - type: scenario - keyword: Сценарий - title: Сложение двух целых чисел - line: 7 - steps: - - { keyword_type: 'Given', type: 'Допустим', text: 'я ввожу число 50', line: 8 } - - { keyword_type: 'Given', type: 'И', text: 'затем ввожу число 70', line: 9 } - - { keyword_type: 'Then', type: 'Если', text: 'я нажимаю "+"', line: 10 } - - { keyword_type: 'When', type: 'То', text: 'результатом должно быть число 120', line: 11 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_commented.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_commented.yml deleted file mode 100644 index 1097d5d5f..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_commented.yml +++ /dev/null @@ -1,11 +0,0 @@ -feature: - title: Тест комментов - keyword: Функционал - language: ru - line: 7 - description: |- - i18n должен правильно считываться - Даже если в начале файла 1000 - комментов! - - scenarios: ~ diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_consecutive_calculations.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_consecutive_calculations.yml deleted file mode 100644 index 358d86787..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_consecutive_calculations.yml +++ /dev/null @@ -1,34 +0,0 @@ -feature: - title: Последовательные вычисления - keyword: Функционал - language: ru - line: 2 - description: |- - Чтобы вычислять сложные выражения - Пользователи хотят проводить вычисления над результатом предыдущей операций - - background: - keyword: Предыстория - line: 6 - steps: - - { keyword_type: Given, type: Допустим, text: я сложил 3 и 5, line: 7 } - - scenarios: - - - type: scenario - keyword: Сценарий - title: сложение с результатом последней операций - line: 9 - steps: - - { keyword_type: Then, type: Если, text: 'я ввожу число 4', line: 10 } - - { keyword_type: Then, type: И, text: 'нажимаю "+"', line: 11 } - - { keyword_type: When, type: То, text: 'результатом должно быть число 12', line: 12 } - - - type: scenario - keyword: Сценарий - title: деление результата последней операции - line: 14 - steps: - - { keyword_type: Then, type: Если, text: 'я ввожу число 2', line: 15 } - - { keyword_type: Then, type: И, text: 'нажимаю "/"', line: 16 } - - { keyword_type: When, type: То, text: 'результатом должно быть число 4', line: 17 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_division.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_division.yml deleted file mode 100644 index b4d3005c7..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/ru_division.yml +++ /dev/null @@ -1,27 +0,0 @@ -feature: - title: Деление чисел - keyword: Функционал - language: ru - line: 2 - description: |- - Поскольку деление сложный процесс и люди часто допускают ошибки - Нужно дать им возможность делить на калькуляторе - - scenarios: - - - type: outline - keyword: Структура сценария - title: Целочисленное деление - line: 6 - steps: - - { keyword_type: Given, type: 'Допустим', text: 'я ввожу число <делимое>', line: 7 } - - { keyword_type: Given, type: 'И', text: 'затем ввожу число <делитель>', line: 8 } - - { keyword_type: Then, type: 'Если', text: 'я нажимаю "/"', line: 9 } - - { keyword_type: When, type: 'То', text: 'результатом должно быть число <частное>', line: 10 } - - examples: - keyword: Значения - 13: [делимое, делитель, частное] - 14: [100, 2, 50] - 15: [28, 7, 4] - 16: [0, 5, 0] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/start_comments.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/start_comments.yml deleted file mode 100644 index 45fcd77cd..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/start_comments.yml +++ /dev/null @@ -1,18 +0,0 @@ -feature: - title: Using the Console Formatter - language: en - line: 3 - description: |- - In order to verify this error - I want to run this feature using the progress format - So that it can be fixed - - scenarios: - - - type: scenario - title: A normal feature - line: 8 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'I have a pending step', line: 9 } - - { keyword_type: 'When', type: 'When', text: 'I run this feature with the progress format', line: 10 } - - { keyword_type: 'Then', type: 'Then', text: "I should get a no method error for 'backtrace_line'", line: 11 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/tables.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/tables.yml deleted file mode 100644 index 97966665f..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/tables.yml +++ /dev/null @@ -1,42 +0,0 @@ -feature: - title: A scenario outline - language: en - line: 1 - description: ~ - - scenarios: - - - type: outline - title: ~ - line: 3 - steps: - - { keyword_type: Given, type: Given, text: I add and , line: 4 } - - - keyword_type: When - type: When - text: I pass a table argument - line: 6 - arguments: - - - type: table - rows: - 7: [foo, bar] - 8: [bar, baz] - - { keyword_type: Then, type: Then, text: I the result should be , line: 10 } - - - keyword_type: Then - type: And - text: the table should be properly escaped: - line: 12 - arguments: - - - type: table - rows: - 13: ['|a', b, c] - 14: [1, '|2', 3] - 15: [2, 3, '|4'] - - examples: - 18: [ a, b, c ] - 19: [ 1, '|2', 3 ] - 20: [ 2, 3, 4 ] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml deleted file mode 100644 index 486d9585c..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/tags_sample.yml +++ /dev/null @@ -1,35 +0,0 @@ -feature: - title: Tag samples - language: en - tags: [sample_one] - line: 2 - description: ~ - - scenarios: - - - type: scenario - title: Passing - tags: [sample_two, sample_four] - line: 5 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'missing', line: 6 } - - - - type: outline - title: ~ - tags: [sample_three] - line: 9 - steps: - - { keyword_type: 'Given', type: 'Given', text: '', line: 10 } - - examples: - 12: [state] - 13: [missing] - - - - type: scenario - title: Skipped - tags: [sample_three, sample_four] - line: 16 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'missing', line: 17 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/test_unit.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/test_unit.yml deleted file mode 100644 index fca7e2075..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/test_unit.yml +++ /dev/null @@ -1,18 +0,0 @@ -feature: - title: Test::Unit - language: en - line: 1 - description: |- - In order to please people who like Test::Unit - As a Cucumber user - I want to be able to use assert* in my step definitions - - scenarios: - - - type: scenario - title: assert_equal - line: 6 - steps: - - { keyword_type: 'Given', type: 'Given', text: 'x = 5', line: 7 } - - { keyword_type: 'Given', type: 'And', text: 'y = 5', line: 8 } - - { keyword_type: 'Then', type: 'Then', text: 'I can assert that x == y', line: 9 } diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/trimpystring.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/trimpystring.yml deleted file mode 100644 index 3857fdf3d..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/trimpystring.yml +++ /dev/null @@ -1,29 +0,0 @@ -feature: - title: A py string feature - language: en - line: 1 - description: ~ - - scenarios: - - - type: scenario - title: ~ - line: 3 - steps: - - - keyword_type: Then - type: Then - text: String must be - line: 4 - arguments: - - - type: pystring - text: |- - - - a string - with something - be - a - u - ti - ful diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/undefined_multiline_args.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/undefined_multiline_args.yml deleted file mode 100644 index 84cb8ff64..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/etalons/undefined_multiline_args.yml +++ /dev/null @@ -1,38 +0,0 @@ -feature: - title: undefined multiline args - language: en - line: 1 - description: ~ - - scenarios: - - - type: scenario - title: pystring - line: 3 - steps: - - - keyword_type: Given - type: Given - text: a pystring - line: 4 - arguments: - - - type: pystring - text: ' example' - - - - type: scenario - title: table - line: 9 - steps: - - - keyword_type: Given - type: Given - text: a table - line: 10 - arguments: - - - type: table - rows: - 11: [table] - 12: [example] diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/addition.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/addition.feature deleted file mode 100644 index 744184f16..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/addition.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -Feature: Addition - In order to avoid silly mistakes - As a math idiot - I want to be told the sum of two numbers - - Scenario: Add two numbers - Given I have entered 11 into the calculator - And I have entered 12 into the calculator - When I press add - Then the result should be 23 on the screen - - Scenario: Div two numbers - Given I have entered 10 into the calculator - And I have entered 2 into the calculator - When I press div - Then the result should be 5 on the screen diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/background.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/background.feature deleted file mode 100644 index 9a3ffb82c..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/background.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Feature with background - - Background: - Given a passing step - - Scenario: - Given a failing step \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/background_title.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/background_title.feature deleted file mode 100644 index d8ed4d3d4..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/background_title.feature +++ /dev/null @@ -1,13 +0,0 @@ -Feature: Feature with titled background - - Background: Some Background - title with - couple - of - | continuous | - """ - strings - Given a passing step - - Scenario: - Given a failing step diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/big_pystring.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/big_pystring.feature deleted file mode 100644 index 900d6c718..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/big_pystring.feature +++ /dev/null @@ -1,26 +0,0 @@ -Feature: Big PyString - Scenario: - Then it should fail with: - """ - - # language: ru - - UUUUUU - - 2 scenarios (2 undefined) - 6 steps (6 undefined) - - You can implement step definitions for undefined steps with these snippets: - - $steps->Given('/^I have entered (\d+)$/', function($world, $arg1) { - throw new \Everzet\Behat\Exception\Pending(); - }); - - $steps->Then('/^I must have (\d+)$/', function($world, $arg1) { - throw new \Everzet\Behat\Exception\Pending(); - }); - - $steps->Then('/^String must be \'([^\']*)\'$/', function($world, $arg1) { - throw new \Everzet\Behat\Exception\Pending(); - }); - """ diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/clean_tags.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/clean_tags.feature deleted file mode 100644 index e3e9cc7bd..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/clean_tags.feature +++ /dev/null @@ -1,10 +0,0 @@ -Feature: Feature N4 - - @normal - Scenario: - Given Some normal step N41 - And Some fast step N42 - - @fast - Scenario: - Given Some slow step N43 diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/commented_out.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/commented_out.feature deleted file mode 100644 index 61d7bef59..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/commented_out.feature +++ /dev/null @@ -1,34 +0,0 @@ -Feature: Fibonacci - In order to calculate super fast fibonacci series - As a pythonista - I want to use Python for that - -# -# Background: -# Given passing without a table -# -# Scenario: I'm a multiline name -# which goes on and on and on for three lines -# yawn -# Given passing without a table -# -# Scenario: -# Then I should see -# """ -# a string with #something -# """ -# -# Scenario Outline: Series -# When I ask python to calculate fibonacci up to -# Then it should give me -# -# Examples: -# | n | series | -# | 1 | [] | -# | 2 | [1, 1] | -# | 3 | [1, 1, 2] | -# | 4 | [1, 1, 2, 3] | -# | 6 | [1, 1, 2, 3, 5] | -# | 9 | [1, 1, 2, 3, 5, 8] | -# | 100 | [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] | -# diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/comments.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/comments.feature deleted file mode 100644 index 02ca25cc9..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/comments.feature +++ /dev/null @@ -1,32 +0,0 @@ -# Users want to use cucumber, so tests are necessary to verify -# it is all working as expected -Feature: Using the Console Formatter -# com -# comment -#com - In order to verify this error # comment - - - - I want to run this feature using the progress format#comment - # COMMENT - # COMMENT - # COMMENT - # COMMENT - So that it can be fixed - - #comment - Scenario: A normal feature #comment in scenario title - #comment - Given I have a pending step #comment - #comment - #comment - When I run this feature with the progress format #comment - - -#comment - #comment - #comment - - Then I should get a no method error for 'backtrace_line' - diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/complex_descriptions.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/complex_descriptions.feature deleted file mode 100644 index 4db3b874e..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/complex_descriptions.feature +++ /dev/null @@ -1,22 +0,0 @@ -# language: en - -# multiline -# comment -# YEAH - -Feature: Complex descriptions - Some description with - | table | row| - - and - - """ - """ - - Scenario: Some - | complex | description | - -""" -hell yeah -""" -Given one two three diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty.feature deleted file mode 100644 index 8a560d71d..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty.feature +++ /dev/null @@ -1,2 +0,0 @@ -Feature: Some feature -# \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenario.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenario.feature deleted file mode 100644 index 6c661c123..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenario.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Cucumber command line - In order to write better software - Developers should be able to execute requirements as tests - - - Scenario: Pending Scenario at the end of a file with whitespace after it - diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenario_without_linefeed.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenario_without_linefeed.feature deleted file mode 100644 index 7e17ff2ad..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenario_without_linefeed.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Cucumber command line - In order to write better software - Developers should be able to execute requirements as tests - - - Scenario: Pending Scenario at the end of a file with whitespace after it - # \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenarios.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenarios.feature deleted file mode 100644 index f17bef5e5..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/empty_scenarios.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Math - In order to avoid silly mistakes - As a math idiot - I want to be told the calculation of two numbers - - Scenario: Add two numbers - Scenario: Div two numbers - - Scenario: Multiply two numbers - - - Scenario: Sub two numbers diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/fibonacci.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/fibonacci.feature deleted file mode 100644 index 4a743ea72..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/fibonacci.feature +++ /dev/null @@ -1,19 +0,0 @@ -Feature: Fibonacci - In order to calculate super fast fibonacci series - As a pythonista - I want to use Python for that - - Scenario Outline: Series - When I ask python to calculate fibonacci up to - Then it should give me - - Examples: - | n | series | - | 1 | [] | - | 2 | [1, 1] | - | 3 | [1, 1, 2] | - | 4 | [1, 1, 2, 3] | - | 6 | [1, 1, 2, 3, 5] | - | 9 | [1, 1, 2, 3, 5, 8] | - | 100 | [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] | - diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/hashes_in_quotes.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/hashes_in_quotes.feature deleted file mode 100644 index e08fa7043..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/hashes_in_quotes.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: en -Feature: Some '#quoted' string - In order to avoid silly mistakes - As a "#math" idiot - I want to be told the sum of two numbers - - Scenario: Add two numbers - Given I have entered 11 into the calculator - And I have entered 12 into the calculator - When I press "#add" - Then the result should be 23 on the screen - - Scenario: Div "#two" numbers # as - Given I have entered 10 into the calculator - And I have entered # 2 into the calculator - When I press div - Then the result should be 5 on the screen diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/issue_13.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/issue_13.feature deleted file mode 100644 index 4218a974a..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/issue_13.feature +++ /dev/null @@ -1,24 +0,0 @@ -Feature: test pystring -second line - -Scenario: testing py string in scenario -second line - -Given the pystring is -""" -Test store name -Denmark, Kolding -6000 -""" - -Scenario Outline: testing py string in scenario outline -second line - -Given the pystring is -""" -Test store name -Denmark, Kolding -6000 -""" - Examples: - || diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ja_addition.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ja_addition.feature deleted file mode 100644 index b843852d9..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ja_addition.feature +++ /dev/null @@ -1,11 +0,0 @@ -# language: ja -フィーチャ: 加算 - バカな間違いを避けるために - 数学オンチとして - 2つの数の合計を知りたい - - シナリオ: 2つの数の加算について - 前提 50 を入力 - かつ 70 を入力 - もし add ボタンを押した - ならば結果は 120 を表示 \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/long_title_feature.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/long_title_feature.feature deleted file mode 100644 index a93cb4d83..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/long_title_feature.feature +++ /dev/null @@ -1,4 +0,0 @@ -Feature: https://rspec.lighthouseapp.com/projects/16211/tickets/246-distorted-console-output-for-slightly-complicated-step-regexp-match - -Scenario: See "No Record(s) Found" for Zero Existing - Given no public holiday exists in the system diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiline_name.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiline_name.feature deleted file mode 100644 index 95b9d5590..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiline_name.feature +++ /dev/null @@ -1,23 +0,0 @@ -Feature: multiline - - Background: - Given passing without a table - - Scenario: I'm a multiline name - which goes on and on and on for three lines - yawn - Given passing without a table - - Scenario Outline: I'm a multiline name - which goes on and on and on for three lines - yawn - Given without a table - Examples: - | state | - |passing| - - Scenario Outline: name - Given without a table - Examples: - | state | - |passing| diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiline_name_with_newlines.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiline_name_with_newlines.feature deleted file mode 100644 index bfc3e064e..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiline_name_with_newlines.feature +++ /dev/null @@ -1,61 +0,0 @@ -Feature: multiline - - Feature description - - With etc. - - - Background: - - - Given passing without a table - - Scenario: - I'm a multiline name - which goes on and on and on for three lines - yawn - - - Given passing without a table - - Scenario Outline: I'm a multiline name - - which goes on and on and on for three lines - - yawn - - - Given without a table - - - Examples: - - - | state | - |passing| - - Scenario Outline: name - - - Given without a table - - - Examples: - - | state | - |passing| - - Scenario Outline: - - I'm a multiline name - which goes on and on and on for three lines - yawn - - - Given without a table - - - Examples: - - | state | - |passing| diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiplepystrings.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiplepystrings.feature deleted file mode 100644 index f5526af42..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/multiplepystrings.feature +++ /dev/null @@ -1,25 +0,0 @@ -Feature: A multiple py string feature - - Scenario: - When I enter a string - """ -- - a string - with something - be - a - u - ti - ful - """ - Then String must be - """ -- - a string - with something - be - a - u - ti - ful - """ diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/outline_with_spaces.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/outline_with_spaces.feature deleted file mode 100644 index e587faada..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/outline_with_spaces.feature +++ /dev/null @@ -1,28 +0,0 @@ -Feature: Login - To ensure the safety of the application - A regular user of the system - Must authenticate before using the app - - - Scenario Outline: Failed Login - Given the user "known_user" - - When I go to the main page - Then I should see the login form - - When I fill in "login" with "" - And I fill in "password" with "" - And I press "Log In" - Then the login request should fail - And I should see the error message "Login or Password incorrect" - - Examples: - | login | password | - | | | - | unknown_user | | - | known_user | | - | | wrong_password | - | | known_userpass | - | unknown_user | wrong_password | - | unknown_user | known_userpass | - | known_user | wrong_password | diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/outline_with_step_table.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/outline_with_step_table.feature deleted file mode 100644 index 9d317719e..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/outline_with_step_table.feature +++ /dev/null @@ -1,13 +0,0 @@ -Feature: Unsubstituted argument placeholder - - Scenario Outline: See Annual Leave Details (as Management & Human Resource) - Given the following users exist in the system - | name | email | role_assignments | group_memberships | - | Jane | jane@fmail.com | | Sales (manager) | - | Max | max@fmail.com | | Sales (member) | - | Carol | carol@fmail.com | | Sales (member) | - | Cat | cat@fmail.com | | | - - Examples: - | role | - | HUMAN RESOURCE | diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/pystring.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/pystring.feature deleted file mode 100644 index 601373f25..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/pystring.feature +++ /dev/null @@ -1,8 +0,0 @@ -Feature: A py string feature - - Scenario: - Then I should see - """ - a string with #something - """ - \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_addition.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_addition.feature deleted file mode 100644 index eda57222a..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_addition.feature +++ /dev/null @@ -1,11 +0,0 @@ -# language: ru -Функционал: Сложение чисел - Чтобы не складывать в уме - Все, у кого с этим туго - Хотят автоматическое сложение целых чисел - - Сценарий: Сложение двух целых чисел - Допустим я ввожу число 50 - И затем ввожу число 70 - Если я нажимаю "+" - То результатом должно быть число 120 \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_commented.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_commented.feature deleted file mode 100644 index 36cbd054e..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_commented.feature +++ /dev/null @@ -1,10 +0,0 @@ -# Comments -# comments -# COOOOOMMEEEENTS -# -# language: ru - -Функционал: Тест комментов - i18n должен правильно считываться - Даже если в начале файла 1000 - комментов! diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_consecutive_calculations.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_consecutive_calculations.feature deleted file mode 100644 index 87cc7f2ee..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_consecutive_calculations.feature +++ /dev/null @@ -1,17 +0,0 @@ -# language: ru -Функционал: Последовательные вычисления - Чтобы вычислять сложные выражения - Пользователи хотят проводить вычисления над результатом предыдущей операций - - Предыстория: - Допустим я сложил 3 и 5 - - Сценарий: сложение с результатом последней операций - Если я ввожу число 4 - И нажимаю "+" - То результатом должно быть число 12 - - Сценарий: деление результата последней операции - Если я ввожу число 2 - И нажимаю "/" - То результатом должно быть число 4 \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_division.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_division.feature deleted file mode 100644 index 7f024f6bb..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/ru_division.feature +++ /dev/null @@ -1,16 +0,0 @@ -# language: ru -Функционал: Деление чисел - Поскольку деление сложный процесс и люди часто допускают ошибки - Нужно дать им возможность делить на калькуляторе - - Структура сценария: Целочисленное деление - Допустим я ввожу число <делимое> - И затем ввожу число <делитель> - Если я нажимаю "/" - То результатом должно быть число <частное> - - Значения: - | делимое | делитель | частное | - | 100 | 2 | 50 | - | 28 | 7 | 4 | - | 0 | 5 | 0 | \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/start_comments.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/start_comments.feature deleted file mode 100644 index 45dd501a3..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/start_comments.feature +++ /dev/null @@ -1,12 +0,0 @@ -# Users want to use cucumber, so tests are necessary to verify -# it is all working as expected -Feature: Using the Console Formatter - In order to verify this error - I want to run this feature using the progress format - So that it can be fixed - - Scenario: A normal feature - Given I have a pending step - When I run this feature with the progress format - Then I should get a no method error for 'backtrace_line' - diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/tables.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/tables.feature deleted file mode 100644 index 92f4c78fc..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/tables.feature +++ /dev/null @@ -1,20 +0,0 @@ -Feature: A scenario outline - # COMMENT - Scenario Outline: - Given I add and - # comment - When I pass a table argument - | foo | bar | - | bar | baz | - #comment - Then I the result should be - # comment - And the table should be properly escaped: - | \|a | b | c | - | 1 | \|2 | 3 | - | 2 | 3 | \|4 | -#comment - Examples: - | a | b | c | - | 1 | \|2 | 3 | - | 2 | 3 | 4 | diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature deleted file mode 100644 index 21eaaab16..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/tags_sample.feature +++ /dev/null @@ -1,17 +0,0 @@ -@sample_one -Feature: Tag samples - - @sample_two @sample_four - Scenario: Passing - Given missing - - @sample_three - Scenario Outline: - Given - Examples: - |state| - |missing| - - @sample_three @sample_four - Scenario: Skipped - Given missing \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/test_unit.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/test_unit.feature deleted file mode 100644 index ed08ba42b..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/test_unit.feature +++ /dev/null @@ -1,9 +0,0 @@ -Feature: Test::Unit - In order to please people who like Test::Unit - As a Cucumber user - I want to be able to use assert* in my step definitions - - Scenario: assert_equal - Given x = 5 - And y = 5 - Then I can assert that x == y diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/trimpystring.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/trimpystring.feature deleted file mode 100644 index 4e04950b0..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/trimpystring.feature +++ /dev/null @@ -1,14 +0,0 @@ -Feature: A py string feature - - Scenario: - Then String must be - """ - - - a string - with something - be - a - u - ti - ful - """ diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/undefined_multiline_args.feature b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/undefined_multiline_args.feature deleted file mode 100644 index ae3c7321b..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/features/undefined_multiline_args.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: undefined multiline args - - Scenario: pystring - Given a pystring - """ - example - """ - - Scenario: table - Given a table - | table | - |example| \ No newline at end of file diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/i18n.yml b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/i18n.yml deleted file mode 100644 index 398c60cf5..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Fixtures/i18n.yml +++ /dev/null @@ -1,606 +0,0 @@ - -# -# !!! DON'T TOUCH THIS FILE, IT WAS AUTODOWNLOADED FROM: -# https://github.com/cucumber/gherkin/blob/master/lib/gherkin/i18n.yml -# - -# encoding: UTF-8 -# -# We use ISO 639-1 (language) and ISO 3166 alpha-2 (region - if applicable): -# http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes -# http://en.wikipedia.org/wiki/ISO_3166-1 -# -# If you want several aliases for a keyword, just separate them -# with a | character. The * is a step keyword alias for all translations. -# -# If you do *not* want a trailing space after a keyword, end it with a < character. -# (See Chinese for examples). -# -"en": - name: English - native: English - feature: Feature - background: Background - scenario: Scenario - scenario_outline: Scenario Outline|Scenario Template - examples: Examples|Scenarios - given: "*|Given" - when: "*|When" - then: "*|Then" - and: "*|And" - but: "*|But" - -# Please keep the grammars in alphabetical order by name from here and down. - -"ar": - name: Arabic - native: العربية - feature: خاصية - background: الخلفية - scenario: سيناريو - scenario_outline: سيناريو مخطط - examples: امثلة - given: "*|بفرض" - when: "*|متى|عندما" - then: "*|اذاً|ثم" - and: "*|و" - but: "*|لكن" -"bg": - name: Bulgarian - native: български - feature: Функционалност - background: Предистория - scenario: Сценарий - scenario_outline: Рамка на сценарий - examples: Примери - given: "*|Дадено" - when: "*|Когато" - then: "*|То" - and: "*|И" - but: "*|Но" -"ca": - name: Catalan - native: català - background: Rerefons|Antecedents - feature: Característica|Funcionalitat - scenario: Escenari - scenario_outline: Esquema de l'escenari - examples: Exemples - given: "*|Donat|Donada|Atès|Atesa" - when: "*|Quan" - then: "*|Aleshores|Cal" - and: "*|I" - but: "*|Però" -"cy-GB": - name: Welsh - native: Cymraeg - background: Cefndir - feature: Arwedd - scenario: Scenario - scenario_outline: Scenario Amlinellol - examples: Enghreifftiau - given: "*|Anrhegedig a" - when: "*|Pryd" - then: "*|Yna" - and: "*|A" - but: "*|Ond" -"cs": - name: Czech - native: Česky - feature: Požadavek - background: Pozadí|Kontext - scenario: Scénář - scenario_outline: Náčrt Scénáře|Osnova scénáře - examples: Příklady - given: "*|Pokud" - when: "*|Když" - then: "*|Pak" - and: "*|A také|A" - but: "*|Ale" -"da": - name: Danish - native: dansk - feature: Egenskab - background: Baggrund - scenario: Scenarie - scenario_outline: Abstrakt Scenario - examples: Eksempler - given: "*|Givet" - when: "*|Når" - then: "*|Så" - and: "*|Og" - but: "*|Men" -"de": - name: German - native: Deutsch - feature: Funktionalität - background: Grundlage - scenario: Szenario - scenario_outline: Szenariogrundriss - examples: Beispiele - given: "*|Angenommen|Gegeben sei" - when: "*|Wenn" - then: "*|Dann" - and: "*|Und" - but: "*|Aber" -"en-au": - name: Australian - native: Australian - feature: Crikey - background: Background - scenario: Mate - scenario_outline: Blokes - examples: Cobber - given: "*|Ya know how" - when: "*|When" - then: "*|Ya gotta" - and: "*|N" - but: "*|Cept" -"en-lol": - name: LOLCAT - native: LOLCAT - feature: OH HAI - background: B4 - scenario: MISHUN - scenario_outline: MISHUN SRSLY - examples: EXAMPLZ - given: "*|I CAN HAZ" - when: "*|WEN" - then: "*|DEN" - and: "*|AN" - but: "*|BUT" -"en-pirate": - name: Pirate - native: Pirate - feature: Ahoy matey! - background: Yo-ho-ho - scenario: Heave to - scenario_outline: Shiver me timbers - examples: Dead men tell no tales - given: "*|Gangway!" - when: "*|Blimey!" - then: "*|Let go and haul" - and: "*|Aye" - but: "*|Avast!" -"en-Scouse": - name: Scouse - native: Scouse - feature: Feature - background: "Dis is what went down" - scenario: "The thing of it is" - scenario_outline: "Wharrimean is" - examples: Examples - given: "*|Givun|Youse know when youse got" - when: "*|Wun|Youse know like when" - then: "*|Dun|Den youse gotta" - and: "*|An" - but: "*|Buh" -"en-tx": - name: Texan - native: Texan - feature: Feature - background: Background - scenario: Scenario - scenario_outline: All y'all - examples: Examples - given: "*|Given y'all" - when: "*|When y'all" - then: "*|Then y'all" - and: "*|And y'all" - but: "*|But y'all" -"eo": - name: Esperanto - native: Esperanto - feature: Trajto - background: Fono - scenario: Scenaro - scenario_outline: Konturo de la scenaro - examples: Ekzemploj - given: "*|Donitaĵo" - when: "*|Se" - then: "*|Do" - and: "*|Kaj" - but: "*|Sed" -"es": - name: Spanish - native: español - background: Antecedentes - feature: Característica - scenario: Escenario - scenario_outline: Esquema del escenario - examples: Ejemplos - given: "*|Dado|Dada|Dados|Dadas" - when: "*|Cuando" - then: "*|Entonces" - and: "*|Y" - but: "*|Pero" -"et": - name: Estonian - native: eesti keel - feature: Omadus - background: Taust - scenario: Stsenaarium - scenario_outline: Raamstsenaarium - examples: Juhtumid - given: "*|Eeldades" - when: "*|Kui" - then: "*|Siis" - and: "*|Ja" - but: "*|Kuid" -"fi": - name: Finnish - native: suomi - feature: Ominaisuus - background: Tausta - scenario: Tapaus - scenario_outline: Tapausaihio - examples: Tapaukset - given: "*|Oletetaan" - when: "*|Kun" - then: "*|Niin" - and: "*|Ja" - but: "*|Mutta" -"fr": - name: French - native: français - feature: Fonctionnalité - background: Contexte - scenario: Scénario - scenario_outline: Plan du scénario|Plan du Scénario - examples: Exemples - given: "*|Soit|Etant donné|Etant donnée|Etant donnés|Etant données|Étant donné|Étant donnée|Étant donnés|Étant données" - when: "*|Quand|Lorsque|Lorsqu'<" - then: "*|Alors" - and: "*|Et" - but: "*|Mais" -"he": - name: Hebrew - native: עברית - feature: תכונה - background: רקע - scenario: תרחיש - scenario_outline: תבנית תרחיש - examples: דוגמאות - given: "*|בהינתן" - when: "*|כאשר" - then: "*|אז|אזי" - and: "*|וגם" - but: "*|אבל" -"hr": - name: Croatian - native: hrvatski - feature: Osobina|Mogućnost|Mogucnost - background: Pozadina - scenario: Scenarij - scenario_outline: Skica|Koncept - examples: Primjeri|Scenariji - given: "*|Zadan|Zadani|Zadano" - when: "*|Kada|Kad" - then: "*|Onda" - and: "*|I" - but: "*|Ali" -"hu": - name: Hungarian - native: magyar - feature: Jellemző - background: Háttér - scenario: Forgatókönyv - scenario_outline: Forgatókönyv vázlat - examples: Példák - given: "*|Amennyiben|Adott" - when: "*|Majd|Ha|Amikor" - then: "*|Akkor" - and: "*|És" - but: "*|De" -"id": - name: Indonesian - native: Bahasa Indonesia - feature: Fitur - background: Dasar - scenario: Skenario - scenario_outline: Skenario konsep - examples: Contoh - given: "*|Dengan" - when: "*|Ketika" - then: "*|Maka" - and: "*|Dan" - but: "*|Tapi" -"is": - name: Icelandic - native: Íslenska - feature: Eiginleiki - background: Bakgrunnur - scenario: Atburðarás - scenario_outline: Lýsing Atburðarásar|Lýsing Dæma - examples: Dæmi|Atburðarásir - given: "*|Ef" - when: "*|Þegar" - then: "*|Þá" - and: "*|Og" - but: "*|En" -"it": - name: Italian - native: italiano - feature: Funzionalità - background: Contesto - scenario: Scenario - scenario_outline: Schema dello scenario - examples: Esempi - given: "*|Dato|Data|Dati|Date" - when: "*|Quando" - then: "*|Allora" - and: "*|E" - but: "*|Ma" -"ja": - name: Japanese - native: 日本語 - feature: フィーチャ|機能 - background: 背景 - scenario: シナリオ - scenario_outline: シナリオアウトライン|シナリオテンプレート|テンプレ|シナリオテンプレ - examples: 例|サンプル - given: "*|前提<" - when: "*|もし<" - then: "*|ならば<" - and: "*|かつ<" - but: "*|しかし<|但し<|ただし<" -"ko": - name: Korean - native: 한국어 - background: 배경 - feature: 기능 - scenario: 시나리오 - scenario_outline: 시나리오 개요 - examples: 예 - given: "*|조건<|먼저<" - when: "*|만일<|만약<" - then: "*|그러면<" - and: "*|그리고<" - but: "*|하지만<|단<" -"lt": - name: Lithuanian - native: lietuvių kalba - feature: Savybė - background: Kontekstas - scenario: Scenarijus - scenario_outline: Scenarijaus šablonas - examples: Pavyzdžiai|Scenarijai|Variantai - given: "*|Duota" - when: "*|Kai" - then: "*|Tada" - and: "*|Ir" - but: "*|Bet" -"lu": - name: Luxemburgish - native: Lëtzebuergesch - feature: Funktionalitéit - background: Hannergrond - scenario: Szenario - scenario_outline: Plang vum Szenario - examples: Beispiller - given: "*|ugeholl" - when: "*|wann" - then: "*|dann" - and: "*|an|a" - but: "*|awer|mä" -"lv": - name: Latvian - native: latviešu - feature: Funkcionalitāte|Fīča - background: Konteksts|Situācija - scenario: Scenārijs - scenario_outline: Scenārijs pēc parauga - examples: Piemēri|Paraugs - given: "*|Kad" - when: "*|Ja" - then: "*|Tad" - and: "*|Un" - but: "*|Bet" -"nl": - name: Dutch - native: Nederlands - feature: Functionaliteit - background: Achtergrond - scenario: Scenario - scenario_outline: Abstract Scenario - examples: Voorbeelden - given: "*|Gegeven|Stel" - when: "*|Als" - then: "*|Dan" - and: "*|En" - but: "*|Maar" -"no": - name: Norwegian - native: norsk - feature: Egenskap - background: Bakgrunn - scenario: Scenario - scenario_outline: Scenariomal|Abstrakt Scenario - examples: Eksempler - given: "*|Gitt" - when: "*|Når" - then: "*|Så" - and: "*|Og" - but: "*|Men" -"pl": - name: Polish - native: polski - feature: Właściwość - background: Założenia - scenario: Scenariusz - scenario_outline: Szablon scenariusza - examples: Przykłady - given: "*|Zakładając|Mając" - when: "*|Jeżeli|Jeśli" - then: "*|Wtedy" - and: "*|Oraz|I" - but: "*|Ale" -"pt": - name: Portuguese - native: português - background: Contexto - feature: Funcionalidade - scenario: Cenário|Cenario - scenario_outline: Esquema do Cenário|Esquema do Cenario - examples: Exemplos - given: "*|Dado|Dada|Dados|Dadas" - when: "*|Quando" - then: "*|Então|Entao" - and: "*|E" - but: "*|Mas" -"ro": - name: Romanian - native: română - background: Context - feature: Functionalitate|Funcționalitate|Funcţionalitate - scenario: Scenariu - scenario_outline: Structura scenariu|Structură scenariu - examples: Exemple - given: "*|Date fiind|Dat fiind|Dati fiind|Dați fiind|Daţi fiind" - when: "*|Cand|Când" - then: "*|Atunci" - and: "*|Si|Și|Şi" - but: "*|Dar" -"ru": - name: Russian - native: русский - feature: Функция|Функционал|Свойство - background: Предыстория|Контекст - scenario: Сценарий - scenario_outline: Структура сценария - examples: Примеры - given: "*|Допустим|Дано|Пусть" - when: "*|Если|Когда" - then: "*|То|Тогда" - and: "*|И|К тому же" - but: "*|Но|А" -"sv": - name: Swedish - native: Svenska - feature: Egenskap - background: Bakgrund - scenario: Scenario - scenario_outline: Abstrakt Scenario|Scenariomall - examples: Exempel - given: "*|Givet" - when: "*|När" - then: "*|Så" - and: "*|Och" - but: "*|Men" -"sk": - name: Slovak - native: Slovensky - feature: Požiadavka - background: Pozadie - scenario: Scenár - scenario_outline: Náčrt Scenáru - examples: Príklady - given: "*|Pokiaľ" - when: "*|Keď" - then: "*|Tak" - and: "*|A" - but: "*|Ale" -"sr-Latn": - name: Serbian (Latin) - native: Srpski (Latinica) - feature: Funkcionalnost|Mogućnost|Mogucnost|Osobina - background: Kontekst|Osnova|Pozadina - scenario: Scenario|Primer - scenario_outline: Struktura scenarija|Skica|Koncept - examples: Primeri|Scenariji - given: "*|Zadato|Zadate|Zatati" - when: "*|Kada|Kad" - then: "*|Onda" - and: "*|I" - but: "*|Ali" -"sr-Cyrl": - name: Serbian - native: Српски - feature: Функционалност|Могућност|Особина - background: Контекст|Основа|Позадина - scenario: Сценарио|Пример - scenario_outline: Структура сценарија|Скица|Концепт - examples: Примери|Сценарији - given: "*|Задато|Задате|Задати" - when: "*|Када|Кад" - then: "*|Онда" - and: "*|И" - but: "*|Али" -"tr": - name: Turkish - native: Türkçe - feature: Özellik - background: Geçmiş - scenario: Senaryo - scenario_outline: Senaryo taslağı - examples: Örnekler - given: "*|Diyelim ki" - when: "*|Eğer ki" - then: "*|O zaman" - and: "*|Ve" - but: "*|Fakat|Ama" -"uk": - name: Ukrainian - native: Українська - feature: Функціонал - background: Передумова - scenario: Сценарій - scenario_outline: Структура сценарію - examples: Приклади - given: "*|Припустимо|Припустимо, що|Нехай|Дано" - when: "*|Якщо|Коли" - then: "*|То|Тоді" - and: "*|І|А також|Та" - but: "*|Але" -"uz": - name: Uzbek - native: Узбекча - feature: Функционал - background: Тарих - scenario: Сценарий - scenario_outline: Сценарий структураси - examples: Мисоллар - given: "*|Агар" - when: "*|Агар" - then: "*|Унда" - and: "*|Ва" - but: "*|Лекин|Бирок|Аммо" -"vi": - name: Vietnamese - native: Tiếng Việt - feature: Tính năng - background: Bối cảnh - scenario: Tình huống|Kịch bản - scenario_outline: Khung tình huống|Khung kịch bản - examples: Dữ liệu - given: "*|Biết|Cho" - when: "*|Khi" - then: "*|Thì" - and: "*|Và" - but: "*|Nhưng" -"zh-CN": - name: Chinese simplified - native: 简体中文 - feature: 功能 - background: 背景 - scenario: 场景 - scenario_outline: 场景大纲 - examples: 例子 - given: "*|假如<" - when: "*|当<" - then: "*|那么<" - and: "*|而且<" - but: "*|但是<" -"zh-TW": - name: Chinese traditional - native: 繁體中文 - feature: 功能 - background: 背景 - scenario: 場景|劇本 - scenario_outline: 場景大綱|劇本大綱 - examples: 例子 - given: "*|假設<" - when: "*|當<" - then: "*|那麼<" - and: "*|而且<|並且<" - but: "*|但是<" diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/GherkinTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/GherkinTest.php deleted file mode 100644 index 9e5c0cb5e..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/GherkinTest.php +++ /dev/null @@ -1,184 +0,0 @@ -getCustomFilterMock(); - $customFilter2 = $this->getCustomFilterMock(); - - $gherkin = new Gherkin(); - $gherkin->addLoader($loader = $this->getLoaderMock()); - $gherkin->addFilter($nameFilter = $this->getNameFilterMock()); - $gherkin->addFilter($tagFilter = $this->getTagFilterMock()); - - $scenario = new ScenarioNode(null, array(), array(), null, null); - $feature = new FeatureNode(null, null, array(), null, array($scenario), null, null, null, null); - - $loader - ->expects($this->once()) - ->method('supports') - ->with($resource = 'some/feature/resource') - ->will($this->returnValue(true)); - $loader - ->expects($this->once()) - ->method('load') - ->with($resource) - ->will($this->returnValue(array($feature))); - - $nameFilter - ->expects($this->once()) - ->method('filterFeature') - ->with($this->identicalTo($feature)) - ->will($this->returnValue($feature)); - $tagFilter - ->expects($this->once()) - ->method('filterFeature') - ->with($this->identicalTo($feature)) - ->will($this->returnValue($feature)); - $customFilter1 - ->expects($this->once()) - ->method('filterFeature') - ->with($this->identicalTo($feature)) - ->will($this->returnValue($feature)); - $customFilter2 - ->expects($this->once()) - ->method('filterFeature') - ->with($this->identicalTo($feature)) - ->will($this->returnValue($feature)); - - $features = $gherkin->load($resource, array($customFilter1, $customFilter2)); - $this->assertEquals(1, count($features)); - - $scenarios = $features[0]->getScenarios(); - $this->assertEquals(1, count($scenarios)); - $this->assertSame($scenario, $scenarios[0]); - } - - public function testNotFoundLoader() - { - $gherkin = new Gherkin(); - - $this->assertEquals(array(), $gherkin->load('some/feature/resource')); - } - - public function testLoaderFiltersFeatures() - { - $gherkin = new Gherkin(); - $gherkin->addLoader($loader = $this->getLoaderMock()); - $gherkin->addFilter($nameFilter = $this->getNameFilterMock()); - - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, null); - - $loader - ->expects($this->once()) - ->method('supports') - ->with($resource = 'some/feature/resource') - ->will($this->returnValue(true)); - $loader - ->expects($this->once()) - ->method('load') - ->with($resource) - ->will($this->returnValue(array($feature))); - - $nameFilter - ->expects($this->once()) - ->method('filterFeature') - ->with($this->identicalTo($feature)) - ->will($this->returnValue($feature)); - $nameFilter - ->expects($this->once()) - ->method('isFeatureMatch') - ->with($this->identicalTo($feature)) - ->will($this->returnValue(false)); - - $features = $gherkin->load($resource); - $this->assertEquals(0, count($features)); - } - - public function testSetFiltersOverridesAllFilters() - { - $gherkin = new Gherkin(); - $gherkin->addLoader($loader = $this->getLoaderMock()); - $gherkin->addFilter($nameFilter = $this->getNameFilterMock()); - $gherkin->setFilters(array()); - - $feature = new FeatureNode(null, null, array(), null, array(), null, null, null, null); - - $loader - ->expects($this->once()) - ->method('supports') - ->with($resource = 'some/feature/resource') - ->will($this->returnValue(true)); - $loader - ->expects($this->once()) - ->method('load') - ->with($resource) - ->will($this->returnValue(array($feature))); - - $nameFilter - ->expects($this->never()) - ->method('filterFeature'); - $nameFilter - ->expects($this->never()) - ->method('isFeatureMatch'); - - $features = $gherkin->load($resource); - $this->assertEquals(1, count($features)); - } - - public function testSetBasePath() - { - $gherkin = new Gherkin(); - $gherkin->addLoader($loader1 = $this->getLoaderMock()); - $gherkin->addLoader($loader2 = $this->getLoaderMock()); - - $loader1 - ->expects($this->once()) - ->method('setBasePath') - ->with($basePath = '/base/path') - ->will($this->returnValue(null)); - - $loader2 - ->expects($this->once()) - ->method('setBasePath') - ->with($basePath = '/base/path') - ->will($this->returnValue(null)); - - $gherkin->setBasePath($basePath); - } - - protected function getLoaderMock() - { - return $this->getMockBuilder('Behat\Gherkin\Loader\GherkinFileLoader') - ->disableOriginalConstructor() - ->getMock(); - } - - protected function getCustomFilterMock() - { - return $this->getMockBuilder('Behat\Gherkin\Filter\FilterInterface') - ->disableOriginalConstructor() - ->getMock(); - } - - protected function getNameFilterMock() - { - return $this->getMockBuilder('Behat\Gherkin\Filter\NameFilter') - ->disableOriginalConstructor() - ->getMock(); - } - - protected function getTagFilterMock() - { - return $this->getMockBuilder('Behat\Gherkin\Filter\TagFilter') - ->disableOriginalConstructor() - ->getMock(); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/ArrayKeywordsTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/ArrayKeywordsTest.php deleted file mode 100644 index e6c18dc94..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/ArrayKeywordsTest.php +++ /dev/null @@ -1,48 +0,0 @@ -getKeywordsArray()); - } - - protected function getKeywordsArray() - { - return array( - 'with_special_chars' => array( - 'and' => 'And/foo', - 'background' => 'Background.', - 'but' => 'But[', - 'examples' => 'Examples|Scenarios', - 'feature' => 'Feature|Business Need|Ability', - 'given' => 'Given', - 'name' => 'English', - 'native' => 'English', - 'scenario' => 'Scenario', - 'scenario_outline' => 'Scenario Outline|Scenario Template', - 'then' => 'Then', - 'when' => 'When', - ), - ); - } - - protected function getSteps($keywords, $text, &$line, $keywordType) - { - $steps = array(); - foreach (explode('|', $keywords) as $keyword) { - if (false !== mb_strpos($keyword, '<')) { - $keyword = mb_substr($keyword, 0, -1); - } - - $steps[] = new StepNode($keyword, $text, array(), $line++, $keywordType); - } - - return $steps; - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/CachedArrayKeywordsTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/CachedArrayKeywordsTest.php deleted file mode 100644 index bf10cb391..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/CachedArrayKeywordsTest.php +++ /dev/null @@ -1,37 +0,0 @@ -keywords = new ArrayKeywords(array( - 'en' => array( - 'feature' => 'Feature', - 'background' => 'Background', - 'scenario' => 'Scenario', - 'scenario_outline' => 'Scenario Outline|Scenario Template', - 'examples' => 'Examples|Scenarios', - 'given' => 'Given', - 'when' => 'When', - 'then' => 'Then', - 'and' => 'And', - 'but' => 'But' - ), - 'ru' => array( - 'feature' => 'Функционал|Фича', - 'background' => 'Предыстория|Бэкграунд', - 'scenario' => 'Сценарий|История', - 'scenario_outline' => 'Структура сценария|Аутлайн', - 'examples' => 'Значения', - 'given' => 'Допустим', - 'when' => 'Если|@', - 'then' => 'То', - 'and' => 'И', - 'but' => 'Но' - ) - )); - } - - public function testEnKeywordsDumper() - { - $dumper = new KeywordsDumper($this->keywords); - - $dumped = $dumper->dump('en'); - $etalon = << - And there is agent - When I erase agent 's memory - Then there should be agent - But there should not be agent - - (Examples|Scenarios): - | agent1 | agent2 | - | D | M | -GHERKIN; - - $this->assertEquals($etalon, $dumped); - } - - public function testRuKeywordsDumper() - { - $dumper = new KeywordsDumper($this->keywords); - - $dumped = $dumper->dump('ru'); - $etalon = << - И there is agent - (Если|@) I erase agent 's memory - То there should be agent - Но there should not be agent - - Значения: - | agent1 | agent2 | - | D | M | -GHERKIN; - - $this->assertEquals($etalon, $dumped); - } - - public function testRuKeywordsCustomKeywordsDumper() - { - $dumper = new KeywordsDumper($this->keywords); - $dumper->setKeywordsDumperFunction(function ($keywords) { - return ''.implode(', ', $keywords).''; - }); - - $dumped = $dumper->dump('ru'); - $etalon = <<Функционал, Фича: Internal operations - In order to stay secret - As a secret organization - We need to be able to erase past agents' memory - - Предыстория, Бэкграунд: - Допустим there is agent A - И there is agent B - - Сценарий, История: Erasing agent memory - Допустим there is agent J - И there is agent K - Если, @ I erase agent K's memory - То there should be agent J - Но there should not be agent K - - Структура сценария, Аутлайн: Erasing other agents' memory - Допустим there is agent - И there is agent - Если, @ I erase agent 's memory - То there should be agent - Но there should not be agent - - Значения: - | agent1 | agent2 | - | D | M | -GHERKIN; - - $this->assertEquals($etalon, $dumped); - } - - public function testExtendedVersionDumper() - { - $dumper = new KeywordsDumper($this->keywords); - - $dumped = $dumper->dump('ru', false); - $etalon = array( - << - И there is agent - Если I erase agent 's memory - @ I erase agent 's memory - То there should be agent - Но there should not be agent - - Значения: - | agent1 | agent2 | - | D | M | - - Аутлайн: Erasing other agents' memory - Допустим there is agent - И there is agent - Если I erase agent 's memory - @ I erase agent 's memory - То there should be agent - Но there should not be agent - - Значения: - | agent1 | agent2 | - | D | M | -GHERKIN - , << - И there is agent - Если I erase agent 's memory - @ I erase agent 's memory - То there should be agent - Но there should not be agent - - Значения: - | agent1 | agent2 | - | D | M | - - Аутлайн: Erasing other agents' memory - Допустим there is agent - И there is agent - Если I erase agent 's memory - @ I erase agent 's memory - То there should be agent - Но there should not be agent - - Значения: - | agent1 | agent2 | - | D | M | -GHERKIN - ); - - $this->assertEquals($etalon, $dumped); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/KeywordsTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/KeywordsTest.php deleted file mode 100644 index fdfc65e29..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Keywords/KeywordsTest.php +++ /dev/null @@ -1,139 +0,0 @@ -getKeywords(); - $lexer = new Lexer($keywords); - $parser = new Parser($lexer); - $dumper = new KeywordsDumper($keywords); - $keywordsArray = $this->getKeywordsArray(); - - // Remove languages with repeated keywords - unset($keywordsArray['en-old'], $keywordsArray['uz']); - - $data = array(); - foreach ($keywordsArray as $lang => $i18nKeywords) { - $features = array(); - foreach (explode('|', $i18nKeywords['feature']) as $transNum => $featureKeyword) { - $line = 1; - if ('en' !== $lang) { - $line = 2; - } - - $featureLine = $line; - $line += 5; - - $keywords = explode('|', $i18nKeywords['background']); - $backgroundLine = $line; - $line += 1; - $background = new BackgroundNode(null, array_merge( - $this->getSteps($i18nKeywords['given'], 'there is agent A', $line, 'Given'), - $this->getSteps($i18nKeywords['and'], 'there is agent B', $line, 'Given') - ), $keywords[0], $backgroundLine); - - $line += 1; - - $scenarios = array(); - - foreach (explode('|', $i18nKeywords['scenario']) as $scenarioKeyword) { - $scenarioLine = $line; - $line += 1; - - $steps = array_merge( - $this->getSteps($i18nKeywords['given'], 'there is agent J', $line, 'Given'), - $this->getSteps($i18nKeywords['and'], 'there is agent K', $line, 'Given'), - $this->getSteps($i18nKeywords['when'], 'I erase agent K\'s memory', $line, 'When'), - $this->getSteps($i18nKeywords['then'], 'there should be agent J', $line, 'Then'), - $this->getSteps($i18nKeywords['but'], 'there should not be agent K', $line, 'Then') - ); - - $scenarios[] = new ScenarioNode('Erasing agent memory', array(), $steps, $scenarioKeyword, $scenarioLine); - $line += 1; - } - foreach (explode('|', $i18nKeywords['scenario_outline']) as $outlineKeyword) { - $outlineLine = $line; - $line += 1; - - $steps = array_merge( - $this->getSteps($i18nKeywords['given'], 'there is agent ', $line, 'Given'), - $this->getSteps($i18nKeywords['and'], 'there is agent ', $line, 'Given'), - $this->getSteps($i18nKeywords['when'], 'I erase agent \'s memory', $line, 'When'), - $this->getSteps($i18nKeywords['then'], 'there should be agent ', $line, 'Then'), - $this->getSteps($i18nKeywords['but'], 'there should not be agent ', $line, 'Then') - ); - $line += 1; - - $keywords = explode('|', $i18nKeywords['examples']); - $table = new ExampleTableNode(array( - ++$line => array('agent1', 'agent2'), - ++$line => array('D', 'M') - ), $keywords[0]); - $line += 1; - - $scenarios[] = new OutlineNode('Erasing other agents\' memory', array(), $steps, $table, $outlineKeyword, $outlineLine); - $line += 1; - } - - $features[] = new FeatureNode( - 'Internal operations', - <<dump($lang, false, true); - $parsed = array(); - try { - foreach ($dumped as $num => $dumpedFeature) { - $parsed[] = $parser->parse($dumpedFeature, $lang . '_' . ($num + 1) . '.feature'); - } - } catch (\Exception $e) { - throw new \Exception($e->getMessage() . ":\n" . json_encode($dumped), 0, $e); - } - - $data[] = array($lang, $features, $parsed); - } - - return $data; - } - - /** - * @dataProvider translationTestDataProvider - * - * @param string $language language name - * @param array $etalon etalon features (to test against) - * @param array $features array of parsed feature(s) - */ - public function testTranslation($language, array $etalon, array $features) - { - $this->assertEquals($etalon, $features); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php deleted file mode 100644 index 697d1d3d8..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/ArrayLoaderTest.php +++ /dev/null @@ -1,379 +0,0 @@ -loader = new ArrayLoader(); - } - - public function testSupports() - { - $this->assertFalse($this->loader->supports(__DIR__)); - $this->assertFalse($this->loader->supports(__FILE__)); - $this->assertFalse($this->loader->supports('string')); - $this->assertFalse($this->loader->supports(array('wrong_root'))); - $this->assertFalse($this->loader->supports(array('features'))); - $this->assertTrue($this->loader->supports(array('features' => array()))); - $this->assertTrue($this->loader->supports(array('feature' => array()))); - } - - public function testLoadEmpty() - { - $this->assertEquals(array(), $this->loader->load(array('features' => array()))); - } - - public function testLoadFeatures() - { - $features = $this->loader->load(array( - 'features' => array( - array( - 'title' => 'First feature', - 'line' => 3, - ), - array( - 'description' => 'Second feature description', - 'language' => 'ru', - 'tags' => array('some', 'tags') - ) - ), - )); - - $this->assertEquals(2, count($features)); - - $this->assertEquals(3, $features[0]->getLine()); - $this->assertEquals('First feature', $features[0]->getTitle()); - $this->assertNull($features[0]->getDescription()); - $this->assertNull($features[0]->getFile()); - $this->assertEquals('en', $features[0]->getLanguage()); - $this->assertFalse($features[0]->hasTags()); - - $this->assertEquals(1, $features[1]->getLine()); - $this->assertNull($features[1]->getTitle()); - $this->assertEquals('Second feature description', $features[1]->getDescription()); - $this->assertNull($features[1]->getFile()); - $this->assertEquals('ru', $features[1]->getLanguage()); - $this->assertEquals(array('some', 'tags'), $features[1]->getTags()); - } - - public function testLoadScenarios() - { - $features = $this->loader->load(array( - 'features' => array( - array( - 'title' => 'Feature', - 'scenarios' => array( - array( - 'title' => 'First scenario', - 'line' => 2 - ), - array( - 'tags' => array('second', 'scenario', 'tags') - ), - array( - 'tags' => array('third', 'scenario'), - 'line' => 3 - ) - ) - ) - ), - )); - - $this->assertEquals(1, count($features)); - - $scenarios = $features[0]->getScenarios(); - - $this->assertEquals(3, count($scenarios)); - - $this->assertInstanceOf('Behat\Gherkin\Node\ScenarioNode', $scenarios[0]); - $this->assertEquals('First scenario', $scenarios[0]->getTitle()); - $this->assertFalse($scenarios[0]->hasTags()); - $this->assertEquals(2, $scenarios[0]->getLine()); - - $this->assertInstanceOf('Behat\Gherkin\Node\ScenarioNode', $scenarios[1]); - $this->assertNull($scenarios[1]->getTitle()); - $this->assertEquals(array('second', 'scenario', 'tags'), $scenarios[1]->getTags()); - $this->assertEquals(1, $scenarios[1]->getLine()); - - $this->assertInstanceOf('Behat\Gherkin\Node\ScenarioNode', $scenarios[2]); - $this->assertNull($scenarios[2]->getTitle()); - $this->assertEquals(array('third', 'scenario'), $scenarios[2]->getTags()); - $this->assertEquals(3, $scenarios[2]->getLine()); - } - - public function testLoadOutline() - { - $features = $this->loader->load(array( - 'features' => array( - array( - 'title' => 'Feature', - 'scenarios' => array( - array( - 'type' => 'outline', - 'title' => 'First outline', - 'line' => 2 - ), - array( - 'type' => 'outline', - 'tags' => array('second', 'outline', 'tags') - ) - ) - ) - ), - )); - - $this->assertEquals(1, count($features)); - - $outlines = $features[0]->getScenarios(); - - $this->assertEquals(2, count($outlines)); - - $this->assertInstanceOf('Behat\Gherkin\Node\OutlineNode', $outlines[0]); - $this->assertEquals('First outline', $outlines[0]->getTitle()); - $this->assertFalse($outlines[0]->hasTags()); - $this->assertEquals(2, $outlines[0]->getLine()); - - $this->assertInstanceOf('Behat\Gherkin\Node\OutlineNode', $outlines[1]); - $this->assertNull($outlines[1]->getTitle()); - $this->assertEquals(array('second', 'outline', 'tags'), $outlines[1]->getTags()); - $this->assertEquals(1, $outlines[1]->getLine()); - } - - public function testOutlineExamples() - { - $features = $this->loader->load(array( - 'features' => array( - array( - 'title' => 'Feature', - 'scenarios' => array( - array( - 'type' => 'outline', - 'title' => 'First outline', - 'line' => 2, - 'examples' => array( - array('user', 'pass'), - array('ever', 'sdsd'), - array('anto', 'fdfd') - ) - ), - array( - 'type' => 'outline', - 'tags' => array('second', 'outline', 'tags') - ) - ) - ) - ), - )); - - $this->assertEquals(1, count($features)); - - $scenarios = $features[0]->getScenarios(); - $scenario = $scenarios[0]; - - $this->assertEquals( - array(array('user' => 'ever', 'pass' => 'sdsd'), array('user' => 'anto', 'pass' => 'fdfd')), - $scenario->getExampleTable()->getHash() - ); - } - - public function testLoadBackground() - { - $features = $this->loader->load(array( - 'features' => array( - array( - ), - array( - 'background' => array() - ), - array( - 'background' => array( - 'line' => 2 - ) - ), - ) - )); - - $this->assertEquals(3, count($features)); - - $this->assertFalse($features[0]->hasBackground()); - $this->assertTrue($features[1]->hasBackground()); - $this->assertEquals(0, $features[1]->getBackground()->getLine()); - $this->assertTrue($features[2]->hasBackground()); - $this->assertEquals(2, $features[2]->getBackground()->getLine()); - } - - public function testLoadSteps() - { - $features = $this->loader->load(array( - 'features' => array( - array( - 'background' => array( - 'steps' => array( - array('type' => 'Gangway!', 'keyword_type' => 'Given', 'text' => 'bg step 1', 'line' => 3), - array('type' => 'Blimey!', 'keyword_type' => 'When', 'text' => 'bg step 2') - ) - ), - 'scenarios' => array( - array( - 'title' => 'Scenario', - 'steps' => array( - array('type' => 'Gangway!', 'keyword_type' => 'Given', 'text' => 'sc step 1'), - array('type' => 'Blimey!', 'keyword_type' => 'When', 'text' => 'sc step 2') - ) - ), - array( - 'title' => 'Outline', - 'type' => 'outline', - 'steps' => array( - array('type' => 'Gangway!', 'keyword_type' => 'Given', 'text' => 'out step 1'), - array('type' => 'Blimey!', 'keyword_type' => 'When', 'text' => 'out step 2') - ) - ) - ) - ) - ) - )); - - $background = $features[0]->getBackground(); - $this->assertTrue($background->hasSteps()); - $this->assertEquals(2, count($background->getSteps())); - $steps = $background->getSteps(); - $this->assertEquals('Gangway!', $steps[0]->getType()); - $this->assertEquals('Gangway!', $steps[0]->getKeyword()); - $this->assertEquals('Given', $steps[0]->getKeywordType()); - $this->assertEquals('bg step 1', $steps[0]->getText()); - $this->assertEquals(3, $steps[0]->getLine()); - $this->assertEquals('Blimey!', $steps[1]->getType()); - $this->assertEquals('Blimey!', $steps[1]->getKeyword()); - $this->assertEquals('When', $steps[1]->getKeywordType()); - $this->assertEquals('bg step 2', $steps[1]->getText()); - $this->assertEquals(1, $steps[1]->getLine()); - - $scenarios = $features[0]->getScenarios(); - - $scenario = $scenarios[0]; - $this->assertTrue($scenario->hasSteps()); - $this->assertEquals(2, count($scenario->getSteps())); - $steps = $scenario->getSteps(); - $this->assertEquals('Gangway!', $steps[0]->getType()); - $this->assertEquals('Gangway!', $steps[0]->getKeyword()); - $this->assertEquals('Given', $steps[0]->getKeywordType()); - $this->assertEquals('sc step 1', $steps[0]->getText()); - $this->assertEquals(0, $steps[0]->getLine()); - $this->assertEquals('Blimey!', $steps[1]->getType()); - $this->assertEquals('Blimey!', $steps[1]->getKeyword()); - $this->assertEquals('When', $steps[1]->getKeywordType()); - $this->assertEquals('sc step 2', $steps[1]->getText()); - $this->assertEquals(1, $steps[1]->getLine()); - - $outline = $scenarios[1]; - $this->assertTrue($outline->hasSteps()); - $this->assertEquals(2, count($outline->getSteps())); - $steps = $outline->getSteps(); - $this->assertEquals('Gangway!', $steps[0]->getType()); - $this->assertEquals('Gangway!', $steps[0]->getKeyword()); - $this->assertEquals('Given', $steps[0]->getKeywordType()); - $this->assertEquals('out step 1', $steps[0]->getText()); - $this->assertEquals(0, $steps[0]->getLine()); - $this->assertEquals('Blimey!', $steps[1]->getType()); - $this->assertEquals('Blimey!', $steps[1]->getKeyword()); - $this->assertEquals('When', $steps[1]->getKeywordType()); - $this->assertEquals('out step 2', $steps[1]->getText()); - $this->assertEquals(1, $steps[1]->getLine()); - } - - public function testLoadStepArguments() - { - $features = $this->loader->load(array( - 'features' => array( - array( - 'background' => array( - 'steps' => array( - array( - 'type' => 'Gangway!', 'keyword_type' => 'Given', 'text' => 'step with table argument', - 'arguments' => array( - array( - 'type' => 'table', - 'rows' => array( - array('key', 'val'), - array(1, 2), - array(3, 4) - ) - ) - ) - ), - array( - 'type' => 'Blimey!', 'keyword_type' => 'When', 'text' => 'step with pystring argument', - 'arguments' => array( - array( - 'type' => 'pystring', - 'text' => ' some text', - ) - ) - ), - array( - 'type' => 'Let go and haul', 'keyword_type' => 'Then', 'text' => '2nd step with pystring argument', - 'arguments' => array( - array( - 'type' => 'pystring', - 'text' => 'some text', - ) - ) - ) - ) - ) - ) - ) - )); - - $background = $features[0]->getBackground(); - - $this->assertTrue($background->hasSteps()); - - $steps = $background->getSteps(); - - $this->assertEquals(3, count($steps)); - - $arguments = $steps[0]->getArguments(); - $this->assertEquals('Gangway!', $steps[0]->getType()); - $this->assertEquals('Gangway!', $steps[0]->getKeyword()); - $this->assertEquals('Given', $steps[0]->getKeywordType()); - $this->assertEquals('step with table argument', $steps[0]->getText()); - $this->assertInstanceOf('Behat\Gherkin\Node\TableNode', $arguments[0]); - $this->assertEquals(array(array('key'=>1, 'val'=>2), array('key'=>3,'val'=>4)), $arguments[0]->getHash()); - - $arguments = $steps[1]->getArguments(); - $this->assertEquals('Blimey!', $steps[1]->getType()); - $this->assertEquals('Blimey!', $steps[1]->getKeyword()); - $this->assertEquals('When', $steps[1]->getKeywordType()); - $this->assertEquals('step with pystring argument', $steps[1]->getText()); - $this->assertInstanceOf('Behat\Gherkin\Node\PyStringNode', $arguments[0]); - $this->assertEquals(' some text', (string) $arguments[0]); - - $arguments = $steps[2]->getArguments(); - $this->assertEquals('Let go and haul', $steps[2]->getType()); - $this->assertEquals('Let go and haul', $steps[2]->getKeyword()); - $this->assertEquals('Then', $steps[2]->getKeywordType()); - $this->assertEquals('2nd step with pystring argument', $steps[2]->getText()); - $this->assertInstanceOf('Behat\Gherkin\Node\PyStringNode', $arguments[0]); - $this->assertEquals('some text', (string) $arguments[0]); - } - - public function testSingleFeatureArray() - { - $features = $this->loader->load(array( - 'feature' => array( - 'title' => 'Some feature' - ) - )); - - $this->assertEquals(1, count($features)); - $this->assertEquals('Some feature', $features[0]->getTitle()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/DirectoryLoaderTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/DirectoryLoaderTest.php deleted file mode 100644 index de3270866..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/DirectoryLoaderTest.php +++ /dev/null @@ -1,92 +0,0 @@ -gherkin = $this->createGherkinMock(); - $this->loader = new DirectoryLoader($this->gherkin); - - $this->featuresPath = realpath(__DIR__ . '/../Fixtures/directories'); - } - - protected function createGherkinMock() - { - $gherkin = $this->getMockBuilder('Behat\Gherkin\Gherkin') - ->disableOriginalConstructor() - ->getMock(); - - return $gherkin; - } - - protected function createGherkinFileLoaderMock() - { - $loader = $this->getMockBuilder('Behat\Gherkin\Loader\GherkinFileLoader') - ->disableOriginalConstructor() - ->getMock(); - - return $loader; - } - - public function testSupports() - { - $this->assertFalse($this->loader->supports('non-existent path')); - $this->assertFalse($this->loader->supports('non-existent path:2')); - - $this->assertFalse($this->loader->supports(__DIR__ . ':d')); - $this->assertFalse($this->loader->supports(__DIR__ . '/../Fixtures/features/pystring.feature')); - $this->assertTrue($this->loader->supports(__DIR__)); - $this->assertTrue($this->loader->supports(__DIR__ . '/../Fixtures/features')); - } - - public function testUndefinedFileLoad() - { - $this->gherkin - ->expects($this->once()) - ->method('resolveLoader') - ->with($this->featuresPath.DIRECTORY_SEPARATOR.'phps'.DIRECTORY_SEPARATOR.'some_file.php') - ->will($this->returnValue(null)); - - $this->assertEquals(array(), $this->loader->load($this->featuresPath . '/phps')); - } - - public function testBasePath() - { - $this->gherkin - ->expects($this->once()) - ->method('resolveLoader') - ->with($this->featuresPath.DIRECTORY_SEPARATOR.'phps'.DIRECTORY_SEPARATOR.'some_file.php') - ->will($this->returnValue(null)); - - $this->loader->setBasePath($this->featuresPath); - - $this->assertEquals(array(), $this->loader->load('phps')); - } - - public function testDefinedFileLoad() - { - $loaderMock = $this->createGherkinFileLoaderMock(); - - $this->gherkin - ->expects($this->once()) - ->method('resolveLoader') - ->with($this->featuresPath.DIRECTORY_SEPARATOR.'phps'.DIRECTORY_SEPARATOR.'some_file.php') - ->will($this->returnValue($loaderMock)); - - $loaderMock - ->expects($this->once()) - ->method('load') - ->with($this->featuresPath.DIRECTORY_SEPARATOR.'phps'.DIRECTORY_SEPARATOR.'some_file.php') - ->will($this->returnValue(array('feature1', 'feature2'))); - - $this->assertEquals(array('feature1', 'feature2'), $this->loader->load($this->featuresPath . '/phps')); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/GherkinFileLoaderTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/GherkinFileLoaderTest.php deleted file mode 100644 index 8f5d2c17d..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/GherkinFileLoaderTest.php +++ /dev/null @@ -1,111 +0,0 @@ -assertFalse($this->loader->supports('non-existent path')); - $this->assertFalse($this->loader->supports('non-existent path:2')); - - $this->assertFalse($this->loader->supports(__DIR__)); - $this->assertFalse($this->loader->supports(__DIR__ . ':d')); - $this->assertFalse($this->loader->supports(__FILE__)); - $this->assertTrue($this->loader->supports(__DIR__ . '/../Fixtures/features/pystring.feature')); - } - - public function testLoad() - { - $features = $this->loader->load($this->featuresPath . '/pystring.feature'); - $this->assertEquals(1, count($features)); - $this->assertEquals('A py string feature', $features[0]->getTitle()); - $this->assertEquals($this->featuresPath . DIRECTORY_SEPARATOR . 'pystring.feature', $features[0]->getFile()); - - $features = $this->loader->load($this->featuresPath . '/multiline_name.feature'); - $this->assertEquals(1, count($features)); - $this->assertEquals('multiline', $features[0]->getTitle()); - $this->assertEquals($this->featuresPath . DIRECTORY_SEPARATOR . 'multiline_name.feature', $features[0]->getFile()); - } - - public function testParsingUncachedFeature() - { - $cache = $this->getMockBuilder('Behat\Gherkin\Cache\CacheInterface')->getMock(); - $this->loader->setCache($cache); - - $cache->expects($this->once()) - ->method('isFresh') - ->with($path = $this->featuresPath . DIRECTORY_SEPARATOR . 'pystring.feature', filemtime($path)) - ->will($this->returnValue(false)); - - $cache->expects($this->once()) - ->method('write'); - - $features = $this->loader->load($this->featuresPath . '/pystring.feature'); - $this->assertEquals(1, count($features)); - } - - public function testParsingCachedFeature() - { - $cache = $this->getMockBuilder('Behat\Gherkin\Cache\CacheInterface')->getMock(); - $this->loader->setCache($cache); - - $cache->expects($this->once()) - ->method('isFresh') - ->with($path = $this->featuresPath . DIRECTORY_SEPARATOR . 'pystring.feature', filemtime($path)) - ->will($this->returnValue(true)); - - $cache->expects($this->once()) - ->method('read') - ->with($path) - ->will($this->returnValue('cache')); - - $cache->expects($this->never()) - ->method('write'); - - $features = $this->loader->load($this->featuresPath . '/pystring.feature'); - $this->assertEquals('cache', $features[0]); - } - - public function testBasePath() - { - $this->assertFalse($this->loader->supports('features')); - $this->assertFalse($this->loader->supports('tables.feature')); - - $this->loader->setBasePath($this->featuresPath . '/../'); - $this->assertFalse($this->loader->supports('features')); - $this->assertFalse($this->loader->supports('tables.feature')); - $this->assertTrue($this->loader->supports('features/tables.feature')); - - $features = $this->loader->load('features/pystring.feature'); - $this->assertEquals(1, count($features)); - $this->assertEquals('A py string feature', $features[0]->getTitle()); - $this->assertEquals('features' . DIRECTORY_SEPARATOR . 'pystring.feature', $features[0]->getFile()); - - $this->loader->setBasePath($this->featuresPath); - $features = $this->loader->load('multiline_name.feature'); - $this->assertEquals(1, count($features)); - $this->assertEquals('multiline', $features[0]->getTitle()); - $this->assertEquals('multiline_name.feature', $features[0]->getFile()); - } - - protected function setUp() - { - $keywords = new CucumberKeywords(__DIR__ . '/../Fixtures/i18n.yml'); - $parser = new Parser(new Lexer($keywords)); - $this->loader = new GherkinFileLoader($parser); - - $this->featuresPath = realpath(__DIR__ . '/../Fixtures/features'); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/YamlFileLoaderTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/YamlFileLoaderTest.php deleted file mode 100644 index 83b27398f..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Loader/YamlFileLoaderTest.php +++ /dev/null @@ -1,67 +0,0 @@ -loader = new YamlFileLoader(); - } - - public function testSupports() - { - $this->assertFalse($this->loader->supports(__DIR__)); - $this->assertFalse($this->loader->supports(__FILE__)); - $this->assertFalse($this->loader->supports('string')); - $this->assertFalse($this->loader->supports(__DIR__ . '/file.yml')); - $this->assertTrue($this->loader->supports(__DIR__ . '/../Fixtures/etalons/addition.yml')); - } - - public function testLoadAddition() - { - $this->loader->setBasePath(__DIR__ . '/../Fixtures'); - $features = $this->loader->load('etalons/addition.yml'); - - $this->assertEquals(1, count($features)); - $this->assertEquals('etalons'.DIRECTORY_SEPARATOR.'addition.yml', $features[0]->getFile()); - $this->assertEquals('Addition', $features[0]->getTitle()); - $this->assertEquals(2, $features[0]->getLine()); - $this->assertEquals('en', $features[0]->getLanguage()); - $expectedDescription = <<assertEquals($expectedDescription, $features[0]->getDescription()); - - $scenarios = $features[0]->getScenarios(); - - $this->assertEquals(2, count($scenarios)); - $this->assertInstanceOf('Behat\Gherkin\Node\ScenarioNode', $scenarios[0]); - $this->assertEquals(7, $scenarios[0]->getLine()); - $this->assertEquals('Add two numbers', $scenarios[0]->getTitle()); - $steps = $scenarios[0]->getSteps(); - $this->assertEquals(4, count($steps)); - $this->assertEquals(9, $steps[1]->getLine()); - $this->assertEquals('And', $steps[1]->getType()); - $this->assertEquals('And', $steps[1]->getKeyword()); - $this->assertEquals('Given', $steps[1]->getKeywordType()); - $this->assertEquals('I have entered 12 into the calculator', $steps[1]->getText()); - - $this->assertInstanceOf('Behat\Gherkin\Node\ScenarioNode', $scenarios[1]); - $this->assertEquals(13, $scenarios[1]->getLine()); - $this->assertEquals('Div two numbers', $scenarios[1]->getTitle()); - $steps = $scenarios[1]->getSteps(); - $this->assertEquals(4, count($steps)); - $this->assertEquals(16, $steps[2]->getLine()); - $this->assertEquals('When', $steps[2]->getType()); - $this->assertEquals('When', $steps[2]->getKeyword()); - $this->assertEquals('When', $steps[2]->getKeywordType()); - $this->assertEquals('I press div', $steps[2]->getText()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/ExampleNodeTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/ExampleNodeTest.php deleted file mode 100644 index c6f46be54..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/ExampleNodeTest.php +++ /dev/null @@ -1,92 +0,0 @@ -', array(), null, 'Given'), - $step2 = new StepNode('Aye!', 'my email is ', array(), null, 'And'), - $step3 = new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - $step4 = new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), - ); - - $table = new ExampleTableNode(array( - array('name', 'email'), - array('everzet', 'ever.zet@gmail.com'), - array('example', 'example@example.com') - ), 'Examples'); - - $outline = new OutlineNode(null, array(), $steps, $table, null, null); - $examples = $outline->getExamples(); - - $this->assertCount(4, $steps = $examples[0]->getSteps()); - - $this->assertEquals('Gangway!', $steps[0]->getType()); - $this->assertEquals('Gangway!', $steps[0]->getKeyword()); - $this->assertEquals('Given', $steps[0]->getKeywordType()); - $this->assertEquals('I am everzet', $steps[0]->getText()); - $this->assertEquals('Aye!', $steps[1]->getType()); - $this->assertEquals('Aye!', $steps[1]->getKeyword()); - $this->assertEquals('And', $steps[1]->getKeywordType()); - $this->assertEquals('my email is ever.zet@gmail.com', $steps[1]->getText()); - $this->assertEquals('Blimey!', $steps[2]->getType()); - $this->assertEquals('Blimey!', $steps[2]->getKeyword()); - $this->assertEquals('When', $steps[2]->getKeywordType()); - $this->assertEquals('I open homepage', $steps[2]->getText()); - - $this->assertCount(4, $steps = $examples[1]->getSteps()); - - $this->assertEquals('Gangway!', $steps[0]->getType()); - $this->assertEquals('Gangway!', $steps[0]->getKeyword()); - $this->assertEquals('Given', $steps[0]->getKeywordType()); - $this->assertEquals('I am example', $steps[0]->getText()); - $this->assertEquals('Aye!', $steps[1]->getType()); - $this->assertEquals('Aye!', $steps[1]->getKeyword()); - $this->assertEquals('And', $steps[1]->getKeywordType()); - $this->assertEquals('my email is example@example.com', $steps[1]->getText()); - $this->assertEquals('Blimey!', $steps[2]->getType()); - $this->assertEquals('Blimey!', $steps[2]->getKeyword()); - $this->assertEquals('When', $steps[2]->getKeywordType()); - $this->assertEquals('I open homepage', $steps[2]->getText()); - } - - public function testCreateExampleStepsWithArguments() - { - $steps = array( - $step1 = new StepNode('Gangway!', 'I am ', array(), null, 'Given'), - $step2 = new StepNode('Aye!', 'my email is ', array(), null, 'And'), - $step3 = new StepNode('Blimey!', 'I open:', array( - new PyStringNode(array('page: '), null) - ), null, 'When'), - $step4 = new StepNode('Let go and haul', 'website should recognise me', array( - new TableNode(array(array('page', ''))) - ), null, 'Then'), - ); - - $table = new ExampleTableNode(array( - array('name', 'email', 'url'), - array('everzet', 'ever.zet@gmail.com', 'homepage'), - array('example', 'example@example.com', 'other page') - ), 'Examples'); - - $outline = new OutlineNode(null, array(), $steps, $table, null, null); - $examples = $outline->getExamples(); - - $steps = $examples[0]->getSteps(); - - $args = $steps[2]->getArguments(); - $this->assertEquals('page: homepage', $args[0]->getRaw()); - - $args = $steps[3]->getArguments(); - $this->assertEquals('| page | homepage |', $args[0]->getTableAsString()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/OutlineNodeTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/OutlineNodeTest.php deleted file mode 100644 index 1e889b80f..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/OutlineNodeTest.php +++ /dev/null @@ -1,68 +0,0 @@ -', array(), null, 'Given'), - new StepNode('Aye!', 'my email is ', array(), null, 'And'), - new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), - ); - - $table = new ExampleTableNode(array( - array('name', 'email'), - array('everzet', 'ever.zet@gmail.com'), - array('example', 'example@example.com') - ), 'Examples'); - - $outline = new OutlineNode(null, array(), $steps, $table, null, null); - - $this->assertCount(2, $examples = $outline->getExamples()); - $this->assertEquals(1, $examples[0]->getLine()); - $this->assertEquals(2, $examples[1]->getLine()); - $this->assertEquals(array('name' => 'everzet', 'email' => 'ever.zet@gmail.com'), $examples[0]->getTokens()); - $this->assertEquals(array('name' => 'example', 'email' => 'example@example.com'), $examples[1]->getTokens()); - } - - public function testCreatesEmptyExamplesForEmptyExampleTable() - { - $steps = array( - new StepNode('Gangway!', 'I am ', array(), null, 'Given'), - new StepNode('Aye!', 'my email is ', array(), null, 'And'), - new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), - ); - - $table = new ExampleTableNode(array( - array('name', 'email') - ), 'Examples'); - - $outline = new OutlineNode(null, array(), $steps, $table, null, null); - - $this->assertCount(0, $examples = $outline->getExamples()); - } - - public function testCreatesEmptyExamplesForNoExampleTable() - { - $steps = array( - new StepNode('Gangway!', 'I am ', array(), null, 'Given'), - new StepNode('Aye!', 'my email is ', array(), null, 'And'), - new StepNode('Blimey!', 'I open homepage', array(), null, 'When'), - new StepNode('Let go and haul', 'website should recognise me', array(), null, 'Then'), - ); - - $table = new ExampleTableNode(array(), 'Examples'); - - $outline = new OutlineNode(null, array(), $steps, $table, null, null); - - $this->assertCount(0, $examples = $outline->getExamples()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/PyStringNodeTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/PyStringNodeTest.php deleted file mode 100644 index 1beed387e..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/PyStringNodeTest.php +++ /dev/null @@ -1,27 +0,0 @@ -assertEquals(array('line1', 'line2', 'line3'), $str->getStrings()); - } - - public function testGetRaw() - { - $str = new PyStringNode(array('line1', 'line2', 'line3'), 0); - - $expected = <<assertEquals($expected, $str->getRaw()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/StepNodeTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/StepNodeTest.php deleted file mode 100644 index d334415d6..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/StepNodeTest.php +++ /dev/null @@ -1,20 +0,0 @@ -setExpectedException('Behat\Gherkin\Exception\NodeException'); - - new StepNode('Gangway!', 'I am on the page:', array( - new PyStringNode(array('one', 'two'), 11), - new TableNode(array(array('one', 'two'))), - ), 10, 'Given'); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/TableNodeTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/TableNodeTest.php deleted file mode 100644 index 43dfff225..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/Node/TableNodeTest.php +++ /dev/null @@ -1,222 +0,0 @@ -assertEquals( - array( - array('username' => 'everzet', 'password' => 'qwerty') - , array('username' => 'antono', 'password' => 'pa$sword') - ), - $table->getHash() - ); - - $table = new TableNode(array( - array('username', 'password'), - array('', 'qwerty'), - array('antono', ''), - array('', '') - )); - - $this->assertEquals( - array( - array('username' => '', 'password' => 'qwerty'), - array('username' => 'antono', 'password' => ''), - array('username' => '', 'password' => ''), - ), - $table->getHash() - ); - } - - public function testIterator() - { - $table = new TableNode(array( - array('username', 'password'), - array('', 'qwerty'), - array('antono', ''), - array('', ''), - )); - - $this->assertEquals( - array( - array('username' => '', 'password' => 'qwerty'), - array('username' => 'antono', 'password' => ''), - array('username' => '', 'password' => ''), - ), - iterator_to_array($table) - ); - } - - public function testRowsHashTable() - { - $table = new TableNode(array( - array('username', 'everzet'), - array('password', 'qwerty'), - array('uid', '35'), - )); - - $this->assertEquals( - array('username' => 'everzet', 'password' => 'qwerty', 'uid' => '35'), - $table->getRowsHash() - ); - } - - public function testLongRowsHashTable() - { - $table = new TableNode(array( - array('username', 'everzet', 'marcello'), - array('password', 'qwerty', '12345'), - array('uid', '35', '22') - )); - - $this->assertEquals(array( - 'username' => array('everzet', 'marcello'), - 'password' => array('qwerty', '12345'), - 'uid' => array('35', '22') - ), $table->getRowsHash()); - } - - public function testGetRows() - { - $table = new TableNode(array( - array('username', 'password'), - array('everzet', 'qwerty'), - array('antono', 'pa$sword') - )); - - $this->assertEquals(array( - array('username', 'password'), - array('everzet', 'qwerty'), - array('antono', 'pa$sword') - ), $table->getRows()); - } - - public function testGetLines() - { - $table = new TableNode(array( - 5 => array('username', 'password'), - 10 => array('everzet', 'qwerty'), - 13 => array('antono', 'pa$sword') - )); - - $this->assertEquals(array(5, 10, 13), $table->getLines()); - } - - public function testGetRow() - { - $table = new TableNode(array( - array('username', 'password'), - array('everzet', 'qwerty'), - array('antono', 'pa$sword') - )); - - $this->assertEquals(array('username', 'password'), $table->getRow(0)); - $this->assertEquals(array('antono', 'pa$sword'), $table->getRow(2)); - } - - public function testGetColumn() - { - $table = new TableNode(array( - array('username', 'password'), - array('everzet', 'qwerty'), - array('antono', 'pa$sword') - )); - - $this->assertEquals(array('username', 'everzet', 'antono'), $table->getColumn(0)); - $this->assertEquals(array('password', 'qwerty', 'pa$sword'), $table->getColumn(1)); - - $table = new TableNode(array( - array('username'), - array('everzet'), - array('antono') - )); - - $this->assertEquals(array('username', 'everzet', 'antono'), $table->getColumn(0)); - } - - public function testGetRowWithLineNumbers() - { - $table = new TableNode(array( - 5 => array('username', 'password'), - 10 => array('everzet', 'qwerty'), - 13 => array('antono', 'pa$sword') - )); - - $this->assertEquals(array('username', 'password'), $table->getRow(0)); - $this->assertEquals(array('antono', 'pa$sword'), $table->getRow(2)); - } - - public function testGetTable() - { - $table = new TableNode($a = array( - 5 => array('username', 'password'), - 10 => array('everzet', 'qwerty'), - 13 => array('antono', 'pa$sword') - )); - - $this->assertEquals($a, $table->getTable()); - } - - public function testGetRowLine() - { - $table = new TableNode(array( - 5 => array('username', 'password'), - 10 => array('everzet', 'qwerty'), - 13 => array('antono', 'pa$sword') - )); - - $this->assertEquals(5, $table->getRowLine(0)); - $this->assertEquals(13, $table->getRowLine(2)); - } - - public function testGetRowAsString() - { - $table = new TableNode(array( - 5 => array('username', 'password'), - 10 => array('everzet', 'qwerty'), - 13 => array('antono', 'pa$sword') - )); - - $this->assertEquals('| username | password |', $table->getRowAsString(0)); - $this->assertEquals('| antono | pa$sword |', $table->getRowAsString(2)); - } - - public function testGetTableAsString() - { - $table = new TableNode(array( - 5 => array('id', 'username', 'password'), - 10 => array('42', 'everzet', 'qwerty'), - 13 => array('2', 'antono', 'pa$sword') - )); - - $expected = <<assertEquals($expected, $table->getTableAsString()); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/ParserExceptionsTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/ParserExceptionsTest.php deleted file mode 100644 index c416ec2e7..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/ParserExceptionsTest.php +++ /dev/null @@ -1,291 +0,0 @@ - array( - 'feature' => 'Feature', - 'background' => 'Background', - 'scenario' => 'Scenario', - 'scenario_outline' => 'Scenario Outline', - 'examples' => 'Examples', - 'given' => 'Given', - 'when' => 'When', - 'then' => 'Then', - 'and' => 'And', - 'but' => 'But' - ), - 'ru' => array( - 'feature' => 'Функционал', - 'background' => 'Предыстория', - 'scenario' => 'Сценарий', - 'scenario_outline' => 'Структура сценария', - 'examples' => 'Значения', - 'given' => 'Допустим', - 'when' => 'То', - 'then' => 'Если', - 'and' => 'И', - 'but' => 'Но' - ) - )); - $this->gherkin = new Parser(new Lexer($keywords)); - } - - public function testStepRightAfterFeature() - { - $feature = <<gherkin->parse($feature); - - $this->assertEquals("\n Given some step-like line", $parsed->getDescription()); - } - - public function testTextInBackground() - { - $feature = <<gherkin->parse($feature); - } - - public function testTextInScenario() - { - $feature = <<gherkin->parse($feature); - - $this->assertCount(2, $scenarios = $feature->getScenarios()); - $firstTitle = <<assertEquals($firstTitle, $scenarios[0]->getTitle()); - $secondTitle = <<assertEquals($secondTitle, $scenarios[1]->getTitle()); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testAmbigiousLanguage() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testEmptyOutline() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testWrongTagPlacement() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testBackgroundWithTag() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testEndlessPyString() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testWrongStepType() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testMultipleBackgrounds() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testMultipleFeatures() - { - $feature = <<gherkin->parse($feature); - } - - /** - * @expectedException \Behat\Gherkin\Exception\ParserException - */ - public function testTableWithoutRightBorder() - { - $feature = <<gherkin->parse($feature); - } -} diff --git a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/ParserTest.php b/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/ParserTest.php deleted file mode 100644 index aff585cda..000000000 --- a/tests/integration/vendor/behat/gherkin/tests/Behat/Gherkin/ParserTest.php +++ /dev/null @@ -1,147 +0,0 @@ -parseEtalon($fixtureName . '.yml'); - $features = $this->parseFixture($fixtureName . '.feature'); - - $this->assertInternalType('array', $features); - $this->assertEquals(1, count($features)); - $fixture = $features[0]; - - $this->assertEquals($etalon, $fixture); - } - - public function testParserResetsTagsBetweenFeatures() - { - $parser = $this->getGherkinParser(); - - $parser->parse(<<parse(<<assertFalse($feature2->hasTags()); - } - - protected function getGherkinParser() - { - if (null === $this->gherkin) { - $keywords = new ArrayKeywords(array( - 'en' => array( - 'feature' => 'Feature', - 'background' => 'Background', - 'scenario' => 'Scenario', - 'scenario_outline' => 'Scenario Outline', - 'examples' => 'Examples', - 'given' => 'Given', - 'when' => 'When', - 'then' => 'Then', - 'and' => 'And', - 'but' => 'But' - ), - 'ru' => array( - 'feature' => 'Функционал', - 'background' => 'Предыстория', - 'scenario' => 'Сценарий', - 'scenario_outline' => 'Структура сценария', - 'examples' => 'Значения', - 'given' => 'Допустим', - 'when' => 'То', - 'then' => 'Если', - 'and' => 'И', - 'but' => 'Но' - ), - 'ja' => array ( - 'feature' => 'フィーチャ', - 'background' => '背景', - 'scenario' => 'シナリオ', - 'scenario_outline' => 'シナリオアウトライン', - 'examples' => '例|サンプル', - 'given' => '前提<', - 'when' => 'もし<', - 'then' => 'ならば<', - 'and' => 'かつ<', - 'but' => 'しかし<' - ) - )); - $this->gherkin = new Parser(new Lexer($keywords)); - } - - return $this->gherkin; - } - - protected function getYamlParser() - { - if (null === $this->yaml) { - $this->yaml = new YamlFileLoader(); - } - - return $this->yaml; - } - - protected function parseFixture($fixture) - { - $file = __DIR__ . '/Fixtures/features/' . $fixture; - - return array($this->getGherkinParser()->parse(file_get_contents($file), $file)); - } - - protected function parseEtalon($etalon) - { - $features = $this->getYamlParser()->load(__DIR__ . '/Fixtures/etalons/' . $etalon); - $feature = $features[0]; - - return new FeatureNode( - $feature->getTitle(), - $feature->getDescription(), - $feature->getTags(), - $feature->getBackground(), - $feature->getScenarios(), - $feature->getKeyword(), - $feature->getLanguage(), - __DIR__ . '/Fixtures/features/' . basename($etalon, '.yml') . '.feature', - $feature->getLine() - ); - } -} diff --git a/tests/integration/vendor/behat/transliterator/.gitignore b/tests/integration/vendor/behat/transliterator/.gitignore deleted file mode 100644 index a79486fe9..000000000 --- a/tests/integration/vendor/behat/transliterator/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor -composer.lock -phpunit.xml diff --git a/tests/integration/vendor/behat/transliterator/.travis.yml b/tests/integration/vendor/behat/transliterator/.travis.yml deleted file mode 100644 index a0aa87550..000000000 --- a/tests/integration/vendor/behat/transliterator/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: php - -sudo: false - -php: [5.3, 5.4, 5.5, 5.6, nightly, hhvm] - -matrix: - fast_finish: true - allow_failures: - - php: nightly - -cache: - directories: - - $HOME/.composer/cache - -install: - - composer install - -script: phpunit -v --coverage-clover=coverage.clover - -after_script: - - if [[ "$TRAVIS_PHP_VERSION" != "nightly" ]] && [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/tests/integration/vendor/behat/transliterator/CHANGELOG.md b/tests/integration/vendor/behat/transliterator/CHANGELOG.md index d441b7067..a6cf15ecc 100644 --- a/tests/integration/vendor/behat/transliterator/CHANGELOG.md +++ b/tests/integration/vendor/behat/transliterator/CHANGELOG.md @@ -1,15 +1,22 @@ -1.1.0 / 2015-09-28 -================== +# 1.3.0 / 2020-01-14 - * Updated unicode bank files - * Added a testsuite for the library +- Fix existing Travis CI builds +- Add CI builds for all PHP versions between 5.3 and 7.4 +- Add preliminary support for PHP 7.4 -1.0.1 / 2014-05-14 -================== +# 1.2.0 / 2017-04-04 - * fixed the regex used to replace non-word characters +- Stop Transliterator::postProcessText() breaking words containing apostrophes -1.0.0 / 2014-01-12 -================== +# 1.1.0 / 2015-09-28 - * Initial release as a standalone component +- Updated unicode bank files +- Added a testsuite for the library + +# 1.0.1 / 2014-05-14 + +- fixed the regex used to replace non-word characters + +# 1.0.0 / 2014-01-12 + +- Initial release as a standalone component diff --git a/tests/integration/vendor/behat/transliterator/CONTRIBUTING.md b/tests/integration/vendor/behat/transliterator/CONTRIBUTING.md new file mode 100644 index 000000000..29d047f53 --- /dev/null +++ b/tests/integration/vendor/behat/transliterator/CONTRIBUTING.md @@ -0,0 +1,18 @@ +Contributing to Behat Transliterator +==================================== + +Updating data +------------- + +Setup dependencies with [Composer](https://getcomposer.org): + +```bash +composer install +``` + +Run, char tables in Behat Transliterator will be synced from Perl library +using version defined in `\Behat\Transliterator\SyncTool::LIB_VERSION` + +```bash +bin/update-data +``` diff --git a/tests/integration/vendor/behat/transliterator/composer.json b/tests/integration/vendor/behat/transliterator/composer.json index 79e444b36..fd1cd0ab1 100644 --- a/tests/integration/vendor/behat/transliterator/composer.json +++ b/tests/integration/vendor/behat/transliterator/composer.json @@ -1,29 +1,34 @@ { - "name": "behat/transliterator", - "description": "String transliterator", - "keywords": ["transliterator", "slug", "i18n"], - "type": "library", - "license": "Artistic-1.0", - - "require": { - "php": ">=5.3.3" - }, - - "autoload": { - "psr-0": { - "Behat\\Transliterator": "src/" - } - }, - - "autoload-dev": { - "psr-4": { - "Behat\\Tests\\Transliterator\\": "tests" - } - }, - - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } + "name": "behat/transliterator", + "description": "String transliterator", + "keywords": [ + "transliterator", + "slug", + "i18n" + ], + "type": "library", + "license": "Artistic-1.0", + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36|^6.3", + "php-yaoi/php-yaoi": "^1.0", + "chuyskywalker/rolling-curl": "^3.1" + }, + "autoload": { + "psr-4": { + "Behat\\Transliterator\\": "src/Behat/Transliterator" } + }, + "autoload-dev": { + "psr-4": { + "Behat\\Tests\\Transliterator\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + } } diff --git a/tests/integration/vendor/behat/transliterator/phpunit.xml.dist b/tests/integration/vendor/behat/transliterator/phpunit.xml.dist deleted file mode 100644 index 2f67f11b4..000000000 --- a/tests/integration/vendor/behat/transliterator/phpunit.xml.dist +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - tests - - - - - - ./src - - - diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php new file mode 100644 index 000000000..87a07c72e --- /dev/null +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php @@ -0,0 +1,213 @@ + '}', + '\\\\' => '\\', + '\\{' => '{', + '\\@' => '@', + '\\$' => '$', + ); + + $this->tokenizer = new Parser(); + $this->tokenizer->addLineStopper('#'); + $this->tokenizer->addQuote('qq{', '}', $escape); + $this->tokenizer->addQuote('q{', '}', $escape); + $this->tokenizer->addQuote('"', '"'); + $this->tokenizer->addQuote("'", "'"); + $this->tokenizer->addBracket('[', ']'); + $this->tokenizer->addDelimiter(';'); + + $this->renderer = new Renderer(); + $this->renderer + ->setBindKey('-~z', 'z~-') + ->strip('#') + ->keepBoundaries('['); + } + + public static function setUpDefinition(\Yaoi\Command\Definition $definition, $options) + { + $definition->name = 'update-data'; + $definition->description = 'Tool for converting char tables for Behat/Transliterator from Perl to PHP'; + } + + public function performAction() + { + $rollingCurl = new RollingCurl(); + + foreach ($this->getPerlTablesUrlList() as $url) { + $rollingCurl->get($url); + } + + $rollingCurl->setCallback(function (Request $request, RollingCurl $rollingCurl) { + $this->response->addContent($request->getUrl()); + $content = $request->getResponseText(); + $this->parsePerlTable($content); + }) + ->execute(); + } + + private function removePhpCharTable($phpFilePath, $reason) + { + $this->response->addContent($reason); + if (file_exists($phpFilePath)) { + if (unlink($phpFilePath)) { + $this->response->success('Deleted'); + } else { + $this->response->error('Failed to delete'); + } + } else { + $this->response->success('No PHP file, skipped'); + } + } + + private function pushItem($item) + { + if ($this->itemIndex >= 16) { + $this->phpTable = trim($this->phpTable); + $this->phpTable .= "\n"; + $this->itemIndex = 0; + } + ++$this->itemIndex; + + $item = new StringValue($item); + if ($item->starts('\x') || $item->starts('\n')) { + $this->phpTable .= '"' . $item . '", '; + $this->nonQuestionBoxFound = true; + } else { + // TODO check if this hack should be removed for chinese letters + if ($item->value === '[?] ') { + $item->value = '[?]'; + } + // + + if ($item->value !== '[?]') { + $this->nonQuestionBoxFound = true; + } + + $this->phpTable .= "'" . str_replace(array('\\', '\''), array('\\\\', '\\\''), $item) . "', "; + } + } + + private function tokenizePerlTable($content) + { + $tokens = $this->tokenizer->tokenize($content); + + $expression = $this->renderer->getExpression($tokens); + $statement = $expression->getStatement(); + /** @var Parsed[] $binds */ + $binds = $expression->getBinds(); + + $parser = new StringParser($statement); + $block = (string)$parser->inner('$Text::Unidecode::Char[', ']'); + if (!$block) { + throw new \Exception('Block not found'); + } + $this->block = $this->renderer->getExpression($binds[$block])->getStatement(); + + $itemsBind = (string)$parser->inner('[', ']'); + + if (!$itemsBind) { + $items = array(); + } + else { + $items = $binds[$itemsBind]; + } + + return $items; + } + + private function parsePerlTable($content) + { + $items = $this->tokenizePerlTable($content); + + $phpFilePath = __DIR__ . '/data/' . substr($this->block, 1) . '.php'; + if (!$items) { + $this->removePhpCharTable($phpFilePath, 'Empty char table for block ' . $this->block); + return; + } + + $this->phpTable = <<block] = array( + +PHP; + + $itemsExpression = $this->renderer->getExpression($items); + $itemsStatement = $itemsExpression->getStatement(); + $itemsBinds = $itemsExpression->getBinds(); + + $itemsStatement = explode(',', $itemsStatement); + $this->itemIndex = 0; + $this->nonQuestionBoxFound = false; + foreach ($itemsStatement as $item) { + $item = trim($item); + if (!$item) { + break; + } + + if (isset($itemsBinds[$item])) { + /** @var Token $token */ + $token = $itemsBinds[$item]; + $item = $token->unEscapedContent; + } + + $this->pushItem($item); + } + + if ($this->nonQuestionBoxFound) { + $this->phpTable = trim($this->phpTable) . "\n" . ');' . "\n"; + if (file_put_contents($phpFilePath, $this->phpTable)) { + $this->response->success('Block ' . $this->block . ' converted to ' . $phpFilePath); + } else { + $this->response->error('Failed to save ' . $phpFilePath); + } + } else { + $this->removePhpCharTable($phpFilePath, 'Block ' . $this->block . ' contains only [?]'); + } + + } + + private function getPerlTablesUrlList() + { + $client = new Client(); + $list = array(); + $page = $client->fetch('http://cpansearch.perl.org/src/SBURKE/Text-Unidecode-' . self::LIB_VERSION . '/lib/Text/Unidecode/'); + foreach (StringParser::create($page)->innerAll('.pm">', '') as $xXXpm) { + $list[] = 'http://cpansearch.perl.org/src/SBURKE/Text-Unidecode-' . self::LIB_VERSION . '/lib/Text/Unidecode/' + . $xXXpm; + } + return $list; + } +} + diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php index 500ec5aa7..5847993b9 100644 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php @@ -354,26 +354,26 @@ public static function utf8ToAscii($str, $unknown = '?') $chars = $ar[0]; foreach ($chars as $i => $c) { - if (ord($c{0}) >= 0 && ord($c{0}) <= 127) { + if (ord($c[0]) >= 0 && ord($c[0]) <= 127) { continue; } // ASCII - next please - if (ord($c{0}) >= 192 && ord($c{0}) <= 223) { - $ord = (ord($c{0}) - 192) * 64 + (ord($c{1}) - 128); + if (ord($c[0]) >= 192 && ord($c[0]) <= 223) { + $ord = (ord($c[0]) - 192) * 64 + (ord($c[1]) - 128); } - if (ord($c{0}) >= 224 && ord($c{0}) <= 239) { - $ord = (ord($c{0}) - 224) * 4096 + (ord($c{1}) - 128) * 64 + (ord($c{2}) - 128); + if (ord($c[0]) >= 224 && ord($c[0]) <= 239) { + $ord = (ord($c[0]) - 224) * 4096 + (ord($c[1]) - 128) * 64 + (ord($c[2]) - 128); } - if (ord($c{0}) >= 240 && ord($c{0}) <= 247) { - $ord = (ord($c{0}) - 240) * 262144 + (ord($c{1}) - 128) * 4096 + (ord($c{2}) - 128) * 64 + (ord($c{3}) - 128); + if (ord($c[0]) >= 240 && ord($c[0]) <= 247) { + $ord = (ord($c[0]) - 240) * 262144 + (ord($c[1]) - 128) * 4096 + (ord($c[2]) - 128) * 64 + (ord($c[3]) - 128); } - if (ord($c{0}) >= 248 && ord($c{0}) <= 251) { - $ord = (ord($c{0}) - 248) * 16777216 + (ord($c{1}) - 128) * 262144 + (ord($c{2}) - 128) * 4096 + (ord($c{3}) - 128) * 64 + (ord($c{4}) - 128); + if (ord($c[0]) >= 248 && ord($c[0]) <= 251) { + $ord = (ord($c[0]) - 248) * 16777216 + (ord($c[1]) - 128) * 262144 + (ord($c[2]) - 128) * 4096 + (ord($c[3]) - 128) * 64 + (ord($c[4]) - 128); } - if (ord($c{0}) >= 252 && ord($c{0}) <= 253) { - $ord = (ord($c{0}) - 252) * 1073741824 + (ord($c{1}) - 128) * 16777216 + (ord($c{2}) - 128) * 262144 + (ord($c{3}) - 128) * 4096 + (ord($c{4}) - 128) * 64 + (ord($c{5}) - 128); + if (ord($c[0]) >= 252 && ord($c[0]) <= 253) { + $ord = (ord($c[0]) - 252) * 1073741824 + (ord($c[1]) - 128) * 16777216 + (ord($c[2]) - 128) * 262144 + (ord($c[3]) - 128) * 4096 + (ord($c[4]) - 128) * 64 + (ord($c[5]) - 128); } - if (ord($c{0}) >= 254 && ord($c{0}) <= 255) { - $chars{$i} = $unknown; + if (ord($c[0]) >= 254 && ord($c[0]) <= 255) { + $chars[$i] = $unknown; continue; } //error @@ -390,9 +390,9 @@ public static function utf8ToAscii($str, $unknown = '?') $newchar = $ord & 255; if (array_key_exists($newchar, $UTF8_TO_ASCII[$bank])) { - $chars{$i} = $UTF8_TO_ASCII[$bank][$newchar]; + $chars[$i] = $UTF8_TO_ASCII[$bank][$newchar]; } else { - $chars{$i} = $unknown; + $chars[$i] = $unknown; } } @@ -460,7 +460,7 @@ public static function validUtf8($str) $len = strlen($str); for ($i = 0; $i < $len; ++$i) { - $in = ord($str{$i}); + $in = ord($str[$i]); if ($mState == 0) { // When mState is zero we expect either a US-ASCII character or a // multi-octet sequence. @@ -573,7 +573,10 @@ private static function postProcessText($text, $separator) $text = strtolower($text); } - // Remove all none word characters + // Remove apostrophes which are not used as quotes around a string + $text = preg_replace('/(\\w)\'(\\w)/', '${1}${2}', $text); + + // Replace all none word characters with a space $text = preg_replace('/\W/', ' ', $text); // More stripping. Replace spaces with dashes diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php index 2ee8bf0ab..9177c746c 100644 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php @@ -5,15 +5,15 @@ ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', -'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ']', '\\', ']', '^', '_', +'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', "\x7f", -'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', -'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'EUR', '', ',', 'f', ',,', '...', '+', '++', '^', '%0', 'S', '<', 'OE', '', 'Z', '', +'', '\'', '\'', '"', '"', '*', '-', '--', '~', 'tm', 's', '>', 'oe', '', 'z', 'Y', ' ', '!', 'C/', 'PS', '$?', 'Y=', '|', 'SS', '"', '(c)', 'a', '<<', '!', '', '(r)', '-', 'deg', '+-', '2', '3', '\'', 'u', 'P', '*', ',', '1', 'o', '>>', '1/4', '1/2', '3/4', '?', 'A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', -'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', 'O', 'U', 'U', 'U', 'U', 'U', 'Th', 'ss', +'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', 'O', 'U', 'U', 'U', 'U', 'Y', 'Th', 'ss', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'd', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u', 'u', 'u', 'y', 'th', 'y', ); diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php index f88fbf3c9..d74681782 100644 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php @@ -3,8 +3,8 @@ 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', -'I', 'i', 'IJ', '', 'J', 'j', 'K', 'k', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', -'l', 'L', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'ng', 'NG', 'O', 'o', 'O', 'o', +'I', 'i', 'IJ', 'ij', 'J', 'j', 'K', 'k', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', +'l', 'L', 'l', 'N', 'n', 'N', 'n', 'N', 'n', '\'n', 'ng', 'NG', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', @@ -12,8 +12,8 @@ 'E', 'F', 'f', 'G', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'l', 'W', 'N', 'n', 'O', 'O', 'o', 'OI', 'oi', 'P', 'p', 'YR', '2', '2', 'SH', 'sh', 't', 'T', 't', 'T', 'U', 'u', 'Y', 'V', 'Y', 'y', 'Z', 'z', 'ZH', 'ZH', 'zh', 'zh', '2', '5', '5', 'ts', 'w', -'|', '||', '|=', '!', 'Dz', 'dz', 'LJ', 'Lj', 'lj', 'NJ', 'Nj', 'nj', 'A', 'a', 'I', +'|', '||', '|=', '!', 'DZ', 'Dz', 'dz', 'LJ', 'Lj', 'lj', 'NJ', 'Nj', 'nj', 'A', 'a', 'I', 'i', 'O', 'o', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', '@', 'A', 'a', 'A', 'a', 'AE', 'ae', 'G', 'g', 'G', 'g', 'K', 'k', 'O', 'o', 'O', 'o', 'ZH', 'zh', -'j', 'DZ', 'D', 'dz', 'G', 'g', 'HV', 'W', 'N', 'n', 'A', 'a', 'AE', 'ae', 'O', 'o', +'j', 'DZ', 'Dz', 'dz', 'G', 'g', 'HV', 'W', 'N', 'n', 'A', 'a', 'AE', 'ae', 'O', 'o', ); diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x02.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x02.php index e6f037dd9..c6e3a08e3 100644 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x02.php +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x02.php @@ -2,16 +2,16 @@ $UTF8_TO_ASCII[0x02] = array( 'A', 'a', 'A', 'a', 'E', 'e', 'E', 'e', 'I', 'i', 'I', 'i', 'O', 'o', 'O', 'o', 'R', 'r', 'R', 'r', 'U', 'u', 'U', 'u', 'S', 's', 'T', 't', 'Y', 'y', 'H', 'h', -'[?]', '[?]', 'OU', 'ou', 'Z', 'z', 'A', 'a', 'E', 'e', 'O', 'o', 'O', 'o', 'O', 'o', -'O', 'o', 'Y', 'y', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', -'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'N', 'd', 'OU', 'ou', 'Z', 'z', 'A', 'a', 'E', 'e', 'O', 'o', 'O', 'o', 'O', 'o', +'O', 'o', 'Y', 'y', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', +'z', '[?]', '[?]', 'B', 'U', '^', 'E', 'e', 'J', 'j', 'q', 'q', 'R', 'r', 'Y', 'y', 'a', 'a', 'a', 'b', 'o', 'c', 'd', 'd', 'e', '@', '@', 'e', 'e', 'e', 'e', 'j', 'g', 'g', 'g', 'g', 'u', 'Y', 'h', 'h', 'i', 'i', 'I', 'l', 'l', 'l', 'lZ', 'W', -'W', 'm', 'n', 'n', 'n', 'o', 'OE', 'O', 'F', 'R', 'R', 'R', 'R', 'r', 'r', 'R', -'R', 'R', 's', 'S', 'j', 'S', 'S', 't', 't', 'U', 'U', 'v', '^', 'W', 'Y', 'Y', +'W', 'm', 'n', 'n', 'n', 'o', 'OE', 'O', 'F', 'r', 'r', 'r', 'r', 'r', 'r', 'r', +'R', 'R', 's', 'S', 'j', 'S', 'S', 't', 't', 'u', 'U', 'v', '^', 'w', 'y', 'Y', 'z', 'z', 'Z', 'Z', '?', '?', '?', 'C', '@', 'B', 'E', 'G', 'H', 'j', 'k', 'L', -'q', '?', '?', 'dz', 'dZ', 'dz', 'ts', 'tS', 'tC', 'fN', 'ls', 'lz', 'WW', ']', '[?]', '[?]', -'k', 'h', 'j', 'r', 'r', 'r', 'r', 'w', 'y', '\'', '"', '`', '\'', '`', '`', '\'', +'q', '?', '?', 'dz', 'dZ', 'dz', 'ts', 'tS', 'tC', 'fN', 'ls', 'lz', 'WW', ']]', 'h', 'h', +'h', 'h', 'j', 'r', 'r', 'r', 'r', 'w', 'y', '\'', '"', '`', '\'', '`', '`', '\'', '?', '?', '<', '>', '^', 'V', '^', 'V', '\'', '-', '/', '\\', ',', '_', '\\', '/', ':', '.', '`', '\'', '^', 'V', '+', '-', 'V', '.', '@', ',', '~', '"', 'R', 'X', 'G', 'l', 's', 'x', '?', '', '', '', '', '', '', '', 'V', '=', '"', '[?]', diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php index 8ff946537..a923ec04e 100644 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php @@ -6,7 +6,7 @@ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', -'', '', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'', '', '', 'a', 'e', 'i', 'o', 'u', 'c', 'd', 'h', 'm', 'r', 't', 'v', 'x', '[?]', '[?]', '[?]', '[?]', '\'', ',', '[?]', '[?]', '[?]', '[?]', '', '[?]', '[?]', '[?]', '?', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', 'A', ';', 'E', 'E', 'I', '[?]', 'O', '[?]', 'U', 'O', 'I', 'A', 'B', 'G', 'D', 'E', 'Z', 'E', 'Th', 'I', 'K', 'L', 'M', 'N', 'Ks', 'O', diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x04.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x04.php index 772a0489f..302181f26 100644 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x04.php +++ b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x04.php @@ -1,17 +1,17 @@ ', '<<', '>> ', '[', '] ', '{', '} ', '[(', ')] ', '@', 'X ', '[', '] ', '[[', ']] ', '((', ')) ', '[[', ']] ', '~ ', '``', '\'\'', ',,', -'@', '1', '2', '3', '4', '5', '6', '7', '8', '9', "", "", "", "", "", "", -'~', '+', '+', '+', '+', "", '@', ' // ', '+10+', '+20+', '+30+', '[?]', '[?]', '[?]', "", "", +'@', '1', '2', '3', '4', '5', '6', '7', '8', '9', '', '', '', '', '', '', +'~', '+', '+', '+', '+', '', '@', ' // ', '+10+', '+20+', '+30+', '[?]', '[?]', '[?]', '', '', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', 'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'si', 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', 'da', 'ti', 'di', 'tu', 'tu', 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', -'wi', 'we', 'wo', 'n', 'vu', '[?]', '[?]', '[?]', '[?]', "", "", "", "", '"', '"', '[?]', +'wi', 'we', 'wo', 'n', 'vu', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '"', '"', '[?]', '[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', 'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'si', 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', 'da', 'ti', 'di', 'tu', 'tu', 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', 'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', -'wi', 'we', 'wo', 'n', 'vu', 'ka', 'ke', 'va', 'vi', 've', 'vo', "", "", '"', '"', +'wi', 'we', 'wo', 'n', 'vu', 'ka', 'ke', 'va', 'vi', 've', 'vo', '', '', '"', '"', ); diff --git a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x4d.php b/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x4d.php deleted file mode 100644 index c54418f6d..000000000 --- a/tests/integration/vendor/behat/transliterator/src/Behat/Transliterator/data/x4d.php +++ /dev/null @@ -1,19 +0,0 @@ -assertCount(1, $UTF8_TO_ASCII, 'Each data file should register a single key in $UTF8_TO_ASCII.'); - - $data = current($UTF8_TO_ASCII); - - $this->assertInternalType('array', $data, 'The value in $UTF8_TO_ASCII should be an array.'); - // Accept 255 elements because of inconsistencies in the data of the original Perl library - $this->assertEquals(256, count($data), 'The value in $UTF8_TO_ASCII should have 255 or 256 elements.', 1); - } - - public function provideDataFiles() - { - $files = array(); - - $iterator = new \FilesystemIterator(__DIR__.'/../src/Behat/Transliterator/data'); - - foreach ($iterator as $file) { - $files[] = array($file->getFilename()); - } - - return $files; - } -} diff --git a/tests/integration/vendor/behat/transliterator/tests/TransliteratorTest.php b/tests/integration/vendor/behat/transliterator/tests/TransliteratorTest.php deleted file mode 100644 index 8f7b16274..000000000 --- a/tests/integration/vendor/behat/transliterator/tests/TransliteratorTest.php +++ /dev/null @@ -1,88 +0,0 @@ -assertSame($expected, Transliterator::utf8ToAscii($input)); - } - - public function provideUtf8ConversionCases() - { - return array( - array('', ''), - array('BonJour', 'BonJour'), - array('Déjà', 'Deja'), - array('trąnslįteration tėst ųsąge ūž', 'transliteration test usage uz'), - array('това е тестово заглавие', 'tova ie tiestovo zaghlaviie'), - array('это тестовый заголовок', 'eto tiestovyi zagholovok'), - array('führen Aktivitäten Haglöfs', 'fuhren Aktivitaten Haglofs'), - ); - } - - /** - * @dataProvider provideTransliterationCases - */ - public function testTransliteration($input, $expected) - { - $this->assertSame($expected, Transliterator::transliterate($input)); - } - - public function provideTransliterationCases() - { - return array( - array('', ''), - array('BonJour', 'bonjour'), - array('BonJour & au revoir', 'bonjour-au-revoir'), - array('Déjà', 'deja'), - array('trąnslįteration tėst ųsąge ūž', 'transliteration-test-usage-uz'), - array('това е тестово заглавие', 'tova-ie-tiestovo-zaghlaviie'), - array('это тестовый заголовок', 'eto-tiestovyi-zagholovok'), - array('führen Aktivitäten Haglöfs', 'fuhren-aktivitaten-haglofs'), - ); - } - - /** - * @dataProvider provideUnaccentCases - */ - public function testUnaccent($input, $expected) - { - $this->assertSame($expected, Transliterator::unaccent($input)); - } - - public function provideUnaccentCases() - { - return array( - array('', ''), - array('BonJour', 'BonJour'), - array('Déjà', 'Deja'), - array('това е тестово заглавие', 'това е тестово заглавие'), - ); - } - - /** - * @dataProvider provideUrlizationCases - */ - public function testUrlization($input, $expected) - { - $this->assertSame($expected, Transliterator::urlize($input)); - } - - public function provideUrlizationCases() - { - return array( - array('', ''), - array('BonJour', 'bonjour'), - array('BonJour & au revoir', 'bonjour-au-revoir'), - array('Déjà', 'deja'), - array('това е тестово заглавие', ''), - ); - } -} diff --git a/tests/integration/vendor/bin/behat b/tests/integration/vendor/bin/behat deleted file mode 120000 index 090aac153..000000000 --- a/tests/integration/vendor/bin/behat +++ /dev/null @@ -1 +0,0 @@ -../behat/behat/bin/behat \ No newline at end of file diff --git a/tests/integration/vendor/bin/behat b/tests/integration/vendor/bin/behat new file mode 100755 index 000000000..084f2c9d3 --- /dev/null +++ b/tests/integration/vendor/bin/behat @@ -0,0 +1,107 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) { + include("phpvfscomposer://" . __DIR__ . '/..'.'/behat/behat/bin/behat'); + exit(0); + } +} + +include __DIR__ . '/..'.'/behat/behat/bin/behat'; diff --git a/tests/integration/vendor/bin/yaml-lint b/tests/integration/vendor/bin/yaml-lint new file mode 100755 index 000000000..a7061e388 --- /dev/null +++ b/tests/integration/vendor/bin/yaml-lint @@ -0,0 +1,107 @@ +#!/usr/bin/env php +realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); + $this->position = 0; + + return (bool) $this->handle; + } + + public function stream_read($count) + { + $data = fread($this->handle, $count); + + if ($this->position === 0) { + $data = preg_replace('{^#!.*\r?\n}', '', $data); + } + + $this->position += strlen($data); + + return $data; + } + + public function stream_cast($castAs) + { + return $this->handle; + } + + public function stream_close() + { + fclose($this->handle); + } + + public function stream_lock($operation) + { + return $operation ? flock($this->handle, $operation) : true; + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return feof($this->handle); + } + + public function stream_stat() + { + return array(); + } + + public function stream_set_option($option, $arg1, $arg2) + { + return true; + } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } + } + } + + if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) { + include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/yaml/Resources/bin/yaml-lint'); + exit(0); + } +} + +include __DIR__ . '/..'.'/symfony/yaml/Resources/bin/yaml-lint'; diff --git a/tests/integration/vendor/composer/ClassLoader.php b/tests/integration/vendor/composer/ClassLoader.php index fce8549f0..afef3fa2a 100644 --- a/tests/integration/vendor/composer/ClassLoader.php +++ b/tests/integration/vendor/composer/ClassLoader.php @@ -37,57 +37,130 @@ * * @author Fabien Potencier * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { + /** @var ?string */ + private $vendorDir; + // PSR-4 + /** + * @var array[] + * @psalm-var array> + */ private $prefixLengthsPsr4 = array(); + /** + * @var array[] + * @psalm-var array> + */ private $prefixDirsPsr4 = array(); + /** + * @var array[] + * @psalm-var array + */ private $fallbackDirsPsr4 = array(); // PSR-0 + /** + * @var array[] + * @psalm-var array> + */ private $prefixesPsr0 = array(); + /** + * @var array[] + * @psalm-var array + */ private $fallbackDirsPsr0 = array(); + /** @var bool */ private $useIncludePath = false; + + /** + * @var string[] + * @psalm-var array + */ private $classMap = array(); + + /** @var bool */ private $classMapAuthoritative = false; + + /** + * @var bool[] + * @psalm-var array + */ private $missingClasses = array(); + + /** @var ?string */ private $apcuPrefix; + /** + * @var self[] + */ + private static $registeredLoaders = array(); + + /** + * @param ?string $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + } + + /** + * @return string[] + */ public function getPrefixes() { if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); } + /** + * @return array[] + * @psalm-return array> + */ public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } + /** + * @return array[] + * @psalm-return array + */ public function getFallbackDirs() { return $this->fallbackDirsPsr0; } + /** + * @return array[] + * @psalm-return array + */ public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } + /** + * @return string[] Array of classname => path + * @psalm-return array + */ public function getClassMap() { return $this->classMap; } /** - * @param array $classMap Class to filename map + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap + * + * @return void */ public function addClassMap(array $classMap) { @@ -102,9 +175,11 @@ public function addClassMap(array $classMap) * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void */ public function add($prefix, $paths, $prepend = false) { @@ -147,11 +222,13 @@ public function add($prefix, $paths, $prepend = false) * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException + * + * @return void */ public function addPsr4($prefix, $paths, $prepend = false) { @@ -195,8 +272,10 @@ public function addPsr4($prefix, $paths, $prepend = false) * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories + * + * @return void */ public function set($prefix, $paths) { @@ -211,10 +290,12 @@ public function set($prefix, $paths) * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException + * + * @return void */ public function setPsr4($prefix, $paths) { @@ -234,6 +315,8 @@ public function setPsr4($prefix, $paths) * Turns on searching the include path for class files. * * @param bool $useIncludePath + * + * @return void */ public function setUseIncludePath($useIncludePath) { @@ -256,6 +339,8 @@ public function getUseIncludePath() * that have not been registered with the class map. * * @param bool $classMapAuthoritative + * + * @return void */ public function setClassMapAuthoritative($classMapAuthoritative) { @@ -276,6 +361,8 @@ public function isClassMapAuthoritative() * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix + * + * @return void */ public function setApcuPrefix($apcuPrefix) { @@ -296,25 +383,44 @@ public function getApcuPrefix() * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } } /** * Unregisters this instance as an autoloader. + * + * @return void */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } } /** * Loads the given class or interface. * * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise + * @return true|null True if loaded, null otherwise */ public function loadClass($class) { @@ -323,6 +429,8 @@ public function loadClass($class) return true; } + + return null; } /** @@ -367,6 +475,21 @@ public function findFile($class) return $file; } + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ private function findFileWithExtension($class, $ext) { // PSR-4 lookup @@ -438,6 +561,10 @@ private function findFileWithExtension($class, $ext) * Scope isolated include. * * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + * @private */ function includeFile($file) { diff --git a/tests/integration/vendor/composer/InstalledVersions.php b/tests/integration/vendor/composer/InstalledVersions.php new file mode 100644 index 000000000..d50e0c9fc --- /dev/null +++ b/tests/integration/vendor/composer/InstalledVersions.php @@ -0,0 +1,350 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints($constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = require __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + $installed[] = self::$installed; + + return $installed; + } +} diff --git a/tests/integration/vendor/composer/autoload_classmap.php b/tests/integration/vendor/composer/autoload_classmap.php index 7a91153b0..087401551 100644 --- a/tests/integration/vendor/composer/autoload_classmap.php +++ b/tests/integration/vendor/composer/autoload_classmap.php @@ -6,4 +6,13 @@ $baseDir = dirname($vendorDir); return array( + 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', + 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', + 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', + 'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', + 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', + 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', ); diff --git a/tests/integration/vendor/composer/autoload_files.php b/tests/integration/vendor/composer/autoload_files.php index 708c2e992..77d1f8dc7 100644 --- a/tests/integration/vendor/composer/autoload_files.php +++ b/tests/integration/vendor/composer/autoload_files.php @@ -6,9 +6,17 @@ $baseDir = dirname($vendorDir); return array( + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', - 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', + 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', ); diff --git a/tests/integration/vendor/composer/autoload_namespaces.php b/tests/integration/vendor/composer/autoload_namespaces.php index 5de315ad9..4e4604cf7 100644 --- a/tests/integration/vendor/composer/autoload_namespaces.php +++ b/tests/integration/vendor/composer/autoload_namespaces.php @@ -6,8 +6,5 @@ $baseDir = dirname($vendorDir); return array( - 'Behat\\Transliterator' => array($vendorDir . '/behat/transliterator/src'), - 'Behat\\Testwork' => array($vendorDir . '/behat/behat/src'), 'Behat\\Gherkin' => array($vendorDir . '/behat/gherkin/src'), - 'Behat\\Behat' => array($vendorDir . '/behat/behat/src'), ); diff --git a/tests/integration/vendor/composer/autoload_psr4.php b/tests/integration/vendor/composer/autoload_psr4.php index 9d551629e..983d81b6d 100644 --- a/tests/integration/vendor/composer/autoload_psr4.php +++ b/tests/integration/vendor/composer/autoload_psr4.php @@ -6,20 +6,34 @@ $baseDir = dirname($vendorDir); return array( + 'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'), + 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), + 'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'), + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'), + 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), + 'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'), + 'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'), + 'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), + 'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'), 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'), - 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), - 'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'), - 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), - 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), - 'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'), + 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), + 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), + 'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), + 'Behat\\Transliterator\\' => array($vendorDir . '/behat/transliterator/src/Behat/Transliterator'), + 'Behat\\Testwork\\' => array($vendorDir . '/behat/behat/src/Behat/Testwork'), + 'Behat\\Step\\' => array($vendorDir . '/behat/behat/src/Behat/Step'), + 'Behat\\Hook\\' => array($vendorDir . '/behat/behat/src/Behat/Hook'), + 'Behat\\Behat\\' => array($vendorDir . '/behat/behat/src/Behat/Behat'), ); diff --git a/tests/integration/vendor/composer/autoload_real.php b/tests/integration/vendor/composer/autoload_real.php index 6259d240d..3f6da5899 100644 --- a/tests/integration/vendor/composer/autoload_real.php +++ b/tests/integration/vendor/composer/autoload_real.php @@ -13,19 +13,24 @@ public static function loadClassLoader($class) } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInit2b078a63e93bc9e9825cefae96ca1eb3', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInit2b078a63e93bc9e9825cefae96ca1eb3', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3::getInitializer($loader)); } else { @@ -60,11 +65,16 @@ public static function getLoader() } } +/** + * @param string $fileIdentifier + * @param string $file + * @return void + */ function composerRequire2b078a63e93bc9e9825cefae96ca1eb3($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; } } diff --git a/tests/integration/vendor/composer/autoload_static.php b/tests/integration/vendor/composer/autoload_static.php index 55eb9166f..4f9f3e4e8 100644 --- a/tests/integration/vendor/composer/autoload_static.php +++ b/tests/integration/vendor/composer/autoload_static.php @@ -7,35 +7,49 @@ class ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3 { public static $files = array ( + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', + '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', - 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', + 'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', ); public static $prefixLengthsPsr4 = array ( 'S' => array ( + 'Symfony\\Polyfill\\Php81\\' => 23, + 'Symfony\\Polyfill\\Php80\\' => 23, + 'Symfony\\Polyfill\\Php73\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33, + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31, + 'Symfony\\Polyfill\\Ctype\\' => 23, + 'Symfony\\Contracts\\Translation\\' => 30, + 'Symfony\\Contracts\\Service\\' => 26, + 'Symfony\\Contracts\\EventDispatcher\\' => 34, 'Symfony\\Component\\Yaml\\' => 23, 'Symfony\\Component\\Translation\\' => 30, + 'Symfony\\Component\\String\\' => 25, 'Symfony\\Component\\Filesystem\\' => 29, 'Symfony\\Component\\EventDispatcher\\' => 34, 'Symfony\\Component\\DependencyInjection\\' => 38, - 'Symfony\\Component\\Debug\\' => 24, 'Symfony\\Component\\Console\\' => 26, 'Symfony\\Component\\Config\\' => 25, - 'Symfony\\Component\\ClassLoader\\' => 30, ), 'P' => array ( - 'Psr\\Log\\' => 8, 'Psr\\Http\\Message\\' => 17, - ), - 'I' => - array ( - 'Interop\\Container\\' => 18, + 'Psr\\Http\\Client\\' => 16, + 'Psr\\EventDispatcher\\' => 20, + 'Psr\\Container\\' => 14, ), 'G' => array ( @@ -43,13 +57,57 @@ class ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3 'GuzzleHttp\\Promise\\' => 19, 'GuzzleHttp\\' => 11, ), + 'B' => + array ( + 'Behat\\Transliterator\\' => 21, + 'Behat\\Testwork\\' => 15, + 'Behat\\Step\\' => 11, + 'Behat\\Hook\\' => 11, + 'Behat\\Behat\\' => 12, + ), ); public static $prefixDirsPsr4 = array ( + 'Symfony\\Polyfill\\Php81\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php81', + ), + 'Symfony\\Polyfill\\Php80\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', + ), + 'Symfony\\Polyfill\\Php73\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php73', + ), 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer', + ), + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme', + ), + 'Symfony\\Polyfill\\Ctype\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', + ), + 'Symfony\\Contracts\\Translation\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/translation-contracts', + ), + 'Symfony\\Contracts\\Service\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/service-contracts', + ), + 'Symfony\\Contracts\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts', + ), 'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', @@ -58,6 +116,10 @@ class ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3 array ( 0 => __DIR__ . '/..' . '/symfony/translation', ), + 'Symfony\\Component\\String\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/string', + ), 'Symfony\\Component\\Filesystem\\' => array ( 0 => __DIR__ . '/..' . '/symfony/filesystem', @@ -70,10 +132,6 @@ class ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3 array ( 0 => __DIR__ . '/..' . '/symfony/dependency-injection', ), - 'Symfony\\Component\\Debug\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/debug', - ), 'Symfony\\Component\\Console\\' => array ( 0 => __DIR__ . '/..' . '/symfony/console', @@ -82,21 +140,22 @@ class ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3 array ( 0 => __DIR__ . '/..' . '/symfony/config', ), - 'Symfony\\Component\\ClassLoader\\' => + 'Psr\\Http\\Message\\' => array ( - 0 => __DIR__ . '/..' . '/symfony/class-loader', + 0 => __DIR__ . '/..' . '/psr/http-factory/src', + 1 => __DIR__ . '/..' . '/psr/http-message/src', ), - 'Psr\\Log\\' => + 'Psr\\Http\\Client\\' => array ( - 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + 0 => __DIR__ . '/..' . '/psr/http-client/src', ), - 'Psr\\Http\\Message\\' => + 'Psr\\EventDispatcher\\' => array ( - 0 => __DIR__ . '/..' . '/psr/http-message/src', + 0 => __DIR__ . '/..' . '/psr/event-dispatcher/src', ), - 'Interop\\Container\\' => + 'Psr\\Container\\' => array ( - 0 => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container', + 0 => __DIR__ . '/..' . '/psr/container/src', ), 'GuzzleHttp\\Psr7\\' => array ( @@ -110,36 +169,57 @@ class ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3 array ( 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', ), + 'Behat\\Transliterator\\' => + array ( + 0 => __DIR__ . '/..' . '/behat/transliterator/src/Behat/Transliterator', + ), + 'Behat\\Testwork\\' => + array ( + 0 => __DIR__ . '/..' . '/behat/behat/src/Behat/Testwork', + ), + 'Behat\\Step\\' => + array ( + 0 => __DIR__ . '/..' . '/behat/behat/src/Behat/Step', + ), + 'Behat\\Hook\\' => + array ( + 0 => __DIR__ . '/..' . '/behat/behat/src/Behat/Hook', + ), + 'Behat\\Behat\\' => + array ( + 0 => __DIR__ . '/..' . '/behat/behat/src/Behat/Behat', + ), ); public static $prefixesPsr0 = array ( 'B' => array ( - 'Behat\\Transliterator' => - array ( - 0 => __DIR__ . '/..' . '/behat/transliterator/src', - ), - 'Behat\\Testwork' => - array ( - 0 => __DIR__ . '/..' . '/behat/behat/src', - ), 'Behat\\Gherkin' => array ( 0 => __DIR__ . '/..' . '/behat/gherkin/src', ), - 'Behat\\Behat' => - array ( - 0 => __DIR__ . '/..' . '/behat/behat/src', - ), ), ); + public static $classMap = array ( + 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', + 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', + 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', + 'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', + 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', + 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', + 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3::$prefixesPsr0; + $loader->classMap = ComposerStaticInit2b078a63e93bc9e9825cefae96ca1eb3::$classMap; }, null, ClassLoader::class); } diff --git a/tests/integration/vendor/composer/installed.json b/tests/integration/vendor/composer/installed.json index def91a9dc..7df4ff526 100644 --- a/tests/integration/vendor/composer/installed.json +++ b/tests/integration/vendor/composer/installed.json @@ -1,1152 +1,2488 @@ -[ - { - "name": "behat/behat", - "version": "v3.3.0", - "version_normalized": "3.3.0.0", - "source": { - "type": "git", - "url": "https://github.com/Behat/Behat.git", - "reference": "15a3a1857457eaa29cdf41564a5e421effb09526" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/15a3a1857457eaa29cdf41564a5e421effb09526", - "reference": "15a3a1857457eaa29cdf41564a5e421effb09526", - "shasum": "" - }, - "require": { - "behat/gherkin": "^4.4.4", - "behat/transliterator": "~1.0", - "container-interop/container-interop": "^1.1", - "ext-mbstring": "*", - "php": ">=5.3.3", - "symfony/class-loader": "~2.1||~3.0", - "symfony/config": "~2.3||~3.0", - "symfony/console": "~2.5||~3.0", - "symfony/dependency-injection": "~2.1||~3.0", - "symfony/event-dispatcher": "~2.1||~3.0", - "symfony/translation": "~2.3||~3.0", - "symfony/yaml": "~2.1||~3.0" - }, - "require-dev": { - "herrera-io/box": "~1.6.1", - "phpunit/phpunit": "~4.5", - "symfony/process": "~2.5|~3.0" - }, - "suggest": { - "behat/mink-extension": "for integration with Mink testing framework", - "behat/symfony2-extension": "for integration with Symfony2 web framework", - "behat/yii-extension": "for integration with Yii web framework" - }, - "time": "2016-12-25T13:43:52+00:00", - "bin": [ - "bin/behat" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Behat\\Behat": "src/", - "Behat\\Testwork": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Scenario-oriented BDD framework for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "Agile", - "BDD", - "ScenarioBDD", - "Scrum", - "StoryBDD", - "User story", - "business", - "development", - "documentation", - "examples", - "symfony", - "testing" - ] - }, - { - "name": "behat/gherkin", - "version": "v4.4.5", - "version_normalized": "4.4.5.0", - "source": { - "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3", - "symfony/yaml": "~2.3|~3" - }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "time": "2016-10-30T11:50:56+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" - ] - }, - { - "name": "behat/transliterator", - "version": "v1.1.0", - "version_normalized": "1.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/Behat/Transliterator.git", - "reference": "868e05be3a9f25ba6424c2dd4849567f50715003" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Transliterator/zipball/868e05be3a9f25ba6424c2dd4849567f50715003", - "reference": "868e05be3a9f25ba6424c2dd4849567f50715003", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "time": "2015-09-28T16:26:35+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Behat\\Transliterator": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Artistic-1.0" - ], - "description": "String transliterator", - "keywords": [ - "i18n", - "slug", - "transliterator" - ] - }, - { - "name": "container-interop/container-interop", - "version": "1.1.0", - "version_normalized": "1.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", - "shasum": "" - }, - "time": "2014-12-30T15:22:37+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)" - }, - { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", - "version_normalized": "6.3.3.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "shasum": "" - }, - "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" - }, - "time": "2018-04-22T15:46:56+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.3-dev" - } - }, - "installation-source": "dist", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ] - }, - { - "name": "guzzlehttp/promises", - "version": "v1.3.1", - "version_normalized": "1.3.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "shasum": "" - }, - "require": { - "php": ">=5.5.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0" - }, - "time": "2016-12-20T10:07:11+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ] - }, - { - "name": "guzzlehttp/psr7", - "version": "1.5.2", - "version_normalized": "1.5.2.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "9f83dded91781a01c63574e387eaa769be769115" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", - "reference": "9f83dded91781a01c63574e387eaa769be769115", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" - }, - "time": "2018-12-04T20:46:45+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ] - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "version_normalized": "1.0.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2016-08-06T14:39:51+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ] - }, - { - "name": "psr/log", - "version": "1.0.2", - "version_normalized": "1.0.2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2016-10-10T12:19:37+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ] - }, - { - "name": "ralouphie/getallheaders", - "version": "2.0.5", - "version_normalized": "2.0.5.0", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "~3.7.0", - "satooshi/php-coveralls": ">=1.0" - }, - "time": "2016-02-11T07:05:27+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders." - }, - { - "name": "symfony/class-loader", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/class-loader.git", - "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/87cd4e69435d98de01d0162c5f9c0ac017075c63", - "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "require-dev": { - "symfony/finder": "~2.8|~3.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" - }, - "time": "2016-11-29T08:26:13+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\ClassLoader\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony ClassLoader Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/config", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "b4ec9f099599cfc5b7f4d07bb2e910781a2be5e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/b4ec9f099599cfc5b7f4d07bb2e910781a2be5e4", - "reference": "b4ec9f099599cfc5b7f4d07bb2e910781a2be5e4", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/filesystem": "~2.8|~3.0" - }, - "require-dev": { - "symfony/yaml": "~3.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "time": "2016-12-09T07:45:17+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Config Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/console", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "d12aa9ca20f4db83ec58410978dab6afcb9d6aaa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d12aa9ca20f4db83ec58410978dab6afcb9d6aaa", - "reference": "d12aa9ca20f4db83ec58410978dab6afcb9d6aaa", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/debug": "~2.8|~3.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/filesystem": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/filesystem": "", - "symfony/process": "" - }, - "time": "2016-12-11T14:34:22+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/debug", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "9f923e68d524a3095c5a2ae5fc7220c7cbc12231" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/9f923e68d524a3095c5a2ae5fc7220c7cbc12231", - "reference": "9f923e68d524a3095c5a2ae5fc7220c7cbc12231", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" - }, - "time": "2016-11-16T22:18:16+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/dependency-injection", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "037054501c41007c93b6de1b5c7a7acb83523593" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/037054501c41007c93b6de1b5c7a7acb83523593", - "reference": "037054501c41007c93b6de1b5c7a7acb83523593", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "conflict": { - "symfony/yaml": "<3.2" - }, - "require-dev": { - "symfony/config": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0", - "symfony/yaml": "~3.2" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "time": "2016-12-08T15:27:33+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DependencyInjection Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/event-dispatcher", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e8f47a327c2f0fd5aa04fa60af2b693006ed7283", - "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/dependency-injection": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "time": "2016-10-13T06:29:04+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/filesystem", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4", - "reference": "8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "time": "2016-11-24T00:46:43+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.3.0", - "version_normalized": "1.3.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "time": "2016-11-14T01:06:16+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ] - }, - { - "name": "symfony/translation", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "5fd18eca88f4d187807a1eba083bc99feaa8635b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/5fd18eca88f4d187807a1eba083bc99feaa8635b", - "reference": "5fd18eca88f4d187807a1eba083bc99feaa8635b", - "shasum": "" - }, - "require": { - "php": ">=5.5.9", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/config": "<2.8" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/intl": "~2.8|~3.0", - "symfony/yaml": "~2.8|~3.0" - }, - "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "time": "2016-11-30T14:40:17+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/yaml", - "version": "v3.2.1", - "version_normalized": "3.2.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "a7095af4b97a0955f85c8989106c249fa649011f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a7095af4b97a0955f85c8989106c249fa649011f", - "reference": "a7095af4b97a0955f85c8989106c249fa649011f", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "require-dev": { - "symfony/console": "~2.8|~3.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "time": "2016-12-10T10:07:06+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com" - } -] +{ + "packages": [ + { + "name": "behat/behat", + "version": "v3.10.0", + "version_normalized": "3.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Behat.git", + "reference": "a55661154079cf881ef643b303bfaf67bae3a09f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Behat/zipball/a55661154079cf881ef643b303bfaf67bae3a09f", + "reference": "a55661154079cf881ef643b303bfaf67bae3a09f", + "shasum": "" + }, + "require": { + "behat/gherkin": "^4.9.0", + "behat/transliterator": "^1.2", + "ext-mbstring": "*", + "php": "^7.2 || ^8.0", + "psr/container": "^1.0", + "symfony/config": "^4.4 || ^5.0 || ^6.0", + "symfony/console": "^4.4 || ^5.0 || ^6.0", + "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", + "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", + "symfony/translation": "^4.4 || ^5.0 || ^6.0", + "symfony/yaml": "^4.4 || ^5.0 || ^6.0" + }, + "require-dev": { + "container-interop/container-interop": "^1.2", + "herrera-io/box": "~1.6.1", + "phpunit/phpunit": "^8.5 || ^9.0", + "symfony/process": "^4.4 || ^5.0 || ^6.0", + "vimeo/psalm": "^4.8" + }, + "suggest": { + "ext-dom": "Needed to output test results in JUnit format." + }, + "time": "2021-11-02T20:09:40+00:00", + "bin": [ + "bin/behat" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Behat\\Hook\\": "src/Behat/Hook/", + "Behat\\Step\\": "src/Behat/Step/", + "Behat\\Behat\\": "src/Behat/Behat/", + "Behat\\Testwork\\": "src/Behat/Testwork/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Scenario-oriented BDD framework for PHP", + "homepage": "http://behat.org/", + "keywords": [ + "Agile", + "BDD", + "ScenarioBDD", + "Scrum", + "StoryBDD", + "User story", + "business", + "development", + "documentation", + "examples", + "symfony", + "testing" + ], + "support": { + "issues": "https://github.com/Behat/Behat/issues", + "source": "https://github.com/Behat/Behat/tree/v3.10.0" + }, + "install-path": "../behat/behat" + }, + { + "name": "behat/gherkin", + "version": "v4.9.0", + "version_normalized": "4.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Gherkin.git", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "shasum": "" + }, + "require": { + "php": "~7.2|~8.0" + }, + "require-dev": { + "cucumber/cucumber": "dev-gherkin-22.0.0", + "phpunit/phpunit": "~8|~9", + "symfony/yaml": "~3|~4|~5" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" + }, + "time": "2021-10-12T13:05:09+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Behat\\Gherkin": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + }, + "install-path": "../behat/gherkin" + }, + { + "name": "behat/transliterator", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc", + "reference": "3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0", + "phpunit/phpunit": "^4.8.36|^6.3" + }, + "time": "2020-01-14T16:39:13+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Behat\\Transliterator\\": "src/Behat/Transliterator" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "install-path": "../behat/transliterator" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.4.1", + "version_normalized": "7.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "time": "2021-12-06T18:43:05+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "install-path": "../guzzlehttp/guzzle" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.1", + "version_normalized": "1.5.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "time": "2021-10-22T20:56:57+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "install-path": "../guzzlehttp/promises" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.1.0", + "version_normalized": "2.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72", + "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "time": "2021-10-06T17:43:30+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "install-path": "../guzzlehttp/psr7" + }, + { + "name": "psr/container", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2017-02-14T16:28:37+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, + "install-path": "../psr/container" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "time": "2019-01-08T18:20:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "install-path": "../psr/event-dispatcher" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "time": "2020-06-29T06:28:15+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "install-path": "../psr/http-client" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "time": "2019-04-30T12:38:16+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "install-path": "../psr/http-factory" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-08-06T14:39:51+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "install-path": "../psr/http-message" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "version_normalized": "3.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "time": "2019-03-08T08:55:37+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "install-path": "../ralouphie/getallheaders" + }, + { + "name": "symfony/config", + "version": "v5.4.3", + "version_normalized": "5.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "d65e1bd990c740e31feb07d2b0927b8d4df9956f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/d65e1bd990c740e31feb07d2b0927b8d4df9956f", + "reference": "d65e1bd990c740e31feb07d2b0927b8d4df9956f", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" + }, + "conflict": { + "symfony/finder": "<4.4" + }, + "require-dev": { + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "time": "2022-01-03T09:50:52+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v5.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/config" + }, + { + "name": "symfony/console", + "version": "v5.4.5", + "version_normalized": "5.4.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "d8111acc99876953f52fe16d4c50eb60940d49ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/d8111acc99876953f52fe16d4c50eb60940d49ad", + "reference": "d8111acc99876953f52fe16d4c50eb60940d49ad", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "time": "2022-02-24T12:45:35+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/console" + }, + { + "name": "symfony/dependency-injection", + "version": "v5.2.12", + "version_normalized": "5.2.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "2f0326ab0e142a3600b1b435cb3e852bc96264b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2f0326ab0e142a3600b1b435cb3e852bc96264b6", + "reference": "2f0326ab0e142a3600b1b435cb3e852bc96264b6", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.0", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1.6|^2" + }, + "conflict": { + "symfony/config": "<5.1", + "symfony/finder": "<4.4", + "symfony/proxy-manager-bridge": "<4.4", + "symfony/yaml": "<4.4" + }, + "provide": { + "psr/container-implementation": "1.0", + "symfony/service-implementation": "1.0|2.0" + }, + "require-dev": { + "symfony/config": "^5.1", + "symfony/expression-language": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "time": "2021-07-23T15:54:19+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v5.2.12" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/dependency-injection" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.0", + "version_normalized": "2.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2021-07-12T14:48:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/deprecation-contracts" + }, + { + "name": "symfony/event-dispatcher", + "version": "v5.4.3", + "version_normalized": "5.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "dec8a9f58d20df252b9cd89f1c6c1530f747685d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dec8a9f58d20df252b9cd89f1c6c1530f747685d", + "reference": "dec8a9f58d20df252b9cd89f1c6c1530f747685d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2022-01-02T09:53:40+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/event-dispatcher" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v2.5.0", + "version_normalized": "2.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "time": "2021-07-12T14:48:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/event-dispatcher-contracts" + }, + { + "name": "symfony/filesystem", + "version": "v5.4.6", + "version_normalized": "5.4.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "d53a45039974952af7f7ebc461ccdd4295e29440" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d53a45039974952af7f7ebc461ccdd4295e29440", + "reference": "d53a45039974952af7f7ebc461ccdd4295e29440", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "time": "2022-03-02T12:42:23+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/filesystem" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "time": "2021-10-20T20:35:02+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-ctype" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", + "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2021-11-23T21:10:46+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-intl-grapheme" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "time": "2021-02-19T12:13:01+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-intl-normalizer" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2021-11-30T18:21:41+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-mbstring" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", + "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2021-06-05T21:20:04+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-php73" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2022-03-04T08:16:47+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-php80" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.25.0", + "version_normalized": "1.25.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "time": "2021-09-13T13:58:11+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/polyfill-php81" + }, + { + "name": "symfony/service-contracts", + "version": "v2.2.0", + "version_normalized": "2.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.0" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "time": "2020-09-07T11:33:47+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/service-contracts" + }, + { + "name": "symfony/string", + "version": "v5.4.3", + "version_normalized": "5.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/92043b7d8383e48104e411bc9434b260dbeb5a10", + "reference": "92043b7d8383e48104e411bc9434b260dbeb5a10", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "time": "2022-01-02T09:53:40+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/string" + }, + { + "name": "symfony/translation", + "version": "v5.4.6", + "version_normalized": "5.4.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "a7ca9fdfffb0174209440c2ffa1dee228e15d95b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/a7ca9fdfffb0174209440c2ffa1dee228e15d95b", + "reference": "a7ca9fdfffb0174209440c2ffa1dee228e15d95b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^2.3" + }, + "conflict": { + "symfony/config": "<4.4", + "symfony/console": "<5.3", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" + }, + "provide": { + "symfony/translation-implementation": "2.3" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "time": "2022-03-02T12:56:28+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v5.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/translation" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.0", + "version_normalized": "2.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "time": "2021-08-17T14:20:01+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/translation-contracts" + }, + { + "name": "symfony/yaml", + "version": "v5.4.3", + "version_normalized": "5.4.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e80f87d2c9495966768310fc531b487ce64237a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2", + "reference": "e80f87d2c9495966768310fc531b487ce64237a2", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.3" + }, + "require-dev": { + "symfony/console": "^5.3|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "time": "2022-01-26T16:32:32+00:00", + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v5.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/yaml" + } + ], + "dev": true, + "dev-package-names": [ + "behat/behat", + "behat/gherkin", + "behat/transliterator", + "guzzlehttp/guzzle", + "guzzlehttp/promises", + "guzzlehttp/psr7", + "psr/container", + "psr/event-dispatcher", + "psr/http-client", + "psr/http-factory", + "psr/http-message", + "ralouphie/getallheaders", + "symfony/config", + "symfony/console", + "symfony/dependency-injection", + "symfony/deprecation-contracts", + "symfony/event-dispatcher", + "symfony/event-dispatcher-contracts", + "symfony/filesystem", + "symfony/polyfill-ctype", + "symfony/polyfill-intl-grapheme", + "symfony/polyfill-intl-normalizer", + "symfony/polyfill-mbstring", + "symfony/polyfill-php73", + "symfony/polyfill-php80", + "symfony/polyfill-php81", + "symfony/service-contracts", + "symfony/string", + "symfony/translation", + "symfony/translation-contracts", + "symfony/yaml" + ] +} diff --git a/tests/integration/vendor/composer/installed.php b/tests/integration/vendor/composer/installed.php new file mode 100644 index 000000000..8f7263632 --- /dev/null +++ b/tests/integration/vendor/composer/installed.php @@ -0,0 +1,356 @@ + array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'reference' => 'cbc480eadc87e4f8dcb722ba76e7fe78903b9e60', + 'name' => '__root__', + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'reference' => 'cbc480eadc87e4f8dcb722ba76e7fe78903b9e60', + 'dev_requirement' => false, + ), + 'behat/behat' => array( + 'pretty_version' => 'v3.10.0', + 'version' => '3.10.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../behat/behat', + 'aliases' => array(), + 'reference' => 'a55661154079cf881ef643b303bfaf67bae3a09f', + 'dev_requirement' => true, + ), + 'behat/gherkin' => array( + 'pretty_version' => 'v4.9.0', + 'version' => '4.9.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../behat/gherkin', + 'aliases' => array(), + 'reference' => '0bc8d1e30e96183e4f36db9dc79caead300beff4', + 'dev_requirement' => true, + ), + 'behat/transliterator' => array( + 'pretty_version' => 'v1.3.0', + 'version' => '1.3.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../behat/transliterator', + 'aliases' => array(), + 'reference' => '3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc', + 'dev_requirement' => true, + ), + 'guzzlehttp/guzzle' => array( + 'pretty_version' => '7.4.1', + 'version' => '7.4.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', + 'aliases' => array(), + 'reference' => 'ee0a041b1760e6a53d2a39c8c34115adc2af2c79', + 'dev_requirement' => true, + ), + 'guzzlehttp/promises' => array( + 'pretty_version' => '1.5.1', + 'version' => '1.5.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../guzzlehttp/promises', + 'aliases' => array(), + 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da', + 'dev_requirement' => true, + ), + 'guzzlehttp/psr7' => array( + 'pretty_version' => '2.1.0', + 'version' => '2.1.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../guzzlehttp/psr7', + 'aliases' => array(), + 'reference' => '089edd38f5b8abba6cb01567c2a8aaa47cec4c72', + 'dev_requirement' => true, + ), + 'psr/container' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/container', + 'aliases' => array(), + 'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f', + 'dev_requirement' => true, + ), + 'psr/container-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/event-dispatcher' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/event-dispatcher', + 'aliases' => array(), + 'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0', + 'dev_requirement' => true, + ), + 'psr/event-dispatcher-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/http-client' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-client', + 'aliases' => array(), + 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621', + 'dev_requirement' => true, + ), + 'psr/http-client-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/http-factory' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-factory', + 'aliases' => array(), + 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be', + 'dev_requirement' => true, + ), + 'psr/http-factory-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/http-message' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/http-message', + 'aliases' => array(), + 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', + 'dev_requirement' => true, + ), + 'psr/http-message-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0', + ), + ), + 'psr/log-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0|2.0', + ), + ), + 'ralouphie/getallheaders' => array( + 'pretty_version' => '3.0.3', + 'version' => '3.0.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../ralouphie/getallheaders', + 'aliases' => array(), + 'reference' => '120b605dfeb996808c31b6477290a714d356e822', + 'dev_requirement' => true, + ), + 'symfony/config' => array( + 'pretty_version' => 'v5.4.3', + 'version' => '5.4.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/config', + 'aliases' => array(), + 'reference' => 'd65e1bd990c740e31feb07d2b0927b8d4df9956f', + 'dev_requirement' => true, + ), + 'symfony/console' => array( + 'pretty_version' => 'v5.4.5', + 'version' => '5.4.5.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/console', + 'aliases' => array(), + 'reference' => 'd8111acc99876953f52fe16d4c50eb60940d49ad', + 'dev_requirement' => true, + ), + 'symfony/dependency-injection' => array( + 'pretty_version' => 'v5.2.12', + 'version' => '5.2.12.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/dependency-injection', + 'aliases' => array(), + 'reference' => '2f0326ab0e142a3600b1b435cb3e852bc96264b6', + 'dev_requirement' => true, + ), + 'symfony/deprecation-contracts' => array( + 'pretty_version' => 'v2.5.0', + 'version' => '2.5.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', + 'aliases' => array(), + 'reference' => '6f981ee24cf69ee7ce9736146d1c57c2780598a8', + 'dev_requirement' => true, + ), + 'symfony/event-dispatcher' => array( + 'pretty_version' => 'v5.4.3', + 'version' => '5.4.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/event-dispatcher', + 'aliases' => array(), + 'reference' => 'dec8a9f58d20df252b9cd89f1c6c1530f747685d', + 'dev_requirement' => true, + ), + 'symfony/event-dispatcher-contracts' => array( + 'pretty_version' => 'v2.5.0', + 'version' => '2.5.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts', + 'aliases' => array(), + 'reference' => '66bea3b09be61613cd3b4043a65a8ec48cfa6d2a', + 'dev_requirement' => true, + ), + 'symfony/event-dispatcher-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '2.0', + ), + ), + 'symfony/filesystem' => array( + 'pretty_version' => 'v5.4.6', + 'version' => '5.4.6.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/filesystem', + 'aliases' => array(), + 'reference' => 'd53a45039974952af7f7ebc461ccdd4295e29440', + 'dev_requirement' => true, + ), + 'symfony/polyfill-ctype' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', + 'aliases' => array(), + 'reference' => '30885182c981ab175d4d034db0f6f469898070ab', + 'dev_requirement' => true, + ), + 'symfony/polyfill-intl-grapheme' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', + 'aliases' => array(), + 'reference' => '81b86b50cf841a64252b439e738e97f4a34e2783', + 'dev_requirement' => true, + ), + 'symfony/polyfill-intl-normalizer' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', + 'aliases' => array(), + 'reference' => '8590a5f561694770bdcd3f9b5c69dde6945028e8', + 'dev_requirement' => true, + ), + 'symfony/polyfill-mbstring' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', + 'aliases' => array(), + 'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825', + 'dev_requirement' => true, + ), + 'symfony/polyfill-php73' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-php73', + 'aliases' => array(), + 'reference' => 'cc5db0e22b3cb4111010e48785a97f670b350ca5', + 'dev_requirement' => true, + ), + 'symfony/polyfill-php80' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-php80', + 'aliases' => array(), + 'reference' => '4407588e0d3f1f52efb65fbe92babe41f37fe50c', + 'dev_requirement' => true, + ), + 'symfony/polyfill-php81' => array( + 'pretty_version' => 'v1.25.0', + 'version' => '1.25.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-php81', + 'aliases' => array(), + 'reference' => '5de4ba2d41b15f9bd0e19b2ab9674135813ec98f', + 'dev_requirement' => true, + ), + 'symfony/service-contracts' => array( + 'pretty_version' => 'v2.2.0', + 'version' => '2.2.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/service-contracts', + 'aliases' => array(), + 'reference' => 'd15da7ba4957ffb8f1747218be9e1a121fd298a1', + 'dev_requirement' => true, + ), + 'symfony/service-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '1.0|2.0', + ), + ), + 'symfony/string' => array( + 'pretty_version' => 'v5.4.3', + 'version' => '5.4.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/string', + 'aliases' => array(), + 'reference' => '92043b7d8383e48104e411bc9434b260dbeb5a10', + 'dev_requirement' => true, + ), + 'symfony/translation' => array( + 'pretty_version' => 'v5.4.6', + 'version' => '5.4.6.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/translation', + 'aliases' => array(), + 'reference' => 'a7ca9fdfffb0174209440c2ffa1dee228e15d95b', + 'dev_requirement' => true, + ), + 'symfony/translation-contracts' => array( + 'pretty_version' => 'v2.5.0', + 'version' => '2.5.0.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/translation-contracts', + 'aliases' => array(), + 'reference' => 'd28150f0f44ce854e942b671fc2620a98aae1b1e', + 'dev_requirement' => true, + ), + 'symfony/translation-implementation' => array( + 'dev_requirement' => true, + 'provided' => array( + 0 => '2.3', + ), + ), + 'symfony/yaml' => array( + 'pretty_version' => 'v5.4.3', + 'version' => '5.4.3.0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/yaml', + 'aliases' => array(), + 'reference' => 'e80f87d2c9495966768310fc531b487ce64237a2', + 'dev_requirement' => true, + ), + ), +); diff --git a/tests/integration/vendor/composer/platform_check.php b/tests/integration/vendor/composer/platform_check.php new file mode 100644 index 000000000..92370c5a0 --- /dev/null +++ b/tests/integration/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70300)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/tests/integration/vendor/container-interop/container-interop/LICENSE b/tests/integration/vendor/container-interop/container-interop/LICENSE deleted file mode 100644 index 7671d9020..000000000 --- a/tests/integration/vendor/container-interop/container-interop/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 container-interop - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. diff --git a/tests/integration/vendor/container-interop/container-interop/README.md b/tests/integration/vendor/container-interop/container-interop/README.md deleted file mode 100644 index ec434d0f2..000000000 --- a/tests/integration/vendor/container-interop/container-interop/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Container Interoperability - -[![Latest Stable Version](https://poser.pugx.org/container-interop/container-interop/v/stable.png)](https://packagist.org/packages/container-interop/container-interop) - -*container-interop* tries to identify and standardize features in *container* objects (service locators, -dependency injection containers, etc.) to achieve interopererability. - -Through discussions and trials, we try to create a standard, made of common interfaces but also recommendations. - -If PHP projects that provide container implementations begin to adopt these common standards, then PHP -applications and projects that use containers can depend on the common interfaces instead of specific -implementations. This facilitates a high-level of interoperability and flexibility that allows users to consume -*any* container implementation that can be adapted to these interfaces. - -The work done in this project is not officially endorsed by the [PHP-FIG](http://www.php-fig.org/), but it is being -worked on by members of PHP-FIG and other good developers. We adhere to the spirit and ideals of PHP-FIG, and hope -this project will pave the way for one or more future PSRs. - - -## Installation - -You can install this package through Composer: - -```json -{ - "require": { - "container-interop/container-interop": "~1.0" - } -} -``` - -The packages adheres to the [SemVer](http://semver.org/) specification, and there will be full backward compatibility -between minor versions. - -## Standards - -### Available - -- [`ContainerInterface`](src/Interop/Container/ContainerInterface.php). -[Description](docs/ContainerInterface.md) [Meta Document](docs/ContainerInterface-meta.md). -Describes the interface of a container that exposes methods to read its entries. -- [*Delegate lookup feature*](docs/Delegate-lookup.md). -[Meta Document](docs/Delegate-lookup-meta.md). -Describes the ability for a container to delegate the lookup of its dependencies to a third-party container. This -feature lets several containers work together in a single application. - -### Proposed - -View open [request for comments](https://github.com/container-interop/container-interop/labels/RFC) - -## Compatible projects - -### Projects implementing `ContainerInterface` - -- [Acclimate](https://github.com/jeremeamia/acclimate-container) -- [dcp-di](https://github.com/estelsmith/dcp-di) -- [Mouf](http://mouf-php.com) -- [Njasm Container](https://github.com/njasm/container) -- [PHP-DI](http://php-di.org) -- [PimpleInterop](https://github.com/moufmouf/pimple-interop) -- [XStatic](https://github.com/jeremeamia/xstatic) - -### Projects implementing the *delegate lookup* feature - -- [Mouf](http://mouf-php.com) -- [PHP-DI](http://php-di.org) -- [PimpleInterop](https://github.com/moufmouf/pimple-interop) - -## Workflow - -Everyone is welcome to join and contribute. - -The general workflow looks like this: - -1. Someone opens a discussion (GitHub issue) to suggest an interface -1. Feedback is gathered -1. The interface is added to a development branch -1. We release alpha versions so that the interface can be experimented with -1. Discussions and edits ensue until the interface is deemed stable by a general consensus -1. A new minor version of the package is released - -We try to not break BC by creating new interfaces instead of editing existing ones. - -While we currently work on interfaces, we are open to anything that might help towards interoperability, may that -be code, best practices, etc. diff --git a/tests/integration/vendor/container-interop/container-interop/composer.json b/tests/integration/vendor/container-interop/container-interop/composer.json deleted file mode 100644 index 84f387528..000000000 --- a/tests/integration/vendor/container-interop/container-interop/composer.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "container-interop/container-interop", - "type": "library", - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "license": "MIT", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" - } - } -} diff --git a/tests/integration/vendor/container-interop/container-interop/docs/ContainerInterface-meta.md b/tests/integration/vendor/container-interop/container-interop/docs/ContainerInterface-meta.md deleted file mode 100644 index 90711c905..000000000 --- a/tests/integration/vendor/container-interop/container-interop/docs/ContainerInterface-meta.md +++ /dev/null @@ -1,114 +0,0 @@ -# ContainerInterface Meta Document - -## Introduction - -This document describes the process and discussions that lead to the `ContainerInterface`. -Its goal is to explain the reasons behind each decision. - -## Goal - -The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a -container to obtain objects and parameters. - -By standardizing such a behavior, frameworks and libraries using the `ContainerInterface` -could work with any compatible container. -That would allow end users to choose their own container based on their own preferences. - -It is important to distinguish the two usages of a container: - -- configuring entries -- fetching entries - -Most of the time, those two sides are not used by the same party. -While it is often end users who tend to configure entries, it is generally the framework that fetch -entries to build the application. - -This is why this interface focuses only on how entries can be fetched from a container. - -## Interface name - -The interface name has been thoroughly discussed and was decided by a vote. - -The list of options considered with their respective votes are: - -- `ContainerInterface`: +8 -- `ProviderInterface`: +2 -- `LocatorInterface`: 0 -- `ReadableContainerInterface`: -5 -- `ServiceLocatorInterface`: -6 -- `ObjectFactory`: -6 -- `ObjectStore`: -8 -- `ConsumerInterface`: -9 - -[Full results of the vote](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote) - -The complete discussion can be read in [the issue #1](https://github.com/container-interop/container-interop/issues/1). - -## Interface methods - -The choice of which methods the interface would contain was made after a statistical analysis of existing containers. -The results of this analysis are available [in this document](https://gist.github.com/mnapoli/6159681). - -The summary of the analysis showed that: - -- all containers offer a method to get an entry by its id -- a large majority name such method `get()` -- for all containers, the `get()` method has 1 mandatory parameter of type string -- some containers have an optional additional argument for `get()`, but it doesn't same the same purpose between containers -- a large majority of the containers offer a method to test if it can return an entry by its id -- a majority name such method `has()` -- for all containers offering `has()`, the method has exactly 1 parameter of type string -- a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()` -- a large majority of the containers don't implement `ArrayAccess` - -The question of whether to include methods to define entries has been discussed in -[issue #1](https://github.com/container-interop/container-interop/issues/1). -It has been judged that such methods do not belong in the interface described here because it is out of its scope -(see the "Goal" section). - -As a result, the `ContainerInterface` contains two methods: - -- `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found. -- `has()`, returning a boolean, with one mandatory string parameter. - -### Number of parameters in `get()` method - -While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with -existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters -as long as they are optional, because the implementation *does* satisfy the interface. - -This issue has been discussed in [issue #6](https://github.com/container-interop/container-interop/issues/6). - -### Type of the `$id` parameter - -The type of the `$id` parameter in `get()` and `has()` has been discussed in -[issue #6](https://github.com/container-interop/container-interop/issues/6). -While `string` is used in all the containers that were analyzed, it was suggested that allowing -anything (such as objects) could allow containers to offer a more advanced query API. - -An example given was to use the container as an object builder. The `$id` parameter would then be an -object that would describe how to create an instance. - -The conclusion of the discussion was that this was beyond the scope of getting entries from a container without -knowing how the container provided them, and it was more fit for a factory. - -## Contributors - -Are listed here all people that contributed in the discussions or votes, by alphabetical order: - -- [Amy Stephen](https://github.com/AmyStephen) -- [David Négrier](https://github.com/moufmouf) -- [Don Gilbert](https://github.com/dongilbert) -- [Jason Judge](https://github.com/judgej) -- [Jeremy Lindblom](https://github.com/jeremeamia) -- [Marco Pivetta](https://github.com/Ocramius) -- [Matthieu Napoli](https://github.com/mnapoli) -- [Paul M. Jones](https://github.com/pmjones) -- [Stephan Hochdörfer](https://github.com/shochdoerfer) -- [Taylor Otwell](https://github.com/taylorotwell) - -## Relevant links - -- [`ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php) -- [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed) -- [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote) diff --git a/tests/integration/vendor/container-interop/container-interop/docs/ContainerInterface.md b/tests/integration/vendor/container-interop/container-interop/docs/ContainerInterface.md deleted file mode 100644 index 9f609674c..000000000 --- a/tests/integration/vendor/container-interop/container-interop/docs/ContainerInterface.md +++ /dev/null @@ -1,153 +0,0 @@ -Container interface -=================== - -This document describes a common interface for dependency injection containers. - -The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a -container to obtain objects and parameters (called *entries* in the rest of this document). - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in [RFC 2119][]. - -The word `implementor` in this document is to be interpreted as someone -implementing the `ContainerInterface` in a depency injection-related library or framework. -Users of dependency injections containers (DIC) are refered to as `user`. - -[RFC 2119]: http://tools.ietf.org/html/rfc2119 - -1. Specification ------------------ - -### 1.1 Basics - -- The `Interop\Container\ContainerInterface` exposes two methods : `get` and `has`. - -- `get` takes one mandatory parameter: an entry identifier. It MUST be a string. - A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier - is not known to the container. Two successive calls to `get` with the same - identifier SHOULD return the same value. However, depending on the `implementor` - design and/or `user` configuration, different values might be returned, so - `user` SHOULD NOT rely on getting the same value on 2 successive calls. - While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations - MAY accept additional optional parameters. - -- `has` takes one unique parameter: an entry identifier. It MUST return `true` - if an entry identifier is known to the container and `false` if it is not. - -### 1.2 Exceptions - -Exceptions directly thrown by the container MUST implement the -[`Interop\Container\Exception\ContainerException`](../src/Interop/Container/Exception/ContainerException.php). - -A call to the `get` method with a non-existing id should throw a -[`Interop\Container\Exception\NotFoundException`](../src/Interop/Container/Exception/NotFoundException.php). - -### 1.3 Additional features - -This section describes additional features that MAY be added to a container. Containers are not -required to implement these features to respect the ContainerInterface. - -#### 1.3.1 Delegate lookup feature - -The goal of the *delegate lookup* feature is to allow several containers to share entries. -Containers implementing this feature can perform dependency lookups in other containers. - -Containers implementing this feature will offer a greater lever of interoperability -with other containers. Implementation of this feature is therefore RECOMMENDED. - -A container implementing this feature: - -- MUST implement the `ContainerInterface` -- MUST provide a way to register a delegate container (using a constructor parameter, or a setter, - or any possible way). The delegate container MUST implement the `ContainerInterface`. - -When a container is configured to use a delegate container for dependencies: - -- Calls to the `get` method should only return an entry if the entry is part of the container. - If the entry is not part of the container, an exception should be thrown - (as requested by the `ContainerInterface`). -- Calls to the `has` method should only return `true` if the entry is part of the container. - If the entry is not part of the container, `false` should be returned. -- If the fetched entry has dependencies, **instead** of performing - the dependency lookup in the container, the lookup is performed on the *delegate container*. - -Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself. - -It is however allowed for containers to provide exception cases for special entries, and a way to lookup -into the same container (or another container) instead of the delegate container. - -2. Package ----------- - -The interfaces and classes described as well as relevant exception are provided as part of the -[container-interop/container-interop](https://packagist.org/packages/container-interop/container-interop) package. - -3. `Interop\Container\ContainerInterface` ------------------------------------------ - -```php -setParentContainer($this); - } - } - ... - } -} - -``` - -**Cons:** - -Cons have been extensively discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777). -Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments, -and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface. - -### 4.4 Alternative: no exception case for delegate lookups - -Originally, the proposed wording for delegate lookup calls was: - -> Important! The lookup MUST be performed on the delegate container **only**, not on the container itself. - -This was later replaced by: - -> Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself. -> -> It is however allowed for containers to provide exception cases for special entries, and a way to lookup -> into the same container (or another container) instead of the delegate container. - -Exception cases have been allowed to avoid breaking dependencies with some services that must be provided -by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235 - -### 4.5 Alternative: having one of the containers act as the composite container - -In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to -add another DI container to this container. Most of the time, the "big" framework will be responsible for -creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted, -the "big" framework will not be aware of the existence of a composite container that it should use instead -of its own container. - -For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container -to make it act as a composite container. - -This has been discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194) -and [here](http://mouf-php.com/container-interop-whats-next#solution4). - -This was implemented in Symfony 2 using: - -- [interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0) -- [framework interop](https://github.com/mnapoli/framework-interop/) - -This was implemented in Silex using: - -- [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di) - -Having a container act as the composite container is not part of the delegate lookup standard because it is -simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop -play nice with other DI containers. - - -5. Implementations ------------------- - -The following projects already implement the delegate lookup feature: - -- [Mouf](http://mouf-php.com), through the [`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120) -- [PHP-DI](http://php-di.org/), through the [`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72) -- [pimple-interop](https://github.com/moufmouf/pimple-interop), through the [`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62) - -6. People ---------- - -Are listed here all people that contributed in the discussions, by alphabetical order: - -- [Alexandru Pătrănescu](https://github.com/drealecs) -- [Ben Peachey](https://github.com/potherca) -- [David Négrier](https://github.com/moufmouf) -- [Jeremy Lindblom](https://github.com/jeremeamia) -- [Marco Pivetta](https://github.com/Ocramius) -- [Matthieu Napoli](https://github.com/mnapoli) -- [Nelson J Morais](https://github.com/njasm) -- [Phil Sturgeon](https://github.com/philsturgeon) -- [Stephan Hochdörfer](https://github.com/shochdoerfer) - -7. Relevant Links ------------------ - -_**Note:** Order descending chronologically._ - -- [Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20) -- [Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8) -- [Original article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next) - diff --git a/tests/integration/vendor/container-interop/container-interop/docs/Delegate-lookup.md b/tests/integration/vendor/container-interop/container-interop/docs/Delegate-lookup.md deleted file mode 100644 index 04eb3aea0..000000000 --- a/tests/integration/vendor/container-interop/container-interop/docs/Delegate-lookup.md +++ /dev/null @@ -1,60 +0,0 @@ -Delegate lookup feature -======================= - -This document describes a standard for dependency injection containers. - -The goal set by the *delegate lookup* feature is to allow several containers to share entries. -Containers implementing this feature can perform dependency lookups in other containers. - -Containers implementing this feature will offer a greater lever of interoperability -with other containers. Implementation of this feature is therefore RECOMMENDED. - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in [RFC 2119][]. - -The word `implementor` in this document is to be interpreted as someone -implementing the delegate lookup feature in a dependency injection-related library or framework. -Users of dependency injections containers (DIC) are refered to as `user`. - -[RFC 2119]: http://tools.ietf.org/html/rfc2119 - -1. Vocabulary -------------- - -In a dependency injection container, the container is used to fetch entries. -Entries can have dependencies on other entries. Usually, these other entries are fetched by the container. - -The *delegate lookup* feature is the ability for a container to fetch dependencies in -another container. In the rest of the document, the word "container" will reference the container -implemented by the implementor. The word "delegate container" will reference the container we are -fetching the dependencies from. - -2. Specification ----------------- - -A container implementing the *delegate lookup* feature: - -- MUST implement the [`ContainerInterface`](ContainerInterface.md) -- MUST provide a way to register a delegate container (using a constructor parameter, or a setter, - or any possible way). The delegate container MUST implement the [`ContainerInterface`](ContainerInterface.md). - -When a container is configured to use a delegate container for dependencies: - -- Calls to the `get` method should only return an entry if the entry is part of the container. - If the entry is not part of the container, an exception should be thrown - (as requested by the [`ContainerInterface`](ContainerInterface.md)). -- Calls to the `has` method should only return `true` if the entry is part of the container. - If the entry is not part of the container, `false` should be returned. -- If the fetched entry has dependencies, **instead** of performing - the dependency lookup in the container, the lookup is performed on the *delegate container*. - -Important: By default, the dependency lookups SHOULD be performed on the delegate container **only**, not on the container itself. - -It is however allowed for containers to provide exception cases for special entries, and a way to lookup -into the same container (or another container) instead of the delegate container. - -3. Package / Interface ----------------------- - -This feature is not tied to any code, interface or package. diff --git a/tests/integration/vendor/container-interop/container-interop/docs/images/interoperating_containers.png b/tests/integration/vendor/container-interop/container-interop/docs/images/interoperating_containers.png deleted file mode 100644 index 9c672e16c..000000000 Binary files a/tests/integration/vendor/container-interop/container-interop/docs/images/interoperating_containers.png and /dev/null differ diff --git a/tests/integration/vendor/container-interop/container-interop/docs/images/priority.png b/tests/integration/vendor/container-interop/container-interop/docs/images/priority.png deleted file mode 100644 index 5760dc71b..000000000 Binary files a/tests/integration/vendor/container-interop/container-interop/docs/images/priority.png and /dev/null differ diff --git a/tests/integration/vendor/container-interop/container-interop/docs/images/side_by_side_containers.png b/tests/integration/vendor/container-interop/container-interop/docs/images/side_by_side_containers.png deleted file mode 100644 index 24ca03c7c..000000000 Binary files a/tests/integration/vendor/container-interop/container-interop/docs/images/side_by_side_containers.png and /dev/null differ diff --git a/tests/integration/vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php b/tests/integration/vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php deleted file mode 100644 index dee5ffa7a..000000000 --- a/tests/integration/vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php +++ /dev/null @@ -1,37 +0,0 @@ - +The MIT License (MIT) + +Copyright (c) 2011 Michael Dowling +Copyright (c) 2012 Jeremy Lindblom +Copyright (c) 2014 Graham Campbell +Copyright (c) 2015 Márk Sági-Kazár +Copyright (c) 2015 Tobias Schultze +Copyright (c) 2016 Tobias Nyholm +Copyright (c) 2016 George Mponos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/guzzlehttp/guzzle/README.md b/tests/integration/vendor/guzzlehttp/guzzle/README.md index bcd18b8e7..0025aa7a1 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/README.md +++ b/tests/integration/vendor/guzzlehttp/guzzle/README.md @@ -1,8 +1,9 @@ -Guzzle, PHP HTTP client -======================= +![Guzzle](.github/logo.png?raw=true) + +# Guzzle, PHP HTTP client [![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases) -[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle) +[![Build Status](https://img.shields.io/github/workflow/status/guzzle/guzzle/CI?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI) [![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle) Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and @@ -14,6 +15,7 @@ trivial to integrate with web services. - Can send both synchronous and asynchronous requests using the same interface. - Uses PSR-7 interfaces for requests, responses, and streams. This allows you to utilize other PSR-7 compatible libraries with Guzzle. +- Supports PSR-18 allowing interoperability between other PSR-18 HTTP Clients. - Abstracts away the underlying HTTP transport, allowing you to write environment and transport agnostic code; i.e., no hard dependency on cURL, PHP streams, sockets, or non-blocking event loops. @@ -21,57 +23,40 @@ trivial to integrate with web services. ```php $client = new \GuzzleHttp\Client(); -$res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); -echo $res->getStatusCode(); -// 200 -echo $res->getHeaderLine('content-type'); -// 'application/json; charset=utf8' -echo $res->getBody(); -// '{"id": 1420053, "name": "guzzle", ...}' +$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); + +echo $response->getStatusCode(); // 200 +echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8' +echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}' // Send an asynchronous request. $request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org'); $promise = $client->sendAsync($request)->then(function ($response) { echo 'I completed! ' . $response->getBody(); }); + $promise->wait(); ``` ## Help and docs +We use GitHub issues only to discuss bugs and new features. For support please refer to: + - [Documentation](http://guzzlephp.org/) - [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle) +- [#guzzle](https://app.slack.com/client/T0D2S9JCT/CE6UAAKL4) channel on [PHP-HTTP Slack](http://slack.httplug.io/) - [Gitter](https://gitter.im/guzzle/guzzle) ## Installing Guzzle The recommended way to install Guzzle is through -[Composer](http://getcomposer.org). +[Composer](https://getcomposer.org/). ```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php -``` - -Next, run the Composer command to install the latest stable version of Guzzle: - -```bash -php composer.phar require guzzlehttp/guzzle -``` - -After installing, you need to require Composer's autoloader: - -```php -require 'vendor/autoload.php'; +composer require guzzlehttp/guzzle ``` -You can then later update Guzzle using composer: - - ```bash -composer.phar update - ``` - ## Version Guidance @@ -79,13 +64,31 @@ composer.phar update |---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------| | 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >= 5.3.3 | | 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >= 5.4 | -| 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 | -| 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 | +| 5.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 | +| 6.x | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 | +| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >= 7.2 | [guzzle-3-repo]: https://github.com/guzzle/guzzle3 [guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x [guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3 -[guzzle-6-repo]: https://github.com/guzzle/guzzle -[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/ -[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/ -[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/ +[guzzle-6-repo]: https://github.com/guzzle/guzzle/tree/6.5 +[guzzle-7-repo]: https://github.com/guzzle/guzzle +[guzzle-3-docs]: http://guzzle3.readthedocs.org +[guzzle-5-docs]: http://docs.guzzlephp.org/en/5.3/ +[guzzle-6-docs]: http://docs.guzzlephp.org/en/6.5/ +[guzzle-7-docs]: http://docs.guzzlephp.org/en/latest/ + + +## Security + +If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/guzzle/security/policy) for more information. + +## License + +Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information. + +## For Enterprise + +Available as part of the Tidelift Subscription + +The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-guzzle?utm_source=packagist-guzzlehttp-guzzle&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/tests/integration/vendor/guzzlehttp/guzzle/UPGRADING.md b/tests/integration/vendor/guzzlehttp/guzzle/UPGRADING.md index 91d1dcc99..45417a7e1 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/UPGRADING.md +++ b/tests/integration/vendor/guzzlehttp/guzzle/UPGRADING.md @@ -1,10 +1,60 @@ Guzzle Upgrade Guide ==================== +6.0 to 7.0 +---------- + +In order to take advantage of the new features of PHP, Guzzle dropped the support +of PHP 5. The minimum supported PHP version is now PHP 7.2. Type hints and return +types for functions and methods have been added wherever possible. + +Please make sure: +- You are calling a function or a method with the correct type. +- If you extend a class of Guzzle; update all signatures on methods you override. + +#### Other backwards compatibility breaking changes + +- Class `GuzzleHttp\UriTemplate` is removed. +- Class `GuzzleHttp\Exception\SeekException` is removed. +- Classes `GuzzleHttp\Exception\BadResponseException`, `GuzzleHttp\Exception\ClientException`, + `GuzzleHttp\Exception\ServerException` can no longer be initialized with an empty + Response as argument. +- Class `GuzzleHttp\Exception\ConnectException` now extends `GuzzleHttp\Exception\TransferException` + instead of `GuzzleHttp\Exception\RequestException`. +- Function `GuzzleHttp\Exception\ConnectException::getResponse()` is removed. +- Function `GuzzleHttp\Exception\ConnectException::hasResponse()` is removed. +- Constant `GuzzleHttp\ClientInterface::VERSION` is removed. Added `GuzzleHttp\ClientInterface::MAJOR_VERSION` instead. +- Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed. + Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative. +- Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed. +- Request option `exception` is removed. Please use `http_errors`. +- Request option `save_to` is removed. Please use `sink`. +- Pool option `pool_size` is removed. Please use `concurrency`. +- We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility. +- The `get`, `head`, `put`, `post`, `patch`, `delete`, `getAsync`, `headAsync`, `putAsync`, `postAsync`, `patchAsync`, and `deleteAsync` methods are now implemented as genuine methods on `GuzzleHttp\Client`, with strong typing. The original `__call` implementation remains unchanged for now, for maximum backwards compatibility, but won't be invoked under normal operation. +- The `log` middleware will log the errors with level `error` instead of `notice` +- Support for international domain names (IDN) is now disabled by default, and enabling it requires installing ext-intl, linked against a modern version of the C library (ICU 4.6 or higher). + +#### Native functions calls + +All internal native functions calls of Guzzle are now prefixed with a slash. This +change makes it impossible for method overloading by other libraries or applications. +Example: + +```php +// Before: +curl_version(); + +// After: +\curl_version(); +``` + +For the full diff you can check [here](https://github.com/guzzle/guzzle/compare/6.5.4..master). + 5.0 to 6.0 ---------- -Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages. +Guzzle now uses [PSR-7](https://www.php-fig.org/psr/psr-7/) for HTTP messages. Due to the fact that these messages are immutable, this prompted a refactoring of Guzzle to use a middleware based system rather than an event system. Any HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be @@ -167,7 +217,7 @@ passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP ## Removed Fluent Interfaces -[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil) +[Fluent interfaces were removed](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) from the following classes: - `GuzzleHttp\Collection` @@ -820,7 +870,7 @@ HeaderInterface (e.g. toArray(), getAll(), etc.). 3.3 to 3.4 ---------- -Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. +Base URLs of a client now follow the rules of https://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. 3.2 to 3.3 ---------- diff --git a/tests/integration/vendor/guzzlehttp/guzzle/composer.json b/tests/integration/vendor/guzzlehttp/guzzle/composer.json index 1f328e308..2549f78b2 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/composer.json +++ b/tests/integration/vendor/guzzlehttp/guzzle/composer.json @@ -1,44 +1,98 @@ { "name": "guzzlehttp/guzzle", - "type": "library", "description": "Guzzle is a PHP HTTP client library", - "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], - "homepage": "http://guzzlephp.org/", + "keywords": [ + "framework", + "http", + "rest", + "web service", + "curl", + "client", + "HTTP client", + "PSR-7", + "PSR-18" + ], "license": "MIT", "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "require": { - "php": ">=5.5", - "guzzlehttp/psr7": "^1.4", - "guzzlehttp/promises": "^1.0" + "php": "^7.2.5 || ^8.0", + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" + "bamarni/composer-bin-plugin": "^1.4.1", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "config": { + "preferred-install": "dist", + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } }, "autoload": { - "files": ["src/functions_include.php"], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "autoload-dev": { "psr-4": { "GuzzleHttp\\Tests\\": "tests/" } - }, - "suggest": { - "psr/log": "Required for using the Log middleware" - }, - "extra": { - "branch-alias": { - "dev-master": "6.3-dev" - } } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/BodySummarizer.php b/tests/integration/vendor/guzzlehttp/guzzle/src/BodySummarizer.php new file mode 100644 index 000000000..6eca94ef9 --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/BodySummarizer.php @@ -0,0 +1,28 @@ +truncateAt = $truncateAt; + } + + /** + * Returns a summarized message body. + */ + public function summarize(MessageInterface $message): ?string + { + return $this->truncateAt === null + ? \GuzzleHttp\Psr7\Message::bodySummary($message) + : \GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt); + } +} diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php b/tests/integration/vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php new file mode 100644 index 000000000..3e02e036e --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.php @@ -0,0 +1,13 @@ +configureDefaults($config); } + /** + * @param string $method + * @param array $args + * + * @return PromiseInterface|ResponseInterface + * + * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0. + */ public function __call($method, $args) { - if (count($args) < 1) { - throw new \InvalidArgumentException('Magic request methods require a URI and optional options array'); + if (\count($args) < 1) { + throw new InvalidArgumentException('Magic request methods require a URI and optional options array'); } $uri = $args[0]; - $opts = isset($args[1]) ? $args[1] : []; + $opts = $args[1] ?? []; - return substr($method, -5) === 'Async' - ? $this->requestAsync(substr($method, 0, -5), $uri, $opts) + return \substr($method, -5) === 'Async' + ? $this->requestAsync(\substr($method, 0, -5), $uri, $opts) : $this->request($method, $uri, $opts); } - public function sendAsync(RequestInterface $request, array $options = []) + /** + * Asynchronously send an HTTP request. + * + * @param array $options Request options to apply to the given + * request and to the transfer. See \GuzzleHttp\RequestOptions. + */ + public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface { // Merge the base URI into the request URI if needed. $options = $this->prepareDefaults($options); @@ -100,23 +109,57 @@ public function sendAsync(RequestInterface $request, array $options = []) ); } - public function send(RequestInterface $request, array $options = []) + /** + * Send an HTTP request. + * + * @param array $options Request options to apply to the given + * request and to the transfer. See \GuzzleHttp\RequestOptions. + * + * @throws GuzzleException + */ + public function send(RequestInterface $request, array $options = []): ResponseInterface + { + $options[RequestOptions::SYNCHRONOUS] = true; + return $this->sendAsync($request, $options)->wait(); + } + + /** + * The HttpClient PSR (PSR-18) specify this method. + * + * @inheritDoc + */ + public function sendRequest(RequestInterface $request): ResponseInterface { $options[RequestOptions::SYNCHRONOUS] = true; + $options[RequestOptions::ALLOW_REDIRECTS] = false; + $options[RequestOptions::HTTP_ERRORS] = false; + return $this->sendAsync($request, $options)->wait(); } - public function requestAsync($method, $uri = '', array $options = []) + /** + * Create and send an asynchronous HTTP request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string $method HTTP method + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions. + */ + public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface { $options = $this->prepareDefaults($options); // Remove request modifying parameter because it can be done up-front. - $headers = isset($options['headers']) ? $options['headers'] : []; - $body = isset($options['body']) ? $options['body'] : null; - $version = isset($options['version']) ? $options['version'] : '1.1'; + $headers = $options['headers'] ?? []; + $body = $options['body'] ?? null; + $version = $options['version'] ?? '1.1'; // Merge the URI into the base URI. - $uri = $this->buildUri($uri, $options); - if (is_array($body)) { - $this->invalidBody(); + $uri = $this->buildUri(Psr7\Utils::uriFor($uri), $options); + if (\is_array($body)) { + throw $this->invalidBody(); } $request = new Psr7\Request($method, $uri, $headers, $body, $version); // Remove the option so that they are not doubly-applied. @@ -125,26 +168,54 @@ public function requestAsync($method, $uri = '', array $options = []) return $this->transfer($request, $options); } - public function request($method, $uri = '', array $options = []) + /** + * Create and send an HTTP request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string $method HTTP method. + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions. + * + * @throws GuzzleException + */ + public function request(string $method, $uri = '', array $options = []): ResponseInterface { $options[RequestOptions::SYNCHRONOUS] = true; return $this->requestAsync($method, $uri, $options)->wait(); } - public function getConfig($option = null) + /** + * Get a client configuration option. + * + * These options include default request options of the client, a "handler" + * (if utilized by the concrete client), and a "base_uri" if utilized by + * the concrete client. + * + * @param string|null $option The config option to retrieve. + * + * @return mixed + * + * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0. + */ + public function getConfig(?string $option = null) { return $option === null ? $this->config - : (isset($this->config[$option]) ? $this->config[$option] : null); + : ($this->config[$option] ?? null); } - private function buildUri($uri, array $config) + private function buildUri(UriInterface $uri, array $config): UriInterface { - // for BC we accept null which would otherwise fail in uri_for - $uri = Psr7\uri_for($uri === null ? '' : $uri); - if (isset($config['base_uri'])) { - $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri); + $uri = Psr7\UriResolver::resolve(Psr7\Utils::uriFor($config['base_uri']), $uri); + } + + if (isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) { + $idnOptions = ($config['idn_conversion'] === true) ? \IDNA_DEFAULT : $config['idn_conversion']; + $uri = Utils::idnUriConvert($uri, $idnOptions); } return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; @@ -152,17 +223,16 @@ private function buildUri($uri, array $config) /** * Configures the default options for a client. - * - * @param array $config */ - private function configureDefaults(array $config) + private function configureDefaults(array $config): void { $defaults = [ 'allow_redirects' => RedirectMiddleware::$defaultSettings, 'http_errors' => true, 'decode_content' => true, 'verify' => true, - 'cookies' => false + 'cookies' => false, + 'idn_conversion' => false, ]; // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. @@ -170,17 +240,17 @@ private function configureDefaults(array $config) // We can only trust the HTTP_PROXY environment variable in a CLI // process due to the fact that PHP has no reliable mechanism to // get environment variables that start with "HTTP_". - if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) { - $defaults['proxy']['http'] = getenv('HTTP_PROXY'); + if (\PHP_SAPI === 'cli' && ($proxy = Utils::getenv('HTTP_PROXY'))) { + $defaults['proxy']['http'] = $proxy; } - if ($proxy = getenv('HTTPS_PROXY')) { + if ($proxy = Utils::getenv('HTTPS_PROXY')) { $defaults['proxy']['https'] = $proxy; } - if ($noProxy = getenv('NO_PROXY')) { - $cleanedNoProxy = str_replace(' ', '', $noProxy); - $defaults['proxy']['no'] = explode(',', $cleanedNoProxy); + if ($noProxy = Utils::getenv('NO_PROXY')) { + $cleanedNoProxy = \str_replace(' ', '', $noProxy); + $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy); } $this->config = $config + $defaults; @@ -191,15 +261,15 @@ private function configureDefaults(array $config) // Add the default user-agent header. if (!isset($this->config['headers'])) { - $this->config['headers'] = ['User-Agent' => default_user_agent()]; + $this->config['headers'] = ['User-Agent' => Utils::defaultUserAgent()]; } else { // Add the User-Agent header if one was not already set. - foreach (array_keys($this->config['headers']) as $name) { - if (strtolower($name) === 'user-agent') { + foreach (\array_keys($this->config['headers']) as $name) { + if (\strtolower($name) === 'user-agent') { return; } } - $this->config['headers']['User-Agent'] = default_user_agent(); + $this->config['headers']['User-Agent'] = Utils::defaultUserAgent(); } } @@ -207,10 +277,8 @@ private function configureDefaults(array $config) * Merges default options into the array. * * @param array $options Options to modify by reference - * - * @return array */ - private function prepareDefaults($options) + private function prepareDefaults(array $options): array { $defaults = $this->config; @@ -222,13 +290,13 @@ private function prepareDefaults($options) // Special handling for headers is required as they are added as // conditional headers and as headers passed to a request ctor. - if (array_key_exists('headers', $options)) { + if (\array_key_exists('headers', $options)) { // Allows default headers to be unset. if ($options['headers'] === null) { - $defaults['_conditional'] = null; + $defaults['_conditional'] = []; unset($options['headers']); - } elseif (!is_array($options['headers'])) { - throw new \InvalidArgumentException('headers must be an array'); + } elseif (!\is_array($options['headers'])) { + throw new InvalidArgumentException('headers must be an array'); } } @@ -251,66 +319,50 @@ private function prepareDefaults($options) * The URI of the request is not modified and the request options are used * as-is without merging in default options. * - * @param RequestInterface $request - * @param array $options - * - * @return Promise\PromiseInterface + * @param array $options See \GuzzleHttp\RequestOptions. */ - private function transfer(RequestInterface $request, array $options) + private function transfer(RequestInterface $request, array $options): PromiseInterface { - // save_to -> sink - if (isset($options['save_to'])) { - $options['sink'] = $options['save_to']; - unset($options['save_to']); - } - - // exceptions -> http_errors - if (isset($options['exceptions'])) { - $options['http_errors'] = $options['exceptions']; - unset($options['exceptions']); - } - $request = $this->applyOptions($request, $options); + /** @var HandlerStack $handler */ $handler = $options['handler']; try { - return Promise\promise_for($handler($request, $options)); + return P\Create::promiseFor($handler($request, $options)); } catch (\Exception $e) { - return Promise\rejection_for($e); + return P\Create::rejectionFor($e); } } /** * Applies the array of request options to a request. - * - * @param RequestInterface $request - * @param array $options - * - * @return RequestInterface */ - private function applyOptions(RequestInterface $request, array &$options) + private function applyOptions(RequestInterface $request, array &$options): RequestInterface { $modify = [ 'set_headers' => [], ]; if (isset($options['headers'])) { + if (array_keys($options['headers']) === range(0, count($options['headers']) - 1)) { + throw new InvalidArgumentException('The headers array must have header name as keys.'); + } $modify['set_headers'] = $options['headers']; unset($options['headers']); } if (isset($options['form_params'])) { if (isset($options['multipart'])) { - throw new \InvalidArgumentException('You cannot use ' + throw new InvalidArgumentException('You cannot use ' . 'form_params and multipart at the same time. Use the ' . 'form_params option if you want to send application/' . 'x-www-form-urlencoded requests, and the multipart ' . 'option to send multipart/form-data requests.'); } - $options['body'] = http_build_query($options['form_params'], '', '&'); + $options['body'] = \http_build_query($options['form_params'], '', '&'); unset($options['form_params']); // Ensure that we don't have the header in different case and set the new value. - $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded'; } @@ -320,10 +372,10 @@ private function applyOptions(RequestInterface $request, array &$options) } if (isset($options['json'])) { - $options['body'] = \GuzzleHttp\json_encode($options['json']); + $options['body'] = Utils::jsonEncode($options['json']); unset($options['json']); // Ensure that we don't have the header in different case and set the new value. - $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/json'; } @@ -331,47 +383,47 @@ private function applyOptions(RequestInterface $request, array &$options) && $options['decode_content'] !== true ) { // Ensure that we don't have the header in different case and set the new value. - $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']); + $options['_conditional'] = Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']); $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; } if (isset($options['body'])) { - if (is_array($options['body'])) { - $this->invalidBody(); + if (\is_array($options['body'])) { + throw $this->invalidBody(); } - $modify['body'] = Psr7\stream_for($options['body']); + $modify['body'] = Psr7\Utils::streamFor($options['body']); unset($options['body']); } - if (!empty($options['auth']) && is_array($options['auth'])) { + if (!empty($options['auth']) && \is_array($options['auth'])) { $value = $options['auth']; - $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; + $type = isset($value[2]) ? \strtolower($value[2]) : 'basic'; switch ($type) { case 'basic': // Ensure that we don't have the header in different case and set the new value. - $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']); + $modify['set_headers'] = Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']); $modify['set_headers']['Authorization'] = 'Basic ' - . base64_encode("$value[0]:$value[1]"); + . \base64_encode("$value[0]:$value[1]"); break; case 'digest': // @todo: Do not rely on curl - $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; - $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_DIGEST; + $options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]"; break; case 'ntlm': - $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM; - $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; + $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM; + $options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]"; break; } } if (isset($options['query'])) { $value = $options['query']; - if (is_array($value)) { - $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986); + if (\is_array($value)) { + $value = \http_build_query($value, '', '&', \PHP_QUERY_RFC3986); } - if (!is_string($value)) { - throw new \InvalidArgumentException('query must be a string or array'); + if (!\is_string($value)) { + throw new InvalidArgumentException('query must be a string or array'); } $modify['query'] = $value; unset($options['query']); @@ -380,16 +432,16 @@ private function applyOptions(RequestInterface $request, array &$options) // Ensure that sink is not an invalid value. if (isset($options['sink'])) { // TODO: Add more sink validation? - if (is_bool($options['sink'])) { - throw new \InvalidArgumentException('sink must not be a boolean'); + if (\is_bool($options['sink'])) { + throw new InvalidArgumentException('sink must not be a boolean'); } } - $request = Psr7\modify_request($request, $modify); + $request = Psr7\Utils::modifyRequest($request, $modify); if ($request->getBody() instanceof Psr7\MultipartStream) { // Use a multipart/form-data POST if a Content-Type is not set. // Ensure that we don't have the header in different case and set the new value. - $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); + $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' . $request->getBody()->getBoundary(); } @@ -403,7 +455,7 @@ private function applyOptions(RequestInterface $request, array &$options) $modify['set_headers'][$k] = $v; } } - $request = Psr7\modify_request($request, $modify); + $request = Psr7\Utils::modifyRequest($request, $modify); // Don't pass this internal value along to middleware/handlers. unset($options['_conditional']); } @@ -411,10 +463,13 @@ private function applyOptions(RequestInterface $request, array &$options) return $request; } - private function invalidBody() + /** + * Return an InvalidArgumentException with pre-set message. + */ + private function invalidBody(): InvalidArgumentException { - throw new \InvalidArgumentException('Passing in the "body" request ' - . 'option as an array to send a POST request has been deprecated. ' + return new InvalidArgumentException('Passing in the "body" request ' + . 'option as an array to send a request is not supported. ' . 'Please use the "form_params" request option to send a ' . 'application/x-www-form-urlencoded request, or the "multipart" ' . 'request option to send a multipart/form-data request.'); diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/tests/integration/vendor/guzzlehttp/guzzle/src/ClientInterface.php index 2dbcffa49..6aaee61af 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/ClientInterface.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/ClientInterface.php @@ -1,8 +1,9 @@ request('GET', $uri, $options); + } + + /** + * Create and send an HTTP HEAD request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @throws GuzzleException + */ + public function head($uri, array $options = []): ResponseInterface + { + return $this->request('HEAD', $uri, $options); + } + + /** + * Create and send an HTTP PUT request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @throws GuzzleException + */ + public function put($uri, array $options = []): ResponseInterface + { + return $this->request('PUT', $uri, $options); + } + + /** + * Create and send an HTTP POST request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @throws GuzzleException + */ + public function post($uri, array $options = []): ResponseInterface + { + return $this->request('POST', $uri, $options); + } + + /** + * Create and send an HTTP PATCH request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @throws GuzzleException + */ + public function patch($uri, array $options = []): ResponseInterface + { + return $this->request('PATCH', $uri, $options); + } + + /** + * Create and send an HTTP DELETE request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + * + * @throws GuzzleException + */ + public function delete($uri, array $options = []): ResponseInterface + { + return $this->request('DELETE', $uri, $options); + } + + /** + * Create and send an asynchronous HTTP request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string $method HTTP method + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + abstract public function requestAsync(string $method, $uri, array $options = []): PromiseInterface; + + /** + * Create and send an asynchronous HTTP GET request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + public function getAsync($uri, array $options = []): PromiseInterface + { + return $this->requestAsync('GET', $uri, $options); + } + + /** + * Create and send an asynchronous HTTP HEAD request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + public function headAsync($uri, array $options = []): PromiseInterface + { + return $this->requestAsync('HEAD', $uri, $options); + } + + /** + * Create and send an asynchronous HTTP PUT request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + public function putAsync($uri, array $options = []): PromiseInterface + { + return $this->requestAsync('PUT', $uri, $options); + } + + /** + * Create and send an asynchronous HTTP POST request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + public function postAsync($uri, array $options = []): PromiseInterface + { + return $this->requestAsync('POST', $uri, $options); + } + + /** + * Create and send an asynchronous HTTP PATCH request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + public function patchAsync($uri, array $options = []): PromiseInterface + { + return $this->requestAsync('PATCH', $uri, $options); + } + + /** + * Create and send an asynchronous HTTP DELETE request. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URL can + * contain the query string as well. Use an array to provide a URL + * template and additional variables to use in the URL template expansion. + * + * @param string|UriInterface $uri URI object or string. + * @param array $options Request options to apply. + */ + public function deleteAsync($uri, array $options = []): PromiseInterface + { + return $this->requestAsync('DELETE', $uri, $options); + } +} diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php index 78f2b79fe..d6757c654 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -1,4 +1,5 @@ strictMode = $strictMode; @@ -39,10 +44,8 @@ public function __construct($strictMode = false, $cookieArray = []) * * @param array $cookies Cookies to create the jar from * @param string $domain Domain to set the cookies to - * - * @return self */ - public static function fromArray(array $cookies, $domain) + public static function fromArray(array $cookies, string $domain): self { $cookieJar = new self(); foreach ($cookies as $name => $value) { @@ -57,26 +60,15 @@ public static function fromArray(array $cookies, $domain) return $cookieJar; } - /** - * @deprecated - */ - public static function getCookieValue($value) - { - return $value; - } - /** * Evaluate if this cookie should be persisted to storage * that survives between requests. * - * @param SetCookie $cookie Being evaluated. - * @param bool $allowSessionCookies If we should persist session cookies - * @return bool + * @param SetCookie $cookie Being evaluated. + * @param bool $allowSessionCookies If we should persist session cookies */ - public static function shouldPersist( - SetCookie $cookie, - $allowSessionCookies = false - ) { + public static function shouldPersist(SetCookie $cookie, bool $allowSessionCookies = false): bool + { if ($cookie->getExpires() || $allowSessionCookies) { if (!$cookie->getDiscard()) { return true; @@ -90,52 +82,57 @@ public static function shouldPersist( * Finds and returns the cookie based on the name * * @param string $name cookie name to search for + * * @return SetCookie|null cookie that was found or null if not found */ - public function getCookieByName($name) + public function getCookieByName(string $name): ?SetCookie { - // don't allow a null name - if ($name === null) { - return null; - } foreach ($this->cookies as $cookie) { - if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) { + if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) { return $cookie; } } + + return null; } - public function toArray() + /** + * @inheritDoc + */ + public function toArray(): array { - return array_map(function (SetCookie $cookie) { + return \array_map(static function (SetCookie $cookie): array { return $cookie->toArray(); }, $this->getIterator()->getArrayCopy()); } - public function clear($domain = null, $path = null, $name = null) + /** + * @inheritDoc + */ + public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void { if (!$domain) { $this->cookies = []; return; } elseif (!$path) { - $this->cookies = array_filter( + $this->cookies = \array_filter( $this->cookies, - function (SetCookie $cookie) use ($path, $domain) { + static function (SetCookie $cookie) use ($domain): bool { return !$cookie->matchesDomain($domain); } ); } elseif (!$name) { - $this->cookies = array_filter( + $this->cookies = \array_filter( $this->cookies, - function (SetCookie $cookie) use ($path, $domain) { + static function (SetCookie $cookie) use ($path, $domain): bool { return !($cookie->matchesPath($path) && $cookie->matchesDomain($domain)); } ); } else { - $this->cookies = array_filter( + $this->cookies = \array_filter( $this->cookies, - function (SetCookie $cookie) use ($path, $domain, $name) { + static function (SetCookie $cookie) use ($path, $domain, $name) { return !($cookie->getName() == $name && $cookie->matchesPath($path) && $cookie->matchesDomain($domain)); @@ -144,17 +141,23 @@ function (SetCookie $cookie) use ($path, $domain, $name) { } } - public function clearSessionCookies() + /** + * @inheritDoc + */ + public function clearSessionCookies(): void { - $this->cookies = array_filter( + $this->cookies = \array_filter( $this->cookies, - function (SetCookie $cookie) { + static function (SetCookie $cookie): bool { return !$cookie->getDiscard() && $cookie->getExpires(); } ); } - public function setCookie(SetCookie $cookie) + /** + * @inheritDoc + */ + public function setCookie(SetCookie $cookie): bool { // If the name string is empty (but not 0), ignore the set-cookie // string entirely. @@ -168,10 +171,9 @@ public function setCookie(SetCookie $cookie) if ($result !== true) { if ($this->strictMode) { throw new \RuntimeException('Invalid cookie: ' . $result); - } else { - $this->removeCookieIfEmpty($cookie); - return false; } + $this->removeCookieIfEmpty($cookie); + return false; } // Resolve conflicts with previously set cookies @@ -215,27 +217,28 @@ public function setCookie(SetCookie $cookie) return true; } - public function count() + public function count(): int { - return count($this->cookies); + return \count($this->cookies); } - public function getIterator() + /** + * @return \ArrayIterator + */ + public function getIterator(): \ArrayIterator { - return new \ArrayIterator(array_values($this->cookies)); + return new \ArrayIterator(\array_values($this->cookies)); } - public function extractCookies( - RequestInterface $request, - ResponseInterface $response - ) { + public function extractCookies(RequestInterface $request, ResponseInterface $response): void + { if ($cookieHeader = $response->getHeader('Set-Cookie')) { foreach ($cookieHeader as $cookie) { $sc = SetCookie::fromString($cookie); if (!$sc->getDomain()) { $sc->setDomain($request->getUri()->getHost()); } - if (0 !== strpos($sc->getPath(), '/')) { + if (0 !== \strpos($sc->getPath(), '/')) { $sc->setPath($this->getCookiePathFromRequest($request)); } $this->setCookie($sc); @@ -247,30 +250,28 @@ public function extractCookies( * Computes cookie path following RFC 6265 section 5.1.4 * * @link https://tools.ietf.org/html/rfc6265#section-5.1.4 - * - * @param RequestInterface $request - * @return string */ - private function getCookiePathFromRequest(RequestInterface $request) + private function getCookiePathFromRequest(RequestInterface $request): string { $uriPath = $request->getUri()->getPath(); - if ('' === $uriPath) { + if ('' === $uriPath) { return '/'; } - if (0 !== strpos($uriPath, '/')) { + if (0 !== \strpos($uriPath, '/')) { return '/'; } if ('/' === $uriPath) { return '/'; } - if (0 === $lastSlashPos = strrpos($uriPath, '/')) { + $lastSlashPos = \strrpos($uriPath, '/'); + if (0 === $lastSlashPos || false === $lastSlashPos) { return '/'; } - return substr($uriPath, 0, $lastSlashPos); + return \substr($uriPath, 0, $lastSlashPos); } - public function withCookieHeader(RequestInterface $request) + public function withCookieHeader(RequestInterface $request): RequestInterface { $values = []; $uri = $request->getUri(); @@ -290,17 +291,15 @@ public function withCookieHeader(RequestInterface $request) } return $values - ? $request->withHeader('Cookie', implode('; ', $values)) + ? $request->withHeader('Cookie', \implode('; ', $values)) : $request; } /** * If a cookie already exists and the server asks to set it again with a * null value, the cookie must be deleted. - * - * @param SetCookie $cookie */ - private function removeCookieIfEmpty(SetCookie $cookie) + private function removeCookieIfEmpty(SetCookie $cookie): void { $cookieValue = $cookie->getValue(); if ($cookieValue === null || $cookieValue === '') { diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php index 2cf298a86..7df374b5b 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -1,4 +1,5 @@ */ interface CookieJarInterface extends \Countable, \IteratorAggregate { @@ -26,7 +28,7 @@ interface CookieJarInterface extends \Countable, \IteratorAggregate * * @return RequestInterface returns the modified request. */ - public function withCookieHeader(RequestInterface $request); + public function withCookieHeader(RequestInterface $request): RequestInterface; /** * Extract cookies from an HTTP response and store them in the CookieJar. @@ -34,10 +36,7 @@ public function withCookieHeader(RequestInterface $request); * @param RequestInterface $request Request that was sent * @param ResponseInterface $response Response that was received */ - public function extractCookies( - RequestInterface $request, - ResponseInterface $response - ); + public function extractCookies(RequestInterface $request, ResponseInterface $response): void; /** * Sets a cookie in the cookie jar. @@ -46,7 +45,7 @@ public function extractCookies( * * @return bool Returns true on success or false on failure */ - public function setCookie(SetCookie $cookie); + public function setCookie(SetCookie $cookie): bool; /** * Remove cookies currently held in the cookie jar. @@ -58,13 +57,11 @@ public function setCookie(SetCookie $cookie); * arguments, then the cookie with the specified name, path and domain is * removed. * - * @param string $domain Clears cookies matching a domain - * @param string $path Clears cookies matching a domain and path - * @param string $name Clears cookies matching a domain, path, and name - * - * @return CookieJarInterface + * @param string|null $domain Clears cookies matching a domain + * @param string|null $path Clears cookies matching a domain and path + * @param string|null $name Clears cookies matching a domain, path, and name */ - public function clear($domain = null, $path = null, $name = null); + public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void; /** * Discard all sessions cookies. @@ -73,12 +70,10 @@ public function clear($domain = null, $path = null, $name = null); * field set to true. To be called when the user agent shuts down according * to RFC 2965. */ - public function clearSessionCookies(); + public function clearSessionCookies(): void; /** * Converts the cookie jar to an array. - * - * @return array */ - public function toArray(); + public function toArray(): array; } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php index 9887c1d54..290236d54 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php @@ -1,32 +1,40 @@ filename = $cookieFile; $this->storeSessionCookies = $storeSessionCookies; - if (file_exists($cookieFile)) { + if (\file_exists($cookieFile)) { $this->load($cookieFile); } } @@ -43,20 +51,21 @@ public function __destruct() * Saves the cookies to a file. * * @param string $filename File to save + * * @throws \RuntimeException if the file cannot be found or created */ - public function save($filename) + public function save(string $filename): void { $json = []; + /** @var SetCookie $cookie */ foreach ($this as $cookie) { - /** @var SetCookie $cookie */ if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } - $jsonStr = \GuzzleHttp\json_encode($json); - if (false === file_put_contents($filename, $jsonStr)) { + $jsonStr = Utils::jsonEncode($json); + if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) { throw new \RuntimeException("Unable to save file {$filename}"); } } @@ -67,23 +76,25 @@ public function save($filename) * Old cookies are kept unless overwritten by newly loaded ones. * * @param string $filename Cookie file to load. + * * @throws \RuntimeException if the file cannot be loaded. */ - public function load($filename) + public function load(string $filename): void { - $json = file_get_contents($filename); + $json = \file_get_contents($filename); if (false === $json) { throw new \RuntimeException("Unable to load file {$filename}"); - } elseif ($json === '') { + } + if ($json === '') { return; } - $data = \GuzzleHttp\json_decode($json, true); - if (is_array($data)) { - foreach (json_decode($json, true) as $cookie) { + $data = Utils::jsonDecode($json, true); + if (\is_array($data)) { + foreach ($data as $cookie) { $this->setCookie(new SetCookie($cookie)); } - } elseif (strlen($data)) { + } elseif (\is_scalar($data) && !empty($data)) { throw new \RuntimeException("Invalid cookie file: {$filename}"); } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php index 4497bcf03..5d51ca982 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -1,4 +1,5 @@ sessionKey = $sessionKey; $this->storeSessionCookies = $storeSessionCookies; $this->load(); @@ -38,33 +44,33 @@ public function __destruct() /** * Save cookies to the client session */ - public function save() + public function save(): void { $json = []; + /** @var SetCookie $cookie */ foreach ($this as $cookie) { - /** @var SetCookie $cookie */ if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } - $_SESSION[$this->sessionKey] = json_encode($json); + $_SESSION[$this->sessionKey] = \json_encode($json); } /** * Load the contents of the client session into the data array */ - protected function load() + protected function load(): void { if (!isset($_SESSION[$this->sessionKey])) { return; } - $data = json_decode($_SESSION[$this->sessionKey], true); - if (is_array($data)) { + $data = \json_decode($_SESSION[$this->sessionKey], true); + if (\is_array($data)) { foreach ($data as $cookie) { $this->setCookie(new SetCookie($cookie)); } - } elseif (strlen($data)) { + } elseif (\strlen($data)) { throw new \RuntimeException("Invalid cookie data"); } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php index f6993943e..7c04034d2 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -1,4 +1,5 @@ null, 'Value' => null, @@ -19,42 +22,42 @@ class SetCookie 'HttpOnly' => false ]; - /** @var array Cookie data */ + /** + * @var array Cookie data + */ private $data; /** - * Create a new SetCookie object from a string + * Create a new SetCookie object from a string. * * @param string $cookie Set-Cookie header string - * - * @return self */ - public static function fromString($cookie) + public static function fromString(string $cookie): self { // Create the default return array $data = self::$defaults; // Explode the cookie string using a series of semicolons - $pieces = array_filter(array_map('trim', explode(';', $cookie))); + $pieces = \array_filter(\array_map('trim', \explode(';', $cookie))); // The name of the cookie (first kvp) must exist and include an equal sign. - if (empty($pieces[0]) || !strpos($pieces[0], '=')) { + if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) { return new self($data); } // Add the cookie pieces into the parsed data array foreach ($pieces as $part) { - $cookieParts = explode('=', $part, 2); - $key = trim($cookieParts[0]); + $cookieParts = \explode('=', $part, 2); + $key = \trim($cookieParts[0]); $value = isset($cookieParts[1]) - ? trim($cookieParts[1], " \n\r\t\0\x0B") + ? \trim($cookieParts[1], " \n\r\t\0\x0B") : true; // Only check for non-cookies when cookies have been found - if (empty($data['Name'])) { + if (!isset($data['Name'])) { $data['Name'] = $key; $data['Value'] = $value; } else { - foreach (array_keys(self::$defaults) as $search) { - if (!strcasecmp($search, $key)) { + foreach (\array_keys(self::$defaults) as $search) { + if (!\strcasecmp($search, $key)) { $data[$search] = $value; continue 2; } @@ -71,39 +74,45 @@ public static function fromString($cookie) */ public function __construct(array $data = []) { - $this->data = array_replace(self::$defaults, $data); + /** @var array|null $replaced will be null in case of replace error */ + $replaced = \array_replace(self::$defaults, $data); + if ($replaced === null) { + throw new \InvalidArgumentException('Unable to replace the default values for the Cookie.'); + } + + $this->data = $replaced; // Extract the Expires value and turn it into a UNIX timestamp if needed if (!$this->getExpires() && $this->getMaxAge()) { // Calculate the Expires date - $this->setExpires(time() + $this->getMaxAge()); - } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { - $this->setExpires($this->getExpires()); + $this->setExpires(\time() + $this->getMaxAge()); + } elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) { + $this->setExpires($expires); } } public function __toString() { - $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; + $str = $this->data['Name'] . '=' . ($this->data['Value'] ?? '') . '; '; foreach ($this->data as $k => $v) { if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) { if ($k === 'Expires') { - $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; + $str .= 'Expires=' . \gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; } else { $str .= ($v === true ? $k : "{$k}={$v}") . '; '; } } } - return rtrim($str, '; '); + return \rtrim($str, '; '); } - public function toArray() + public function toArray(): array { return $this->data; } /** - * Get the cookie name + * Get the cookie name. * * @return string */ @@ -113,19 +122,23 @@ public function getName() } /** - * Set the cookie name + * Set the cookie name. * * @param string $name Cookie name */ - public function setName($name) + public function setName($name): void { - $this->data['Name'] = $name; + if (!is_string($name)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Name'] = (string) $name; } /** - * Get the cookie value + * Get the cookie value. * - * @return string + * @return string|null */ public function getValue() { @@ -133,17 +146,21 @@ public function getValue() } /** - * Set the cookie value + * Set the cookie value. * * @param string $value Cookie value */ - public function setValue($value) + public function setValue($value): void { - $this->data['Value'] = $value; + if (!is_string($value)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Value'] = (string) $value; } /** - * Get the domain + * Get the domain. * * @return string|null */ @@ -153,17 +170,21 @@ public function getDomain() } /** - * Set the domain of the cookie + * Set the domain of the cookie. * - * @param string $domain + * @param string|null $domain */ - public function setDomain($domain) + public function setDomain($domain): void { - $this->data['Domain'] = $domain; + if (!is_string($domain) && null !== $domain) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Domain'] = null === $domain ? null : (string) $domain; } /** - * Get the path + * Get the path. * * @return string */ @@ -173,39 +194,47 @@ public function getPath() } /** - * Set the path of the cookie + * Set the path of the cookie. * * @param string $path Path of the cookie */ - public function setPath($path) + public function setPath($path): void { - $this->data['Path'] = $path; + if (!is_string($path)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Path'] = (string) $path; } /** - * Maximum lifetime of the cookie in seconds + * Maximum lifetime of the cookie in seconds. * * @return int|null */ public function getMaxAge() { - return $this->data['Max-Age']; + return null === $this->data['Max-Age'] ? null : (int) $this->data['Max-Age']; } /** - * Set the max-age of the cookie + * Set the max-age of the cookie. * - * @param int $maxAge Max age of the cookie in seconds + * @param int|null $maxAge Max age of the cookie in seconds */ - public function setMaxAge($maxAge) + public function setMaxAge($maxAge): void { - $this->data['Max-Age'] = $maxAge; + if (!is_int($maxAge) && null !== $maxAge) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Max-Age'] = $maxAge === null ? null : (int) $maxAge; } /** - * The UNIX timestamp when the cookie Expires + * The UNIX timestamp when the cookie Expires. * - * @return mixed + * @return string|int|null */ public function getExpires() { @@ -213,21 +242,23 @@ public function getExpires() } /** - * Set the unix timestamp for which the cookie will expire + * Set the unix timestamp for which the cookie will expire. * - * @param int $timestamp Unix timestamp + * @param int|string|null $timestamp Unix timestamp or any English textual datetime description. */ - public function setExpires($timestamp) + public function setExpires($timestamp): void { - $this->data['Expires'] = is_numeric($timestamp) - ? (int) $timestamp - : strtotime($timestamp); + if (!is_int($timestamp) && !is_string($timestamp) && null !== $timestamp) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int, string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Expires'] = null === $timestamp ? null : (\is_numeric($timestamp) ? (int) $timestamp : \strtotime((string) $timestamp)); } /** - * Get whether or not this is a secure cookie + * Get whether or not this is a secure cookie. * - * @return null|bool + * @return bool */ public function getSecure() { @@ -235,19 +266,23 @@ public function getSecure() } /** - * Set whether or not the cookie is secure + * Set whether or not the cookie is secure. * * @param bool $secure Set to true or false if secure */ - public function setSecure($secure) + public function setSecure($secure): void { - $this->data['Secure'] = $secure; + if (!is_bool($secure)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Secure'] = (bool) $secure; } /** - * Get whether or not this is a session cookie + * Get whether or not this is a session cookie. * - * @return null|bool + * @return bool|null */ public function getDiscard() { @@ -255,17 +290,21 @@ public function getDiscard() } /** - * Set whether or not this is a session cookie + * Set whether or not this is a session cookie. * * @param bool $discard Set to true or false if this is a session cookie */ - public function setDiscard($discard) + public function setDiscard($discard): void { - $this->data['Discard'] = $discard; + if (!is_bool($discard)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Discard'] = (bool) $discard; } /** - * Get whether or not this is an HTTP only cookie + * Get whether or not this is an HTTP only cookie. * * @return bool */ @@ -275,13 +314,17 @@ public function getHttpOnly() } /** - * Set whether or not this is an HTTP only cookie + * Set whether or not this is an HTTP only cookie. * * @param bool $httpOnly Set to true or false if this is HTTP only */ - public function setHttpOnly($httpOnly) + public function setHttpOnly($httpOnly): void { - $this->data['HttpOnly'] = $httpOnly; + if (!is_bool($httpOnly)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['HttpOnly'] = (bool) $httpOnly; } /** @@ -298,10 +341,8 @@ public function setHttpOnly($httpOnly) * path is a %x2F ("/") character. * * @param string $requestPath Path to check against - * - * @return bool */ - public function matchesPath($requestPath) + public function matchesPath(string $requestPath): bool { $cookiePath = $this->getPath(); @@ -311,71 +352,71 @@ public function matchesPath($requestPath) } // Ensure that the cookie-path is a prefix of the request path. - if (0 !== strpos($requestPath, $cookiePath)) { + if (0 !== \strpos($requestPath, $cookiePath)) { return false; } // Match if the last character of the cookie-path is "/" - if (substr($cookiePath, -1, 1) === '/') { + if (\substr($cookiePath, -1, 1) === '/') { return true; } // Match if the first character not included in cookie path is "/" - return substr($requestPath, strlen($cookiePath), 1) === '/'; + return \substr($requestPath, \strlen($cookiePath), 1) === '/'; } /** - * Check if the cookie matches a domain value + * Check if the cookie matches a domain value. * * @param string $domain Domain to check against - * - * @return bool */ - public function matchesDomain($domain) + public function matchesDomain(string $domain): bool { + $cookieDomain = $this->getDomain(); + if (null === $cookieDomain) { + return true; + } + // Remove the leading '.' as per spec in RFC 6265. - // http://tools.ietf.org/html/rfc6265#section-5.2.3 - $cookieDomain = ltrim($this->getDomain(), '.'); + // https://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = \ltrim($cookieDomain, '.'); // Domain not set or exact match. - if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + if (!$cookieDomain || !\strcasecmp($domain, $cookieDomain)) { return true; } // Matching the subdomain according to RFC 6265. - // http://tools.ietf.org/html/rfc6265#section-5.1.3 - if (filter_var($domain, FILTER_VALIDATE_IP)) { + // https://tools.ietf.org/html/rfc6265#section-5.1.3 + if (\filter_var($domain, \FILTER_VALIDATE_IP)) { return false; } - return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain); + return (bool) \preg_match('/\.' . \preg_quote($cookieDomain, '/') . '$/', $domain); } /** - * Check if the cookie is expired - * - * @return bool + * Check if the cookie is expired. */ - public function isExpired() + public function isExpired(): bool { - return $this->getExpires() !== null && time() > $this->getExpires(); + return $this->getExpires() !== null && \time() > $this->getExpires(); } /** - * Check if the cookie is valid according to RFC 6265 + * Check if the cookie is valid according to RFC 6265. * * @return bool|string Returns true if valid or an error message if invalid */ public function validate() { - // Names must not be empty, but can be 0 $name = $this->getName(); - if (empty($name) && !is_numeric($name)) { + if ($name === '') { return 'The cookie name must not be empty'; } // Check if any of the invalid characters are present in the cookie name - if (preg_match( + if (\preg_match( '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', $name )) { @@ -384,17 +425,17 @@ public function validate() . 'following characters: ()<>@,;:\"/?={}'; } - // Value must not be empty, but can be 0 + // Value must not be null. 0 and empty string are valid. Empty strings + // are technically against RFC 6265, but known to happen in the wild. $value = $this->getValue(); - if (empty($value) && !is_numeric($value)) { + if ($value === null) { return 'The cookie value must not be empty'; } - // Domains must not be empty, but can be 0 - // A "0" is not a valid internet domain, but may be used as server name - // in a private network. + // Domains must not be empty, but can be 0. "0" is not a valid internet + // domain, but may be used as server name in a private network. $domain = $this->getDomain(); - if (empty($domain) && !is_numeric($domain)) { + if ($domain === null || $domain === '') { return 'The cookie domain must not be empty'; } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php index 427d896fb..a80956c9d 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php @@ -1,4 +1,5 @@ request = $request; + $this->handlerContext = $handlerContext; } /** - * @return null + * Get the request that caused the exception */ - public function getResponse() + public function getRequest(): RequestInterface { - return null; + return $this->request; } /** - * @return bool + * Get contextual information about the error from the underlying handler. + * + * The contents of this array will vary depending on which handler you are + * using. It may also be just an empty array. Relying on this data will + * couple you to a specific handler, but can give more debug information + * when needed. */ - public function hasResponse() + public function getHandlerContext(): array { - return false; + return $this->handlerContext; } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php index 510778f6e..fa3ed6998 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php @@ -1,13 +1,9 @@ getStatusCode() - : 0; + $code = $response ? $response->getStatusCode() : 0; parent::__construct($message, $code, $previous); $this->request = $request; $this->response = $response; @@ -39,46 +46,39 @@ public function __construct( /** * Wrap non-RequestExceptions with a RequestException - * - * @param RequestInterface $request - * @param \Exception $e - * - * @return RequestException */ - public static function wrapException(RequestInterface $request, \Exception $e) + public static function wrapException(RequestInterface $request, \Throwable $e): RequestException { - return $e instanceof RequestException - ? $e - : new RequestException($e->getMessage(), $request, null, $e); + return $e instanceof RequestException ? $e : new RequestException($e->getMessage(), $request, null, $e); } /** * Factory method to create a new exception with a normalized error message * - * @param RequestInterface $request Request - * @param ResponseInterface $response Response received - * @param \Exception $previous Previous exception - * @param array $ctx Optional handler context. - * - * @return self + * @param RequestInterface $request Request sent + * @param ResponseInterface $response Response received + * @param \Throwable|null $previous Previous exception + * @param array $handlerContext Optional handler context + * @param BodySummarizerInterface|null $bodySummarizer Optional body summarizer */ public static function create( RequestInterface $request, ResponseInterface $response = null, - \Exception $previous = null, - array $ctx = [] - ) { + \Throwable $previous = null, + array $handlerContext = [], + BodySummarizerInterface $bodySummarizer = null + ): self { if (!$response) { return new self( 'Error completing request', $request, null, $previous, - $ctx + $handlerContext ); } - $level = (int) floor($response->getStatusCode() / 100); + $level = (int) \floor($response->getStatusCode() / 100); if ($level === 4) { $label = 'Client error'; $className = ClientException::class; @@ -95,76 +95,33 @@ public static function create( // Client Error: `GET /` resulted in a `404 Not Found` response: // ... (truncated) - $message = sprintf( + $message = \sprintf( '%s: `%s %s` resulted in a `%s %s` response', $label, $request->getMethod(), - $uri, + $uri->__toString(), $response->getStatusCode(), $response->getReasonPhrase() ); - $summary = static::getResponseBodySummary($response); + $summary = ($bodySummarizer ?? new BodySummarizer())->summarize($response); if ($summary !== null) { $message .= ":\n{$summary}\n"; } - return new $className($message, $request, $response, $previous, $ctx); - } - - /** - * Get a short summary of the response - * - * Will return `null` if the response is not printable. - * - * @param ResponseInterface $response - * - * @return string|null - */ - public static function getResponseBodySummary(ResponseInterface $response) - { - $body = $response->getBody(); - - if (!$body->isSeekable()) { - return null; - } - - $size = $body->getSize(); - - if ($size === 0) { - return null; - } - - $summary = $body->read(120); - $body->rewind(); - - if ($size > 120) { - $summary .= ' (truncated...)'; - } - - // Matches any printable character, including unicode characters: - // letters, marks, numbers, punctuation, spacing, and separators. - if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { - return null; - } - - return $summary; + return new $className($message, $request, $response, $previous, $handlerContext); } /** - * Obfuscates URI if there is an username and a password present - * - * @param UriInterface $uri - * - * @return UriInterface + * Obfuscates URI if there is a username and a password present */ - private static function obfuscateUri($uri) + private static function obfuscateUri(UriInterface $uri): UriInterface { $userInfo = $uri->getUserInfo(); - if (false !== ($pos = strpos($userInfo, ':'))) { - return $uri->withUserInfo(substr($userInfo, 0, $pos), '***'); + if (false !== ($pos = \strpos($userInfo, ':'))) { + return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***'); } return $uri; @@ -172,30 +129,24 @@ private static function obfuscateUri($uri) /** * Get the request that caused the exception - * - * @return RequestInterface */ - public function getRequest() + public function getRequest(): RequestInterface { return $this->request; } /** * Get the associated response - * - * @return ResponseInterface|null */ - public function getResponse() + public function getResponse(): ?ResponseInterface { return $this->response; } /** * Check if a response was received - * - * @return bool */ - public function hasResponse() + public function hasResponse(): bool { return $this->response !== null; } @@ -207,10 +158,8 @@ public function hasResponse() * using. It may also be just an empty array. Relying on this data will * couple you to a specific handler, but can give more debug information * when needed. - * - * @return array */ - public function getHandlerContext() + public function getHandlerContext(): array { return $this->handlerContext; } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php deleted file mode 100644 index a77c28926..000000000 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php +++ /dev/null @@ -1,27 +0,0 @@ -stream = $stream; - $msg = $msg ?: 'Could not seek the stream to position ' . $pos; - parent::__construct($msg); - } - - /** - * @return StreamInterface - */ - public function getStream() - { - return $this->stream; - } -} diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php index 7cdd34086..8055e067c 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php @@ -1,7 +1,10 @@ maxHandles = $maxHandles; } - public function create(RequestInterface $request, array $options) + public function create(RequestInterface $request, array $options): EasyHandle { if (isset($options['curl']['body_as_string'])) { $options['_body_as_string'] = $options['curl']['body_as_string']; @@ -46,35 +62,33 @@ public function create(RequestInterface $request, array $options) // Add handler options from the request configuration options if (isset($options['curl'])) { - $conf = array_replace($conf, $options['curl']); + $conf = \array_replace($conf, $options['curl']); } - $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); - $easy->handle = $this->handles - ? array_pop($this->handles) - : curl_init(); + $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); + $easy->handle = $this->handles ? \array_pop($this->handles) : \curl_init(); curl_setopt_array($easy->handle, $conf); return $easy; } - public function release(EasyHandle $easy) + public function release(EasyHandle $easy): void { $resource = $easy->handle; unset($easy->handle); - if (count($this->handles) >= $this->maxHandles) { - curl_close($resource); + if (\count($this->handles) >= $this->maxHandles) { + \curl_close($resource); } else { // Remove all callback functions as they can hold onto references // and are not cleaned up by curl_reset. Using curl_setopt_array // does not work for some reason, so removing each one // individually. - curl_setopt($resource, CURLOPT_HEADERFUNCTION, null); - curl_setopt($resource, CURLOPT_READFUNCTION, null); - curl_setopt($resource, CURLOPT_WRITEFUNCTION, null); - curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null); - curl_reset($resource); + \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null); + \curl_setopt($resource, \CURLOPT_READFUNCTION, null); + \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null); + \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null); + \curl_reset($resource); $this->handles[] = $resource; } } @@ -83,17 +97,11 @@ public function release(EasyHandle $easy) * Completes a cURL transaction, either returning a response promise or a * rejected promise. * - * @param callable $handler - * @param EasyHandle $easy - * @param CurlFactoryInterface $factory Dictates how the handle is released - * - * @return \GuzzleHttp\Promise\PromiseInterface + * @param callable(RequestInterface, array): PromiseInterface $handler + * @param CurlFactoryInterface $factory Dictates how the handle is released */ - public static function finish( - callable $handler, - EasyHandle $easy, - CurlFactoryInterface $factory - ) { + public static function finish(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory): PromiseInterface + { if (isset($easy->options['on_stats'])) { self::invokeStats($easy); } @@ -114,9 +122,10 @@ public static function finish( return new FulfilledPromise($easy->response); } - private static function invokeStats(EasyHandle $easy) + private static function invokeStats(EasyHandle $easy): void { - $curlStats = curl_getinfo($easy->handle); + $curlStats = \curl_getinfo($easy->handle); + $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME); $stats = new TransferStats( $easy->request, $easy->response, @@ -124,45 +133,57 @@ private static function invokeStats(EasyHandle $easy) $easy->errno, $curlStats ); - call_user_func($easy->options['on_stats'], $stats); + ($easy->options['on_stats'])($stats); } - private static function finishError( - callable $handler, - EasyHandle $easy, - CurlFactoryInterface $factory - ) { + /** + * @param callable(RequestInterface, array): PromiseInterface $handler + */ + private static function finishError(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory): PromiseInterface + { // Get error information and release the handle to the factory. $ctx = [ 'errno' => $easy->errno, - 'error' => curl_error($easy->handle), - ] + curl_getinfo($easy->handle); + 'error' => \curl_error($easy->handle), + 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME), + ] + \curl_getinfo($easy->handle); + $ctx[self::CURL_VERSION_STR] = \curl_version()['version']; $factory->release($easy); // Retry when nothing is present or when curl failed to rewind. - if (empty($easy->options['_err_message']) - && (!$easy->errno || $easy->errno == 65) - ) { + if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65)) { return self::retryFailedRewind($handler, $easy, $ctx); } return self::createRejection($easy, $ctx); } - private static function createRejection(EasyHandle $easy, array $ctx) + private static function createRejection(EasyHandle $easy, array $ctx): PromiseInterface { static $connectionErrors = [ - CURLE_OPERATION_TIMEOUTED => true, - CURLE_COULDNT_RESOLVE_HOST => true, - CURLE_COULDNT_CONNECT => true, - CURLE_SSL_CONNECT_ERROR => true, - CURLE_GOT_NOTHING => true, + \CURLE_OPERATION_TIMEOUTED => true, + \CURLE_COULDNT_RESOLVE_HOST => true, + \CURLE_COULDNT_CONNECT => true, + \CURLE_SSL_CONNECT_ERROR => true, + \CURLE_GOT_NOTHING => true, ]; + if ($easy->createResponseException) { + return P\Create::rejectionFor( + new RequestException( + 'An error was encountered while creating the response', + $easy->request, + $easy->response, + $easy->createResponseException, + $ctx + ) + ); + } + // If an exception was encountered during the onHeaders event, then // return a rejected promise that wraps that exception. if ($easy->onHeadersException) { - return \GuzzleHttp\Promise\rejection_for( + return P\Create::rejectionFor( new RequestException( 'An error was encountered during the on_headers event', $easy->request, @@ -173,49 +194,56 @@ private static function createRejection(EasyHandle $easy, array $ctx) ); } - $message = sprintf( + $message = \sprintf( 'cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], - 'see http://curl.haxx.se/libcurl/c/libcurl-errors.html' + 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' ); + $uriString = (string) $easy->request->getUri(); + if ($uriString !== '' && false === \strpos($ctx['error'], $uriString)) { + $message .= \sprintf(' for %s', $uriString); + } // Create a connection exception if it was a specific error code. $error = isset($connectionErrors[$easy->errno]) ? new ConnectException($message, $easy->request, null, $ctx) : new RequestException($message, $easy->request, $easy->response, null, $ctx); - return \GuzzleHttp\Promise\rejection_for($error); + return P\Create::rejectionFor($error); } - private function getDefaultConf(EasyHandle $easy) + /** + * @return array + */ + private function getDefaultConf(EasyHandle $easy): array { $conf = [ - '_headers' => $easy->request->getHeaders(), - CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), - CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), - CURLOPT_RETURNTRANSFER => false, - CURLOPT_HEADER => false, - CURLOPT_CONNECTTIMEOUT => 150, + '_headers' => $easy->request->getHeaders(), + \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), + \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), + \CURLOPT_RETURNTRANSFER => false, + \CURLOPT_HEADER => false, + \CURLOPT_CONNECTTIMEOUT => 150, ]; - if (defined('CURLOPT_PROTOCOLS')) { - $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + if (\defined('CURLOPT_PROTOCOLS')) { + $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS; } $version = $easy->request->getProtocolVersion(); if ($version == 1.1) { - $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; } elseif ($version == 2.0) { - $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; } else { - $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0; } return $conf; } - private function applyMethod(EasyHandle $easy, array &$conf) + private function applyMethod(EasyHandle $easy, array &$conf): void { $body = $easy->request->getBody(); $size = $body->getSize(); @@ -227,22 +255,22 @@ private function applyMethod(EasyHandle $easy, array &$conf) $method = $easy->request->getMethod(); if ($method === 'PUT' || $method === 'POST') { - // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + // See https://tools.ietf.org/html/rfc7230#section-3.3.2 if (!$easy->request->hasHeader('Content-Length')) { - $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; + $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; } } elseif ($method === 'HEAD') { - $conf[CURLOPT_NOBODY] = true; + $conf[\CURLOPT_NOBODY] = true; unset( - $conf[CURLOPT_WRITEFUNCTION], - $conf[CURLOPT_READFUNCTION], - $conf[CURLOPT_FILE], - $conf[CURLOPT_INFILE] + $conf[\CURLOPT_WRITEFUNCTION], + $conf[\CURLOPT_READFUNCTION], + $conf[\CURLOPT_FILE], + $conf[\CURLOPT_INFILE] ); } } - private function applyBody(RequestInterface $request, array $options, array &$conf) + private function applyBody(RequestInterface $request, array $options, array &$conf): void { $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') @@ -250,40 +278,38 @@ private function applyBody(RequestInterface $request, array $options, array &$co // Send the body as a string if the size is less than 1MB OR if the // [curl][body_as_string] request value is set. - if (($size !== null && $size < 1000000) || - !empty($options['_body_as_string']) - ) { - $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + if (($size !== null && $size < 1000000) || !empty($options['_body_as_string'])) { + $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody(); // Don't duplicate the Content-Length header $this->removeHeader('Content-Length', $conf); $this->removeHeader('Transfer-Encoding', $conf); } else { - $conf[CURLOPT_UPLOAD] = true; + $conf[\CURLOPT_UPLOAD] = true; if ($size !== null) { - $conf[CURLOPT_INFILESIZE] = $size; + $conf[\CURLOPT_INFILESIZE] = $size; $this->removeHeader('Content-Length', $conf); } $body = $request->getBody(); if ($body->isSeekable()) { $body->rewind(); } - $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { + $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use ($body) { return $body->read($length); }; } // If the Expect header is not present, prevent curl from adding it if (!$request->hasHeader('Expect')) { - $conf[CURLOPT_HTTPHEADER][] = 'Expect:'; + $conf[\CURLOPT_HTTPHEADER][] = 'Expect:'; } // cURL sometimes adds a content-type by default. Prevent this. if (!$request->hasHeader('Content-Type')) { - $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + $conf[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; } } - private function applyHeaders(EasyHandle $easy, array &$conf) + private function applyHeaders(EasyHandle $easy, array &$conf): void { foreach ($conf['_headers'] as $name => $values) { foreach ($values as $value) { @@ -291,16 +317,16 @@ private function applyHeaders(EasyHandle $easy, array &$conf) if ($value === '') { // cURL requires a special format for empty headers. // See https://github.com/guzzle/guzzle/issues/1882 for more details. - $conf[CURLOPT_HTTPHEADER][] = "$name;"; + $conf[\CURLOPT_HTTPHEADER][] = "$name;"; } else { - $conf[CURLOPT_HTTPHEADER][] = "$name: $value"; + $conf[\CURLOPT_HTTPHEADER][] = "$name: $value"; } } } // Remove the Accept header if one was not set if (!$easy->request->hasHeader('Accept')) { - $conf[CURLOPT_HTTPHEADER][] = 'Accept:'; + $conf[\CURLOPT_HTTPHEADER][] = 'Accept:'; } } @@ -310,115 +336,115 @@ private function applyHeaders(EasyHandle $easy, array &$conf) * @param string $name Case-insensitive header to remove * @param array $options Array of options to modify */ - private function removeHeader($name, array &$options) + private function removeHeader(string $name, array &$options): void { - foreach (array_keys($options['_headers']) as $key) { - if (!strcasecmp($key, $name)) { + foreach (\array_keys($options['_headers']) as $key) { + if (!\strcasecmp($key, $name)) { unset($options['_headers'][$key]); return; } } } - private function applyHandlerOptions(EasyHandle $easy, array &$conf) + private function applyHandlerOptions(EasyHandle $easy, array &$conf): void { $options = $easy->options; if (isset($options['verify'])) { if ($options['verify'] === false) { - unset($conf[CURLOPT_CAINFO]); - $conf[CURLOPT_SSL_VERIFYHOST] = 0; - $conf[CURLOPT_SSL_VERIFYPEER] = false; + unset($conf[\CURLOPT_CAINFO]); + $conf[\CURLOPT_SSL_VERIFYHOST] = 0; + $conf[\CURLOPT_SSL_VERIFYPEER] = false; } else { - $conf[CURLOPT_SSL_VERIFYHOST] = 2; - $conf[CURLOPT_SSL_VERIFYPEER] = true; - if (is_string($options['verify'])) { + $conf[\CURLOPT_SSL_VERIFYHOST] = 2; + $conf[\CURLOPT_SSL_VERIFYPEER] = true; + if (\is_string($options['verify'])) { // Throw an error if the file/folder/link path is not valid or doesn't exist. - if (!file_exists($options['verify'])) { - throw new \InvalidArgumentException( - "SSL CA bundle not found: {$options['verify']}" - ); + if (!\file_exists($options['verify'])) { + throw new \InvalidArgumentException("SSL CA bundle not found: {$options['verify']}"); } // If it's a directory or a link to a directory use CURLOPT_CAPATH. // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. - if (is_dir($options['verify']) || - (is_link($options['verify']) && is_dir(readlink($options['verify'])))) { - $conf[CURLOPT_CAPATH] = $options['verify']; + if ( + \is_dir($options['verify']) || + ( + \is_link($options['verify']) === true && + ($verifyLink = \readlink($options['verify'])) !== false && + \is_dir($verifyLink) + ) + ) { + $conf[\CURLOPT_CAPATH] = $options['verify']; } else { - $conf[CURLOPT_CAINFO] = $options['verify']; + $conf[\CURLOPT_CAINFO] = $options['verify']; } } } } - if (!empty($options['decode_content'])) { + if (!isset($options['curl'][\CURLOPT_ENCODING]) && !empty($options['decode_content'])) { $accept = $easy->request->getHeaderLine('Accept-Encoding'); if ($accept) { - $conf[CURLOPT_ENCODING] = $accept; + $conf[\CURLOPT_ENCODING] = $accept; } else { - $conf[CURLOPT_ENCODING] = ''; - // Don't let curl send the header over the wire - $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; + // The empty string enables all available decoders and implicitly + // sets a matching 'Accept-Encoding' header. + $conf[\CURLOPT_ENCODING] = ''; + // But as the user did not specify any acceptable encodings we need + // to overwrite this implicit header with an empty one. + $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; } } - if (isset($options['sink'])) { - $sink = $options['sink']; - if (!is_string($sink)) { - $sink = \GuzzleHttp\Psr7\stream_for($sink); - } elseif (!is_dir(dirname($sink))) { - // Ensure that the directory exists before failing in curl. - throw new \RuntimeException(sprintf( - 'Directory %s does not exist for sink value of %s', - dirname($sink), - $sink - )); - } else { - $sink = new LazyOpenStream($sink, 'w+'); - } - $easy->sink = $sink; - $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) { - return $sink->write($write); - }; - } else { + if (!isset($options['sink'])) { // Use a default temp stream if no sink was set. - $conf[CURLOPT_FILE] = fopen('php://temp', 'w+'); - $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]); + $options['sink'] = \GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); + } + $sink = $options['sink']; + if (!\is_string($sink)) { + $sink = \GuzzleHttp\Psr7\Utils::streamFor($sink); + } elseif (!\is_dir(\dirname($sink))) { + // Ensure that the directory exists before failing in curl. + throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink)); + } else { + $sink = new LazyOpenStream($sink, 'w+'); } + $easy->sink = $sink; + $conf[\CURLOPT_WRITEFUNCTION] = static function ($ch, $write) use ($sink): int { + return $sink->write($write); + }; + $timeoutRequiresNoSignal = false; if (isset($options['timeout'])) { $timeoutRequiresNoSignal |= $options['timeout'] < 1; - $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; + $conf[\CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; } // CURL default value is CURL_IPRESOLVE_WHATEVER if (isset($options['force_ip_resolve'])) { if ('v4' === $options['force_ip_resolve']) { - $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; + $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4; } elseif ('v6' === $options['force_ip_resolve']) { - $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6; + $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V6; } } if (isset($options['connect_timeout'])) { $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; - $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; + $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; } - if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { - $conf[CURLOPT_NOSIGNAL] = true; + if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') { + $conf[\CURLOPT_NOSIGNAL] = true; } if (isset($options['proxy'])) { - if (!is_array($options['proxy'])) { - $conf[CURLOPT_PROXY] = $options['proxy']; + if (!\is_array($options['proxy'])) { + $conf[\CURLOPT_PROXY] = $options['proxy']; } else { $scheme = $easy->request->getUri()->getScheme(); if (isset($options['proxy'][$scheme])) { $host = $easy->request->getUri()->getHost(); - if (!isset($options['proxy']['no']) || - !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no']) - ) { - $conf[CURLOPT_PROXY] = $options['proxy'][$scheme]; + if (!isset($options['proxy']['no']) || !Utils::isHostInNoProxy($host, $options['proxy']['no'])) { + $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; } } } @@ -426,53 +452,53 @@ private function applyHandlerOptions(EasyHandle $easy, array &$conf) if (isset($options['cert'])) { $cert = $options['cert']; - if (is_array($cert)) { - $conf[CURLOPT_SSLCERTPASSWD] = $cert[1]; + if (\is_array($cert)) { + $conf[\CURLOPT_SSLCERTPASSWD] = $cert[1]; $cert = $cert[0]; } - if (!file_exists($cert)) { - throw new \InvalidArgumentException( - "SSL certificate not found: {$cert}" - ); + if (!\file_exists($cert)) { + throw new \InvalidArgumentException("SSL certificate not found: {$cert}"); + } + # OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. + # see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html + $ext = pathinfo($cert, \PATHINFO_EXTENSION); + if (preg_match('#^(der|p12)$#i', $ext)) { + $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext); } - $conf[CURLOPT_SSLCERT] = $cert; + $conf[\CURLOPT_SSLCERT] = $cert; } if (isset($options['ssl_key'])) { - $sslKey = $options['ssl_key']; - if (is_array($sslKey)) { - $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1]; - $sslKey = $sslKey[0]; + if (\is_array($options['ssl_key'])) { + if (\count($options['ssl_key']) === 2) { + [$sslKey, $conf[\CURLOPT_SSLKEYPASSWD]] = $options['ssl_key']; + } else { + [$sslKey] = $options['ssl_key']; + } } - if (!file_exists($sslKey)) { - throw new \InvalidArgumentException( - "SSL private key not found: {$sslKey}" - ); + + $sslKey = $sslKey ?? $options['ssl_key']; + + if (!\file_exists($sslKey)) { + throw new \InvalidArgumentException("SSL private key not found: {$sslKey}"); } - $conf[CURLOPT_SSLKEY] = $sslKey; + $conf[\CURLOPT_SSLKEY] = $sslKey; } if (isset($options['progress'])) { $progress = $options['progress']; - if (!is_callable($progress)) { - throw new \InvalidArgumentException( - 'progress client option must be callable' - ); + if (!\is_callable($progress)) { + throw new \InvalidArgumentException('progress client option must be callable'); } - $conf[CURLOPT_NOPROGRESS] = false; - $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) { - $args = func_get_args(); - // PHP 5.5 pushed the handle onto the start of the args - if (is_resource($args[0])) { - array_shift($args); - } - call_user_func_array($progress, $args); + $conf[\CURLOPT_NOPROGRESS] = false; + $conf[\CURLOPT_PROGRESSFUNCTION] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use ($progress) { + $progress($downloadSize, $downloaded, $uploadSize, $uploaded); }; } if (!empty($options['debug'])) { - $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']); - $conf[CURLOPT_VERBOSE] = true; + $conf[\CURLOPT_STDERR] = Utils::debugResource($options['debug']); + $conf[\CURLOPT_VERBOSE] = true; } } @@ -484,12 +510,11 @@ private function applyHandlerOptions(EasyHandle $easy, array &$conf) * stream, and then encountered a "necessary data rewind wasn't possible" * error, causing the request to be sent through curl_multi_info_read() * without an error status. + * + * @param callable(RequestInterface, array): PromiseInterface $handler */ - private static function retryFailedRewind( - callable $handler, - EasyHandle $easy, - array $ctx - ) { + private static function retryFailedRewind(callable $handler, EasyHandle $easy, array $ctx): PromiseInterface + { try { // Only rewind if the body has been read from. $body = $easy->request->getBody(); @@ -522,27 +547,32 @@ private static function retryFailedRewind( return $handler($easy->request, $easy->options); } - private function createHeaderFn(EasyHandle $easy) + private function createHeaderFn(EasyHandle $easy): callable { if (isset($easy->options['on_headers'])) { $onHeaders = $easy->options['on_headers']; - if (!is_callable($onHeaders)) { + if (!\is_callable($onHeaders)) { throw new \InvalidArgumentException('on_headers must be callable'); } } else { $onHeaders = null; } - return function ($ch, $h) use ( + return static function ($ch, $h) use ( $onHeaders, $easy, &$startingResponse ) { - $value = trim($h); + $value = \trim($h); if ($value === '') { $startingResponse = true; - $easy->createResponse(); + try { + $easy->createResponse(); + } catch (\Exception $e) { + $easy->createResponseException = $e; + return -1; + } if ($onHeaders !== null) { try { $onHeaders($easy->response); @@ -559,7 +589,7 @@ private function createHeaderFn(EasyHandle $easy) } else { $easy->headers[] = $value; } - return strlen($h); + return \strlen($h); }; } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php index b0fc23685..fe57ed5d5 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php @@ -1,4 +1,5 @@ factory = isset($options['handle_factory']) - ? $options['handle_factory'] - : new CurlFactory(3); + $this->factory = $options['handle_factory'] + ?? new CurlFactory(3); } - public function __invoke(RequestInterface $request, array $options) + public function __invoke(RequestInterface $request, array $options): PromiseInterface { if (isset($options['delay'])) { - usleep($options['delay'] * 1000); + \usleep($options['delay'] * 1000); } $easy = $this->factory->create($request, $options); - curl_exec($easy->handle); - $easy->errno = curl_errno($easy->handle); + \curl_exec($easy->handle); + $easy->errno = \curl_errno($easy->handle); return CurlFactory::finish($this, $easy, $this->factory); } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php index 2754d8e43..9e2e4703e 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -1,9 +1,11 @@ An array of delay times, indexed by handle id in `addRequest`. + * + * @see CurlMultiHandler::addRequest + */ private $delays = []; + /** + * @var array An associative array of CURLMOPT_* options and corresponding values for curl_multi_setopt() + */ + private $options = []; + /** * This handler accepts the following options: * * - handle_factory: An optional factory used to create curl handles * - select_timeout: Optional timeout (in seconds) to block before timing * out while selecting curl handles. Defaults to 1 second. - * - * @param array $options + * - options: An associative array of CURLMOPT_* options and + * corresponding values for curl_multi_setopt() */ public function __construct(array $options = []) { - $this->factory = isset($options['handle_factory']) - ? $options['handle_factory'] : new CurlFactory(50); - $this->selectTimeout = isset($options['select_timeout']) - ? $options['select_timeout'] : 1; + $this->factory = $options['handle_factory'] ?? new CurlFactory(50); + + if (isset($options['select_timeout'])) { + $this->selectTimeout = $options['select_timeout']; + } elseif ($selectTimeout = Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { + @trigger_error('Since guzzlehttp/guzzle 7.2.0: Using environment variable GUZZLE_CURL_SELECT_TIMEOUT is deprecated. Use option "select_timeout" instead.', \E_USER_DEPRECATED); + $this->selectTimeout = (int) $selectTimeout; + } else { + $this->selectTimeout = 1; + } + + $this->options = $options['options'] ?? []; } + /** + * @param string $name + * + * @return resource|\CurlMultiHandle + * + * @throws \BadMethodCallException when another field as `_mh` will be gotten + * @throws \RuntimeException when curl can not initialize a multi handle + */ public function __get($name) { - if ($name === '_mh') { - return $this->_mh = curl_multi_init(); + if ($name !== '_mh') { + throw new \BadMethodCallException("Can not get other property as '_mh'."); } - throw new \BadMethodCallException(); + $multiHandle = \curl_multi_init(); + + if (false === $multiHandle) { + throw new \RuntimeException('Can not initialize curl multi handle.'); + } + + $this->_mh = $multiHandle; + + foreach ($this->options as $option => $value) { + // A warning is raised in case of a wrong option. + curl_multi_setopt($this->_mh, $option, $value); + } + + return $this->_mh; } public function __destruct() { if (isset($this->_mh)) { - curl_multi_close($this->_mh); + \curl_multi_close($this->_mh); unset($this->_mh); } } - public function __invoke(RequestInterface $request, array $options) + public function __invoke(RequestInterface $request, array $options): PromiseInterface { $easy = $this->factory->create($request, $options); $id = (int) $easy->handle; @@ -78,15 +138,15 @@ function () use ($id) { /** * Ticks the curl event loop. */ - public function tick() + public function tick(): void { // Add any delayed handles if needed. if ($this->delays) { - $currentTime = microtime(true); + $currentTime = Utils::currentTime(); foreach ($this->delays as $id => $delay) { if ($currentTime >= $delay) { unset($this->delays[$id]); - curl_multi_add_handle( + \curl_multi_add_handle( $this->_mh, $this->handles[$id]['easy']->handle ); @@ -95,17 +155,15 @@ public function tick() } // Step through the task queue which may add additional requests. - P\queue()->run(); + P\Utils::queue()->run(); - if ($this->active && - curl_multi_select($this->_mh, $this->selectTimeout) === -1 - ) { + if ($this->active && \curl_multi_select($this->_mh, $this->selectTimeout) === -1) { // Perform a usleep if a select returns -1. // See: https://bugs.php.net/bug.php?id=61141 - usleep(250); + \usleep(250); } - while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); + while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM); $this->processMessages(); } @@ -113,28 +171,28 @@ public function tick() /** * Runs until all outstanding connections have completed. */ - public function execute() + public function execute(): void { - $queue = P\queue(); + $queue = P\Utils::queue(); while ($this->handles || !$queue->isEmpty()) { // If there are no transfers, then sleep for the next delay if (!$this->active && $this->delays) { - usleep($this->timeToNext()); + \usleep($this->timeToNext()); } $this->tick(); } } - private function addRequest(array $entry) + private function addRequest(array $entry): void { $easy = $entry['easy']; $id = (int) $easy->handle; $this->handles[$id] = $entry; if (empty($easy->options['delay'])) { - curl_multi_add_handle($this->_mh, $easy->handle); + \curl_multi_add_handle($this->_mh, $easy->handle); } else { - $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000); + $this->delays[$id] = Utils::currentTime() + ($easy->options['delay'] / 1000); } } @@ -145,8 +203,12 @@ private function addRequest(array $entry) * * @return bool True on success, false on failure. */ - private function cancel($id) + private function cancel($id): bool { + if (!is_int($id)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an integer to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + // Cannot cancel if it has been processed. if (!isset($this->handles[$id])) { return false; @@ -154,17 +216,21 @@ private function cancel($id) $handle = $this->handles[$id]['easy']->handle; unset($this->delays[$id], $this->handles[$id]); - curl_multi_remove_handle($this->_mh, $handle); - curl_close($handle); + \curl_multi_remove_handle($this->_mh, $handle); + \curl_close($handle); return true; } - private function processMessages() + private function processMessages(): void { - while ($done = curl_multi_info_read($this->_mh)) { + while ($done = \curl_multi_info_read($this->_mh)) { + if ($done['msg'] !== \CURLMSG_DONE) { + // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216 + continue; + } $id = (int) $done['handle']; - curl_multi_remove_handle($this->_mh, $done['handle']); + \curl_multi_remove_handle($this->_mh, $done['handle']); if (!isset($this->handles[$id])) { // Probably was cancelled. @@ -175,25 +241,21 @@ private function processMessages() unset($this->handles[$id], $this->delays[$id]); $entry['easy']->errno = $done['result']; $entry['deferred']->resolve( - CurlFactory::finish( - $this, - $entry['easy'], - $this->factory - ) + CurlFactory::finish($this, $entry['easy'], $this->factory) ); } } - private function timeToNext() + private function timeToNext(): int { - $currentTime = microtime(true); - $nextTime = PHP_INT_MAX; + $currentTime = Utils::currentTime(); + $nextTime = \PHP_INT_MAX; foreach ($this->delays as $time) { if ($time < $nextTime) { $nextTime = $time; } } - return max(0, $nextTime - $currentTime) * 1000000; + return ((int) \max(0, $nextTime - $currentTime)) * 1000000; } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php index 7754e9111..224344d7c 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php @@ -1,7 +1,9 @@ headers)) { - throw new \RuntimeException('No headers have been received'); - } + [$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($this->headers); - // HTTP-version SP status-code SP reason-phrase - $startLine = explode(' ', array_shift($this->headers), 3); - $headers = \GuzzleHttp\headers_from_lines($this->headers); - $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + $normalizedKeys = Utils::normalizeHeaderKeys($headers); - if (!empty($this->options['decode_content']) - && isset($normalizedKeys['content-encoding']) - ) { - $headers['x-encoded-content-encoding'] - = $headers[$normalizedKeys['content-encoding']]; + if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding'])) { + $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; unset($headers[$normalizedKeys['content-encoding']]); if (isset($normalizedKeys['content-length'])) { - $headers['x-encoded-content-length'] - = $headers[$normalizedKeys['content-length']]; + $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; $bodyLength = (int) $this->sink->getSize(); if ($bodyLength) { @@ -74,19 +89,24 @@ public function createResponse() // Attach a response to the easy handle with the parsed headers. $this->response = new Response( - $startLine[1], + $status, $headers, $this->sink, - substr($startLine[0], 5), - isset($startLine[2]) ? (string) $startLine[2] : null + $ver, + $reason ); } + /** + * @param string $name + * + * @return void + * + * @throws \BadMethodCallException + */ public function __get($name) { - $msg = $name === 'handle' - ? 'The EasyHandle has been released' - : 'Invalid property: ' . $name; + $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: ' . $name; throw new \BadMethodCallException($msg); } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php new file mode 100644 index 000000000..a0988845f --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php @@ -0,0 +1,42 @@ +|null $queue The parameters to be passed to the append function, as an indexed array. + * @param callable|null $onFulfilled Callback to invoke when the return value is fulfilled. + * @param callable|null $onRejected Callback to invoke when the return value is rejected. */ - public function __construct( - array $queue = null, - callable $onFulfilled = null, - callable $onRejected = null - ) { + public function __construct(array $queue = null, callable $onFulfilled = null, callable $onRejected = null) + { $this->onFulfilled = $onFulfilled; $this->onRejected = $onRejected; if ($queue) { - call_user_func_array([$this, 'append'], $queue); + // array_values included for BC + $this->append(...array_values($queue)); } } - public function __invoke(RequestInterface $request, array $options) + public function __invoke(RequestInterface $request, array $options): PromiseInterface { if (!$this->queue) { throw new \OutOfBoundsException('Mock queue is empty'); } - if (isset($options['delay'])) { - usleep($options['delay'] * 1000); + if (isset($options['delay']) && \is_numeric($options['delay'])) { + \usleep((int) $options['delay'] * 1000); } $this->lastRequest = $request; $this->lastOptions = $options; - $response = array_shift($this->queue); + $response = \array_shift($this->queue); if (isset($options['on_headers'])) { - if (!is_callable($options['on_headers'])) { + if (!\is_callable($options['on_headers'])) { throw new \InvalidArgumentException('on_headers must be callable'); } try { @@ -86,29 +103,30 @@ public function __invoke(RequestInterface $request, array $options) } } - if (is_callable($response)) { - $response = call_user_func($response, $request, $options); + if (\is_callable($response)) { + $response = $response($request, $options); } - $response = $response instanceof \Exception - ? \GuzzleHttp\Promise\rejection_for($response) - : \GuzzleHttp\Promise\promise_for($response); + $response = $response instanceof \Throwable + ? P\Create::rejectionFor($response) + : P\Create::promiseFor($response); return $response->then( - function ($value) use ($request, $options) { + function (?ResponseInterface $value) use ($request, $options) { $this->invokeStats($request, $options, $value); if ($this->onFulfilled) { - call_user_func($this->onFulfilled, $value); + ($this->onFulfilled)($value); } - if (isset($options['sink'])) { + + if ($value !== null && isset($options['sink'])) { $contents = (string) $value->getBody(); $sink = $options['sink']; - if (is_resource($sink)) { - fwrite($sink, $contents); - } elseif (is_string($sink)) { - file_put_contents($sink, $contents); - } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) { + if (\is_resource($sink)) { + \fwrite($sink, $contents); + } elseif (\is_string($sink)) { + \file_put_contents($sink, $contents); + } elseif ($sink instanceof StreamInterface) { $sink->write($contents); } } @@ -118,9 +136,9 @@ function ($value) use ($request, $options) { function ($reason) use ($request, $options) { $this->invokeStats($request, $options, null, $reason); if ($this->onRejected) { - call_user_func($this->onRejected, $reason); + ($this->onRejected)($reason); } - return \GuzzleHttp\Promise\rejection_for($reason); + return P\Create::rejectionFor($reason); } ); } @@ -128,62 +146,66 @@ function ($reason) use ($request, $options) { /** * Adds one or more variadic requests, exceptions, callables, or promises * to the queue. + * + * @param mixed ...$values */ - public function append() + public function append(...$values): void { - foreach (func_get_args() as $value) { + foreach ($values as $value) { if ($value instanceof ResponseInterface - || $value instanceof \Exception + || $value instanceof \Throwable || $value instanceof PromiseInterface - || is_callable($value) + || \is_callable($value) ) { $this->queue[] = $value; } else { - throw new \InvalidArgumentException('Expected a response or ' - . 'exception. Found ' . \GuzzleHttp\describe_type($value)); + throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found ' . Utils::describeType($value)); } } } /** * Get the last received request. - * - * @return RequestInterface */ - public function getLastRequest() + public function getLastRequest(): ?RequestInterface { return $this->lastRequest; } /** * Get the last received request options. - * - * @return array */ - public function getLastOptions() + public function getLastOptions(): array { return $this->lastOptions; } /** * Returns the number of remaining items in the queue. - * - * @return int */ - public function count() + public function count(): int { - return count($this->queue); + return \count($this->queue); } + public function reset(): void + { + $this->queue = []; + } + + /** + * @param mixed $reason Promise or reason. + */ private function invokeStats( RequestInterface $request, array $options, ResponseInterface $response = null, $reason = null - ) { + ): void { if (isset($options['on_stats'])) { - $stats = new TransferStats($request, $response, 0, $reason); - call_user_func($options['on_stats'], $stats); + $transferTime = $options['transfer_time'] ?? 0; + $stats = new TransferStats($request, $response, $transferTime, $reason); + ($options['on_stats'])($stats); } } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php index f8b00be0b..f045b526c 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php @@ -1,11 +1,15 @@ getBody()->getSize()) { - $request = $request->withHeader('Content-Length', 0); + $request = $request->withHeader('Content-Length', '0'); } return $this->createResponse( @@ -57,80 +64,80 @@ public function __invoke(RequestInterface $request, array $options) // Determine if the error was a networking error. $message = $e->getMessage(); // This list can probably get more comprehensive. - if (strpos($message, 'getaddrinfo') // DNS lookup failed - || strpos($message, 'Connection refused') - || strpos($message, "couldn't connect to host") // error on HHVM - || strpos($message, "connection attempt failed") + if (false !== \strpos($message, 'getaddrinfo') // DNS lookup failed + || false !== \strpos($message, 'Connection refused') + || false !== \strpos($message, "couldn't connect to host") // error on HHVM + || false !== \strpos($message, "connection attempt failed") ) { $e = new ConnectException($e->getMessage(), $request, $e); + } else { + $e = RequestException::wrapException($request, $e); } - $e = RequestException::wrapException($request, $e); $this->invokeStats($options, $request, $startTime, null, $e); - return \GuzzleHttp\Promise\rejection_for($e); + return P\Create::rejectionFor($e); } } private function invokeStats( array $options, RequestInterface $request, - $startTime, + ?float $startTime, ResponseInterface $response = null, - $error = null - ) { + \Throwable $error = null + ): void { if (isset($options['on_stats'])) { - $stats = new TransferStats( - $request, - $response, - microtime(true) - $startTime, - $error, - [] - ); - call_user_func($options['on_stats'], $stats); + $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []); + ($options['on_stats'])($stats); } } - private function createResponse( - RequestInterface $request, - array $options, - $stream, - $startTime - ) { + /** + * @param resource $stream + */ + private function createResponse(RequestInterface $request, array $options, $stream, ?float $startTime): PromiseInterface + { $hdrs = $this->lastHeaders; $this->lastHeaders = []; - $parts = explode(' ', array_shift($hdrs), 3); - $ver = explode('/', $parts[0])[1]; - $status = $parts[1]; - $reason = isset($parts[2]) ? $parts[2] : null; - $headers = \GuzzleHttp\headers_from_lines($hdrs); - list($stream, $headers) = $this->checkDecode($options, $headers, $stream); - $stream = Psr7\stream_for($stream); + + try { + [$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($hdrs); + } catch (\Exception $e) { + return P\Create::rejectionFor( + new RequestException('An error was encountered while creating the response', $request, null, $e) + ); + } + + [$stream, $headers] = $this->checkDecode($options, $headers, $stream); + $stream = Psr7\Utils::streamFor($stream); $sink = $stream; - if (strcasecmp('HEAD', $request->getMethod())) { + if (\strcasecmp('HEAD', $request->getMethod())) { $sink = $this->createSink($stream, $options); } - $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + try { + $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); + } catch (\Exception $e) { + return P\Create::rejectionFor( + new RequestException('An error was encountered while creating the response', $request, null, $e) + ); + } if (isset($options['on_headers'])) { try { $options['on_headers']($response); } catch (\Exception $e) { - $msg = 'An error was encountered during the on_headers event'; - $ex = new RequestException($msg, $request, $response, $e); - return \GuzzleHttp\Promise\rejection_for($ex); + return P\Create::rejectionFor( + new RequestException('An error was encountered during the on_headers event', $request, $response, $e) + ); } } // Do not drain when the request is a HEAD request because they have // no body. if ($sink !== $stream) { - $this->drain( - $stream, - $sink, - $response->getHeaderLine('Content-Length') - ); + $this->drain($stream, $sink, $response->getHeaderLine('Content-Length')); } $this->invokeStats($options, $request, $startTime, $response, null); @@ -138,41 +145,37 @@ private function createResponse( return new FulfilledPromise($response); } - private function createSink(StreamInterface $stream, array $options) + private function createSink(StreamInterface $stream, array $options): StreamInterface { if (!empty($options['stream'])) { return $stream; } - $sink = isset($options['sink']) - ? $options['sink'] - : fopen('php://temp', 'r+'); + $sink = $options['sink'] ?? Psr7\Utils::tryFopen('php://temp', 'r+'); - return is_string($sink) - ? new Psr7\LazyOpenStream($sink, 'w+') - : Psr7\stream_for($sink); + return \is_string($sink) ? new Psr7\LazyOpenStream($sink, 'w+') : Psr7\Utils::streamFor($sink); } - private function checkDecode(array $options, array $headers, $stream) + /** + * @param resource $stream + */ + private function checkDecode(array $options, array $headers, $stream): array { // Automatically decode responses when instructed. if (!empty($options['decode_content'])) { - $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); + $normalizedKeys = Utils::normalizeHeaderKeys($headers); if (isset($normalizedKeys['content-encoding'])) { $encoding = $headers[$normalizedKeys['content-encoding']]; if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { - $stream = new Psr7\InflateStream( - Psr7\stream_for($stream) - ); - $headers['x-encoded-content-encoding'] - = $headers[$normalizedKeys['content-encoding']]; + $stream = new Psr7\InflateStream(Psr7\Utils::streamFor($stream)); + $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; + // Remove content-encoding header unset($headers[$normalizedKeys['content-encoding']]); + // Fix content-length header if (isset($normalizedKeys['content-length'])) { - $headers['x-encoded-content-length'] - = $headers[$normalizedKeys['content-length']]; - + $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; $length = (int) $stream->getSize(); if ($length === 0) { unset($headers[$normalizedKeys['content-length']]); @@ -190,27 +193,21 @@ private function checkDecode(array $options, array $headers, $stream) /** * Drains the source stream into the "sink" client option. * - * @param StreamInterface $source - * @param StreamInterface $sink - * @param string $contentLength Header specifying the amount of - * data to read. + * @param string $contentLength Header specifying the amount of + * data to read. * - * @return StreamInterface * @throws \RuntimeException when the sink option is invalid. */ - private function drain( - StreamInterface $source, - StreamInterface $sink, - $contentLength - ) { + private function drain(StreamInterface $source, StreamInterface $sink, string $contentLength): StreamInterface + { // If a content-length header is provided, then stop reading once // that number of bytes has been read. This can prevent infinitely // reading from a stream when dealing with servers that do not honor // Connection: Close headers. - Psr7\copy_to_stream( + Psr7\Utils::copyToStream( $source, $sink, - (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1 + (\strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1 ); $sink->seek(0); @@ -225,12 +222,13 @@ private function drain( * @param callable $callback Callable that returns stream resource * * @return resource + * * @throws \RuntimeException on error */ private function createResource(callable $callback) { - $errors = null; - set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors = []; + \set_error_handler(static function ($_, $msg, $file, $line) use (&$errors): bool { $errors[] = [ 'message' => $msg, 'file' => $file, @@ -239,27 +237,33 @@ private function createResource(callable $callback) return true; }); - $resource = $callback(); - restore_error_handler(); + try { + $resource = $callback(); + } finally { + \restore_error_handler(); + } if (!$resource) { $message = 'Error creating resource: '; foreach ($errors as $err) { foreach ($err as $key => $value) { - $message .= "[$key] $value" . PHP_EOL; + $message .= "[$key] $value" . \PHP_EOL; } } - throw new \RuntimeException(trim($message)); + throw new \RuntimeException(\trim($message)); } return $resource; } + /** + * @return resource + */ private function createStream(RequestInterface $request, array $options) { static $methods; if (!$methods) { - $methods = array_flip(get_class_methods(__CLASS__)); + $methods = \array_flip(\get_class_methods(__CLASS__)); } // HTTP/1.1 streams using the PHP stream wrapper require a @@ -278,7 +282,7 @@ private function createStream(RequestInterface $request, array $options) $params = []; $context = $this->getDefaultContext($request); - if (isset($options['on_headers']) && !is_callable($options['on_headers'])) { + if (isset($options['on_headers']) && !\is_callable($options['on_headers'])) { throw new \InvalidArgumentException('on_headers must be callable'); } @@ -292,42 +296,39 @@ private function createStream(RequestInterface $request, array $options) } if (isset($options['stream_context'])) { - if (!is_array($options['stream_context'])) { + if (!\is_array($options['stream_context'])) { throw new \InvalidArgumentException('stream_context must be an array'); } - $context = array_replace_recursive( - $context, - $options['stream_context'] - ); + $context = \array_replace_recursive($context, $options['stream_context']); } // Microsoft NTLM authentication only supported with curl handler - if (isset($options['auth']) - && is_array($options['auth']) - && isset($options['auth'][2]) - && 'ntlm' == $options['auth'][2] - ) { + if (isset($options['auth'][2]) && 'ntlm' === $options['auth'][2]) { throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); } $uri = $this->resolveHost($request, $options); - $context = $this->createResource( - function () use ($context, $params) { - return stream_context_create($context, $params); + $contextResource = $this->createResource( + static function () use ($context, $params) { + return \stream_context_create($context, $params); } ); return $this->createResource( - function () use ($uri, &$http_response_header, $context, $options) { - $resource = fopen((string) $uri, 'r', null, $context); + function () use ($uri, &$http_response_header, $contextResource, $context, $options, $request) { + $resource = @\fopen((string) $uri, 'r', false, $contextResource); $this->lastHeaders = $http_response_header; + if (false === $resource) { + throw new ConnectException(sprintf('Connection refused for URI %s', $uri), $request, null, $context); + } + if (isset($options['read_timeout'])) { $readTimeout = $options['read_timeout']; $sec = (int) $readTimeout; $usec = ($readTimeout - $sec) * 100000; - stream_set_timeout($resource, $sec, $usec); + \stream_set_timeout($resource, $sec, $usec); } return $resource; @@ -335,30 +336,31 @@ function () use ($uri, &$http_response_header, $context, $options) { ); } - private function resolveHost(RequestInterface $request, array $options) + private function resolveHost(RequestInterface $request, array $options): UriInterface { $uri = $request->getUri(); - if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) { + if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) { if ('v4' === $options['force_ip_resolve']) { - $records = dns_get_record($uri->getHost(), DNS_A); - if (!isset($records[0]['ip'])) { - throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); + $records = \dns_get_record($uri->getHost(), \DNS_A); + if (false === $records || !isset($records[0]['ip'])) { + throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); } - $uri = $uri->withHost($records[0]['ip']); - } elseif ('v6' === $options['force_ip_resolve']) { - $records = dns_get_record($uri->getHost(), DNS_AAAA); - if (!isset($records[0]['ipv6'])) { - throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); + return $uri->withHost($records[0]['ip']); + } + if ('v6' === $options['force_ip_resolve']) { + $records = \dns_get_record($uri->getHost(), \DNS_AAAA); + if (false === $records || !isset($records[0]['ipv6'])) { + throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); } - $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']'); + return $uri->withHost('[' . $records[0]['ipv6'] . ']'); } } return $uri; } - private function getDefaultContext(RequestInterface $request) + private function getDefaultContext(RequestInterface $request): array { $headers = ''; foreach ($request->getHeaders() as $name => $value) { @@ -387,55 +389,100 @@ private function getDefaultContext(RequestInterface $request) } } - $context['http']['header'] = rtrim($context['http']['header']); + $context['http']['header'] = \rtrim($context['http']['header']); return $context; } - private function add_proxy(RequestInterface $request, &$options, $value, &$params) + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_proxy(RequestInterface $request, array &$options, $value, array &$params): void { - if (!is_array($value)) { - $options['http']['proxy'] = $value; + $uri = null; + + if (!\is_array($value)) { + $uri = $value; } else { $scheme = $request->getUri()->getScheme(); if (isset($value[$scheme])) { - if (!isset($value['no']) - || !\GuzzleHttp\is_host_in_noproxy( - $request->getUri()->getHost(), - $value['no'] - ) - ) { - $options['http']['proxy'] = $value[$scheme]; + if (!isset($value['no']) || !Utils::isHostInNoProxy($request->getUri()->getHost(), $value['no'])) { + $uri = $value[$scheme]; } } } + + if (!$uri) { + return; + } + + $parsed = $this->parse_proxy($uri); + $options['http']['proxy'] = $parsed['proxy']; + + if ($parsed['auth']) { + if (!isset($options['http']['header'])) { + $options['http']['header'] = []; + } + $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}"; + } } - private function add_timeout(RequestInterface $request, &$options, $value, &$params) + /** + * Parses the given proxy URL to make it compatible with the format PHP's stream context expects. + */ + private function parse_proxy(string $url): array + { + $parsed = \parse_url($url); + + if ($parsed !== false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') { + if (isset($parsed['host']) && isset($parsed['port'])) { + $auth = null; + if (isset($parsed['user']) && isset($parsed['pass'])) { + $auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}"); + } + + return [ + 'proxy' => "tcp://{$parsed['host']}:{$parsed['port']}", + 'auth' => $auth ? "Basic {$auth}" : null, + ]; + } + } + + // Return proxy as-is. + return [ + 'proxy' => $url, + 'auth' => null, + ]; + } + + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_timeout(RequestInterface $request, array &$options, $value, array &$params): void { if ($value > 0) { $options['http']['timeout'] = $value; } } - private function add_verify(RequestInterface $request, &$options, $value, &$params) + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_verify(RequestInterface $request, array &$options, $value, array &$params): void { - if ($value === true) { - // PHP 5.6 or greater will find the system cert by default. When - // < 5.6, use the Guzzle bundled cacert. - if (PHP_VERSION_ID < 50600) { - $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle(); - } - } elseif (is_string($value)) { - $options['ssl']['cafile'] = $value; - if (!file_exists($value)) { - throw new \RuntimeException("SSL CA bundle not found: $value"); - } - } elseif ($value === false) { + if ($value === false) { $options['ssl']['verify_peer'] = false; $options['ssl']['verify_peer_name'] = false; + return; - } else { + } + + if (\is_string($value)) { + $options['ssl']['cafile'] = $value; + if (!\file_exists($value)) { + throw new \RuntimeException("SSL CA bundle not found: $value"); + } + } elseif ($value !== true) { throw new \InvalidArgumentException('Invalid verify request option'); } @@ -444,88 +491,95 @@ private function add_verify(RequestInterface $request, &$options, $value, &$para $options['ssl']['allow_self_signed'] = false; } - private function add_cert(RequestInterface $request, &$options, $value, &$params) + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_cert(RequestInterface $request, array &$options, $value, array &$params): void { - if (is_array($value)) { + if (\is_array($value)) { $options['ssl']['passphrase'] = $value[1]; $value = $value[0]; } - if (!file_exists($value)) { + if (!\file_exists($value)) { throw new \RuntimeException("SSL certificate not found: {$value}"); } $options['ssl']['local_cert'] = $value; } - private function add_progress(RequestInterface $request, &$options, $value, &$params) + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_progress(RequestInterface $request, array &$options, $value, array &$params): void { - $this->addNotification( + self::addNotification( $params, - function ($code, $a, $b, $c, $transferred, $total) use ($value) { - if ($code == STREAM_NOTIFY_PROGRESS) { - $value($total, $transferred, null, null); + static function ($code, $a, $b, $c, $transferred, $total) use ($value) { + if ($code == \STREAM_NOTIFY_PROGRESS) { + // The upload progress cannot be determined. Use 0 for cURL compatibility: + // https://curl.se/libcurl/c/CURLOPT_PROGRESSFUNCTION.html + $value($total, $transferred, 0, 0); } } ); } - private function add_debug(RequestInterface $request, &$options, $value, &$params) + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_debug(RequestInterface $request, array &$options, $value, array &$params): void { if ($value === false) { return; } static $map = [ - STREAM_NOTIFY_CONNECT => 'CONNECT', - STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', - STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', - STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', - STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', - STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', - STREAM_NOTIFY_PROGRESS => 'PROGRESS', - STREAM_NOTIFY_FAILURE => 'FAILURE', - STREAM_NOTIFY_COMPLETED => 'COMPLETED', - STREAM_NOTIFY_RESOLVE => 'RESOLVE', + \STREAM_NOTIFY_CONNECT => 'CONNECT', + \STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', + \STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', + \STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', + \STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', + \STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', + \STREAM_NOTIFY_PROGRESS => 'PROGRESS', + \STREAM_NOTIFY_FAILURE => 'FAILURE', + \STREAM_NOTIFY_COMPLETED => 'COMPLETED', + \STREAM_NOTIFY_RESOLVE => 'RESOLVE', ]; - static $args = ['severity', 'message', 'message_code', - 'bytes_transferred', 'bytes_max']; + static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max']; - $value = \GuzzleHttp\debug_resource($value); + $value = Utils::debugResource($value); $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment(''); - $this->addNotification( + self::addNotification( $params, - function () use ($ident, $value, $map, $args) { - $passed = func_get_args(); - $code = array_shift($passed); - fprintf($value, '<%s> [%s] ', $ident, $map[$code]); - foreach (array_filter($passed) as $i => $v) { - fwrite($value, $args[$i] . ': "' . $v . '" '); + static function (int $code, ...$passed) use ($ident, $value, $map, $args): void { + \fprintf($value, '<%s> [%s] ', $ident, $map[$code]); + foreach (\array_filter($passed) as $i => $v) { + \fwrite($value, $args[$i] . ': "' . $v . '" '); } - fwrite($value, "\n"); + \fwrite($value, "\n"); } ); } - private function addNotification(array &$params, callable $notify) + private static function addNotification(array &$params, callable $notify): void { // Wrap the existing function if needed. if (!isset($params['notification'])) { $params['notification'] = $notify; } else { - $params['notification'] = $this->callArray([ + $params['notification'] = self::callArray([ $params['notification'], $notify ]); } } - private function callArray(array $functions) + private static function callArray(array $functions): callable { - return function () use ($functions) { - $args = func_get_args(); + return static function (...$args) use ($functions) { foreach ($functions as $fn) { - call_user_func_array($fn, $args); + $fn(...$args); } }; } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/tests/integration/vendor/guzzlehttp/guzzle/src/HandlerStack.php index 24c46fd9f..e0a1d1191 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/HandlerStack.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/HandlerStack.php @@ -1,21 +1,32 @@ push(Middleware::httpErrors(), 'http_errors'); $stack->push(Middleware::redirect(), 'allow_redirects'); $stack->push(Middleware::cookies(), 'cookies'); @@ -47,7 +56,7 @@ public static function create(callable $handler = null) } /** - * @param callable $handler Underlying HTTP handler. + * @param (callable(RequestInterface, array): PromiseInterface)|null $handler Underlying HTTP handler. */ public function __construct(callable $handler = null) { @@ -57,8 +66,7 @@ public function __construct(callable $handler = null) /** * Invokes the handler stack as a composed handler * - * @param RequestInterface $request - * @param array $options + * @return ResponseInterface|PromiseInterface */ public function __invoke(RequestInterface $request, array $options) { @@ -76,12 +84,13 @@ public function __toString() { $depth = 0; $stack = []; - if ($this->handler) { + + if ($this->handler !== null) { $stack[] = "0) Handler: " . $this->debugCallable($this->handler); } $result = ''; - foreach (array_reverse($this->stack) as $tuple) { + foreach (\array_reverse($this->stack) as $tuple) { $depth++; $str = "{$depth}) Name: '{$tuple[1]}', "; $str .= "Function: " . $this->debugCallable($tuple[0]); @@ -89,7 +98,7 @@ public function __toString() $stack[] = $str; } - foreach (array_keys($stack) as $k) { + foreach (\array_keys($stack) as $k) { $result .= "< {$stack[$k]}\n"; } @@ -99,10 +108,10 @@ public function __toString() /** * Set the HTTP handler that actually returns a promise. * - * @param callable $handler Accepts a request and array of options and - * returns a Promise. + * @param callable(RequestInterface, array): PromiseInterface $handler Accepts a request and array of options and + * returns a Promise. */ - public function setHandler(callable $handler) + public function setHandler(callable $handler): void { $this->handler = $handler; $this->cached = null; @@ -110,33 +119,31 @@ public function setHandler(callable $handler) /** * Returns true if the builder has a handler. - * - * @return bool */ - public function hasHandler() + public function hasHandler(): bool { - return (bool) $this->handler; + return $this->handler !== null ; } /** * Unshift a middleware to the bottom of the stack. * - * @param callable $middleware Middleware function - * @param string $name Name to register for this middleware. + * @param callable(callable): callable $middleware Middleware function + * @param string $name Name to register for this middleware. */ - public function unshift(callable $middleware, $name = null) + public function unshift(callable $middleware, ?string $name = null): void { - array_unshift($this->stack, [$middleware, $name]); + \array_unshift($this->stack, [$middleware, $name]); $this->cached = null; } /** * Push a middleware to the top of the stack. * - * @param callable $middleware Middleware function - * @param string $name Name to register for this middleware. + * @param callable(callable): callable $middleware Middleware function + * @param string $name Name to register for this middleware. */ - public function push(callable $middleware, $name = '') + public function push(callable $middleware, string $name = ''): void { $this->stack[] = [$middleware, $name]; $this->cached = null; @@ -145,11 +152,11 @@ public function push(callable $middleware, $name = '') /** * Add a middleware before another middleware by name. * - * @param string $findName Middleware to find - * @param callable $middleware Middleware function - * @param string $withName Name to register for this middleware. + * @param string $findName Middleware to find + * @param callable(callable): callable $middleware Middleware function + * @param string $withName Name to register for this middleware. */ - public function before($findName, callable $middleware, $withName = '') + public function before(string $findName, callable $middleware, string $withName = ''): void { $this->splice($findName, $withName, $middleware, true); } @@ -157,11 +164,11 @@ public function before($findName, callable $middleware, $withName = '') /** * Add a middleware after another middleware by name. * - * @param string $findName Middleware to find - * @param callable $middleware Middleware function - * @param string $withName Name to register for this middleware. + * @param string $findName Middleware to find + * @param callable(callable): callable $middleware Middleware function + * @param string $withName Name to register for this middleware. */ - public function after($findName, callable $middleware, $withName = '') + public function after(string $findName, callable $middleware, string $withName = ''): void { $this->splice($findName, $withName, $middleware, false); } @@ -171,13 +178,17 @@ public function after($findName, callable $middleware, $withName = '') * * @param callable|string $remove Middleware to remove by instance or name. */ - public function remove($remove) + public function remove($remove): void { + if (!is_string($remove) && !is_callable($remove)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a callable or string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + $this->cached = null; - $idx = is_callable($remove) ? 0 : 1; - $this->stack = array_values(array_filter( + $idx = \is_callable($remove) ? 0 : 1; + $this->stack = \array_values(\array_filter( $this->stack, - function ($tuple) use ($idx, $remove) { + static function ($tuple) use ($idx, $remove) { return $tuple[$idx] !== $remove; } )); @@ -186,16 +197,17 @@ function ($tuple) use ($idx, $remove) { /** * Compose the middleware and handler into a single callable function. * - * @return callable + * @return callable(RequestInterface, array): PromiseInterface */ - public function resolve() + public function resolve(): callable { - if (!$this->cached) { - if (!($prev = $this->handler)) { + if ($this->cached === null) { + if (($prev = $this->handler) === null) { throw new \LogicException('No handler has been specified'); } - foreach (array_reverse($this->stack) as $fn) { + foreach (\array_reverse($this->stack) as $fn) { + /** @var callable(RequestInterface, array): PromiseInterface $prev */ $prev = $fn[0]($prev); } @@ -205,11 +217,7 @@ public function resolve() return $this->cached; } - /** - * @param $name - * @return int - */ - private function findByName($name) + private function findByName(string $name): int { foreach ($this->stack as $k => $v) { if ($v[1] === $name) { @@ -222,13 +230,8 @@ private function findByName($name) /** * Splices a function into the middleware list at a specific position. - * - * @param $findName - * @param $withName - * @param callable $middleware - * @param $before */ - private function splice($findName, $withName, callable $middleware, $before) + private function splice(string $findName, string $withName, callable $middleware, bool $before): void { $this->cached = null; $idx = $this->findByName($findName); @@ -236,38 +239,37 @@ private function splice($findName, $withName, callable $middleware, $before) if ($before) { if ($idx === 0) { - array_unshift($this->stack, $tuple); + \array_unshift($this->stack, $tuple); } else { $replacement = [$tuple, $this->stack[$idx]]; - array_splice($this->stack, $idx, 1, $replacement); + \array_splice($this->stack, $idx, 1, $replacement); } - } elseif ($idx === count($this->stack) - 1) { + } elseif ($idx === \count($this->stack) - 1) { $this->stack[] = $tuple; } else { $replacement = [$this->stack[$idx], $tuple]; - array_splice($this->stack, $idx, 1, $replacement); + \array_splice($this->stack, $idx, 1, $replacement); } } /** * Provides a debug string for a given callable. * - * @param array|callable $fn Function to write as a string. - * - * @return string + * @param callable|string $fn Function to write as a string. */ - private function debugCallable($fn) + private function debugCallable($fn): string { - if (is_string($fn)) { + if (\is_string($fn)) { return "callable({$fn})"; } - if (is_array($fn)) { - return is_string($fn[0]) + if (\is_array($fn)) { + return \is_string($fn[0]) ? "callable({$fn[0]}::{$fn[1]})" - : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])"; + : "callable(['" . \get_class($fn[0]) . "', '{$fn[1]}'])"; } - return 'callable(' . spl_object_hash($fn) . ')'; + /** @var object $fn */ + return 'callable(' . \spl_object_hash($fn) . ')'; } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatter.php index 663ac7391..da499547f 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatter.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatter.php @@ -1,4 +1,5 @@ >>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; - const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; + public const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}"; + public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; + public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; - /** @var string Template used to format log messages */ + /** + * @var string Template used to format log messages + */ private $template; /** * @param string $template Log message template */ - public function __construct($template = self::CLF) + public function __construct(?string $template = self::CLF) { $this->template = $template ?: self::CLF; } @@ -57,20 +64,16 @@ public function __construct($template = self::CLF) /** * Returns a formatted message string. * - * @param RequestInterface $request Request that was sent - * @param ResponseInterface $response Response that was received - * @param \Exception $error Exception that was received - * - * @return string + * @param RequestInterface $request Request that was sent + * @param ResponseInterface|null $response Response that was received + * @param \Throwable|null $error Exception that was received */ - public function format( - RequestInterface $request, - ResponseInterface $response = null, - \Exception $error = null - ) { + public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string + { $cache = []; - return preg_replace_callback( + /** @var string */ + return \preg_replace_callback( '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', function (array $matches) use ($request, $response, $error, &$cache) { if (isset($cache[$matches[1]])) { @@ -80,20 +83,20 @@ function (array $matches) use ($request, $response, $error, &$cache) { $result = ''; switch ($matches[1]) { case 'request': - $result = Psr7\str($request); + $result = Psr7\Message::toString($request); break; case 'response': - $result = $response ? Psr7\str($response) : ''; + $result = $response ? Psr7\Message::toString($response) : ''; break; case 'req_headers': - $result = trim($request->getMethod() + $result = \trim($request->getMethod() . ' ' . $request->getRequestTarget()) . ' HTTP/' . $request->getProtocolVersion() . "\r\n" . $this->headers($request); break; case 'res_headers': $result = $response ? - sprintf( + \sprintf( 'HTTP/%s %d %s', $response->getProtocolVersion(), $response->getStatusCode(), @@ -102,17 +105,29 @@ function (array $matches) use ($request, $response, $error, &$cache) { : 'NULL'; break; case 'req_body': - $result = $request->getBody(); + $result = $request->getBody()->__toString(); break; case 'res_body': - $result = $response ? $response->getBody() : 'NULL'; + if (!$response instanceof ResponseInterface) { + $result = 'NULL'; + break; + } + + $body = $response->getBody(); + + if (!$body->isSeekable()) { + $result = 'RESPONSE_NOT_LOGGEABLE'; + break; + } + + $result = $response->getBody()->__toString(); break; case 'ts': case 'date_iso_8601': - $result = gmdate('c'); + $result = \gmdate('c'); break; case 'date_common_log': - $result = date('d/M/Y:H:i:s O'); + $result = \date('d/M/Y:H:i:s O'); break; case 'method': $result = $request->getMethod(); @@ -122,7 +137,7 @@ function (array $matches) use ($request, $response, $error, &$cache) { break; case 'uri': case 'url': - $result = $request->getUri(); + $result = $request->getUri()->__toString(); break; case 'target': $result = $request->getRequestTarget(); @@ -139,7 +154,7 @@ function (array $matches) use ($request, $response, $error, &$cache) { $result = $request->getHeaderLine('Host'); break; case 'hostname': - $result = gethostname(); + $result = \gethostname(); break; case 'code': $result = $response ? $response->getStatusCode() : 'NULL'; @@ -152,11 +167,11 @@ function (array $matches) use ($request, $response, $error, &$cache) { break; default: // handle prefixed dynamic headers - if (strpos($matches[1], 'req_header_') === 0) { - $result = $request->getHeaderLine(substr($matches[1], 11)); - } elseif (strpos($matches[1], 'res_header_') === 0) { + if (\strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeaderLine(\substr($matches[1], 11)); + } elseif (\strpos($matches[1], 'res_header_') === 0) { $result = $response - ? $response->getHeaderLine(substr($matches[1], 11)) + ? $response->getHeaderLine(\substr($matches[1], 11)) : 'NULL'; } } @@ -168,13 +183,16 @@ function (array $matches) use ($request, $response, $error, &$cache) { ); } - private function headers(MessageInterface $message) + /** + * Get headers from message as string + */ + private function headers(MessageInterface $message): string { $result = ''; foreach ($message->getHeaders() as $name => $values) { - $result .= $name . ': ' . implode(', ', $values) . "\r\n"; + $result .= $name . ': ' . \implode(', ', $values) . "\r\n"; } - return trim($result); + return \trim($result); } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php b/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php new file mode 100644 index 000000000..a39ac248e --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php @@ -0,0 +1,18 @@ +withCookieHeader($request); return $handler($request, $options) ->then( - function ($response) use ($cookieJar, $request) { + static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface { $cookieJar->extractCookies($request, $response); return $response; } - ); + ); }; }; } /** * Middleware that throws exceptions for 4xx or 5xx responses when the - * "http_error" request option is set to true. + * "http_errors" request option is set to true. * - * @return callable Returns a function that accepts the next handler. + * @param BodySummarizerInterface|null $bodySummarizer The body summarizer to use in exception messages. + * + * @return callable(callable): callable Returns a function that accepts the next handler. */ - public static function httpErrors() + public static function httpErrors(BodySummarizerInterface $bodySummarizer = null): callable { - return function (callable $handler) { - return function ($request, array $options) use ($handler) { + return static function (callable $handler) use ($bodySummarizer): callable { + return static function ($request, array $options) use ($handler, $bodySummarizer) { if (empty($options['http_errors'])) { return $handler($request, $options); } return $handler($request, $options)->then( - function (ResponseInterface $response) use ($request, $handler) { + static function (ResponseInterface $response) use ($request, $bodySummarizer) { $code = $response->getStatusCode(); if ($code < 400) { return $response; } - throw RequestException::create($request, $response); + throw RequestException::create($request, $response, null, [], $bodySummarizer); } ); }; @@ -73,21 +76,22 @@ function (ResponseInterface $response) use ($request, $handler) { /** * Middleware that pushes history data to an ArrayAccess container. * - * @param array|\ArrayAccess $container Container to hold the history (by reference). + * @param array|\ArrayAccess $container Container to hold the history (by reference). + * + * @return callable(callable): callable Returns a function that accepts the next handler. * - * @return callable Returns a function that accepts the next handler. * @throws \InvalidArgumentException if container is not an array or ArrayAccess. */ - public static function history(&$container) + public static function history(&$container): callable { - if (!is_array($container) && !$container instanceof \ArrayAccess) { + if (!\is_array($container) && !$container instanceof \ArrayAccess) { throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess'); } - return function (callable $handler) use (&$container) { - return function ($request, array $options) use ($handler, &$container) { + return static function (callable $handler) use (&$container): callable { + return static function (RequestInterface $request, array $options) use ($handler, &$container) { return $handler($request, $options)->then( - function ($value) use ($request, &$container, $options) { + static function ($value) use ($request, &$container, $options) { $container[] = [ 'request' => $request, 'response' => $value, @@ -96,14 +100,14 @@ function ($value) use ($request, &$container, $options) { ]; return $value; }, - function ($reason) use ($request, &$container, $options) { + static function ($reason) use ($request, &$container, $options) { $container[] = [ 'request' => $request, 'response' => null, 'error' => $reason, 'options' => $options ]; - return \GuzzleHttp\Promise\rejection_for($reason); + return P\Create::rejectionFor($reason); } ); }; @@ -123,10 +127,10 @@ function ($reason) use ($request, &$container, $options) { * * @return callable Returns a function that accepts the next handler. */ - public static function tap(callable $before = null, callable $after = null) + public static function tap(callable $before = null, callable $after = null): callable { - return function (callable $handler) use ($before, $after) { - return function ($request, array $options) use ($handler, $before, $after) { + return static function (callable $handler) use ($before, $after): callable { + return static function (RequestInterface $request, array $options) use ($handler, $before, $after) { if ($before) { $before($request, $options); } @@ -144,9 +148,9 @@ public static function tap(callable $before = null, callable $after = null) * * @return callable Returns a function that accepts the next handler. */ - public static function redirect() + public static function redirect(): callable { - return function (callable $handler) { + return static function (callable $handler): RedirectMiddleware { return new RedirectMiddleware($handler); }; } @@ -166,9 +170,9 @@ public static function redirect() * * @return callable Returns a function that accepts the next handler. */ - public static function retry(callable $decider, callable $delay = null) + public static function retry(callable $decider, callable $delay = null): callable { - return function (callable $handler) use ($decider, $delay) { + return static function (callable $handler) use ($decider, $delay): RetryMiddleware { return new RetryMiddleware($decider, $handler, $delay); }; } @@ -177,29 +181,34 @@ public static function retry(callable $decider, callable $delay = null) * Middleware that logs requests, responses, and errors using a message * formatter. * - * @param LoggerInterface $logger Logs messages. - * @param MessageFormatter $formatter Formatter used to create message strings. - * @param string $logLevel Level at which to log requests. + * @phpstan-param \Psr\Log\LogLevel::* $logLevel Level at which to log requests. + * + * @param LoggerInterface $logger Logs messages. + * @param MessageFormatterInterface|MessageFormatter $formatter Formatter used to create message strings. + * @param string $logLevel Level at which to log requests. * * @return callable Returns a function that accepts the next handler. */ - public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO) + public static function log(LoggerInterface $logger, $formatter, string $logLevel = 'info'): callable { - return function (callable $handler) use ($logger, $formatter, $logLevel) { - return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) { + // To be compatible with Guzzle 7.1.x we need to allow users to pass a MessageFormatter + if (!$formatter instanceof MessageFormatter && !$formatter instanceof MessageFormatterInterface) { + throw new \LogicException(sprintf('Argument 2 to %s::log() must be of type %s', self::class, MessageFormatterInterface::class)); + } + + return static function (callable $handler) use ($logger, $formatter, $logLevel): callable { + return static function (RequestInterface $request, array $options = []) use ($handler, $logger, $formatter, $logLevel) { return $handler($request, $options)->then( - function ($response) use ($logger, $request, $formatter, $logLevel) { + static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface { $message = $formatter->format($request, $response); $logger->log($logLevel, $message); return $response; }, - function ($reason) use ($logger, $request, $formatter) { - $response = $reason instanceof RequestException - ? $reason->getResponse() - : null; - $message = $formatter->format($request, $response, $reason); - $logger->notice($message); - return \GuzzleHttp\Promise\rejection_for($reason); + static function ($reason) use ($logger, $request, $formatter): PromiseInterface { + $response = $reason instanceof RequestException ? $reason->getResponse() : null; + $message = $formatter->format($request, $response, P\Create::exceptionFor($reason)); + $logger->error($message); + return P\Create::rejectionFor($reason); } ); }; @@ -209,12 +218,10 @@ function ($reason) use ($logger, $request, $formatter) { /** * This middleware adds a default content-type if possible, a default * content-length or transfer-encoding header, and the expect header. - * - * @return callable */ - public static function prepareBody() + public static function prepareBody(): callable { - return function (callable $handler) { + return static function (callable $handler): PrepareBodyMiddleware { return new PrepareBodyMiddleware($handler); }; } @@ -225,12 +232,11 @@ public static function prepareBody() * * @param callable $fn Function that accepts a RequestInterface and returns * a RequestInterface. - * @return callable */ - public static function mapRequest(callable $fn) + public static function mapRequest(callable $fn): callable { - return function (callable $handler) use ($fn) { - return function ($request, array $options) use ($handler, $fn) { + return static function (callable $handler) use ($fn): callable { + return static function (RequestInterface $request, array $options) use ($handler, $fn) { return $handler($fn($request), $options); }; }; @@ -242,12 +248,11 @@ public static function mapRequest(callable $fn) * * @param callable $fn Function that accepts a ResponseInterface and * returns a ResponseInterface. - * @return callable */ - public static function mapResponse(callable $fn) + public static function mapResponse(callable $fn): callable { - return function (callable $handler) use ($fn) { - return function ($request, array $options) use ($handler, $fn) { + return static function (callable $handler) use ($fn): callable { + return static function (RequestInterface $request, array $options) use ($handler, $fn) { return $handler($request, $options)->then($fn); }; }; diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Pool.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Pool.php index 8f1be33cd..6277c61fb 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/Pool.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Pool.php @@ -1,12 +1,15 @@ $rfn) { if ($rfn instanceof RequestInterface) { yield $key => $client->sendAsync($rfn, $opts); - } elseif (is_callable($rfn)) { + } elseif (\is_callable($rfn)) { yield $key => $rfn($opts); } else { - throw new \InvalidArgumentException('Each value yielded by ' - . 'the iterator must be a Psr7\Http\Message\RequestInterface ' - . 'or a callable that returns a promise that fulfills ' - . 'with a Psr7\Message\Http\ResponseInterface object.'); + throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr7\Http\Message\RequestInterface or a callable that returns a promise that fulfills with a Psr7\Message\Http\ResponseInterface object.'); } } }; @@ -69,7 +67,10 @@ public function __construct( $this->each = new EachPromise($requests(), $config); } - public function promise() + /** + * Get promise + */ + public function promise(): PromiseInterface { return $this->each->promise(); } @@ -85,36 +86,37 @@ public function promise() * @param ClientInterface $client Client used to send the requests * @param array|\Iterator $requests Requests to send concurrently. * @param array $options Passes through the options available in - * {@see GuzzleHttp\Pool::__construct} + * {@see \GuzzleHttp\Pool::__construct} * * @return array Returns an array containing the response or an exception * in the same order that the requests were sent. + * * @throws \InvalidArgumentException if the event format is incorrect. */ - public static function batch( - ClientInterface $client, - $requests, - array $options = [] - ) { + public static function batch(ClientInterface $client, $requests, array $options = []): array + { $res = []; self::cmpCallback($options, 'fulfilled', $res); self::cmpCallback($options, 'rejected', $res); $pool = new static($client, $requests, $options); $pool->promise()->wait(); - ksort($res); + \ksort($res); return $res; } - private static function cmpCallback(array &$options, $name, array &$results) + /** + * Execute callback(s) + */ + private static function cmpCallback(array &$options, string $name, array &$results): void { if (!isset($options[$name])) { - $options[$name] = function ($v, $k) use (&$results) { + $options[$name] = static function ($v, $k) use (&$results) { $results[$k] = $v; }; } else { $currentFn = $options[$name]; - $options[$name] = function ($v, $k) use (&$results, $currentFn) { + $options[$name] = static function ($v, $k) use (&$results, $currentFn) { $currentFn($v, $k); $results[$k] = $v; }; diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/tests/integration/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php index 2eb95f9b2..7ca628338 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php @@ -1,34 +1,32 @@ nextHandler = $nextHandler; } - /** - * @param RequestInterface $request - * @param array $options - * - * @return PromiseInterface - */ - public function __invoke(RequestInterface $request, array $options) + public function __invoke(RequestInterface $request, array $options): PromiseInterface { $fn = $this->nextHandler; @@ -42,7 +40,7 @@ public function __invoke(RequestInterface $request, array $options) // Add a default content-type if possible. if (!$request->hasHeader('Content-Type')) { if ($uri = $request->getBody()->getMetadata('uri')) { - if ($type = Psr7\mimetype_from_filename($uri)) { + if (is_string($uri) && $type = Psr7\MimeType::fromFilename($uri)) { $modify['set_headers']['Content-Type'] = $type; } } @@ -63,20 +61,20 @@ public function __invoke(RequestInterface $request, array $options) // Add the expect header if needed. $this->addExpectHeader($request, $options, $modify); - return $fn(Psr7\modify_request($request, $modify), $options); + return $fn(Psr7\Utils::modifyRequest($request, $modify), $options); } - private function addExpectHeader( - RequestInterface $request, - array $options, - array &$modify - ) { + /** + * Add expect header + */ + private function addExpectHeader(RequestInterface $request, array $options, array &$modify): void + { // Determine if the Expect header should be used if ($request->hasHeader('Expect')) { return; } - $expect = isset($options['expect']) ? $options['expect'] : null; + $expect = $options['expect'] ?? null; // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 if ($expect === false || $request->getProtocolVersion() < 1.1) { diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/tests/integration/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php index 131b77179..1dd38614f 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php @@ -1,10 +1,10 @@ 5, 'protocols' => ['http', 'https'], @@ -29,24 +34,20 @@ class RedirectMiddleware 'track_redirects' => false, ]; - /** @var callable */ + /** + * @var callable(RequestInterface, array): PromiseInterface + */ private $nextHandler; /** - * @param callable $nextHandler Next handler to invoke. + * @param callable(RequestInterface, array): PromiseInterface $nextHandler Next handler to invoke. */ public function __construct(callable $nextHandler) { $this->nextHandler = $nextHandler; } - /** - * @param RequestInterface $request - * @param array $options - * - * @return PromiseInterface - */ - public function __invoke(RequestInterface $request, array $options) + public function __invoke(RequestInterface $request, array $options): PromiseInterface { $fn = $this->nextHandler; @@ -56,7 +57,7 @@ public function __invoke(RequestInterface $request, array $options) if ($options['allow_redirects'] === true) { $options['allow_redirects'] = self::$defaultSettings; - } elseif (!is_array($options['allow_redirects'])) { + } elseif (!\is_array($options['allow_redirects'])) { throw new \InvalidArgumentException('allow_redirects must be true, false, or array'); } else { // Merge the default settings with the provided settings @@ -74,36 +75,27 @@ public function __invoke(RequestInterface $request, array $options) } /** - * @param RequestInterface $request - * @param array $options - * @param ResponseInterface|PromiseInterface $response - * * @return ResponseInterface|PromiseInterface */ - public function checkRedirect( - RequestInterface $request, - array $options, - ResponseInterface $response - ) { - if (substr($response->getStatusCode(), 0, 1) != '3' + public function checkRedirect(RequestInterface $request, array $options, ResponseInterface $response) + { + if (\strpos((string) $response->getStatusCode(), '3') !== 0 || !$response->hasHeader('Location') ) { return $response; } - $this->guardMax($request, $options); + $this->guardMax($request, $response, $options); $nextRequest = $this->modifyRequest($request, $options, $response); if (isset($options['allow_redirects']['on_redirect'])) { - call_user_func( - $options['allow_redirects']['on_redirect'], + ($options['allow_redirects']['on_redirect'])( $request, $response, $nextRequest->getUri() ); } - /** @var PromiseInterface|ResponseInterface $promise */ $promise = $this($nextRequest, $options); // Add headers to be able to track history of redirects. @@ -118,51 +110,46 @@ public function checkRedirect( return $promise; } - private function withTracking(PromiseInterface $promise, $uri, $statusCode) + /** + * Enable tracking on promise. + */ + private function withTracking(PromiseInterface $promise, string $uri, int $statusCode): PromiseInterface { return $promise->then( - function (ResponseInterface $response) use ($uri, $statusCode) { + static function (ResponseInterface $response) use ($uri, $statusCode) { // Note that we are pushing to the front of the list as this // would be an earlier response than what is currently present // in the history header. $historyHeader = $response->getHeader(self::HISTORY_HEADER); $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER); - array_unshift($historyHeader, $uri); - array_unshift($statusHeader, $statusCode); + \array_unshift($historyHeader, $uri); + \array_unshift($statusHeader, (string) $statusCode); + return $response->withHeader(self::HISTORY_HEADER, $historyHeader) ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader); } ); } - private function guardMax(RequestInterface $request, array &$options) + /** + * Check for too many redirects + * + * @throws TooManyRedirectsException Too many redirects. + */ + private function guardMax(RequestInterface $request, ResponseInterface $response, array &$options): void { - $current = isset($options['__redirect_count']) - ? $options['__redirect_count'] - : 0; + $current = $options['__redirect_count'] + ?? 0; $options['__redirect_count'] = $current + 1; $max = $options['allow_redirects']['max']; if ($options['__redirect_count'] > $max) { - throw new TooManyRedirectsException( - "Will not follow more than {$max} redirects", - $request - ); + throw new TooManyRedirectsException("Will not follow more than {$max} redirects", $request, $response); } } - /** - * @param RequestInterface $request - * @param array $options - * @param ResponseInterface $response - * - * @return RequestInterface - */ - public function modifyRequest( - RequestInterface $request, - array $options, - ResponseInterface $response - ) { + public function modifyRequest(RequestInterface $request, array $options, ResponseInterface $response): RequestInterface + { // Request modifications to apply. $modify = []; $protocols = $options['allow_redirects']['protocols']; @@ -172,21 +159,30 @@ public function modifyRequest( // would do. $statusCode = $response->getStatusCode(); if ($statusCode == 303 || - ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict']) + ($statusCode <= 302 && !$options['allow_redirects']['strict']) ) { - $modify['method'] = 'GET'; + $safeMethods = ['GET', 'HEAD', 'OPTIONS']; + $requestMethod = $request->getMethod(); + + $modify['method'] = in_array($requestMethod, $safeMethods) ? $requestMethod : 'GET'; $modify['body'] = ''; } - $modify['uri'] = $this->redirectUri($request, $response, $protocols); - Psr7\rewind_body($request); + $uri = $this->redirectUri($request, $response, $protocols); + if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) { + $idnOptions = ($options['idn_conversion'] === true) ? \IDNA_DEFAULT : $options['idn_conversion']; + $uri = Utils::idnUriConvert($uri, $idnOptions); + } + + $modify['uri'] = $uri; + Psr7\Message::rewindBody($request); // Add the Referer header if it is told to do so and only // add the header if we are not redirecting from https to http. if ($options['allow_redirects']['referer'] && $modify['uri']->getScheme() === $request->getUri()->getScheme() ) { - $uri = $request->getUri()->withUserInfo('', ''); + $uri = $request->getUri()->withUserInfo(''); $modify['set_headers']['Referer'] = (string) $uri; } else { $modify['remove_headers'][] = 'Referer'; @@ -197,39 +193,22 @@ public function modifyRequest( $modify['remove_headers'][] = 'Authorization'; } - return Psr7\modify_request($request, $modify); + return Psr7\Utils::modifyRequest($request, $modify); } /** * Set the appropriate URL on the request based on the location header - * - * @param RequestInterface $request - * @param ResponseInterface $response - * @param array $protocols - * - * @return UriInterface */ - private function redirectUri( - RequestInterface $request, - ResponseInterface $response, - array $protocols - ) { + private function redirectUri(RequestInterface $request, ResponseInterface $response, array $protocols): UriInterface + { $location = Psr7\UriResolver::resolve( $request->getUri(), new Psr7\Uri($response->getHeaderLine('Location')) ); // Ensure that the redirect URI is allowed based on the protocols. - if (!in_array($location->getScheme(), $protocols)) { - throw new BadResponseException( - sprintf( - 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s', - $location, - implode(', ', $protocols) - ), - $request, - $response - ); + if (!\in_array($location->getScheme(), $protocols)) { + throw new BadResponseException(\sprintf('Redirect URI, %s, does not use one of the allowed redirect protocols: %s', $location, \implode(', ', $protocols)), $request, $response); } return $location; diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/tests/integration/vendor/guzzlehttp/guzzle/src/RequestOptions.php index c6aacfb15..20b31bc20 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/RequestOptions.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/RequestOptions.php @@ -1,4 +1,5 @@ decider = $decider; $this->nextHandler = $nextHandler; $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; @@ -42,22 +50,14 @@ public function __construct( /** * Default exponential backoff delay function. * - * @param $retries - * - * @return int + * @return int milliseconds. */ - public static function exponentialDelay($retries) + public static function exponentialDelay(int $retries): int { - return (int) pow(2, $retries - 1); + return (int) \pow(2, $retries - 1) * 1000; } - /** - * @param RequestInterface $request - * @param array $options - * - * @return PromiseInterface - */ - public function __invoke(RequestInterface $request, array $options) + public function __invoke(RequestInterface $request, array $options): PromiseInterface { if (!isset($options['retries'])) { $options['retries'] = 0; @@ -71,41 +71,45 @@ public function __invoke(RequestInterface $request, array $options) ); } - private function onFulfilled(RequestInterface $req, array $options) + /** + * Execute fulfilled closure + */ + private function onFulfilled(RequestInterface $request, array $options): callable { - return function ($value) use ($req, $options) { - if (!call_user_func( - $this->decider, + return function ($value) use ($request, $options) { + if (!($this->decider)( $options['retries'], - $req, + $request, $value, null )) { return $value; } - return $this->doRetry($req, $options, $value); + return $this->doRetry($request, $options, $value); }; } - private function onRejected(RequestInterface $req, array $options) + /** + * Execute rejected closure + */ + private function onRejected(RequestInterface $req, array $options): callable { return function ($reason) use ($req, $options) { - if (!call_user_func( - $this->decider, + if (!($this->decider)( $options['retries'], $req, null, $reason )) { - return \GuzzleHttp\Promise\rejection_for($reason); + return P\Create::rejectionFor($reason); } return $this->doRetry($req, $options); }; } - private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) + private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null): PromiseInterface { - $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response); + $options['delay'] = ($this->delay)(++$options['retries'], $response); return $this($request, $options); } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/TransferStats.php b/tests/integration/vendor/guzzlehttp/guzzle/src/TransferStats.php index 15f717e1e..93fa334c8 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/TransferStats.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/TransferStats.php @@ -1,4 +1,5 @@ request = $request; $this->response = $response; @@ -38,30 +58,23 @@ public function __construct( $this->handlerStats = $handlerStats; } - /** - * @return RequestInterface - */ - public function getRequest() + public function getRequest(): RequestInterface { return $this->request; } /** * Returns the response that was received (if any). - * - * @return ResponseInterface|null */ - public function getResponse() + public function getResponse(): ?ResponseInterface { return $this->response; } /** * Returns true if a response was received. - * - * @return bool */ - public function hasResponse() + public function hasResponse(): bool { return $this->response !== null; } @@ -82,10 +95,8 @@ public function getHandlerErrorData() /** * Get the effective URI the request was sent to. - * - * @return UriInterface */ - public function getEffectiveUri() + public function getEffectiveUri(): UriInterface { return $this->request->getUri(); } @@ -93,19 +104,17 @@ public function getEffectiveUri() /** * Get the estimated time the request was being transferred by the handler. * - * @return float Time in seconds. + * @return float|null Time in seconds. */ - public function getTransferTime() + public function getTransferTime(): ?float { return $this->transferTime; } /** * Gets an array of all of the handler specific transfer data. - * - * @return array */ - public function getHandlerStats() + public function getHandlerStats(): array { return $this->handlerStats; } @@ -117,10 +126,8 @@ public function getHandlerStats() * * @return mixed|null */ - public function getHandlerStat($stat) + public function getHandlerStat(string $stat) { - return isset($this->handlerStats[$stat]) - ? $this->handlerStats[$stat] - : null; + return $this->handlerStats[$stat] ?? null; } } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/tests/integration/vendor/guzzlehttp/guzzle/src/UriTemplate.php deleted file mode 100644 index 96dcfd09c..000000000 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/UriTemplate.php +++ /dev/null @@ -1,237 +0,0 @@ - ['prefix' => '', 'joiner' => ',', 'query' => false], - '+' => ['prefix' => '', 'joiner' => ',', 'query' => false], - '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false], - '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false], - '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false], - ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true], - '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true], - '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true] - ]; - - /** @var array Delimiters */ - private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$', - '&', '\'', '(', ')', '*', '+', ',', ';', '=']; - - /** @var array Percent encoded delimiters */ - private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D', - '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', - '%3B', '%3D']; - - public function expand($template, array $variables) - { - if (false === strpos($template, '{')) { - return $template; - } - - $this->template = $template; - $this->variables = $variables; - - return preg_replace_callback( - '/\{([^\}]+)\}/', - [$this, 'expandMatch'], - $this->template - ); - } - - /** - * Parse an expression into parts - * - * @param string $expression Expression to parse - * - * @return array Returns an associative array of parts - */ - private function parseExpression($expression) - { - $result = []; - - if (isset(self::$operatorHash[$expression[0]])) { - $result['operator'] = $expression[0]; - $expression = substr($expression, 1); - } else { - $result['operator'] = ''; - } - - foreach (explode(',', $expression) as $value) { - $value = trim($value); - $varspec = []; - if ($colonPos = strpos($value, ':')) { - $varspec['value'] = substr($value, 0, $colonPos); - $varspec['modifier'] = ':'; - $varspec['position'] = (int) substr($value, $colonPos + 1); - } elseif (substr($value, -1) === '*') { - $varspec['modifier'] = '*'; - $varspec['value'] = substr($value, 0, -1); - } else { - $varspec['value'] = (string) $value; - $varspec['modifier'] = ''; - } - $result['values'][] = $varspec; - } - - return $result; - } - - /** - * Process an expansion - * - * @param array $matches Matches met in the preg_replace_callback - * - * @return string Returns the replacement string - */ - private function expandMatch(array $matches) - { - static $rfc1738to3986 = ['+' => '%20', '%7e' => '~']; - - $replacements = []; - $parsed = self::parseExpression($matches[1]); - $prefix = self::$operatorHash[$parsed['operator']]['prefix']; - $joiner = self::$operatorHash[$parsed['operator']]['joiner']; - $useQuery = self::$operatorHash[$parsed['operator']]['query']; - - foreach ($parsed['values'] as $value) { - if (!isset($this->variables[$value['value']])) { - continue; - } - - $variable = $this->variables[$value['value']]; - $actuallyUseQuery = $useQuery; - $expanded = ''; - - if (is_array($variable)) { - $isAssoc = $this->isAssoc($variable); - $kvp = []; - foreach ($variable as $key => $var) { - if ($isAssoc) { - $key = rawurlencode($key); - $isNestedArray = is_array($var); - } else { - $isNestedArray = false; - } - - if (!$isNestedArray) { - $var = rawurlencode($var); - if ($parsed['operator'] === '+' || - $parsed['operator'] === '#' - ) { - $var = $this->decodeReserved($var); - } - } - - if ($value['modifier'] === '*') { - if ($isAssoc) { - if ($isNestedArray) { - // Nested arrays must allow for deeply nested - // structures. - $var = strtr( - http_build_query([$key => $var]), - $rfc1738to3986 - ); - } else { - $var = $key . '=' . $var; - } - } elseif ($key > 0 && $actuallyUseQuery) { - $var = $value['value'] . '=' . $var; - } - } - - $kvp[$key] = $var; - } - - if (empty($variable)) { - $actuallyUseQuery = false; - } elseif ($value['modifier'] === '*') { - $expanded = implode($joiner, $kvp); - if ($isAssoc) { - // Don't prepend the value name when using the explode - // modifier with an associative array. - $actuallyUseQuery = false; - } - } else { - if ($isAssoc) { - // When an associative array is encountered and the - // explode modifier is not set, then the result must be - // a comma separated list of keys followed by their - // respective values. - foreach ($kvp as $k => &$v) { - $v = $k . ',' . $v; - } - } - $expanded = implode(',', $kvp); - } - } else { - if ($value['modifier'] === ':') { - $variable = substr($variable, 0, $value['position']); - } - $expanded = rawurlencode($variable); - if ($parsed['operator'] === '+' || $parsed['operator'] === '#') { - $expanded = $this->decodeReserved($expanded); - } - } - - if ($actuallyUseQuery) { - if (!$expanded && $joiner !== '&') { - $expanded = $value['value']; - } else { - $expanded = $value['value'] . '=' . $expanded; - } - } - - $replacements[] = $expanded; - } - - $ret = implode($joiner, $replacements); - if ($ret && $prefix) { - return $prefix . $ret; - } - - return $ret; - } - - /** - * Determines if an array is associative. - * - * This makes the assumption that input arrays are sequences or hashes. - * This assumption is a tradeoff for accuracy in favor of speed, but it - * should work in almost every case where input is supplied for a URI - * template. - * - * @param array $array Array to check - * - * @return bool - */ - private function isAssoc(array $array) - { - return $array && array_keys($array)[0] !== 0; - } - - /** - * Removes percent encoding on reserved characters (used with + and # - * modifiers). - * - * @param string $string String to fix - * - * @return string - */ - private function decodeReserved($string) - { - return str_replace(self::$delimsPct, self::$delims, $string); - } -} diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/Utils.php b/tests/integration/vendor/guzzlehttp/guzzle/src/Utils.php new file mode 100644 index 000000000..91591da2e --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/Utils.php @@ -0,0 +1,382 @@ +getHost()) { + $asciiHost = self::idnToAsci($uri->getHost(), $options, $info); + if ($asciiHost === false) { + $errorBitSet = $info['errors'] ?? 0; + + $errorConstants = array_filter(array_keys(get_defined_constants()), static function (string $name): bool { + return substr($name, 0, 11) === 'IDNA_ERROR_'; + }); + + $errors = []; + foreach ($errorConstants as $errorConstant) { + if ($errorBitSet & constant($errorConstant)) { + $errors[] = $errorConstant; + } + } + + $errorMessage = 'IDN conversion failed'; + if ($errors) { + $errorMessage .= ' (errors: ' . implode(', ', $errors) . ')'; + } + + throw new InvalidArgumentException($errorMessage); + } + if ($uri->getHost() !== $asciiHost) { + // Replace URI only if the ASCII version is different + $uri = $uri->withHost($asciiHost); + } + } + + return $uri; + } + + /** + * @internal + */ + public static function getenv(string $name): ?string + { + if (isset($_SERVER[$name])) { + return (string) $_SERVER[$name]; + } + + if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== false && $value !== null) { + return (string) $value; + } + + return null; + } + + /** + * @return string|false + */ + private static function idnToAsci(string $domain, int $options, ?array &$info = []) + { + if (\function_exists('idn_to_ascii') && \defined('INTL_IDNA_VARIANT_UTS46')) { + return \idn_to_ascii($domain, $options, \INTL_IDNA_VARIANT_UTS46, $info); + } + + throw new \Error('ext-idn or symfony/polyfill-intl-idn not loaded or too old'); + } +} diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/functions.php b/tests/integration/vendor/guzzlehttp/guzzle/src/functions.php index a3ac450db..a70d2cbf3 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/functions.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/functions.php @@ -1,77 +1,34 @@ expand($template, $variables); -} +namespace GuzzleHttp; /** * Debug function used to describe the provided value type and class. * - * @param mixed $input + * @param mixed $input Any type of variable to describe the type of. This + * parameter misses a typehint because of that. * * @return string Returns a string containing the type of the variable and * if a class is provided, the class name. + * + * @deprecated describe_type will be removed in guzzlehttp/guzzle:8.0. Use Utils::describeType instead. */ -function describe_type($input) +function describe_type($input): string { - switch (gettype($input)) { - case 'object': - return 'object(' . get_class($input) . ')'; - case 'array': - return 'array(' . count($input) . ')'; - default: - ob_start(); - var_dump($input); - // normalize float vs double - return str_replace('double(', 'float(', rtrim(ob_get_clean())); - } + return Utils::describeType($input); } /** * Parses an array of header lines into an associative array of headers. * - * @param array $lines Header lines array of strings in the following - * format: "Name: Value" - * @return array + * @param iterable $lines Header lines array of strings in the following + * format: "Name: Value" + * + * @deprecated headers_from_lines will be removed in guzzlehttp/guzzle:8.0. Use Utils::headersFromLines instead. */ -function headers_from_lines($lines) +function headers_from_lines(iterable $lines): array { - $headers = []; - - foreach ($lines as $line) { - $parts = explode(':', $line, 2); - $headers[trim($parts[0])][] = isset($parts[1]) - ? trim($parts[1]) - : null; - } - - return $headers; + return Utils::headersFromLines($lines); } /** @@ -80,16 +37,12 @@ function headers_from_lines($lines) * @param mixed $value Optional value * * @return resource + * + * @deprecated debug_resource will be removed in guzzlehttp/guzzle:8.0. Use Utils::debugResource instead. */ function debug_resource($value = null) { - if (is_resource($value)) { - return $value; - } elseif (defined('STDOUT')) { - return STDOUT; - } - - return fopen('php://output', 'w'); + return Utils::debugResource($value); } /** @@ -98,49 +51,24 @@ function debug_resource($value = null) * The returned handler is not wrapped by any default middlewares. * * @throws \RuntimeException if no viable Handler is available. - * @return callable Returns the best handler for the given system. + * + * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system. + * + * @deprecated choose_handler will be removed in guzzlehttp/guzzle:8.0. Use Utils::chooseHandler instead. */ -function choose_handler() +function choose_handler(): callable { - $handler = null; - if (function_exists('curl_multi_exec') && function_exists('curl_exec')) { - $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); - } elseif (function_exists('curl_exec')) { - $handler = new CurlHandler(); - } elseif (function_exists('curl_multi_exec')) { - $handler = new CurlMultiHandler(); - } - - if (ini_get('allow_url_fopen')) { - $handler = $handler - ? Proxy::wrapStreaming($handler, new StreamHandler()) - : new StreamHandler(); - } elseif (!$handler) { - throw new \RuntimeException('GuzzleHttp requires cURL, the ' - . 'allow_url_fopen ini setting, or a custom HTTP handler.'); - } - - return $handler; + return Utils::chooseHandler(); } /** - * Get the default User-Agent string to use with Guzzle + * Get the default User-Agent string to use with Guzzle. * - * @return string + * @deprecated default_user_agent will be removed in guzzlehttp/guzzle:8.0. Use Utils::defaultUserAgent instead. */ -function default_user_agent() +function default_user_agent(): string { - static $defaultAgent = ''; - - if (!$defaultAgent) { - $defaultAgent = 'GuzzleHttp/' . Client::VERSION; - if (extension_loaded('curl') && function_exists('curl_version')) { - $defaultAgent .= ' curl/' . \curl_version()['version']; - } - $defaultAgent .= ' PHP/' . PHP_VERSION; - } - - return $defaultAgent; + return Utils::defaultUserAgent(); } /** @@ -154,81 +82,24 @@ function default_user_agent() * * Note: the result of this function is cached for subsequent calls. * - * @return string * @throws \RuntimeException if no bundle can be found. + * + * @deprecated default_ca_bundle will be removed in guzzlehttp/guzzle:8.0. This function is not needed in PHP 5.6+. */ -function default_ca_bundle() +function default_ca_bundle(): string { - static $cached = null; - static $cafiles = [ - // Red Hat, CentOS, Fedora (provided by the ca-certificates package) - '/etc/pki/tls/certs/ca-bundle.crt', - // Ubuntu, Debian (provided by the ca-certificates package) - '/etc/ssl/certs/ca-certificates.crt', - // FreeBSD (provided by the ca_root_nss package) - '/usr/local/share/certs/ca-root-nss.crt', - // SLES 12 (provided by the ca-certificates package) - '/var/lib/ca-certificates/ca-bundle.pem', - // OS X provided by homebrew (using the default path) - '/usr/local/etc/openssl/cert.pem', - // Google app engine - '/etc/ca-certificates.crt', - // Windows? - 'C:\\windows\\system32\\curl-ca-bundle.crt', - 'C:\\windows\\curl-ca-bundle.crt', - ]; - - if ($cached) { - return $cached; - } - - if ($ca = ini_get('openssl.cafile')) { - return $cached = $ca; - } - - if ($ca = ini_get('curl.cainfo')) { - return $cached = $ca; - } - - foreach ($cafiles as $filename) { - if (file_exists($filename)) { - return $cached = $filename; - } - } - - throw new \RuntimeException(<<< EOT -No system CA bundle could be found in any of the the common system locations. -PHP versions earlier than 5.6 are not properly configured to use the system's -CA bundle by default. In order to verify peer certificates, you will need to -supply the path on disk to a certificate bundle to the 'verify' request -option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not -need a specific certificate bundle, then Mozilla provides a commonly used CA -bundle which can be downloaded here (provided by the maintainer of cURL): -https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once -you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP -ini setting to point to the path to the file, allowing you to omit the 'verify' -request option. See http://curl.haxx.se/docs/sslcerts.html for more -information. -EOT - ); + return Utils::defaultCaBundle(); } /** * Creates an associative array of lowercase header names to the actual * header casing. * - * @param array $headers - * - * @return array + * @deprecated normalize_header_keys will be removed in guzzlehttp/guzzle:8.0. Use Utils::normalizeHeaderKeys instead. */ -function normalize_header_keys(array $headers) +function normalize_header_keys(array $headers): array { - $result = []; - foreach (array_keys($headers) as $key) { - $result[strtolower($key)] = $key; - } - - return $result; + return Utils::normalizeHeaderKeys($headers); } /** @@ -245,89 +116,52 @@ function normalize_header_keys(array $headers) * 3. The area starts with "." and the area is the last part of the host. e.g. * '.mit.edu' will match any host that ends with '.mit.edu'. * - * @param string $host Host to check against the patterns. - * @param array $noProxyArray An array of host patterns. + * @param string $host Host to check against the patterns. + * @param string[] $noProxyArray An array of host patterns. + * + * @throws Exception\InvalidArgumentException * - * @return bool + * @deprecated is_host_in_noproxy will be removed in guzzlehttp/guzzle:8.0. Use Utils::isHostInNoProxy instead. */ -function is_host_in_noproxy($host, array $noProxyArray) +function is_host_in_noproxy(string $host, array $noProxyArray): bool { - if (strlen($host) === 0) { - throw new \InvalidArgumentException('Empty host provided'); - } - - // Strip port if present. - if (strpos($host, ':')) { - $host = explode($host, ':', 2)[0]; - } - - foreach ($noProxyArray as $area) { - // Always match on wildcards. - if ($area === '*') { - return true; - } elseif (empty($area)) { - // Don't match on empty values. - continue; - } elseif ($area === $host) { - // Exact matches. - return true; - } else { - // Special match if the area when prefixed with ".". Remove any - // existing leading "." and add a new leading ".". - $area = '.' . ltrim($area, '.'); - if (substr($host, -(strlen($area))) === $area) { - return true; - } - } - } - - return false; + return Utils::isHostInNoProxy($host, $noProxyArray); } /** * Wrapper for json_decode that throws when an error occurs. * * @param string $json JSON data to parse - * @param bool $assoc When true, returned objects will be converted + * @param bool $assoc When true, returned objects will be converted * into associative arrays. * @param int $depth User specified recursion depth. * @param int $options Bitmask of JSON decode options. * - * @return mixed - * @throws \InvalidArgumentException if the JSON cannot be decoded. - * @link http://www.php.net/manual/en/function.json-decode.php + * @return object|array|string|int|float|bool|null + * + * @throws Exception\InvalidArgumentException if the JSON cannot be decoded. + * + * @link https://www.php.net/manual/en/function.json-decode.php + * @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonDecode instead. */ -function json_decode($json, $assoc = false, $depth = 512, $options = 0) +function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) { - $data = \json_decode($json, $assoc, $depth, $options); - if (JSON_ERROR_NONE !== json_last_error()) { - throw new \InvalidArgumentException( - 'json_decode error: ' . json_last_error_msg() - ); - } - - return $data; + return Utils::jsonDecode($json, $assoc, $depth, $options); } /** * Wrapper for JSON encoding that throws when an error occurs. * * @param mixed $value The value being encoded - * @param int $options JSON encode option bitmask - * @param int $depth Set the maximum depth. Must be greater than zero. + * @param int $options JSON encode option bitmask + * @param int $depth Set the maximum depth. Must be greater than zero. * - * @return string - * @throws \InvalidArgumentException if the JSON cannot be encoded. - * @link http://www.php.net/manual/en/function.json-encode.php + * @throws Exception\InvalidArgumentException if the JSON cannot be encoded. + * + * @link https://www.php.net/manual/en/function.json-encode.php + * @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonEncode instead. */ -function json_encode($value, $options = 0, $depth = 512) +function json_encode($value, int $options = 0, int $depth = 512): string { - $json = \json_encode($value, $options, $depth); - if (JSON_ERROR_NONE !== json_last_error()) { - throw new \InvalidArgumentException( - 'json_encode error: ' . json_last_error_msg() - ); - } - - return $json; + return Utils::jsonEncode($value, $options, $depth); } diff --git a/tests/integration/vendor/guzzlehttp/guzzle/src/functions_include.php b/tests/integration/vendor/guzzlehttp/guzzle/src/functions_include.php index a93393acc..6636a4224 100644 --- a/tests/integration/vendor/guzzlehttp/guzzle/src/functions_include.php +++ b/tests/integration/vendor/guzzlehttp/guzzle/src/functions_include.php @@ -1,6 +1,6 @@ +The MIT License (MIT) + +Copyright (c) 2015 Michael Dowling +Copyright (c) 2015 Graham Campbell +Copyright (c) 2017 Tobias Schultze +Copyright (c) 2020 Tobias Nyholm Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/guzzlehttp/promises/README.md b/tests/integration/vendor/guzzlehttp/promises/README.md index 7b607e28b..c175fec76 100644 --- a/tests/integration/vendor/guzzlehttp/promises/README.md +++ b/tests/integration/vendor/guzzlehttp/promises/README.md @@ -26,7 +26,7 @@ for a general introduction to promises. - Promises can be cancelled. - Works with any object that has a `then` function. - C# style async/await coroutine promises using - `GuzzleHttp\Promise\coroutine()`. + `GuzzleHttp\Promise\Coroutine::of()`. # Quick start @@ -88,7 +88,7 @@ $promise }); // Resolving the promise triggers the $onFulfilled callbacks and outputs -// "Hello, reader". +// "Hello, reader." $promise->resolve('reader.'); ``` @@ -150,7 +150,7 @@ use GuzzleHttp\Promise\Promise; $promise = new Promise(); $promise->then(null, function ($reason) { - throw new \Exception($reason); + throw new Exception($reason); })->then(null, function ($reason) { assert($reason->getMessage() === 'Error!'); }); @@ -182,7 +182,6 @@ invoked using the value returned from the `$onRejected` callback. ```php use GuzzleHttp\Promise\Promise; -use GuzzleHttp\Promise\RejectedPromise; $promise = new Promise(); $promise @@ -220,7 +219,7 @@ the promise is rejected with the exception and the exception is thrown. ```php $promise = new Promise(function () use (&$promise) { - throw new \Exception('foo'); + throw new Exception('foo'); }); $promise->wait(); // throws the exception. @@ -397,7 +396,7 @@ $deferred = new React\Promise\Deferred(); $reactPromise = $deferred->promise(); // Create a Guzzle promise that is fulfilled with a React promise. -$guzzlePromise = new \GuzzleHttp\Promise\Promise(); +$guzzlePromise = new GuzzleHttp\Promise\Promise(); $guzzlePromise->then(function ($value) use ($reactPromise) { // Do something something with the value... // Return the React promise @@ -424,7 +423,7 @@ instance. ```php // Get the global task queue -$queue = \GuzzleHttp\Promise\queue(); +$queue = GuzzleHttp\Promise\Utils::queue(); $queue->run(); ``` @@ -502,3 +501,47 @@ $promise->then(function ($value) { echo $value; }); $promise->resolve('foo'); // prints "foo" ``` + + +## Upgrading from Function API + +A static API was first introduced in 1.4.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience: + +| Original Function | Replacement Method | +|----------------|----------------| +| `queue` | `Utils::queue` | +| `task` | `Utils::task` | +| `promise_for` | `Create::promiseFor` | +| `rejection_for` | `Create::rejectionFor` | +| `exception_for` | `Create::exceptionFor` | +| `iter_for` | `Create::iterFor` | +| `inspect` | `Utils::inspect` | +| `inspect_all` | `Utils::inspectAll` | +| `unwrap` | `Utils::unwrap` | +| `all` | `Utils::all` | +| `some` | `Utils::some` | +| `any` | `Utils::any` | +| `settle` | `Utils::settle` | +| `each` | `Each::of` | +| `each_limit` | `Each::ofLimit` | +| `each_limit_all` | `Each::ofLimitAll` | +| `!is_fulfilled` | `Is::pending` | +| `is_fulfilled` | `Is::fulfilled` | +| `is_rejected` | `Is::rejected` | +| `is_settled` | `Is::settled` | +| `coroutine` | `Coroutine::of` | + + +## Security + +If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information. + +## License + +Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information. + +## For Enterprise + +Available as part of the Tidelift Subscription + +The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/tests/integration/vendor/guzzlehttp/promises/composer.json b/tests/integration/vendor/guzzlehttp/promises/composer.json index ec41a61e6..c959fb32b 100644 --- a/tests/integration/vendor/guzzlehttp/promises/composer.json +++ b/tests/integration/vendor/guzzlehttp/promises/composer.json @@ -4,17 +4,32 @@ "keywords": ["promise"], "license": "MIT", "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "require": { - "php": ">=5.5.0" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^4.0" + "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "autoload": { "psr-4": { @@ -22,13 +37,22 @@ }, "files": ["src/functions_include.php"] }, + "autoload-dev": { + "psr-4": { + "GuzzleHttp\\Promise\\Tests\\": "tests/" + } + }, "scripts": { - "test": "vendor/bin/phpunit", - "test-ci": "vendor/bin/phpunit --coverage-text" + "test": "vendor/bin/simple-phpunit", + "test-ci": "vendor/bin/simple-phpunit --coverage-text" }, "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } + }, + "config": { + "preferred-install": "dist", + "sort-packages": true } } diff --git a/tests/integration/vendor/guzzlehttp/promises/src/AggregateException.php b/tests/integration/vendor/guzzlehttp/promises/src/AggregateException.php index 6a5690c37..d2b5712b9 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/AggregateException.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/AggregateException.php @@ -1,4 +1,5 @@ currentPromise->wait(); } }); - $this->nextCoroutine($this->generator->current()); + try { + $this->nextCoroutine($this->generator->current()); + } catch (\Exception $exception) { + $this->result->reject($exception); + } catch (Throwable $throwable) { + $this->result->reject($throwable); + } + } + + /** + * Create a new coroutine. + * + * @return self + */ + public static function of(callable $generatorFn) + { + return new self($generatorFn); } public function then( @@ -108,7 +126,7 @@ public function cancel() private function nextCoroutine($yielded) { - $this->currentPromise = promise_for($yielded) + $this->currentPromise = Create::promiseFor($yielded) ->then([$this, '_handleSuccess'], [$this, '_handleFailure']); } @@ -139,7 +157,7 @@ public function _handleFailure($reason) { unset($this->currentPromise); try { - $nextYield = $this->generator->throw(exception_for($reason)); + $nextYield = $this->generator->throw(Create::exceptionFor($reason)); // The throw was caught, so keep iterating on the coroutine $this->nextCoroutine($nextYield); } catch (Exception $exception) { diff --git a/tests/integration/vendor/guzzlehttp/promises/src/Create.php b/tests/integration/vendor/guzzlehttp/promises/src/Create.php new file mode 100644 index 000000000..8d038e9c1 --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/promises/src/Create.php @@ -0,0 +1,84 @@ +then([$promise, 'resolve'], [$promise, 'reject']); + return $promise; + } + + return new FulfilledPromise($value); + } + + /** + * Creates a rejected promise for a reason if the reason is not a promise. + * If the provided reason is a promise, then it is returned as-is. + * + * @param mixed $reason Promise or reason. + * + * @return PromiseInterface + */ + public static function rejectionFor($reason) + { + if ($reason instanceof PromiseInterface) { + return $reason; + } + + return new RejectedPromise($reason); + } + + /** + * Create an exception for a rejected promise value. + * + * @param mixed $reason + * + * @return \Exception|\Throwable + */ + public static function exceptionFor($reason) + { + if ($reason instanceof \Exception || $reason instanceof \Throwable) { + return $reason; + } + + return new RejectionException($reason); + } + + /** + * Returns an iterator for the given value. + * + * @param mixed $value + * + * @return \Iterator + */ + public static function iterFor($value) + { + if ($value instanceof \Iterator) { + return $value; + } + + if (is_array($value)) { + return new \ArrayIterator($value); + } + + return new \ArrayIterator([$value]); + } +} diff --git a/tests/integration/vendor/guzzlehttp/promises/src/Each.php b/tests/integration/vendor/guzzlehttp/promises/src/Each.php new file mode 100644 index 000000000..1dda35499 --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/promises/src/Each.php @@ -0,0 +1,90 @@ + $onFulfilled, + 'rejected' => $onRejected + ]))->promise(); + } + + /** + * Like of, but only allows a certain number of outstanding promises at any + * given time. + * + * $concurrency may be an integer or a function that accepts the number of + * pending promises and returns a numeric concurrency limit value to allow + * for dynamic a concurrency size. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * @param callable $onRejected + * + * @return PromiseInterface + */ + public static function ofLimit( + $iterable, + $concurrency, + callable $onFulfilled = null, + callable $onRejected = null + ) { + return (new EachPromise($iterable, [ + 'fulfilled' => $onFulfilled, + 'rejected' => $onRejected, + 'concurrency' => $concurrency + ]))->promise(); + } + + /** + * Like limit, but ensures that no promise in the given $iterable argument + * is rejected. If any promise is rejected, then the aggregate promise is + * rejected with the encountered rejection. + * + * @param mixed $iterable + * @param int|callable $concurrency + * @param callable $onFulfilled + * + * @return PromiseInterface + */ + public static function ofLimitAll( + $iterable, + $concurrency, + callable $onFulfilled = null + ) { + return each_limit( + $iterable, + $concurrency, + $onFulfilled, + function ($reason, $idx, PromiseInterface $aggregate) { + $aggregate->reject($reason); + } + ); + } +} diff --git a/tests/integration/vendor/guzzlehttp/promises/src/EachPromise.php b/tests/integration/vendor/guzzlehttp/promises/src/EachPromise.php index d0ddf603f..38ecb59b1 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/EachPromise.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/EachPromise.php @@ -1,4 +1,5 @@ iterable = iter_for($iterable); + $this->iterable = Create::iterFor($iterable); if (isset($config['concurrency'])) { $this->concurrency = $config['concurrency']; @@ -65,6 +68,7 @@ public function __construct($iterable, array $config = []) } } + /** @psalm-suppress InvalidNullableReturnType */ public function promise() { if ($this->aggregate) { @@ -73,14 +77,27 @@ public function promise() try { $this->createPromise(); + /** @psalm-assert Promise $this->aggregate */ $this->iterable->rewind(); $this->refillPending(); } catch (\Throwable $e) { + /** + * @psalm-suppress NullReference + * @phpstan-ignore-next-line + */ $this->aggregate->reject($e); } catch (\Exception $e) { + /** + * @psalm-suppress NullReference + * @phpstan-ignore-next-line + */ $this->aggregate->reject($e); } + /** + * @psalm-suppress NullableReturnStatement + * @phpstan-ignore-next-line + */ return $this->aggregate; } @@ -88,18 +105,16 @@ private function createPromise() { $this->mutex = false; $this->aggregate = new Promise(function () { - reset($this->pending); - if (empty($this->pending) && !$this->iterable->valid()) { - $this->aggregate->resolve(null); + if ($this->checkIfFinished()) { return; } - + reset($this->pending); // Consume a potentially fluctuating list of promises while // ensuring that indexes are maintained (precluding array_shift). while ($promise = current($this->pending)) { next($this->pending); $promise->wait(); - if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + if (Is::settled($this->aggregate)) { return; } } @@ -109,6 +124,7 @@ private function createPromise() $clearFn = function () { $this->iterable = $this->concurrency = $this->pending = null; $this->onFulfilled = $this->onRejected = null; + $this->nextPendingIndex = 0; }; $this->aggregate->then($clearFn, $clearFn); @@ -148,22 +164,32 @@ private function addPending() return false; } - $promise = promise_for($this->iterable->current()); - $idx = $this->iterable->key(); + $promise = Create::promiseFor($this->iterable->current()); + $key = $this->iterable->key(); + + // Iterable keys may not be unique, so we use a counter to + // guarantee uniqueness + $idx = $this->nextPendingIndex++; $this->pending[$idx] = $promise->then( - function ($value) use ($idx) { + function ($value) use ($idx, $key) { if ($this->onFulfilled) { call_user_func( - $this->onFulfilled, $value, $idx, $this->aggregate + $this->onFulfilled, + $value, + $key, + $this->aggregate ); } $this->step($idx); }, - function ($reason) use ($idx) { + function ($reason) use ($idx, $key) { if ($this->onRejected) { call_user_func( - $this->onRejected, $reason, $idx, $this->aggregate + $this->onRejected, + $reason, + $key, + $this->aggregate ); } $this->step($idx); @@ -201,7 +227,7 @@ private function advanceIterator() private function step($idx) { // If the promise was already resolved, then ignore this step. - if ($this->aggregate->getState() !== PromiseInterface::PENDING) { + if (Is::settled($this->aggregate)) { return; } diff --git a/tests/integration/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/tests/integration/vendor/guzzlehttp/promises/src/FulfilledPromise.php index dbbeeb9f7..98f72a62a 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/FulfilledPromise.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/FulfilledPromise.php @@ -1,4 +1,5 @@ value = $value; @@ -30,11 +32,11 @@ public function then( return $this; } - $queue = queue(); + $queue = Utils::queue(); $p = new Promise([$queue, 'run']); $value = $this->value; $queue->add(static function () use ($p, $value, $onFulfilled) { - if ($p->getState() === self::PENDING) { + if (Is::pending($p)) { try { $p->resolve($onFulfilled($value)); } catch (\Throwable $e) { diff --git a/tests/integration/vendor/guzzlehttp/promises/src/Is.php b/tests/integration/vendor/guzzlehttp/promises/src/Is.php new file mode 100644 index 000000000..c3ed8d014 --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/promises/src/Is.php @@ -0,0 +1,46 @@ +getState() === PromiseInterface::PENDING; + } + + /** + * Returns true if a promise is fulfilled or rejected. + * + * @return bool + */ + public static function settled(PromiseInterface $promise) + { + return $promise->getState() !== PromiseInterface::PENDING; + } + + /** + * Returns true if a promise is fulfilled. + * + * @return bool + */ + public static function fulfilled(PromiseInterface $promise) + { + return $promise->getState() === PromiseInterface::FULFILLED; + } + + /** + * Returns true if a promise is rejected. + * + * @return bool + */ + public static function rejected(PromiseInterface $promise) + { + return $promise->getState() === PromiseInterface::REJECTED; + } +} diff --git a/tests/integration/vendor/guzzlehttp/promises/src/Promise.php b/tests/integration/vendor/guzzlehttp/promises/src/Promise.php index 844ada073..75939057b 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/Promise.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/Promise.php @@ -1,4 +1,5 @@ state === self::FULFILLED) { - return $onFulfilled - ? promise_for($this->result)->then($onFulfilled) - : promise_for($this->result); + $promise = Create::promiseFor($this->result); + return $onFulfilled ? $promise->then($onFulfilled) : $promise; } // It's either cancelled or rejected, so return a rejected promise // and immediately invoke any callbacks. - $rejection = rejection_for($this->result); + $rejection = Create::rejectionFor($this->result); return $onRejected ? $rejection->then(null, $onRejected) : $rejection; } @@ -61,19 +61,15 @@ public function wait($unwrap = true) { $this->waitIfPending(); - $inner = $this->result instanceof PromiseInterface - ? $this->result->wait($unwrap) - : $this->result; - + if ($this->result instanceof PromiseInterface) { + return $this->result->wait($unwrap); + } if ($unwrap) { - if ($this->result instanceof PromiseInterface - || $this->state === self::FULFILLED - ) { - return $inner; - } else { - // It's rejected so "unwrap" and throw an exception. - throw exception_for($inner); + if ($this->state === self::FULFILLED) { + return $this->result; } + // It's rejected so "unwrap" and throw an exception. + throw Create::exceptionFor($this->result); } } @@ -103,6 +99,7 @@ public function cancel() } // Reject the promise only if it wasn't rejected in a then callback. + /** @psalm-suppress RedundantCondition */ if ($this->state === self::PENDING) { $this->reject(new CancellationException('Promise has been cancelled')); } @@ -148,17 +145,15 @@ private function settle($state, $value) // If the value was not a settled promise or a thenable, then resolve // it in the task queue using the correct ID. - if (!method_exists($value, 'then')) { + if (!is_object($value) || !method_exists($value, 'then')) { $id = $state === self::FULFILLED ? 1 : 2; // It's a success, so resolve the handlers in the queue. - queue()->add(static function () use ($id, $value, $handlers) { + Utils::queue()->add(static function () use ($id, $value, $handlers) { foreach ($handlers as $handler) { self::callHandler($id, $value, $handler); } }); - } elseif ($value instanceof Promise - && $value->getState() === self::PENDING - ) { + } elseif ($value instanceof Promise && Is::pending($value)) { // We can just merge our handlers onto the next promise. $value->handlers = array_merge($value->handlers, $handlers); } else { @@ -184,8 +179,6 @@ static function ($reason) use ($handlers) { * @param int $index 1 (resolve) or 2 (reject). * @param mixed $value Value to pass to the callback. * @param array $handler Array of handler data (promise and callbacks). - * - * @return array Returns the next group to resolve. */ private static function callHandler($index, $value, array $handler) { @@ -194,13 +187,21 @@ private static function callHandler($index, $value, array $handler) // The promise may have been cancelled or resolved before placing // this thunk in the queue. - if ($promise->getState() !== self::PENDING) { + if (Is::settled($promise)) { return; } try { if (isset($handler[$index])) { - $promise->resolve($handler[$index]($value)); + /* + * If $f throws an exception, then $handler will be in the exception + * stack trace. Since $handler contains a reference to the callable + * itself we get a circular reference. We clear the $handler + * here to avoid that memory leak. + */ + $f = $handler[$index]; + unset($handler); + $promise->resolve($f($value)); } elseif ($index === 1) { // Forward resolution values as-is. $promise->resolve($value); @@ -224,15 +225,16 @@ private function waitIfPending() } elseif ($this->waitList) { $this->invokeWaitList(); } else { - // If there's not wait function, then reject the promise. + // If there's no wait function, then reject the promise. $this->reject('Cannot wait on a promise that has ' . 'no internal wait function. You must provide a wait ' . 'function when constructing the promise to be able to ' . 'wait on a promise.'); } - queue()->run(); + Utils::queue()->run(); + /** @psalm-suppress RedundantCondition */ if ($this->state === self::PENDING) { $this->reject('Invoking the wait callback did not resolve the promise'); } @@ -263,17 +265,13 @@ private function invokeWaitList() $this->waitList = null; foreach ($waitList as $result) { - while (true) { + do { $result->waitIfPending(); + $result = $result->result; + } while ($result instanceof Promise); - if ($result->result instanceof Promise) { - $result = $result->result; - } else { - if ($result->result instanceof PromiseInterface) { - $result->result->wait(false); - } - break; - } + if ($result instanceof PromiseInterface) { + $result->wait(false); } } } diff --git a/tests/integration/vendor/guzzlehttp/promises/src/PromiseInterface.php b/tests/integration/vendor/guzzlehttp/promises/src/PromiseInterface.php index 8f5f4b99b..e59833143 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/PromiseInterface.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/PromiseInterface.php @@ -1,4 +1,5 @@ reason = $reason; @@ -30,11 +32,11 @@ public function then( return $this; } - $queue = queue(); + $queue = Utils::queue(); $reason = $this->reason; $p = new Promise([$queue, 'run']); $queue->add(static function () use ($p, $reason, $onRejected) { - if ($p->getState() === self::PENDING) { + if (Is::pending($p)) { try { // Return a resolved promise if onRejected does not throw. $p->resolve($onRejected($reason)); @@ -59,8 +61,10 @@ public function otherwise(callable $onRejected) public function wait($unwrap = true, $defaultDelivery = null) { if ($unwrap) { - throw exception_for($this->reason); + throw Create::exceptionFor($this->reason); } + + return null; } public function getState() diff --git a/tests/integration/vendor/guzzlehttp/promises/src/RejectionException.php b/tests/integration/vendor/guzzlehttp/promises/src/RejectionException.php index 07c1136da..e2f137707 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/RejectionException.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/RejectionException.php @@ -1,4 +1,5 @@ run(); + * GuzzleHttp\Promise\Utils::queue()->run(); */ class TaskQueue implements TaskQueueInterface { @@ -42,8 +43,8 @@ public function add(callable $task) public function run() { - /** @var callable $task */ while ($task = array_shift($this->queue)) { + /** @var callable $task */ $task(); } } diff --git a/tests/integration/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/tests/integration/vendor/guzzlehttp/promises/src/TaskQueueInterface.php index ac8306e19..723d4d54e 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/TaskQueueInterface.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/TaskQueueInterface.php @@ -1,4 +1,5 @@ + * while ($eventLoop->isRunning()) { + * GuzzleHttp\Promise\Utils::queue()->run(); + * } + * + * + * @param TaskQueueInterface $assign Optionally specify a new queue instance. + * + * @return TaskQueueInterface + */ + public static function queue(TaskQueueInterface $assign = null) + { + static $queue; + + if ($assign) { + $queue = $assign; + } elseif (!$queue) { + $queue = new TaskQueue(); + } + + return $queue; + } + + /** + * Adds a function to run in the task queue when it is next `run()` and + * returns a promise that is fulfilled or rejected with the result. + * + * @param callable $task Task function to run. + * + * @return PromiseInterface + */ + public static function task(callable $task) + { + $queue = self::queue(); + $promise = new Promise([$queue, 'run']); + $queue->add(function () use ($task, $promise) { + try { + if (Is::pending($promise)) { + $promise->resolve($task()); + } + } catch (\Throwable $e) { + $promise->reject($e); + } catch (\Exception $e) { + $promise->reject($e); + } + }); + + return $promise; + } + + /** + * Synchronously waits on a promise to resolve and returns an inspection + * state array. + * + * Returns a state associative array containing a "state" key mapping to a + * valid promise state. If the state of the promise is "fulfilled", the + * array will contain a "value" key mapping to the fulfilled value of the + * promise. If the promise is rejected, the array will contain a "reason" + * key mapping to the rejection reason of the promise. + * + * @param PromiseInterface $promise Promise or value. + * + * @return array + */ + public static function inspect(PromiseInterface $promise) + { + try { + return [ + 'state' => PromiseInterface::FULFILLED, + 'value' => $promise->wait() + ]; + } catch (RejectionException $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()]; + } catch (\Throwable $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; + } catch (\Exception $e) { + return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; + } + } + + /** + * Waits on all of the provided promises, but does not unwrap rejected + * promises as thrown exception. + * + * Returns an array of inspection state arrays. + * + * @see inspect for the inspection state array format. + * + * @param PromiseInterface[] $promises Traversable of promises to wait upon. + * + * @return array + */ + public static function inspectAll($promises) + { + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = inspect($promise); + } + + return $results; + } + + /** + * Waits on all of the provided promises and returns the fulfilled values. + * + * Returns an array that contains the value of each promise (in the same + * order the promises were provided). An exception is thrown if any of the + * promises are rejected. + * + * @param iterable $promises Iterable of PromiseInterface objects to wait on. + * + * @return array + * + * @throws \Exception on error + * @throws \Throwable on error in PHP >=7 + */ + public static function unwrap($promises) + { + $results = []; + foreach ($promises as $key => $promise) { + $results[$key] = $promise->wait(); + } + + return $results; + } + + /** + * Given an array of promises, return a promise that is fulfilled when all + * the items in the array are fulfilled. + * + * The promise's fulfillment value is an array with fulfillment values at + * respective positions to the original array. If any promise in the array + * rejects, the returned promise is rejected with the rejection reason. + * + * @param mixed $promises Promises or values. + * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution. + * + * @return PromiseInterface + */ + public static function all($promises, $recursive = false) + { + $results = []; + $promise = Each::of( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = $value; + }, + function ($reason, $idx, Promise $aggregate) { + $aggregate->reject($reason); + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); + + if (true === $recursive) { + $promise = $promise->then(function ($results) use ($recursive, &$promises) { + foreach ($promises as $promise) { + if (Is::pending($promise)) { + return self::all($promises, $recursive); + } + } + return $results; + }); + } + + return $promise; + } + + /** + * Initiate a competitive race between multiple promises or values (values + * will become immediately fulfilled promises). + * + * When count amount of promises have been fulfilled, the returned promise + * is fulfilled with an array that contains the fulfillment values of the + * winners in order of resolution. + * + * This promise is rejected with a {@see AggregateException} if the number + * of fulfilled promises is less than the desired $count. + * + * @param int $count Total number of promises. + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ + public static function some($count, $promises) + { + $results = []; + $rejections = []; + + return Each::of( + $promises, + function ($value, $idx, PromiseInterface $p) use (&$results, $count) { + if (Is::settled($p)) { + return; + } + $results[$idx] = $value; + if (count($results) >= $count) { + $p->resolve(null); + } + }, + function ($reason) use (&$rejections) { + $rejections[] = $reason; + } + )->then( + function () use (&$results, &$rejections, $count) { + if (count($results) !== $count) { + throw new AggregateException( + 'Not enough promises to fulfill count', + $rejections + ); + } + ksort($results); + return array_values($results); + } + ); + } + + /** + * Like some(), with 1 as count. However, if the promise fulfills, the + * fulfillment value is not an array of 1 but the value directly. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ + public static function any($promises) + { + return self::some(1, $promises)->then(function ($values) { + return $values[0]; + }); + } + + /** + * Returns a promise that is fulfilled when all of the provided promises have + * been fulfilled or rejected. + * + * The returned promise is fulfilled with an array of inspection state arrays. + * + * @see inspect for the inspection state array format. + * + * @param mixed $promises Promises or values. + * + * @return PromiseInterface + */ + public static function settle($promises) + { + $results = []; + + return Each::of( + $promises, + function ($value, $idx) use (&$results) { + $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; + }, + function ($reason, $idx) use (&$results) { + $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; + } + )->then(function () use (&$results) { + ksort($results); + return $results; + }); + } +} diff --git a/tests/integration/vendor/guzzlehttp/promises/src/functions.php b/tests/integration/vendor/guzzlehttp/promises/src/functions.php index 4e27709af..c03d39d02 100644 --- a/tests/integration/vendor/guzzlehttp/promises/src/functions.php +++ b/tests/integration/vendor/guzzlehttp/promises/src/functions.php @@ -1,4 +1,5 @@ add(function () use ($task, $promise) { - try { - $promise->resolve($task()); - } catch (\Throwable $e) { - $promise->reject($e); - } catch (\Exception $e) { - $promise->reject($e); - } - }); - - return $promise; + return Utils::task($task); } /** @@ -62,23 +47,12 @@ function task(callable $task) * @param mixed $value Promise or value. * * @return PromiseInterface + * + * @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead. */ function promise_for($value) { - if ($value instanceof PromiseInterface) { - return $value; - } - - // Return a Guzzle promise that shadows the given promise. - if (method_exists($value, 'then')) { - $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null; - $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null; - $promise = new Promise($wfn, $cfn); - $value->then([$promise, 'resolve'], [$promise, 'reject']); - return $promise; - } - - return new FulfilledPromise($value); + return Create::promiseFor($value); } /** @@ -88,14 +62,12 @@ function promise_for($value) * @param mixed $reason Promise or reason. * * @return PromiseInterface + * + * @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead. */ function rejection_for($reason) { - if ($reason instanceof PromiseInterface) { - return $reason; - } - - return new RejectedPromise($reason); + return Create::rejectionFor($reason); } /** @@ -104,12 +76,12 @@ function rejection_for($reason) * @param mixed $reason * * @return \Exception|\Throwable + * + * @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead. */ function exception_for($reason) { - return $reason instanceof \Exception || $reason instanceof \Throwable - ? $reason - : new RejectionException($reason); + return Create::exceptionFor($reason); } /** @@ -118,16 +90,12 @@ function exception_for($reason) * @param mixed $value * * @return \Iterator + * + * @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead. */ function iter_for($value) { - if ($value instanceof \Iterator) { - return $value; - } elseif (is_array($value)) { - return new \ArrayIterator($value); - } else { - return new \ArrayIterator([$value]); - } + return Create::iterFor($value); } /** @@ -143,21 +111,12 @@ function iter_for($value) * @param PromiseInterface $promise Promise or value. * * @return array + * + * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead. */ function inspect(PromiseInterface $promise) { - try { - return [ - 'state' => PromiseInterface::FULFILLED, - 'value' => $promise->wait() - ]; - } catch (RejectionException $e) { - return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()]; - } catch (\Throwable $e) { - return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; - } catch (\Exception $e) { - return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; - } + return Utils::inspect($promise); } /** @@ -166,19 +125,17 @@ function inspect(PromiseInterface $promise) * * Returns an array of inspection state arrays. * + * @see inspect for the inspection state array format. + * * @param PromiseInterface[] $promises Traversable of promises to wait upon. * * @return array - * @see GuzzleHttp\Promise\inspect for the inspection state array format. + * + * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead. */ function inspect_all($promises) { - $results = []; - foreach ($promises as $key => $promise) { - $results[$key] = inspect($promise); - } - - return $results; + return Utils::inspectAll($promises); } /** @@ -188,20 +145,18 @@ function inspect_all($promises) * the promises were provided). An exception is thrown if any of the promises * are rejected. * - * @param mixed $promises Iterable of PromiseInterface objects to wait on. + * @param iterable $promises Iterable of PromiseInterface objects to wait on. * * @return array + * * @throws \Exception on error * @throws \Throwable on error in PHP >=7 + * + * @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead. */ function unwrap($promises) { - $results = []; - foreach ($promises as $key => $promise) { - $results[$key] = $promise->wait(); - } - - return $results; + return Utils::unwrap($promises); } /** @@ -212,25 +167,16 @@ function unwrap($promises) * respective positions to the original array. If any promise in the array * rejects, the returned promise is rejected with the rejection reason. * - * @param mixed $promises Promises or values. + * @param mixed $promises Promises or values. + * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution. * * @return PromiseInterface + * + * @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead. */ -function all($promises) +function all($promises, $recursive = false) { - $results = []; - return each( - $promises, - function ($value, $idx) use (&$results) { - $results[$idx] = $value; - }, - function ($reason, $idx, Promise $aggregate) { - $aggregate->reject($reason); - } - )->then(function () use (&$results) { - ksort($results); - return $results; - }); + return Utils::all($promises, $recursive); } /** @@ -241,45 +187,19 @@ function ($reason, $idx, Promise $aggregate) { * fulfilled with an array that contains the fulfillment values of the winners * in order of resolution. * - * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException} - * if the number of fulfilled promises is less than the desired $count. + * This promise is rejected with a {@see AggregateException} if the number of + * fulfilled promises is less than the desired $count. * * @param int $count Total number of promises. * @param mixed $promises Promises or values. * * @return PromiseInterface + * + * @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead. */ function some($count, $promises) { - $results = []; - $rejections = []; - - return each( - $promises, - function ($value, $idx, PromiseInterface $p) use (&$results, $count) { - if ($p->getState() !== PromiseInterface::PENDING) { - return; - } - $results[$idx] = $value; - if (count($results) >= $count) { - $p->resolve(null); - } - }, - function ($reason) use (&$rejections) { - $rejections[] = $reason; - } - )->then( - function () use (&$results, &$rejections, $count) { - if (count($results) !== $count) { - throw new AggregateException( - 'Not enough promises to fulfill count', - $rejections - ); - } - ksort($results); - return array_values($results); - } - ); + return Utils::some($count, $promises); } /** @@ -289,10 +209,12 @@ function () use (&$results, &$rejections, $count) { * @param mixed $promises Promises or values. * * @return PromiseInterface + * + * @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead. */ function any($promises) { - return some(1, $promises)->then(function ($values) { return $values[0]; }); + return Utils::any($promises); } /** @@ -301,27 +223,17 @@ function any($promises) * * The returned promise is fulfilled with an array of inspection state arrays. * + * @see inspect for the inspection state array format. + * * @param mixed $promises Promises or values. * * @return PromiseInterface - * @see GuzzleHttp\Promise\inspect for the inspection state array format. + * + * @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead. */ function settle($promises) { - $results = []; - - return each( - $promises, - function ($value, $idx) use (&$results) { - $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; - }, - function ($reason, $idx) use (&$results) { - $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; - } - )->then(function () use (&$results) { - ksort($results); - return $results; - }); + return Utils::settle($promises); } /** @@ -329,29 +241,28 @@ function ($reason, $idx) use (&$results) { * fulfilled with a null value when the iterator has been consumed or the * aggregate promise has been fulfilled or rejected. * - * $onFulfilled is a function that accepts the fulfilled value, iterator - * index, and the aggregate promise. The callback can invoke any necessary side - * effects and choose to resolve or reject the aggregate promise if needed. + * $onFulfilled is a function that accepts the fulfilled value, iterator index, + * and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate if needed. * - * $onRejected is a function that accepts the rejection reason, iterator - * index, and the aggregate promise. The callback can invoke any necessary side - * effects and choose to resolve or reject the aggregate promise if needed. + * $onRejected is a function that accepts the rejection reason, iterator index, + * and the aggregate promise. The callback can invoke any necessary side + * effects and choose to resolve or reject the aggregate if needed. * * @param mixed $iterable Iterator or array to iterate over. * @param callable $onFulfilled * @param callable $onRejected * * @return PromiseInterface + * + * @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead. */ function each( $iterable, callable $onFulfilled = null, callable $onRejected = null ) { - return (new EachPromise($iterable, [ - 'fulfilled' => $onFulfilled, - 'rejected' => $onRejected - ]))->promise(); + return Each::of($iterable, $onFulfilled, $onRejected); } /** @@ -368,6 +279,8 @@ function each( * @param callable $onRejected * * @return PromiseInterface + * + * @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead. */ function each_limit( $iterable, @@ -375,11 +288,7 @@ function each_limit( callable $onFulfilled = null, callable $onRejected = null ) { - return (new EachPromise($iterable, [ - 'fulfilled' => $onFulfilled, - 'rejected' => $onRejected, - 'concurrency' => $concurrency - ]))->promise(); + return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected); } /** @@ -392,66 +301,63 @@ function each_limit( * @param callable $onFulfilled * * @return PromiseInterface + * + * @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead. */ function each_limit_all( $iterable, $concurrency, callable $onFulfilled = null ) { - return each_limit( - $iterable, - $concurrency, - $onFulfilled, - function ($reason, $idx, PromiseInterface $aggregate) { - $aggregate->reject($reason); - } - ); + return Each::ofLimitAll($iterable, $concurrency, $onFulfilled); } /** * Returns true if a promise is fulfilled. * - * @param PromiseInterface $promise - * * @return bool + * + * @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead. */ function is_fulfilled(PromiseInterface $promise) { - return $promise->getState() === PromiseInterface::FULFILLED; + return Is::fulfilled($promise); } /** * Returns true if a promise is rejected. * - * @param PromiseInterface $promise - * * @return bool + * + * @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead. */ function is_rejected(PromiseInterface $promise) { - return $promise->getState() === PromiseInterface::REJECTED; + return Is::rejected($promise); } /** * Returns true if a promise is fulfilled or rejected. * - * @param PromiseInterface $promise - * * @return bool + * + * @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead. */ function is_settled(PromiseInterface $promise) { - return $promise->getState() !== PromiseInterface::PENDING; + return Is::settled($promise); } /** - * @see Coroutine + * Create a new coroutine. * - * @param callable $generatorFn + * @see Coroutine * * @return PromiseInterface + * + * @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead. */ function coroutine(callable $generatorFn) { - return new Coroutine($generatorFn); + return Coroutine::of($generatorFn); } diff --git a/tests/integration/vendor/guzzlehttp/psr7/.editorconfig b/tests/integration/vendor/guzzlehttp/psr7/.editorconfig deleted file mode 100644 index 677e36e29..000000000 --- a/tests/integration/vendor/guzzlehttp/psr7/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/tests/integration/vendor/guzzlehttp/psr7/CHANGELOG.md b/tests/integration/vendor/guzzlehttp/psr7/CHANGELOG.md index 27b65f095..740b2877b 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/CHANGELOG.md +++ b/tests/integration/vendor/guzzlehttp/psr7/CHANGELOG.md @@ -1,13 +1,114 @@ # Change Log - All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +## 2.1.0 - 2021-10-06 + +### Changed + +- Attempting to create a `Uri` object from a malformed URI will no longer throw a generic + `InvalidArgumentException`, but rather a `MalformedUriException`, which inherits from the former + for backwards compatibility. Callers relying on the exception being thrown to detect invalid + URIs should catch the new exception. + +### Fixed + +- Return `null` in caching stream size if remote size is `null` + +## 2.0.0 - 2021-06-30 + +Identical to the RC release. + +## 2.0.0@RC-1 - 2021-04-29 + +### Fixed + +- Handle possibly unset `url` in `stream_get_meta_data` +## 2.0.0@beta-1 - 2021-03-21 + +### Added + +- PSR-17 factories +- Made classes final +- PHP7 type hints + +### Changed + +- When building a query string, booleans are represented as 1 and 0. + +### Removed + +- PHP < 7.2 support +- All functions in the Guzzle\Psr7 namespace + +## 1.8.1 - 2021-03-21 + +### Fixed + +- Issue parsing IPv6 URLs +- Issue modifying ServerRequest lost all its attributes + +## 1.8.0 - 2021-03-21 + +### Added + +- Locale independent URL parsing +- Most classes got a `@final` annotation to prepare for 2.0 + +### Fixed + +- Issue when creating stream from `php://input` and curl-ext is not installed +- Broken `Utils::tryFopen()` on PHP 8 + +## 1.7.0 - 2020-09-30 + +### Added + +- Replaced functions by static methods + +### Fixed + +- Converting a non-seekable stream to a string +- Handle multiple Set-Cookie correctly +- Ignore array keys in header values when merging +- Allow multibyte characters to be parsed in `Message:bodySummary()` + +### Changed + +- Restored partial HHVM 3 support + + +## [1.6.1] - 2019-07-02 + +### Fixed + +- Accept null and bool header values again + + +## [1.6.0] - 2019-06-30 + +### Added + +- Allowed version `^3.0` of `ralouphie/getallheaders` dependency (#244) +- Added MIME type for WEBP image format (#246) +- Added more validation of values according to PSR-7 and RFC standards, e.g. status code range (#250, #272) + +### Changed + +- Tests don't pass with HHVM 4.0, so HHVM support got dropped. Other libraries like composer have done the same. (#262) +- Accept port number 0 to be valid (#270) + +### Fixed -## [Unreleased] +- Fixed subsequent reads from `php://input` in ServerRequest (#247) +- Fixed readable/writable detection for certain stream modes (#248) +- Fixed encoding of special characters in the `userInfo` component of an URI (#253) ## [1.5.2] - 2018-12-04 @@ -209,7 +310,7 @@ Currently unsupported: -[Unreleased]: https://github.com/guzzle/psr7/compare/1.5.2...HEAD +[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0 [1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2 [1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1 [1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0 diff --git a/tests/integration/vendor/guzzlehttp/psr7/LICENSE b/tests/integration/vendor/guzzlehttp/psr7/LICENSE index 581d95f92..51c7ec81c 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/LICENSE +++ b/tests/integration/vendor/guzzlehttp/psr7/LICENSE @@ -1,4 +1,11 @@ -Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling +The MIT License (MIT) + +Copyright (c) 2015 Michael Dowling +Copyright (c) 2015 Márk Sági-Kazár +Copyright (c) 2015 Graham Campbell +Copyright (c) 2016 Tobias Schultze +Copyright (c) 2016 George Mponos +Copyright (c) 2018 Tobias Nyholm Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/guzzlehttp/psr7/README.md b/tests/integration/vendor/guzzlehttp/psr7/README.md index c60a6a38d..ed81c927e 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/README.md +++ b/tests/integration/vendor/guzzlehttp/psr7/README.md @@ -4,8 +4,8 @@ This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/) message implementation, several stream decorators, and some helpful functionality like query string parsing. - -[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7) +![CI](https://github.com/guzzle/psr7/workflows/CI/badge.svg) +![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg) # Stream implementation @@ -23,11 +23,11 @@ Reads from multiple streams, one after the other. ```php use GuzzleHttp\Psr7; -$a = Psr7\stream_for('abc, '); -$b = Psr7\stream_for('123.'); +$a = Psr7\Utils::streamFor('abc, '); +$b = Psr7\Utils::streamFor('123.'); $composed = new Psr7\AppendStream([$a, $b]); -$composed->addStream(Psr7\stream_for(' Above all listen to me')); +$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me')); echo $composed; // abc, 123. Above all listen to me. ``` @@ -65,7 +65,7 @@ then on disk. ```php use GuzzleHttp\Psr7; -$original = Psr7\stream_for(fopen('http://www.google.com', 'r')); +$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r')); $stream = new Psr7\CachingStream($original); $stream->read(1024); @@ -89,7 +89,7 @@ stream becomes too full. use GuzzleHttp\Psr7; // Create an empty stream -$stream = Psr7\stream_for(); +$stream = Psr7\Utils::streamFor(); // Start dropping data when the stream has more than 10 bytes $dropping = new Psr7\DroppingStream($stream, 10); @@ -112,7 +112,7 @@ to create a concrete class for a simple extension point. use GuzzleHttp\Psr7; -$stream = Psr7\stream_for('hi'); +$stream = Psr7\Utils::streamFor('hi'); $fnStream = Psr7\FnStream::decorate($stream, [ 'rewind' => function () use ($stream) { echo 'About to rewind - '; @@ -130,10 +130,9 @@ $fnStream->rewind(); `GuzzleHttp\Psr7\InflateStream` -Uses PHP's zlib.inflate filter to inflate deflate or gzipped content. +Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content. -This stream decorator skips the first 10 bytes of the given stream to remove -the gzip header, converts the provided stream to a PHP stream resource, +This stream decorator converts the provided stream to a PHP stream resource, then appends the zlib.inflate filter. The stream is then converted back to a Guzzle stream resource to be used as a Guzzle stream. @@ -167,7 +166,7 @@ chunks (e.g. Amazon S3's multipart upload API). ```php use GuzzleHttp\Psr7; -$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+')); +$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+')); echo $original->getSize(); // >>> 1048576 @@ -197,7 +196,7 @@ NoSeekStream wraps a stream and does not allow seeking. ```php use GuzzleHttp\Psr7; -$original = Psr7\stream_for('foo'); +$original = Psr7\Utils::streamFor('foo'); $noSeek = new Psr7\NoSeekStream($original); echo $noSeek->read(3); @@ -271,7 +270,7 @@ This decorator could be added to any existing stream and used like so: ```php use GuzzleHttp\Psr7; -$original = Psr7\stream_for('foo'); +$original = Psr7\Utils::streamFor('foo'); $eofStream = new EofCallbackStream($original, function () { echo 'EOF!'; @@ -297,228 +296,292 @@ stream from a PSR-7 stream. ```php use GuzzleHttp\Psr7\StreamWrapper; -$stream = GuzzleHttp\Psr7\stream_for('hello!'); +$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!'); $resource = StreamWrapper::getResource($stream); echo fread($resource, 6); // outputs hello! ``` -# Function API +# Static API -There are various functions available under the `GuzzleHttp\Psr7` namespace. +There are various static methods available under the `GuzzleHttp\Psr7` namespace. -## `function str` +## `GuzzleHttp\Psr7\Message::toString` -`function str(MessageInterface $message)` +`public static function toString(MessageInterface $message): string` Returns the string representation of an HTTP message. ```php $request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com'); -echo GuzzleHttp\Psr7\str($request); +echo GuzzleHttp\Psr7\Message::toString($request); ``` -## `function uri_for` +## `GuzzleHttp\Psr7\Message::bodySummary` -`function uri_for($uri)` +`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null` -This function accepts a string or `Psr\Http\Message\UriInterface` and returns a -UriInterface for the given value. If the value is already a `UriInterface`, it -is returned as-is. +Get a short summary of the message body. -```php -$uri = GuzzleHttp\Psr7\uri_for('http://example.com'); -assert($uri === GuzzleHttp\Psr7\uri_for($uri)); -``` +Will return `null` if the response is not printable. -## `function stream_for` +## `GuzzleHttp\Psr7\Message::rewindBody` -`function stream_for($resource = '', array $options = [])` +`public static function rewindBody(MessageInterface $message): void` -Create a new stream based on the input type. +Attempts to rewind a message body and throws an exception on failure. -Options is an associative array that can contain the following keys: +The body of the message will only be rewound if a call to `tell()` +returns a value other than `0`. -* - metadata: Array of custom metadata. -* - size: Size of the stream. -This method accepts the following `$resource` types: +## `GuzzleHttp\Psr7\Message::parseMessage` -- `Psr\Http\Message\StreamInterface`: Returns the value as-is. -- `string`: Creates a stream object that uses the given string as the contents. -- `resource`: Creates a stream object that wraps the given PHP stream resource. -- `Iterator`: If the provided value implements `Iterator`, then a read-only - stream object will be created that wraps the given iterable. Each time the - stream is read from, data from the iterator will fill a buffer and will be - continuously called until the buffer is equal to the requested read size. - Subsequent read calls will first read from the buffer and then call `next` - on the underlying iterator until it is exhausted. -- `object` with `__toString()`: If the object has the `__toString()` method, - the object will be cast to a string and then a stream will be returned that - uses the string value. -- `NULL`: When `null` is passed, an empty stream object is returned. -- `callable` When a callable is passed, a read-only stream object will be - created that invokes the given callable. The callable is invoked with the - number of suggested bytes to read. The callable can return any number of - bytes, but MUST return `false` when there is no more data to return. The - stream object that wraps the callable will invoke the callable until the - number of requested bytes are available. Any additional bytes will be - buffered and used in subsequent reads. +`public static function parseMessage(string $message): array` -```php -$stream = GuzzleHttp\Psr7\stream_for('foo'); -$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r')); +Parses an HTTP message into an associative array. -$generator = function ($bytes) { - for ($i = 0; $i < $bytes; $i++) { - yield ' '; - } -} +The array contains the "start-line" key containing the start line of +the message, "headers" key containing an associative array of header +array values, and a "body" key containing the body of the message. -$stream = GuzzleHttp\Psr7\stream_for($generator(100)); -``` +## `GuzzleHttp\Psr7\Message::parseRequestUri` -## `function parse_header` +`public static function parseRequestUri(string $path, array $headers): string` -`function parse_header($header)` +Constructs a URI for an HTTP request message. -Parse an array of header values containing ";" separated data into an array of -associative arrays representing the header key value pair data of the header. -When a parameter does not contain a value, but just contains a key, this -function will inject a key with a '' string value. +## `GuzzleHttp\Psr7\Message::parseRequest` -## `function normalize_header` +`public static function parseRequest(string $message): Request` -`function normalize_header($header)` +Parses a request message string into a request object. -Converts an array of header values that may contain comma separated headers -into an array of headers with no comma separated values. +## `GuzzleHttp\Psr7\Message::parseResponse` -## `function modify_request` +`public static function parseResponse(string $message): Response` -`function modify_request(RequestInterface $request, array $changes)` +Parses a response message string into a response object. -Clone and modify a request with the given changes. This method is useful for -reducing the number of clones needed to mutate a message. -The changes can be one of: +## `GuzzleHttp\Psr7\Header::parse` -- method: (string) Changes the HTTP method. -- set_headers: (array) Sets the given headers. -- remove_headers: (array) Remove the given headers. -- body: (mixed) Sets the given body. -- uri: (UriInterface) Set the URI. -- query: (string) Set the query string value of the URI. -- version: (string) Set the protocol version. +`public static function parse(string|array $header): array` +Parse an array of header values containing ";" separated data into an +array of associative arrays representing the header key value pair data +of the header. When a parameter does not contain a value, but just +contains a key, this function will inject a key with a '' string value. -## `function rewind_body` -`function rewind_body(MessageInterface $message)` +## `GuzzleHttp\Psr7\Header::normalize` -Attempts to rewind a message body and throws an exception on failure. The body -of the message will only be rewound if a call to `tell()` returns a value other -than `0`. +`public static function normalize(string|array $header): array` +Converts an array of header values that may contain comma separated +headers into an array of headers with no comma separated values. -## `function try_fopen` -`function try_fopen($filename, $mode)` +## `GuzzleHttp\Psr7\Query::parse` -Safely opens a PHP stream resource using a filename. +`public static function parse(string $str, int|bool $urlEncoding = true): array` -When fopen fails, PHP normally raises a warning. This function adds an error -handler that checks for errors and throws an exception instead. +Parse a query string into an associative array. +If multiple values are found for the same key, the value of that key +value pair will become an array. This function does not parse nested +PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` +will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`. -## `function copy_to_string` -`function copy_to_string(StreamInterface $stream, $maxLen = -1)` +## `GuzzleHttp\Psr7\Query::build` -Copy the contents of a stream into a string until the given number of bytes -have been read. +`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string` +Build a query string from an array of key value pairs. -## `function copy_to_stream` +This function can use the return value of `parse()` to build a query +string. This function does not modify the provided keys when an array is +encountered (like `http_build_query()` would). -`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)` -Copy the contents of a stream into another stream until the given number of +## `GuzzleHttp\Psr7\Utils::caselessRemove` + +`public static function caselessRemove(iterable $keys, $keys, array $data): array` + +Remove the items given by the keys, case insensitively from the data. + + +## `GuzzleHttp\Psr7\Utils::copyToStream` + +`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void` + +Copy the contents of a stream into another stream until the given number +of bytes have been read. + + +## `GuzzleHttp\Psr7\Utils::copyToString` + +`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string` + +Copy the contents of a stream into a string until the given number of bytes have been read. -## `function hash` +## `GuzzleHttp\Psr7\Utils::hash` + +`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string` -`function hash(StreamInterface $stream, $algo, $rawOutput = false)` +Calculate a hash of a stream. -Calculate a hash of a Stream. This method reads the entire stream to calculate -a rolling hash (based on PHP's hash_init functions). +This method reads the entire stream to calculate a rolling hash, based on +PHP's `hash_init` functions. -## `function readline` +## `GuzzleHttp\Psr7\Utils::modifyRequest` -`function readline(StreamInterface $stream, $maxLength = null)` +`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface` + +Clone and modify a request with the given changes. + +This method is useful for reducing the number of clones needed to mutate +a message. + +- method: (string) Changes the HTTP method. +- set_headers: (array) Sets the given headers. +- remove_headers: (array) Remove the given headers. +- body: (mixed) Sets the given body. +- uri: (UriInterface) Set the URI. +- query: (string) Set the query string value of the URI. +- version: (string) Set the protocol version. + + +## `GuzzleHttp\Psr7\Utils::readLine` + +`public static function readLine(StreamInterface $stream, int $maxLength = null): string` Read a line from the stream up to the maximum allowed buffer length. -## `function parse_request` +## `GuzzleHttp\Psr7\Utils::streamFor` -`function parse_request($message)` +`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface` -Parses a request message string into a request object. +Create a new stream based on the input type. +Options is an associative array that can contain the following keys: -## `function parse_response` +- metadata: Array of custom metadata. +- size: Size of the stream. -`function parse_response($message)` +This method accepts the following `$resource` types: -Parses a response message string into a response object. +- `Psr\Http\Message\StreamInterface`: Returns the value as-is. +- `string`: Creates a stream object that uses the given string as the contents. +- `resource`: Creates a stream object that wraps the given PHP stream resource. +- `Iterator`: If the provided value implements `Iterator`, then a read-only + stream object will be created that wraps the given iterable. Each time the + stream is read from, data from the iterator will fill a buffer and will be + continuously called until the buffer is equal to the requested read size. + Subsequent read calls will first read from the buffer and then call `next` + on the underlying iterator until it is exhausted. +- `object` with `__toString()`: If the object has the `__toString()` method, + the object will be cast to a string and then a stream will be returned that + uses the string value. +- `NULL`: When `null` is passed, an empty stream object is returned. +- `callable` When a callable is passed, a read-only stream object will be + created that invokes the given callable. The callable is invoked with the + number of suggested bytes to read. The callable can return any number of + bytes, but MUST return `false` when there is no more data to return. The + stream object that wraps the callable will invoke the callable until the + number of requested bytes are available. Any additional bytes will be + buffered and used in subsequent reads. +```php +$stream = GuzzleHttp\Psr7\Utils::streamFor('foo'); +$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r')); -## `function parse_query` +$generator = function ($bytes) { + for ($i = 0; $i < $bytes; $i++) { + yield ' '; + } +} -`function parse_query($str, $urlEncoding = true)` +$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100)); +``` -Parse a query string into an associative array. -If multiple values are found for the same key, the value of that key value pair -will become an array. This function does not parse nested PHP style arrays into -an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into -`['foo[a]' => '1', 'foo[b]' => '2']`). +## `GuzzleHttp\Psr7\Utils::tryFopen` +`public static function tryFopen(string $filename, string $mode): resource` -## `function build_query` +Safely opens a PHP stream resource using a filename. -`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)` +When fopen fails, PHP normally raises a warning. This function adds an +error handler that checks for errors and throws an exception instead. -Build a query string from an array of key value pairs. -This function can use the return value of parse_query() to build a query string. -This function does not modify the provided keys when an array is encountered -(like http_build_query would). +## `GuzzleHttp\Psr7\Utils::uriFor` + +`public static function uriFor(string|UriInterface $uri): UriInterface` + +Returns a UriInterface for the given value. + +This function accepts a string or UriInterface and returns a +UriInterface for the given value. If the value is already a +UriInterface, it is returned as-is. -## `function mimetype_from_filename` +## `GuzzleHttp\Psr7\MimeType::fromFilename` -`function mimetype_from_filename($filename)` +`public static function fromFilename(string $filename): string|null` Determines the mimetype of a file by looking at its extension. -## `function mimetype_from_extension` +## `GuzzleHttp\Psr7\MimeType::fromExtension` -`function mimetype_from_extension($extension)` +`public static function fromExtension(string $extension): string|null` Maps a file extensions to a mimetype. +## Upgrading from Function API + +The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience: + +| Original Function | Replacement Method | +|----------------|----------------| +| `str` | `Message::toString` | +| `uri_for` | `Utils::uriFor` | +| `stream_for` | `Utils::streamFor` | +| `parse_header` | `Header::parse` | +| `normalize_header` | `Header::normalize` | +| `modify_request` | `Utils::modifyRequest` | +| `rewind_body` | `Message::rewindBody` | +| `try_fopen` | `Utils::tryFopen` | +| `copy_to_string` | `Utils::copyToString` | +| `copy_to_stream` | `Utils::copyToStream` | +| `hash` | `Utils::hash` | +| `readline` | `Utils::readLine` | +| `parse_request` | `Message::parseRequest` | +| `parse_response` | `Message::parseResponse` | +| `parse_query` | `Query::parse` | +| `build_query` | `Query::build` | +| `mimetype_from_filename` | `MimeType::fromFilename` | +| `mimetype_from_extension` | `MimeType::fromExtension` | +| `_parse_message` | `Message::parseMessage` | +| `_parse_request_uri` | `Message::parseRequestUri` | +| `get_message_body_summary` | `Message::bodySummary` | +| `_caseless_remove` | `Utils::caselessRemove` | + + # Additional URI Methods Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class, @@ -743,3 +806,18 @@ Whether two URIs can be considered equivalent. Both URIs are normalized automati `$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent. This of course assumes they will be resolved against the same base URI. If this is not the case, determination of equivalence or difference of relative references does not mean anything. + + +## Security + +If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information. + +## License + +Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information. + +## For Enterprise + +Available as part of the Tidelift Subscription + +The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/tests/integration/vendor/guzzlehttp/psr7/composer.json b/tests/integration/vendor/guzzlehttp/psr7/composer.json index 2add153ec..885aa1da0 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/composer.json +++ b/tests/integration/vendor/guzzlehttp/psr7/composer.json @@ -1,36 +1,76 @@ { "name": "guzzlehttp/psr7", - "type": "library", "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": ["request", "response", "message", "stream", "http", "uri", "url", "psr-7"], + "keywords": [ + "request", + "response", + "message", + "stream", + "http", + "uri", + "url", + "psr-7" + ], "license": "MIT", "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5" - }, - "require-dev": { - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" - }, - "files": ["src/functions_include.php"] + } }, "autoload-dev": { "psr-4": { @@ -39,7 +79,11 @@ }, "extra": { "branch-alias": { - "dev-master": "1.5-dev" + "dev-master": "2.1-dev" } + }, + "config": { + "preferred-install": "dist", + "sort-packages": true } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/AppendStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/AppendStream.php index 472a0d61b..967925f3f 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/AppendStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/AppendStream.php @@ -1,4 +1,7 @@ rewind(); return $this->getContents(); - } catch (\Exception $e) { + } catch (\Throwable $e) { + if (\PHP_VERSION_ID >= 70400) { + throw $e; + } + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } @@ -45,7 +57,7 @@ public function __toString() * * @throws \InvalidArgumentException if the stream is not readable */ - public function addStream(StreamInterface $stream) + public function addStream(StreamInterface $stream): void { if (!$stream->isReadable()) { throw new \InvalidArgumentException('Each stream must be readable'); @@ -59,17 +71,15 @@ public function addStream(StreamInterface $stream) $this->streams[] = $stream; } - public function getContents() + public function getContents(): string { - return copy_to_string($this); + return Utils::copyToString($this); } /** * Closes each attached stream. - * - * {@inheritdoc} */ - public function close() + public function close(): void { $this->pos = $this->current = 0; $this->seekable = true; @@ -85,8 +95,6 @@ public function close() * Detaches each attached stream. * * Returns null as it's not clear which underlying stream resource to return. - * - * {@inheritdoc} */ public function detach() { @@ -98,9 +106,11 @@ public function detach() } $this->streams = []; + + return null; } - public function tell() + public function tell(): int { return $this->pos; } @@ -110,10 +120,8 @@ public function tell() * * If any of the streams do not return a valid number, then the size of the * append stream cannot be determined and null is returned. - * - * {@inheritdoc} */ - public function getSize() + public function getSize(): ?int { $size = 0; @@ -128,24 +136,22 @@ public function getSize() return $size; } - public function eof() + public function eof(): bool { return !$this->streams || ($this->current >= count($this->streams) - 1 && $this->streams[$this->current]->eof()); } - public function rewind() + public function rewind(): void { $this->seek(0); } /** * Attempts to seek to the given position. Only supports SEEK_SET. - * - * {@inheritdoc} */ - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { if (!$this->seekable) { throw new \RuntimeException('This AppendStream is not seekable'); @@ -176,10 +182,8 @@ public function seek($offset, $whence = SEEK_SET) /** * Reads from all of the appended streams until the length is met or EOF. - * - * {@inheritdoc} */ - public function read($length) + public function read($length): string { $buffer = ''; $total = count($this->streams) - 1; @@ -199,8 +203,7 @@ public function read($length) $result = $this->streams[$this->current]->read($remaining); - // Using a loose comparison here to match on '', false, and null - if ($result == null) { + if ($result === '') { $progressToNext = true; continue; } @@ -214,26 +217,31 @@ public function read($length) return $buffer; } - public function isReadable() + public function isReadable(): bool { return true; } - public function isWritable() + public function isWritable(): bool { return false; } - public function isSeekable() + public function isSeekable(): bool { return $this->seekable; } - public function write($string) + public function write($string): int { throw new \RuntimeException('Cannot write to an AppendStream'); } + /** + * {@inheritdoc} + * + * @return mixed + */ public function getMetadata($key = null) { return $key ? null : []; diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/BufferStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/BufferStream.php index af4d4c227..21be8c0a9 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/BufferStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/BufferStream.php @@ -1,4 +1,7 @@ hwm = $hwm; } - public function __toString() + public function __toString(): string { return $this->getContents(); } - public function getContents() + public function getContents(): string { $buffer = $this->buffer; $this->buffer = ''; @@ -41,7 +47,7 @@ public function getContents() return $buffer; } - public function close() + public function close(): void { $this->buffer = ''; } @@ -49,44 +55,46 @@ public function close() public function detach() { $this->close(); + + return null; } - public function getSize() + public function getSize(): ?int { return strlen($this->buffer); } - public function isReadable() + public function isReadable(): bool { return true; } - public function isWritable() + public function isWritable(): bool { return true; } - public function isSeekable() + public function isSeekable(): bool { return false; } - public function rewind() + public function rewind(): void { $this->seek(0); } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { throw new \RuntimeException('Cannot seek a BufferStream'); } - public function eof() + public function eof(): bool { return strlen($this->buffer) === 0; } - public function tell() + public function tell(): int { throw new \RuntimeException('Cannot determine the position of a BufferStream'); } @@ -94,7 +102,7 @@ public function tell() /** * Reads data from the buffer. */ - public function read($length) + public function read($length): string { $currentLength = strlen($this->buffer); @@ -114,21 +122,25 @@ public function read($length) /** * Writes data to the buffer. */ - public function write($string) + public function write($string): int { $this->buffer .= $string; - // TODO: What should happen here? if (strlen($this->buffer) >= $this->hwm) { - return false; + return 0; } return strlen($string); } + /** + * {@inheritdoc} + * + * @return mixed + */ public function getMetadata($key = null) { - if ($key == 'hwm') { + if ($key === 'hwm') { return $this->hwm; } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/CachingStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/CachingStream.php index ed68f0861..7a70ee942 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/CachingStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/CachingStream.php @@ -1,4 +1,7 @@ remoteStream = $stream; - $this->stream = $target ?: new Stream(fopen('php://temp', 'r+')); + $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+')); } - public function getSize() + public function getSize(): ?int { - return max($this->stream->getSize(), $this->remoteStream->getSize()); + $remoteSize = $this->remoteStream->getSize(); + + if (null === $remoteSize) { + return null; + } + + return max($this->stream->getSize(), $remoteSize); } - public function rewind() + public function rewind(): void { $this->seek(0); } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { - if ($whence == SEEK_SET) { + if ($whence === SEEK_SET) { $byte = $offset; - } elseif ($whence == SEEK_CUR) { + } elseif ($whence === SEEK_CUR) { $byte = $offset + $this->tell(); - } elseif ($whence == SEEK_END) { + } elseif ($whence === SEEK_END) { $size = $this->remoteStream->getSize(); if ($size === null) { $size = $this->cacheEntireStream(); @@ -72,7 +81,7 @@ public function seek($offset, $whence = SEEK_SET) } } - public function read($length) + public function read($length): string { // Perform a regular read on any previously read data from the buffer $data = $this->stream->read($length); @@ -101,7 +110,7 @@ public function read($length) return $data; } - public function write($string) + public function write($string): int { // When appending to the end of the currently read stream, you'll want // to skip bytes from being read from the remote stream to emulate @@ -115,7 +124,7 @@ public function write($string) return $this->stream->write($string); } - public function eof() + public function eof(): bool { return $this->stream->eof() && $this->remoteStream->eof(); } @@ -123,15 +132,16 @@ public function eof() /** * Close both the remote stream and buffer stream */ - public function close() + public function close(): void { - $this->remoteStream->close() && $this->stream->close(); + $this->remoteStream->close(); + $this->stream->close(); } - private function cacheEntireStream() + private function cacheEntireStream(): int { $target = new FnStream(['write' => 'strlen']); - copy_to_stream($this, $target); + Utils::copyToStream($this, $target); return $this->tell(); } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/DroppingStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/DroppingStream.php index 8935c80d7..d78070ae2 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/DroppingStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/DroppingStream.php @@ -1,4 +1,7 @@ stream = $stream; $this->maxLength = $maxLength; } - public function write($string) + public function write($string): int { $diff = $this->maxLength - $this->stream->getSize(); diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php b/tests/integration/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php new file mode 100644 index 000000000..3a084779a --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php @@ -0,0 +1,14 @@ + */ + private $methods; /** - * @param array $methods Hash of method name to a callable. + * @param array $methods Hash of method name to a callable. */ public function __construct(array $methods) { @@ -34,9 +38,10 @@ public function __construct(array $methods) /** * Lazily determine which methods are not implemented. + * * @throws \BadMethodCallException */ - public function __get($name) + public function __get(string $name): void { throw new \BadMethodCallException(str_replace('_fn_', '', $name) . '() is not implemented in the FnStream'); @@ -54,9 +59,10 @@ public function __destruct() /** * An unserialize would allow the __destruct to run when the unserialized value goes out of scope. + * * @throws \LogicException */ - public function __wakeup() + public function __wakeup(): void { throw new \LogicException('FnStream should never be unserialized'); } @@ -65,8 +71,8 @@ public function __wakeup() * Adds custom functionality to an underlying stream by intercepting * specific method calls. * - * @param StreamInterface $stream Stream to decorate - * @param array $methods Hash of method name to a closure + * @param StreamInterface $stream Stream to decorate + * @param array $methods Hash of method name to a closure * * @return FnStream */ @@ -74,21 +80,31 @@ public static function decorate(StreamInterface $stream, array $methods) { // If any of the required methods were not provided, then simply // proxy to the decorated stream. - foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { - $methods[$diff] = [$stream, $diff]; + foreach (array_diff(self::SLOTS, array_keys($methods)) as $diff) { + /** @var callable $callable */ + $callable = [$stream, $diff]; + $methods[$diff] = $callable; } return new self($methods); } - public function __toString() + public function __toString(): string { - return call_user_func($this->_fn___toString); + try { + return call_user_func($this->_fn___toString); + } catch (\Throwable $e) { + if (\PHP_VERSION_ID >= 70400) { + throw $e; + } + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); + return ''; + } } - public function close() + public function close(): void { - return call_user_func($this->_fn_close); + call_user_func($this->_fn_close); } public function detach() @@ -96,61 +112,66 @@ public function detach() return call_user_func($this->_fn_detach); } - public function getSize() + public function getSize(): ?int { return call_user_func($this->_fn_getSize); } - public function tell() + public function tell(): int { return call_user_func($this->_fn_tell); } - public function eof() + public function eof(): bool { return call_user_func($this->_fn_eof); } - public function isSeekable() + public function isSeekable(): bool { return call_user_func($this->_fn_isSeekable); } - public function rewind() + public function rewind(): void { call_user_func($this->_fn_rewind); } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { call_user_func($this->_fn_seek, $offset, $whence); } - public function isWritable() + public function isWritable(): bool { return call_user_func($this->_fn_isWritable); } - public function write($string) + public function write($string): int { return call_user_func($this->_fn_write, $string); } - public function isReadable() + public function isReadable(): bool { return call_user_func($this->_fn_isReadable); } - public function read($length) + public function read($length): string { return call_user_func($this->_fn_read, $length); } - public function getContents() + public function getContents(): string { return call_user_func($this->_fn_getContents); } + /** + * {@inheritdoc} + * + * @return mixed + */ public function getMetadata($key = null) { return call_user_func($this->_fn_getMetadata, $key); diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Header.php b/tests/integration/vendor/guzzlehttp/psr7/src/Header.php new file mode 100644 index 000000000..0e79a71a8 --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Header.php @@ -0,0 +1,69 @@ +]+>|[^=]+/', $kvp, $matches)) { + $m = $matches[0]; + if (isset($m[1])) { + $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); + } else { + $part[] = trim($m[0], $trimmed); + } + } + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * Converts an array of header values that may contain comma separated + * headers into an array of headers with no comma separated values. + * + * @param string|array $header Header to normalize. + */ + public static function normalize($header): array + { + if (!is_array($header)) { + return array_map('trim', explode(',', $header)); + } + + $result = []; + foreach ($header as $value) { + foreach ((array) $value as $v) { + if (strpos($v, ',') === false) { + $result[] = $v; + continue; + } + foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { + $result[] = trim($vv); + } + } + } + + return $result; + } +} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/HttpFactory.php b/tests/integration/vendor/guzzlehttp/psr7/src/HttpFactory.php new file mode 100644 index 000000000..30be222fc --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/HttpFactory.php @@ -0,0 +1,100 @@ +getSize(); + } + + return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType); + } + + public function createStream(string $content = ''): StreamInterface + { + return Utils::streamFor($content); + } + + public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface + { + try { + $resource = Utils::tryFopen($file, $mode); + } catch (\RuntimeException $e) { + if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], true)) { + throw new \InvalidArgumentException(sprintf('Invalid file opening mode "%s"', $mode), 0, $e); + } + + throw $e; + } + + return Utils::streamFor($resource); + } + + public function createStreamFromResource($resource): StreamInterface + { + return Utils::streamFor($resource); + } + + public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface + { + if (empty($method)) { + if (!empty($serverParams['REQUEST_METHOD'])) { + $method = $serverParams['REQUEST_METHOD']; + } else { + throw new \InvalidArgumentException('Cannot determine HTTP method'); + } + } + + return new ServerRequest($method, $uri, [], null, '1.1', $serverParams); + } + + public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface + { + return new Response($code, [], null, '1.1', $reasonPhrase); + } + + public function createRequest(string $method, $uri): RequestInterface + { + return new Request($method, $uri); + } + + public function createUri(string $uri = ''): UriInterface + { + return new Uri($uri); + } +} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/InflateStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/InflateStream.php index 5e4f6028c..8e3cf1717 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/InflateStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/InflateStream.php @@ -1,52 +1,34 @@ read(10); - $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header); - // Skip the header, that is 10 + length of filename + 1 (nil) bytes - $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); $resource = StreamWrapper::getResource($stream); - stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); + // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data + // See http://www.zlib.net/manual.html#Advanced definition of inflateInit2 + // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection" + // Default window size is 15. + stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]); $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource)); } - - /** - * @param StreamInterface $stream - * @param $header - * @return int - */ - private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header) - { - $filename_header_length = 0; - - if (substr(bin2hex($header), 6, 2) === '08') { - // we have a filename, read until nil - $filename_header_length = 1; - while ($stream->read(1) !== chr(0)) { - $filename_header_length++; - } - } - - return $filename_header_length; - } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/LazyOpenStream.php index 02cec3af4..6b6042963 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/LazyOpenStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/LazyOpenStream.php @@ -1,4 +1,7 @@ filename = $filename; $this->mode = $mode; @@ -29,11 +32,9 @@ public function __construct($filename, $mode) /** * Creates the underlying stream lazily when required. - * - * @return StreamInterface */ - protected function createStream() + protected function createStream(): StreamInterface { - return stream_for(try_fopen($this->filename, $this->mode)); + return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode)); } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/LimitStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/LimitStream.php index 3c13d4f41..9762d38aa 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/LimitStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/LimitStream.php @@ -1,13 +1,15 @@ stream = $stream; $this->setLimit($limit); $this->setOffset($offset); } - public function eof() + public function eof(): bool { // Always return true if the underlying stream is EOF if ($this->stream->eof()) { @@ -42,7 +44,7 @@ public function eof() } // No limit and the underlying stream is not at EOF - if ($this->limit == -1) { + if ($this->limit === -1) { return false; } @@ -51,13 +53,12 @@ public function eof() /** * Returns the size of the limited subset of data - * {@inheritdoc} */ - public function getSize() + public function getSize(): ?int { if (null === ($length = $this->stream->getSize())) { return null; - } elseif ($this->limit == -1) { + } elseif ($this->limit === -1) { return $length - $this->offset; } else { return min($this->limit, $length - $this->offset); @@ -66,13 +67,12 @@ public function getSize() /** * Allow for a bounded seek on the read limited stream - * {@inheritdoc} */ - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { if ($whence !== SEEK_SET || $offset < 0) { throw new \RuntimeException(sprintf( - 'Cannot seek to offset % with whence %s', + 'Cannot seek to offset %s with whence %s', $offset, $whence )); @@ -91,9 +91,8 @@ public function seek($offset, $whence = SEEK_SET) /** * Give a relative tell() - * {@inheritdoc} */ - public function tell() + public function tell(): int { return $this->stream->tell() - $this->offset; } @@ -105,7 +104,7 @@ public function tell() * * @throws \RuntimeException if the stream cannot be seeked. */ - public function setOffset($offset) + public function setOffset(int $offset): void { $current = $this->stream->tell(); @@ -130,14 +129,14 @@ public function setOffset($offset) * @param int $limit Number of bytes to allow to be read from the stream. * Use -1 for no limit. */ - public function setLimit($limit) + public function setLimit(int $limit): void { $this->limit = $limit; } - public function read($length) + public function read($length): string { - if ($this->limit == -1) { + if ($this->limit === -1) { return $this->stream->read($length); } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Message.php b/tests/integration/vendor/guzzlehttp/psr7/src/Message.php new file mode 100644 index 000000000..9b825b300 --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Message.php @@ -0,0 +1,242 @@ +getMethod() . ' ' + . $message->getRequestTarget()) + . ' HTTP/' . $message->getProtocolVersion(); + if (!$message->hasHeader('host')) { + $msg .= "\r\nHost: " . $message->getUri()->getHost(); + } + } elseif ($message instanceof ResponseInterface) { + $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' + . $message->getStatusCode() . ' ' + . $message->getReasonPhrase(); + } else { + throw new \InvalidArgumentException('Unknown message type'); + } + + foreach ($message->getHeaders() as $name => $values) { + if (strtolower($name) === 'set-cookie') { + foreach ($values as $value) { + $msg .= "\r\n{$name}: " . $value; + } + } else { + $msg .= "\r\n{$name}: " . implode(', ', $values); + } + } + + return "{$msg}\r\n\r\n" . $message->getBody(); + } + + /** + * Get a short summary of the message body. + * + * Will return `null` if the response is not printable. + * + * @param MessageInterface $message The message to get the body summary + * @param int $truncateAt The maximum allowed size of the summary + */ + public static function bodySummary(MessageInterface $message, int $truncateAt = 120): ?string + { + $body = $message->getBody(); + + if (!$body->isSeekable() || !$body->isReadable()) { + return null; + } + + $size = $body->getSize(); + + if ($size === 0) { + return null; + } + + $summary = $body->read($truncateAt); + $body->rewind(); + + if ($size > $truncateAt) { + $summary .= ' (truncated...)'; + } + + // Matches any printable character, including unicode characters: + // letters, marks, numbers, punctuation, spacing, and separators. + if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) { + return null; + } + + return $summary; + } + + /** + * Attempts to rewind a message body and throws an exception on failure. + * + * The body of the message will only be rewound if a call to `tell()` + * returns a value other than `0`. + * + * @param MessageInterface $message Message to rewind + * + * @throws \RuntimeException + */ + public static function rewindBody(MessageInterface $message): void + { + $body = $message->getBody(); + + if ($body->tell()) { + $body->rewind(); + } + } + + /** + * Parses an HTTP message into an associative array. + * + * The array contains the "start-line" key containing the start line of + * the message, "headers" key containing an associative array of header + * array values, and a "body" key containing the body of the message. + * + * @param string $message HTTP request or response to parse. + */ + public static function parseMessage(string $message): array + { + if (!$message) { + throw new \InvalidArgumentException('Invalid message'); + } + + $message = ltrim($message, "\r\n"); + + $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); + + if ($messageParts === false || count($messageParts) !== 2) { + throw new \InvalidArgumentException('Invalid message: Missing header delimiter'); + } + + [$rawHeaders, $body] = $messageParts; + $rawHeaders .= "\r\n"; // Put back the delimiter we split previously + $headerParts = preg_split("/\r?\n/", $rawHeaders, 2); + + if ($headerParts === false || count($headerParts) !== 2) { + throw new \InvalidArgumentException('Invalid message: Missing status line'); + } + + [$startLine, $rawHeaders] = $headerParts; + + if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') { + // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0 + $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); + } + + /** @var array[] $headerLines */ + $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER); + + // If these aren't the same, then one line didn't match and there's an invalid header. + if ($count !== substr_count($rawHeaders, "\n")) { + // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4 + if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { + throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); + } + + throw new \InvalidArgumentException('Invalid header syntax'); + } + + $headers = []; + + foreach ($headerLines as $headerLine) { + $headers[$headerLine[1]][] = $headerLine[2]; + } + + return [ + 'start-line' => $startLine, + 'headers' => $headers, + 'body' => $body, + ]; + } + + /** + * Constructs a URI for an HTTP request message. + * + * @param string $path Path from the start-line + * @param array $headers Array of headers (each value an array). + */ + public static function parseRequestUri(string $path, array $headers): string + { + $hostKey = array_filter(array_keys($headers), function ($k) { + return strtolower($k) === 'host'; + }); + + // If no host is found, then a full URI cannot be constructed. + if (!$hostKey) { + return $path; + } + + $host = $headers[reset($hostKey)][0]; + $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; + + return $scheme . '://' . $host . '/' . ltrim($path, '/'); + } + + /** + * Parses a request message string into a request object. + * + * @param string $message Request message string. + */ + public static function parseRequest(string $message): RequestInterface + { + $data = self::parseMessage($message); + $matches = []; + if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { + throw new \InvalidArgumentException('Invalid request string'); + } + $parts = explode(' ', $data['start-line'], 3); + $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; + + $request = new Request( + $parts[0], + $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1], + $data['headers'], + $data['body'], + $version + ); + + return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); + } + + /** + * Parses a response message string into a response object. + * + * @param string $message Response message string. + */ + public static function parseResponse(string $message): ResponseInterface + { + $data = self::parseMessage($message); + // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space + // between status-code and reason-phrase is required. But browsers accept + // responses without space and reason as well. + if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { + throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']); + } + $parts = explode(' ', $data['start-line'], 3); + + return new Response( + (int) $parts[1], + $data['headers'], + $data['body'], + explode('/', $parts[0])[1], + $parts[2] ?? null + ); + } +} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/MessageTrait.php b/tests/integration/vendor/guzzlehttp/psr7/src/MessageTrait.php index 1e4da649a..503c280be 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/MessageTrait.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -1,6 +1,10 @@ array of values */ + /** @var array Map of all registered headers, as original name => array of values */ private $headers = []; - /** @var array Map of lowercase header name => original name at registration */ + /** @var array Map of lowercase header name => original name at registration */ private $headerNames = []; /** @var string */ private $protocol = '1.1'; - /** @var StreamInterface */ + /** @var StreamInterface|null */ private $stream; - public function getProtocolVersion() + public function getProtocolVersion(): string { return $this->protocol; } - public function withProtocolVersion($version) + public function withProtocolVersion($version): MessageInterface { if ($this->protocol === $version) { return $this; @@ -36,17 +40,17 @@ public function withProtocolVersion($version) return $new; } - public function getHeaders() + public function getHeaders(): array { return $this->headers; } - public function hasHeader($header) + public function hasHeader($header): bool { return isset($this->headerNames[strtolower($header)]); } - public function getHeader($header) + public function getHeader($header): array { $header = strtolower($header); @@ -59,18 +63,15 @@ public function getHeader($header) return $this->headers[$header]; } - public function getHeaderLine($header) + public function getHeaderLine($header): string { return implode(', ', $this->getHeader($header)); } - public function withHeader($header, $value) + public function withHeader($header, $value): MessageInterface { - if (!is_array($value)) { - $value = [$value]; - } - - $value = $this->trimHeaderValues($value); + $this->assertHeader($header); + $value = $this->normalizeHeaderValue($value); $normalized = strtolower($header); $new = clone $this; @@ -83,13 +84,10 @@ public function withHeader($header, $value) return $new; } - public function withAddedHeader($header, $value) + public function withAddedHeader($header, $value): MessageInterface { - if (!is_array($value)) { - $value = [$value]; - } - - $value = $this->trimHeaderValues($value); + $this->assertHeader($header); + $value = $this->normalizeHeaderValue($value); $normalized = strtolower($header); $new = clone $this; @@ -104,7 +102,7 @@ public function withAddedHeader($header, $value) return $new; } - public function withoutHeader($header) + public function withoutHeader($header): MessageInterface { $normalized = strtolower($header); @@ -120,16 +118,16 @@ public function withoutHeader($header) return $new; } - public function getBody() + public function getBody(): StreamInterface { if (!$this->stream) { - $this->stream = stream_for(''); + $this->stream = Utils::streamFor(''); } return $this->stream; } - public function withBody(StreamInterface $body) + public function withBody(StreamInterface $body): MessageInterface { if ($body === $this->stream) { return $this; @@ -140,15 +138,20 @@ public function withBody(StreamInterface $body) return $new; } - private function setHeaders(array $headers) + /** + * @param array $headers + */ + private function setHeaders(array $headers): void { $this->headerNames = $this->headers = []; foreach ($headers as $header => $value) { - if (!is_array($value)) { - $value = [$value]; + if (is_int($header)) { + // Numeric array keys are converted to int by PHP but having a header name '123' is not forbidden by the spec + // and also allowed in withHeader(). So we need to cast it to string again for the following assertion to pass. + $header = (string) $header; } - - $value = $this->trimHeaderValues($value); + $this->assertHeader($header); + $value = $this->normalizeHeaderValue($value); $normalized = strtolower($header); if (isset($this->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; @@ -160,6 +163,24 @@ private function setHeaders(array $headers) } } + /** + * @param mixed $value + * + * @return string[] + */ + private function normalizeHeaderValue($value): array + { + if (!is_array($value)) { + return $this->trimHeaderValues([$value]); + } + + if (count($value) === 0) { + throw new \InvalidArgumentException('Header value can not be an empty array.'); + } + + return $this->trimHeaderValues($value); + } + /** * Trims whitespace from the header values. * @@ -168,16 +189,47 @@ private function setHeaders(array $headers) * header-field = field-name ":" OWS field-value OWS * OWS = *( SP / HTAB ) * - * @param string[] $values Header values + * @param mixed[] $values Header values * * @return string[] Trimmed header values * * @see https://tools.ietf.org/html/rfc7230#section-3.2.4 */ - private function trimHeaderValues(array $values) + private function trimHeaderValues(array $values): array { return array_map(function ($value) { - return trim($value, " \t"); - }, $values); + if (!is_scalar($value) && null !== $value) { + throw new \InvalidArgumentException(sprintf( + 'Header value must be scalar or null but %s provided.', + is_object($value) ? get_class($value) : gettype($value) + )); + } + + return trim((string) $value, " \t"); + }, array_values($values)); + } + + /** + * @see https://tools.ietf.org/html/rfc7230#section-3.2 + * + * @param mixed $header + */ + private function assertHeader($header): void + { + if (!is_string($header)) { + throw new \InvalidArgumentException(sprintf( + 'Header name must be a string but %s provided.', + is_object($header) ? get_class($header) : gettype($header) + )); + } + + if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) { + throw new \InvalidArgumentException( + sprintf( + '"%s" is not valid header name', + $header + ) + ); + } } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/MimeType.php b/tests/integration/vendor/guzzlehttp/psr7/src/MimeType.php new file mode 100644 index 000000000..dfa94251a --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/MimeType.php @@ -0,0 +1,130 @@ + 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aac' => 'audio/x-aac', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'atom' => 'application/atom+xml', + 'avi' => 'video/x-msvideo', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bzip2', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'deb' => 'application/x-debian-package', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dvi' => 'application/x-dvi', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'etx' => 'text/x-setext', + 'flac' => 'audio/flac', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gz' => 'application/gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ini' => 'text/plain', + 'iso' => 'application/x-iso9660-image', + 'jar' => 'application/java-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'latex' => 'application/x-latex', + 'log' => 'text/plain', + 'm4a' => 'audio/mp4', + 'm4v' => 'video/mp4', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mkv' => 'video/x-matroska', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4v' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'pbm' => 'image/x-portable-bitmap', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 'tar' => 'application/x-tar', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'torrent' => 'application/x-bittorrent', + 'ttf' => 'application/x-font-ttf', + 'txt' => 'text/plain', + 'wav' => 'audio/x-wav', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'video/x-ms-wmv', + 'woff' => 'application/x-font-woff', + 'wsdl' => 'application/wsdl+xml', + 'xbm' => 'image/x-xbitmap', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'yaml' => 'text/yaml', + 'yml' => 'text/yaml', + 'zip' => 'application/zip', + ]; + + /** + * Determines the mimetype of a file by looking at its extension. + */ + public static function fromFilename(string $filename): ?string + { + return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } + + /** + * Maps a file extensions to a mimetype. + * + * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types + */ + public static function fromExtension(string $extension): ?string + { + return self::MIME_TYPES[strtolower($extension)] ?? null; + } +} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/MultipartStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/MultipartStream.php index c0fd584f7..f76d7c614 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/MultipartStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/MultipartStream.php @@ -1,4 +1,7 @@ boundary = $boundary ?: sha1(uniqid('', true)); $this->stream = $this->createStream($elements); } - /** - * Get the boundary - * - * @return string - */ - public function getBoundary() + public function getBoundary(): string { return $this->boundary; } - public function isWritable() + public function isWritable(): bool { return false; } /** * Get the headers needed before transferring the content of a POST file + * + * @param array $headers */ - private function getHeaders(array $headers) + private function getHeaders(array $headers): string { $str = ''; foreach ($headers as $key => $value) { @@ -62,7 +63,7 @@ private function getHeaders(array $headers) /** * Create the aggregate stream that will be used to upload the POST data */ - protected function createStream(array $elements) + protected function createStream(array $elements = []): StreamInterface { $stream = new AppendStream(); @@ -71,12 +72,12 @@ protected function createStream(array $elements) } // Add the trailing boundary with CRLF - $stream->addStream(stream_for("--{$this->boundary}--\r\n")); + $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n")); return $stream; } - private function addElement(AppendStream $stream, array $element) + private function addElement(AppendStream $stream, array $element): void { foreach (['contents', 'name'] as $key) { if (!array_key_exists($key, $element)) { @@ -84,7 +85,7 @@ private function addElement(AppendStream $stream, array $element) } } - $element['contents'] = stream_for($element['contents']); + $element['contents'] = Utils::streamFor($element['contents']); if (empty($element['filename'])) { $uri = $element['contents']->getMetadata('uri'); @@ -93,30 +94,29 @@ private function addElement(AppendStream $stream, array $element) } } - list($body, $headers) = $this->createElement( + [$body, $headers] = $this->createElement( $element['name'], $element['contents'], - isset($element['filename']) ? $element['filename'] : null, - isset($element['headers']) ? $element['headers'] : [] + $element['filename'] ?? null, + $element['headers'] ?? [] ); - $stream->addStream(stream_for($this->getHeaders($headers))); + $stream->addStream(Utils::streamFor($this->getHeaders($headers))); $stream->addStream($body); - $stream->addStream(stream_for("\r\n")); + $stream->addStream(Utils::streamFor("\r\n")); } - /** - * @return array - */ - private function createElement($name, StreamInterface $stream, $filename, array $headers) + private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array { // Set a default content-disposition header if one was no provided $disposition = $this->getHeader($headers, 'content-disposition'); if (!$disposition) { $headers['Content-Disposition'] = ($filename === '0' || $filename) - ? sprintf('form-data; name="%s"; filename="%s"', + ? sprintf( + 'form-data; name="%s"; filename="%s"', $name, - basename($filename)) + basename($filename) + ) : "form-data; name=\"{$name}\""; } @@ -131,7 +131,7 @@ private function createElement($name, StreamInterface $stream, $filename, array // Set a default Content-Type if one was not supplied $type = $this->getHeader($headers, 'content-type'); if (!$type && ($filename === '0' || $filename)) { - if ($type = mimetype_from_filename($filename)) { + if ($type = MimeType::fromFilename($filename)) { $headers['Content-Type'] = $type; } } @@ -139,7 +139,7 @@ private function createElement($name, StreamInterface $stream, $filename, array return [$stream, $headers]; } - private function getHeader(array $headers, $key) + private function getHeader(array $headers, string $key) { $lowercaseHeader = strtolower($key); foreach ($headers as $k => $v) { diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/tests/integration/vendor/guzzlehttp/psr7/src/NoSeekStream.php index 233221805..99e25b9e3 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/NoSeekStream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/NoSeekStream.php @@ -1,21 +1,24 @@ source = $source; - $this->size = isset($options['size']) ? $options['size'] : null; - $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; + $this->size = $options['size'] ?? null; + $this->metadata = $options['metadata'] ?? []; $this->buffer = new BufferStream(); } - public function __toString() + public function __toString(): string { try { - return copy_to_string($this); - } catch (\Exception $e) { + return Utils::copyToString($this); + } catch (\Throwable $e) { + if (\PHP_VERSION_ID >= 70400) { + throw $e; + } + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } - public function close() + public function close(): void { $this->detach(); } public function detach() { - $this->tellPos = false; + $this->tellPos = 0; $this->source = null; + + return null; } - public function getSize() + public function getSize(): ?int { return $this->size; } - public function tell() + public function tell(): int { return $this->tellPos; } - public function eof() + public function eof(): bool { - return !$this->source; + return $this->source === null; } - public function isSeekable() + public function isSeekable(): bool { return false; } - public function rewind() + public function rewind(): void { $this->seek(0); } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { throw new \RuntimeException('Cannot seek a PumpStream'); } - public function isWritable() + public function isWritable(): bool { return false; } - public function write($string) + public function write($string): int { throw new \RuntimeException('Cannot write to a PumpStream'); } - public function isReadable() + public function isReadable(): bool { return true; } - public function read($length) + public function read($length): string { $data = $this->buffer->read($length); $readLen = strlen($data); @@ -129,7 +138,7 @@ public function read($length) return $data; } - public function getContents() + public function getContents(): string { $result = ''; while (!$this->eof()) { @@ -139,16 +148,21 @@ public function getContents() return $result; } + /** + * {@inheritdoc} + * + * @return mixed + */ public function getMetadata($key = null) { if (!$key) { return $this->metadata; } - return isset($this->metadata[$key]) ? $this->metadata[$key] : null; + return $this->metadata[$key] ?? null; } - private function pump($length) + private function pump(int $length): void { if ($this->source) { do { diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Query.php b/tests/integration/vendor/guzzlehttp/psr7/src/Query.php new file mode 100644 index 000000000..4fd0ca96d --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Query.php @@ -0,0 +1,113 @@ + '1', 'foo[b]' => '2'])`. + * + * @param string $str Query string to parse + * @param int|bool $urlEncoding How the query string is encoded + */ + public static function parse(string $str, $urlEncoding = true): array + { + $result = []; + + if ($str === '') { + return $result; + } + + if ($urlEncoding === true) { + $decoder = function ($value) { + return rawurldecode(str_replace('+', ' ', (string) $value)); + }; + } elseif ($urlEncoding === PHP_QUERY_RFC3986) { + $decoder = 'rawurldecode'; + } elseif ($urlEncoding === PHP_QUERY_RFC1738) { + $decoder = 'urldecode'; + } else { + $decoder = function ($str) { + return $str; + }; + } + + foreach (explode('&', $str) as $kvp) { + $parts = explode('=', $kvp, 2); + $key = $decoder($parts[0]); + $value = isset($parts[1]) ? $decoder($parts[1]) : null; + if (!isset($result[$key])) { + $result[$key] = $value; + } else { + if (!is_array($result[$key])) { + $result[$key] = [$result[$key]]; + } + $result[$key][] = $value; + } + } + + return $result; + } + + /** + * Build a query string from an array of key value pairs. + * + * This function can use the return value of `parse()` to build a query + * string. This function does not modify the provided keys when an array is + * encountered (like `http_build_query()` would). + * + * @param array $params Query string parameters. + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 + * to encode using RFC3986, or PHP_QUERY_RFC1738 + * to encode using RFC1738. + */ + public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string + { + if (!$params) { + return ''; + } + + if ($encoding === false) { + $encoder = function (string $str): string { + return $str; + }; + } elseif ($encoding === PHP_QUERY_RFC3986) { + $encoder = 'rawurlencode'; + } elseif ($encoding === PHP_QUERY_RFC1738) { + $encoder = 'urlencode'; + } else { + throw new \InvalidArgumentException('Invalid type'); + } + + $qs = ''; + foreach ($params as $k => $v) { + $k = $encoder((string) $k); + if (!is_array($v)) { + $qs .= $k; + $v = is_bool($v) ? (int) $v : $v; + if ($v !== null) { + $qs .= '=' . $encoder((string) $v); + } + $qs .= '&'; + } else { + foreach ($v as $vv) { + $qs .= $k; + $vv = is_bool($vv) ? (int) $vv : $vv; + if ($vv !== null) { + $qs .= '=' . $encoder((string) $vv); + } + $qs .= '&'; + } + } + } + + return $qs ? (string) substr($qs, 0, -1) : ''; + } +} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Request.php b/tests/integration/vendor/guzzlehttp/psr7/src/Request.php index 000664240..b17af66a2 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/Request.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Request.php @@ -1,4 +1,7 @@ $headers Request headers + * @param string|resource|StreamInterface|null $body Request body * @param string $version Protocol version */ public function __construct( - $method, + string $method, $uri, array $headers = [], $body = null, - $version = '1.1' + string $version = '1.1' ) { + $this->assertMethod($method); if (!($uri instanceof UriInterface)) { $uri = new Uri($uri); } @@ -50,18 +54,18 @@ public function __construct( } if ($body !== '' && $body !== null) { - $this->stream = stream_for($body); + $this->stream = Utils::streamFor($body); } } - public function getRequestTarget() + public function getRequestTarget(): string { if ($this->requestTarget !== null) { return $this->requestTarget; } $target = $this->uri->getPath(); - if ($target == '') { + if ($target === '') { $target = '/'; } if ($this->uri->getQuery() != '') { @@ -71,7 +75,7 @@ public function getRequestTarget() return $target; } - public function withRequestTarget($requestTarget) + public function withRequestTarget($requestTarget): RequestInterface { if (preg_match('#\s#', $requestTarget)) { throw new InvalidArgumentException( @@ -84,24 +88,25 @@ public function withRequestTarget($requestTarget) return $new; } - public function getMethod() + public function getMethod(): string { return $this->method; } - public function withMethod($method) + public function withMethod($method): RequestInterface { + $this->assertMethod($method); $new = clone $this; $new->method = strtoupper($method); return $new; } - public function getUri() + public function getUri(): UriInterface { return $this->uri; } - public function withUri(UriInterface $uri, $preserveHost = false) + public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface { if ($uri === $this->uri) { return $this; @@ -117,7 +122,7 @@ public function withUri(UriInterface $uri, $preserveHost = false) return $new; } - private function updateHostFromUri() + private function updateHostFromUri(): void { $host = $this->uri->getHost(); @@ -139,4 +144,14 @@ private function updateHostFromUri() // See: http://tools.ietf.org/html/rfc7230#section-5.4 $this->headers = [$header => [$host]] + $this->headers; } + + /** + * @param mixed $method + */ + private function assertMethod($method): void + { + if (!is_string($method) || $method === '') { + throw new InvalidArgumentException('Method must be a non-empty string.'); + } + } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Response.php b/tests/integration/vendor/guzzlehttp/psr7/src/Response.php index 6e72c06b8..4c6ee6f03 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/Response.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Response.php @@ -1,4 +1,7 @@ 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', @@ -33,6 +36,7 @@ class Response implements ResponseInterface 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', @@ -70,42 +74,41 @@ class Response implements ResponseInterface 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', + 510 => 'Not Extended', 511 => 'Network Authentication Required', ]; /** @var string */ - private $reasonPhrase = ''; + private $reasonPhrase; /** @var int */ - private $statusCode = 200; + private $statusCode; /** * @param int $status Status code - * @param array $headers Response headers - * @param string|null|resource|StreamInterface $body Response body + * @param array $headers Response headers + * @param string|resource|StreamInterface|null $body Response body * @param string $version Protocol version * @param string|null $reason Reason phrase (when empty a default will be used based on the status code) */ public function __construct( - $status = 200, + int $status = 200, array $headers = [], $body = null, - $version = '1.1', - $reason = null + string $version = '1.1', + string $reason = null ) { - if (filter_var($status, FILTER_VALIDATE_INT) === false) { - throw new \InvalidArgumentException('Status code must be an integer value.'); - } + $this->assertStatusCodeRange($status); - $this->statusCode = (int) $status; + $this->statusCode = $status; if ($body !== '' && $body !== null) { - $this->stream = stream_for($body); + $this->stream = Utils::streamFor($body); } $this->setHeaders($headers); - if ($reason == '' && isset(self::$phrases[$this->statusCode])) { - $this->reasonPhrase = self::$phrases[$this->statusCode]; + if ($reason == '' && isset(self::PHRASES[$this->statusCode])) { + $this->reasonPhrase = self::PHRASES[$this->statusCode]; } else { $this->reasonPhrase = (string) $reason; } @@ -113,24 +116,45 @@ public function __construct( $this->protocol = $version; } - public function getStatusCode() + public function getStatusCode(): int { return $this->statusCode; } - public function getReasonPhrase() + public function getReasonPhrase(): string { return $this->reasonPhrase; } - public function withStatus($code, $reasonPhrase = '') + public function withStatus($code, $reasonPhrase = ''): ResponseInterface { + $this->assertStatusCodeIsInteger($code); + $code = (int) $code; + $this->assertStatusCodeRange($code); + $new = clone $this; - $new->statusCode = (int) $code; - if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) { - $reasonPhrase = self::$phrases[$new->statusCode]; + $new->statusCode = $code; + if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) { + $reasonPhrase = self::PHRASES[$new->statusCode]; } - $new->reasonPhrase = $reasonPhrase; + $new->reasonPhrase = (string) $reasonPhrase; return $new; } + + /** + * @param mixed $statusCode + */ + private function assertStatusCodeIsInteger($statusCode): void + { + if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) { + throw new \InvalidArgumentException('Status code must be an integer value.'); + } + } + + private function assertStatusCodeRange(int $statusCode): void + { + if ($statusCode < 100 || $statusCode >= 600) { + throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.'); + } + } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Rfc7230.php b/tests/integration/vendor/guzzlehttp/psr7/src/Rfc7230.php index 505e4742b..30224018d 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/Rfc7230.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Rfc7230.php @@ -1,18 +1,23 @@ @,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; - const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; + public const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; + public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/ServerRequest.php b/tests/integration/vendor/guzzlehttp/psr7/src/ServerRequest.php index 99f453a51..6ae3f9beb 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/ServerRequest.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/ServerRequest.php @@ -1,12 +1,14 @@ $headers Request headers + * @param string|resource|StreamInterface|null $body Request body * @param string $version Protocol version * @param array $serverParams Typically the $_SERVER superglobal */ public function __construct( - $method, + string $method, $uri, array $headers = [], $body = null, - $version = '1.1', + string $version = '1.1', array $serverParams = [] ) { $this->serverParams = $serverParams; @@ -79,10 +81,10 @@ public function __construct( * Return an UploadedFile instance array. * * @param array $files A array which respect $_FILES structure + * * @throws InvalidArgumentException for unrecognized values - * @return array */ - public static function normalizeFiles(array $files) + public static function normalizeFiles(array $files): array { $normalized = []; @@ -109,7 +111,8 @@ public static function normalizeFiles(array $files) * delegate to normalizeNestedFileSpec() and return that return value. * * @param array $value $_FILES struct - * @return array|UploadedFileInterface + * + * @return UploadedFileInterface|UploadedFileInterface[] */ private static function createUploadedFileFromSpec(array $value) { @@ -132,10 +135,9 @@ private static function createUploadedFileFromSpec(array $value) * Loops through all nested files and returns a normalized array of * UploadedFileInterface instances. * - * @param array $files * @return UploadedFileInterface[] */ - private static function normalizeNestedFileSpec(array $files = []) + private static function normalizeNestedFileSpec(array $files = []): array { $normalizedFiles = []; @@ -160,15 +162,13 @@ private static function normalizeNestedFileSpec(array $files = []) * $_COOKIE * $_FILES * $_SERVER - * - * @return ServerRequestInterface */ - public static function fromGlobals() + public static function fromGlobals(): ServerRequestInterface { - $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $headers = getallheaders(); $uri = self::getUriFromGlobals(); - $body = new LazyOpenStream('php://input', 'r+'); + $body = new CachingStream(new LazyOpenStream('php://input', 'r+')); $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); @@ -180,26 +180,24 @@ public static function fromGlobals() ->withUploadedFiles(self::normalizeFiles($_FILES)); } - private static function extractHostAndPortFromAuthority($authority) + private static function extractHostAndPortFromAuthority(string $authority): array { - $uri = 'http://'.$authority; + $uri = 'http://' . $authority; $parts = parse_url($uri); if (false === $parts) { return [null, null]; } - $host = isset($parts['host']) ? $parts['host'] : null; - $port = isset($parts['port']) ? $parts['port'] : null; + $host = $parts['host'] ?? null; + $port = $parts['port'] ?? null; return [$host, $port]; } /** * Get a Uri populated with values from $_SERVER. - * - * @return UriInterface */ - public static function getUriFromGlobals() + public static function getUriFromGlobals(): UriInterface { $uri = new Uri(''); @@ -207,7 +205,7 @@ public static function getUriFromGlobals() $hasPort = false; if (isset($_SERVER['HTTP_HOST'])) { - list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); + [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); if ($host !== null) { $uri = $uri->withHost($host); } @@ -243,27 +241,17 @@ public static function getUriFromGlobals() return $uri; } - - /** - * {@inheritdoc} - */ - public function getServerParams() + public function getServerParams(): array { return $this->serverParams; } - /** - * {@inheritdoc} - */ - public function getUploadedFiles() + public function getUploadedFiles(): array { return $this->uploadedFiles; } - /** - * {@inheritdoc} - */ - public function withUploadedFiles(array $uploadedFiles) + public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface { $new = clone $this; $new->uploadedFiles = $uploadedFiles; @@ -271,18 +259,12 @@ public function withUploadedFiles(array $uploadedFiles) return $new; } - /** - * {@inheritdoc} - */ - public function getCookieParams() + public function getCookieParams(): array { return $this->cookieParams; } - /** - * {@inheritdoc} - */ - public function withCookieParams(array $cookies) + public function withCookieParams(array $cookies): ServerRequestInterface { $new = clone $this; $new->cookieParams = $cookies; @@ -290,18 +272,12 @@ public function withCookieParams(array $cookies) return $new; } - /** - * {@inheritdoc} - */ - public function getQueryParams() + public function getQueryParams(): array { return $this->queryParams; } - /** - * {@inheritdoc} - */ - public function withQueryParams(array $query) + public function withQueryParams(array $query): ServerRequestInterface { $new = clone $this; $new->queryParams = $query; @@ -311,16 +287,15 @@ public function withQueryParams(array $query) /** * {@inheritdoc} + * + * @return array|object|null */ public function getParsedBody() { return $this->parsedBody; } - /** - * {@inheritdoc} - */ - public function withParsedBody($data) + public function withParsedBody($data): ServerRequestInterface { $new = clone $this; $new->parsedBody = $data; @@ -328,16 +303,15 @@ public function withParsedBody($data) return $new; } - /** - * {@inheritdoc} - */ - public function getAttributes() + public function getAttributes(): array { return $this->attributes; } /** * {@inheritdoc} + * + * @return mixed */ public function getAttribute($attribute, $default = null) { @@ -348,10 +322,7 @@ public function getAttribute($attribute, $default = null) return $this->attributes[$attribute]; } - /** - * {@inheritdoc} - */ - public function withAttribute($attribute, $value) + public function withAttribute($attribute, $value): ServerRequestInterface { $new = clone $this; $new->attributes[$attribute] = $value; @@ -359,10 +330,7 @@ public function withAttribute($attribute, $value) return $new; } - /** - * {@inheritdoc} - */ - public function withoutAttribute($attribute) + public function withoutAttribute($attribute): ServerRequestInterface { if (false === array_key_exists($attribute, $this->attributes)) { return $this; diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Stream.php b/tests/integration/vendor/guzzlehttp/psr7/src/Stream.php index 111795eb0..d389427c6 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/Stream.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Stream.php @@ -1,39 +1,38 @@ [ - 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, - 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a+' => true, 'rb+' => true, - ], - 'write' => [ - 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, - 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, 'rb+' => true, - 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true - ] - ]; - /** * This constructor accepts an associative array of options. * @@ -43,12 +42,12 @@ class Stream implements StreamInterface * - metadata: (array) Any additional metadata to return when the metadata * of the stream is accessed. * - * @param resource $stream Stream resource to wrap. - * @param array $options Associative array of options. + * @param resource $stream Stream resource to wrap. + * @param array{size?: int, metadata?: array} $options Associative array of options. * * @throws \InvalidArgumentException if the stream is not a stream resource */ - public function __construct($stream, $options = []) + public function __construct($stream, array $options = []) { if (!is_resource($stream)) { throw new \InvalidArgumentException('Stream must be a resource'); @@ -58,15 +57,12 @@ public function __construct($stream, $options = []) $this->size = $options['size']; } - $this->customMetadata = isset($options['metadata']) - ? $options['metadata'] - : []; - + $this->customMetadata = $options['metadata'] ?? []; $this->stream = $stream; $meta = stream_get_meta_data($this->stream); $this->seekable = $meta['seekable']; - $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); - $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']); + $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']); $this->uri = $this->getMetadata('uri'); } @@ -78,17 +74,23 @@ public function __destruct() $this->close(); } - public function __toString() + public function __toString(): string { try { - $this->seek(0); - return (string) stream_get_contents($this->stream); - } catch (\Exception $e) { + if ($this->isSeekable()) { + $this->seek(0); + } + return $this->getContents(); + } catch (\Throwable $e) { + if (\PHP_VERSION_ID >= 70400) { + throw $e; + } + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } - public function getContents() + public function getContents(): string { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); @@ -103,7 +105,7 @@ public function getContents() return $contents; } - public function close() + public function close(): void { if (isset($this->stream)) { if (is_resource($this->stream)) { @@ -127,7 +129,7 @@ public function detach() return $result; } - public function getSize() + public function getSize(): ?int { if ($this->size !== null) { return $this->size; @@ -143,7 +145,7 @@ public function getSize() } $stats = fstat($this->stream); - if (isset($stats['size'])) { + if (is_array($stats) && isset($stats['size'])) { $this->size = $stats['size']; return $this->size; } @@ -151,22 +153,22 @@ public function getSize() return null; } - public function isReadable() + public function isReadable(): bool { return $this->readable; } - public function isWritable() + public function isWritable(): bool { return $this->writable; } - public function isSeekable() + public function isSeekable(): bool { return $this->seekable; } - public function eof() + public function eof(): bool { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); @@ -175,7 +177,7 @@ public function eof() return feof($this->stream); } - public function tell() + public function tell(): int { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); @@ -190,13 +192,15 @@ public function tell() return $result; } - public function rewind() + public function rewind(): void { $this->seek(0); } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { + $whence = (int) $whence; + if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } @@ -209,7 +213,7 @@ public function seek($offset, $whence = SEEK_SET) } } - public function read($length) + public function read($length): string { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); @@ -233,7 +237,7 @@ public function read($length) return $string; } - public function write($string) + public function write($string): int { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); @@ -253,6 +257,11 @@ public function write($string) return $result; } + /** + * {@inheritdoc} + * + * @return mixed + */ public function getMetadata($key = null) { if (!isset($this->stream)) { @@ -265,6 +274,6 @@ public function getMetadata($key = null) $meta = stream_get_meta_data($this->stream); - return isset($meta[$key]) ? $meta[$key] : null; + return $meta[$key] ?? null; } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/tests/integration/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php index daec6f52e..56d4104d4 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php @@ -1,11 +1,15 @@ stream = $this->createStream(); return $this->stream; } @@ -35,47 +37,52 @@ public function __get($name) throw new \UnexpectedValueException("$name not found on class"); } - public function __toString() + public function __toString(): string { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); - } catch (\Exception $e) { - // Really, PHP? https://bugs.php.net/bug.php?id=53648 - trigger_error('StreamDecorator::__toString exception: ' - . (string) $e, E_USER_ERROR); + } catch (\Throwable $e) { + if (\PHP_VERSION_ID >= 70400) { + throw $e; + } + trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } - public function getContents() + public function getContents(): string { - return copy_to_string($this); + return Utils::copyToString($this); } /** * Allow decorators to implement custom methods * - * @param string $method Missing method name - * @param array $args Method arguments - * * @return mixed */ - public function __call($method, array $args) + public function __call(string $method, array $args) { - $result = call_user_func_array([$this->stream, $method], $args); + /** @var callable $callable */ + $callable = [$this->stream, $method]; + $result = call_user_func_array($callable, $args); // Always return the wrapped object if the result is a return $this return $result === $this->stream ? $this : $result; } - public function close() + public function close(): void { $this->stream->close(); } + /** + * {@inheritdoc} + * + * @return mixed + */ public function getMetadata($key = null) { return $this->stream->getMetadata($key); @@ -86,52 +93,52 @@ public function detach() return $this->stream->detach(); } - public function getSize() + public function getSize(): ?int { return $this->stream->getSize(); } - public function eof() + public function eof(): bool { return $this->stream->eof(); } - public function tell() + public function tell(): int { return $this->stream->tell(); } - public function isReadable() + public function isReadable(): bool { return $this->stream->isReadable(); } - public function isWritable() + public function isWritable(): bool { return $this->stream->isWritable(); } - public function isSeekable() + public function isSeekable(): bool { return $this->stream->isSeekable(); } - public function rewind() + public function rewind(): void { $this->seek(0); } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { $this->stream->seek($offset, $whence); } - public function read($length) + public function read($length): string { return $this->stream->read($length); } - public function write($string) + public function write($string): int { return $this->stream->write($string); } @@ -139,10 +146,9 @@ public function write($string) /** * Implement in subclasses to dynamically create streams when requested. * - * @return StreamInterface * @throws \BadMethodCallException */ - protected function createStream() + protected function createStream(): StreamInterface { throw new \BadMethodCallException('Not implemented'); } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/tests/integration/vendor/guzzlehttp/psr7/src/StreamWrapper.php index 0f3a2856a..2a9346403 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/StreamWrapper.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/StreamWrapper.php @@ -1,12 +1,17 @@ context); @@ -79,41 +83,48 @@ public function stream_open($path, $mode, $options, &$opened_path) return true; } - public function stream_read($count) + public function stream_read(int $count): string { return $this->stream->read($count); } - public function stream_write($data) + public function stream_write(string $data): int { - return (int) $this->stream->write($data); + return $this->stream->write($data); } - public function stream_tell() + public function stream_tell(): int { return $this->stream->tell(); } - public function stream_eof() + public function stream_eof(): bool { return $this->stream->eof(); } - public function stream_seek($offset, $whence) + public function stream_seek(int $offset, int $whence): bool { $this->stream->seek($offset, $whence); return true; } - public function stream_cast($cast_as) + /** + * @return resource|false + */ + public function stream_cast(int $cast_as) { $stream = clone($this->stream); + $resource = $stream->detach(); - return $stream->detach(); + return $resource ?? false; } - public function stream_stat() + /** + * @return array + */ + public function stream_stat(): array { static $modeMap = [ 'r' => 33060, @@ -140,7 +151,10 @@ public function stream_stat() ]; } - public function url_stat($path, $flags) + /** + * @return array + */ + public function url_stat(string $path, int $flags): array { return [ 'dev' => 0, diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/UploadedFile.php b/tests/integration/vendor/guzzlehttp/psr7/src/UploadedFile.php index e62bd5c80..b1521bcf8 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/UploadedFile.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/UploadedFile.php @@ -1,4 +1,7 @@ setError($errorStatus); - $this->setSize($size); - $this->setClientFilename($clientFilename); - $this->setClientMediaType($clientMediaType); + $this->size = $size; + $this->clientFilename = $clientFilename; + $this->clientMediaType = $clientMediaType; if ($this->isOk()) { $this->setStreamOrFile($streamOrFile); @@ -84,10 +80,11 @@ public function __construct( /** * Depending on the value set file or stream variable * - * @param mixed $streamOrFile + * @param StreamInterface|string|resource $streamOrFile + * * @throws InvalidArgumentException */ - private function setStreamOrFile($streamOrFile) + private function setStreamOrFile($streamOrFile): void { if (is_string($streamOrFile)) { $this->file = $streamOrFile; @@ -103,18 +100,11 @@ private function setStreamOrFile($streamOrFile) } /** - * @param int $error * @throws InvalidArgumentException */ - private function setError($error) + private function setError(int $error): void { - if (false === is_int($error)) { - throw new InvalidArgumentException( - 'Upload file error status must be an integer' - ); - } - - if (false === in_array($error, UploadedFile::$errors)) { + if (false === in_array($error, UploadedFile::ERRORS, true)) { throw new InvalidArgumentException( 'Invalid error status for UploadedFile' ); @@ -123,83 +113,20 @@ private function setError($error) $this->error = $error; } - /** - * @param int $size - * @throws InvalidArgumentException - */ - private function setSize($size) - { - if (false === is_int($size)) { - throw new InvalidArgumentException( - 'Upload file size must be an integer' - ); - } - - $this->size = $size; - } - - /** - * @param mixed $param - * @return boolean - */ - private function isStringOrNull($param) - { - return in_array(gettype($param), ['string', 'NULL']); - } - - /** - * @param mixed $param - * @return boolean - */ - private function isStringNotEmpty($param) + private function isStringNotEmpty($param): bool { return is_string($param) && false === empty($param); } - /** - * @param string|null $clientFilename - * @throws InvalidArgumentException - */ - private function setClientFilename($clientFilename) - { - if (false === $this->isStringOrNull($clientFilename)) { - throw new InvalidArgumentException( - 'Upload file client filename must be a string or null' - ); - } - - $this->clientFilename = $clientFilename; - } - - /** - * @param string|null $clientMediaType - * @throws InvalidArgumentException - */ - private function setClientMediaType($clientMediaType) - { - if (false === $this->isStringOrNull($clientMediaType)) { - throw new InvalidArgumentException( - 'Upload file client media type must be a string or null' - ); - } - - $this->clientMediaType = $clientMediaType; - } - /** * Return true if there is no upload error - * - * @return boolean */ - private function isOk() + private function isOk(): bool { return $this->error === UPLOAD_ERR_OK; } - /** - * @return boolean - */ - public function isMoved() + public function isMoved(): bool { return $this->moved; } @@ -207,7 +134,7 @@ public function isMoved() /** * @throws RuntimeException if is moved or not ok */ - private function validateActive() + private function validateActive(): void { if (false === $this->isOk()) { throw new RuntimeException('Cannot retrieve stream due to upload error'); @@ -218,11 +145,7 @@ private function validateActive() } } - /** - * {@inheritdoc} - * @throws RuntimeException if the upload was not successful. - */ - public function getStream() + public function getStream(): StreamInterface { $this->validateActive(); @@ -230,21 +153,13 @@ public function getStream() return $this->stream; } - return new LazyOpenStream($this->file, 'r+'); + /** @var string $file */ + $file = $this->file; + + return new LazyOpenStream($file, 'r+'); } - /** - * {@inheritdoc} - * - * @see http://php.net/is_uploaded_file - * @see http://php.net/move_uploaded_file - * @param string $targetPath Path to which to move the uploaded file. - * @throws RuntimeException if the upload was not successful. - * @throws InvalidArgumentException if the $path specified is invalid. - * @throws RuntimeException on any error during the move operation, or on - * the second or subsequent call to the method. - */ - public function moveTo($targetPath) + public function moveTo($targetPath): void { $this->validateActive(); @@ -255,11 +170,11 @@ public function moveTo($targetPath) } if ($this->file) { - $this->moved = php_sapi_name() == 'cli' + $this->moved = PHP_SAPI === 'cli' ? rename($this->file, $targetPath) : move_uploaded_file($this->file, $targetPath); } else { - copy_to_stream( + Utils::copyToStream( $this->getStream(), new LazyOpenStream($targetPath, 'w') ); @@ -274,42 +189,22 @@ public function moveTo($targetPath) } } - /** - * {@inheritdoc} - * - * @return int|null The file size in bytes or null if unknown. - */ - public function getSize() + public function getSize(): ?int { return $this->size; } - /** - * {@inheritdoc} - * - * @see http://php.net/manual/en/features.file-upload.errors.php - * @return int One of PHP's UPLOAD_ERR_XXX constants. - */ - public function getError() + public function getError(): int { return $this->error; } - /** - * {@inheritdoc} - * - * @return string|null The filename sent by the client or null if none - * was provided. - */ - public function getClientFilename() + public function getClientFilename(): ?string { return $this->clientFilename; } - /** - * {@inheritdoc} - */ - public function getClientMediaType() + public function getClientMediaType(): ?string { return $this->clientMediaType; } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Uri.php b/tests/integration/vendor/guzzlehttp/psr7/src/Uri.php index 36219568c..3898f7830 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/Uri.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Uri.php @@ -1,6 +1,10 @@ 80, 'https' => 443, 'ftp' => 21, @@ -34,9 +38,20 @@ class Uri implements UriInterface 'ldap' => 389, ]; - private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; - private static $charSubDelims = '!\$&\'\(\)\*\+,;='; - private static $replaceQuery = ['=' => '%3D', '&' => '%26']; + /** + * Unreserved characters for use in a regex. + * + * @link https://tools.ietf.org/html/rfc3986#section-2.3 + */ + private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~'; + + /** + * Sub-delims for use in a regex. + * + * @link https://tools.ietf.org/html/rfc3986#section-2.2 + */ + private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;='; + private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26']; /** @var string Uri scheme. */ private $scheme = ''; @@ -59,30 +74,75 @@ class Uri implements UriInterface /** @var string Uri fragment. */ private $fragment = ''; - /** - * @param string $uri URI to parse - */ - public function __construct($uri = '') + /** @var string|null String representation */ + private $composedComponents; + + public function __construct(string $uri = '') { - // weak type check to also accept null until we can add scalar type hints - if ($uri != '') { - $parts = parse_url($uri); + if ($uri !== '') { + $parts = self::parse($uri); if ($parts === false) { - throw new \InvalidArgumentException("Unable to parse URI: $uri"); + throw new MalformedUriException("Unable to parse URI: $uri"); } $this->applyParts($parts); } } + /** + * UTF-8 aware \parse_url() replacement. + * + * The internal function produces broken output for non ASCII domain names + * (IDN) when used with locales other than "C". + * + * On the other hand, cURL understands IDN correctly only when UTF-8 locale + * is configured ("C.UTF-8", "en_US.UTF-8", etc.). + * + * @see https://bugs.php.net/bug.php?id=52923 + * @see https://www.php.net/manual/en/function.parse-url.php#114817 + * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING + * + * @return array|false + */ + private static function parse(string $url) + { + // If IPv6 + $prefix = ''; + if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) { + /** @var array{0:string, 1:string, 2:string} $matches */ + $prefix = $matches[1]; + $url = $matches[2]; + } - public function __toString() - { - return self::composeComponents( - $this->scheme, - $this->getAuthority(), - $this->path, - $this->query, - $this->fragment + /** @var string */ + $encodedUrl = preg_replace_callback( + '%[^:/@?&=#]+%usD', + static function ($matches) { + return urlencode($matches[0]); + }, + $url ); + + $result = parse_url($prefix . $encodedUrl); + + if ($result === false) { + return false; + } + + return array_map('urldecode', $result); + } + + public function __toString(): string + { + if ($this->composedComponents === null) { + $this->composedComponents = self::composeComponents( + $this->scheme, + $this->getAuthority(), + $this->path, + $this->query, + $this->fragment + ); + } + + return $this->composedComponents; } /** @@ -101,17 +161,9 @@ public function __toString() * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to * that format). * - * @param string $scheme - * @param string $authority - * @param string $path - * @param string $query - * @param string $fragment - * - * @return string - * * @link https://tools.ietf.org/html/rfc3986#section-5.3 */ - public static function composeComponents($scheme, $authority, $path, $query, $fragment) + public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string { $uri = ''; @@ -142,15 +194,11 @@ public static function composeComponents($scheme, $authority, $path, $query, $fr * * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used * independently of the implementation. - * - * @param UriInterface $uri - * - * @return bool */ - public static function isDefaultPort(UriInterface $uri) + public static function isDefaultPort(UriInterface $uri): bool { return $uri->getPort() === null - || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]); + || (isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()]); } /** @@ -163,15 +211,12 @@ public static function isDefaultPort(UriInterface $uri) * - absolute-path references, e.g. '/path' * - relative-path references, e.g. 'subpath' * - * @param UriInterface $uri - * - * @return bool * @see Uri::isNetworkPathReference * @see Uri::isAbsolutePathReference * @see Uri::isRelativePathReference * @link https://tools.ietf.org/html/rfc3986#section-4 */ - public static function isAbsolute(UriInterface $uri) + public static function isAbsolute(UriInterface $uri): bool { return $uri->getScheme() !== ''; } @@ -181,12 +226,9 @@ public static function isAbsolute(UriInterface $uri) * * A relative reference that begins with two slash characters is termed an network-path reference. * - * @param UriInterface $uri - * - * @return bool * @link https://tools.ietf.org/html/rfc3986#section-4.2 */ - public static function isNetworkPathReference(UriInterface $uri) + public static function isNetworkPathReference(UriInterface $uri): bool { return $uri->getScheme() === '' && $uri->getAuthority() !== ''; } @@ -196,12 +238,9 @@ public static function isNetworkPathReference(UriInterface $uri) * * A relative reference that begins with a single slash character is termed an absolute-path reference. * - * @param UriInterface $uri - * - * @return bool * @link https://tools.ietf.org/html/rfc3986#section-4.2 */ - public static function isAbsolutePathReference(UriInterface $uri) + public static function isAbsolutePathReference(UriInterface $uri): bool { return $uri->getScheme() === '' && $uri->getAuthority() === '' @@ -214,12 +253,9 @@ public static function isAbsolutePathReference(UriInterface $uri) * * A relative reference that does not begin with a slash character is termed a relative-path reference. * - * @param UriInterface $uri - * - * @return bool * @link https://tools.ietf.org/html/rfc3986#section-4.2 */ - public static function isRelativePathReference(UriInterface $uri) + public static function isRelativePathReference(UriInterface $uri): bool { return $uri->getScheme() === '' && $uri->getAuthority() === '' @@ -236,10 +272,9 @@ public static function isRelativePathReference(UriInterface $uri) * @param UriInterface $uri The URI to check * @param UriInterface|null $base An optional base URI to compare against * - * @return bool * @link https://tools.ietf.org/html/rfc3986#section-4.4 */ - public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null) + public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool { if ($base !== null) { $uri = UriResolver::resolve($base, $uri); @@ -253,41 +288,6 @@ public static function isSameDocumentReference(UriInterface $uri, UriInterface $ return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === ''; } - /** - * Removes dot segments from a path and returns the new path. - * - * @param string $path - * - * @return string - * - * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead. - * @see UriResolver::removeDotSegments - */ - public static function removeDotSegments($path) - { - return UriResolver::removeDotSegments($path); - } - - /** - * Converts the relative URI into a new URI that is resolved against the base URI. - * - * @param UriInterface $base Base URI - * @param string|UriInterface $rel Relative URI - * - * @return UriInterface - * - * @deprecated since version 1.4. Use UriResolver::resolve instead. - * @see UriResolver::resolve - */ - public static function resolve(UriInterface $base, $rel) - { - if (!($rel instanceof UriInterface)) { - $rel = new self($rel); - } - - return UriResolver::resolve($base, $rel); - } - /** * Creates a new URI with a specific query string value removed. * @@ -296,10 +296,8 @@ public static function resolve(UriInterface $base, $rel) * * @param UriInterface $uri URI to use as a base. * @param string $key Query string key to remove. - * - * @return UriInterface */ - public static function withoutQueryValue(UriInterface $uri, $key) + public static function withoutQueryValue(UriInterface $uri, string $key): UriInterface { $result = self::getFilteredQueryString($uri, [$key]); @@ -318,10 +316,8 @@ public static function withoutQueryValue(UriInterface $uri, $key) * @param UriInterface $uri URI to use as a base. * @param string $key Key to set. * @param string|null $value Value to set - * - * @return UriInterface */ - public static function withQueryValue(UriInterface $uri, $key, $value) + public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface { $result = self::getFilteredQueryString($uri, [$key]); @@ -335,17 +331,15 @@ public static function withQueryValue(UriInterface $uri, $key, $value) * * It has the same behavior as withQueryValue() but for an associative array of key => value. * - * @param UriInterface $uri URI to use as a base. - * @param array $keyValueArray Associative array of key and values - * - * @return UriInterface + * @param UriInterface $uri URI to use as a base. + * @param array $keyValueArray Associative array of key and values */ - public static function withQueryValues(UriInterface $uri, array $keyValueArray) + public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface { $result = self::getFilteredQueryString($uri, array_keys($keyValueArray)); foreach ($keyValueArray as $key => $value) { - $result[] = self::generateQueryString($key, $value); + $result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null); } return $uri->withQuery(implode('&', $result)); @@ -354,14 +348,11 @@ public static function withQueryValues(UriInterface $uri, array $keyValueArray) /** * Creates a URI from a hash of `parse_url` components. * - * @param array $parts - * - * @return UriInterface * @link http://php.net/manual/en/function.parse-url.php * - * @throws \InvalidArgumentException If the components do not form a valid URI. + * @throws MalformedUriException If the components do not form a valid URI. */ - public static function fromParts(array $parts) + public static function fromParts(array $parts): UriInterface { $uri = new self(); $uri->applyParts($parts); @@ -370,12 +361,12 @@ public static function fromParts(array $parts) return $uri; } - public function getScheme() + public function getScheme(): string { return $this->scheme; } - public function getAuthority() + public function getAuthority(): string { $authority = $this->host; if ($this->userInfo !== '') { @@ -389,37 +380,37 @@ public function getAuthority() return $authority; } - public function getUserInfo() + public function getUserInfo(): string { return $this->userInfo; } - public function getHost() + public function getHost(): string { return $this->host; } - public function getPort() + public function getPort(): ?int { return $this->port; } - public function getPath() + public function getPath(): string { return $this->path; } - public function getQuery() + public function getQuery(): string { return $this->query; } - public function getFragment() + public function getFragment(): string { return $this->fragment; } - public function withScheme($scheme) + public function withScheme($scheme): UriInterface { $scheme = $this->filterScheme($scheme); @@ -429,17 +420,18 @@ public function withScheme($scheme) $new = clone $this; $new->scheme = $scheme; + $new->composedComponents = null; $new->removeDefaultPort(); $new->validateState(); return $new; } - public function withUserInfo($user, $password = null) + public function withUserInfo($user, $password = null): UriInterface { - $info = $user; - if ($password != '') { - $info .= ':' . $password; + $info = $this->filterUserInfoComponent($user); + if ($password !== null) { + $info .= ':' . $this->filterUserInfoComponent($password); } if ($this->userInfo === $info) { @@ -448,12 +440,13 @@ public function withUserInfo($user, $password = null) $new = clone $this; $new->userInfo = $info; + $new->composedComponents = null; $new->validateState(); return $new; } - public function withHost($host) + public function withHost($host): UriInterface { $host = $this->filterHost($host); @@ -463,12 +456,13 @@ public function withHost($host) $new = clone $this; $new->host = $host; + $new->composedComponents = null; $new->validateState(); return $new; } - public function withPort($port) + public function withPort($port): UriInterface { $port = $this->filterPort($port); @@ -478,13 +472,14 @@ public function withPort($port) $new = clone $this; $new->port = $port; + $new->composedComponents = null; $new->removeDefaultPort(); $new->validateState(); return $new; } - public function withPath($path) + public function withPath($path): UriInterface { $path = $this->filterPath($path); @@ -494,12 +489,13 @@ public function withPath($path) $new = clone $this; $new->path = $path; + $new->composedComponents = null; $new->validateState(); return $new; } - public function withQuery($query) + public function withQuery($query): UriInterface { $query = $this->filterQueryAndFragment($query); @@ -509,11 +505,12 @@ public function withQuery($query) $new = clone $this; $new->query = $query; + $new->composedComponents = null; return $new; } - public function withFragment($fragment) + public function withFragment($fragment): UriInterface { $fragment = $this->filterQueryAndFragment($fragment); @@ -523,6 +520,7 @@ public function withFragment($fragment) $new = clone $this; $new->fragment = $fragment; + $new->composedComponents = null; return $new; } @@ -532,12 +530,14 @@ public function withFragment($fragment) * * @param array $parts Array of parse_url parts to apply. */ - private function applyParts(array $parts) + private function applyParts(array $parts): void { $this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : ''; - $this->userInfo = isset($parts['user']) ? $parts['user'] : ''; + $this->userInfo = isset($parts['user']) + ? $this->filterUserInfoComponent($parts['user']) + : ''; $this->host = isset($parts['host']) ? $this->filterHost($parts['host']) : ''; @@ -554,61 +554,73 @@ private function applyParts(array $parts) ? $this->filterQueryAndFragment($parts['fragment']) : ''; if (isset($parts['pass'])) { - $this->userInfo .= ':' . $parts['pass']; + $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']); } $this->removeDefaultPort(); } /** - * @param string $scheme - * - * @return string + * @param mixed $scheme * * @throws \InvalidArgumentException If the scheme is invalid. */ - private function filterScheme($scheme) + private function filterScheme($scheme): string { if (!is_string($scheme)) { throw new \InvalidArgumentException('Scheme must be a string'); } - return strtolower($scheme); + return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } /** - * @param string $host + * @param mixed $component * - * @return string + * @throws \InvalidArgumentException If the user info is invalid. + */ + private function filterUserInfoComponent($component): string + { + if (!is_string($component)) { + throw new \InvalidArgumentException('User info must be a string'); + } + + return preg_replace_callback( + '/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/', + [$this, 'rawurlencodeMatchZero'], + $component + ); + } + + /** + * @param mixed $host * * @throws \InvalidArgumentException If the host is invalid. */ - private function filterHost($host) + private function filterHost($host): string { if (!is_string($host)) { throw new \InvalidArgumentException('Host must be a string'); } - return strtolower($host); + return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } /** - * @param int|null $port - * - * @return int|null + * @param mixed $port * * @throws \InvalidArgumentException If the port is invalid. */ - private function filterPort($port) + private function filterPort($port): ?int { if ($port === null) { return null; } $port = (int) $port; - if (1 > $port || 0xffff < $port) { + if (0 > $port || 0xffff < $port) { throw new \InvalidArgumentException( - sprintf('Invalid port: %d. Must be between 1 and 65535', $port) + sprintf('Invalid port: %d. Must be between 0 and 65535', $port) ); } @@ -616,12 +628,11 @@ private function filterPort($port) } /** - * @param UriInterface $uri - * @param array $keys - * - * @return array + * @param string[] $keys + * + * @return string[] */ - private static function getFilteredQueryString(UriInterface $uri, array $keys) + private static function getFilteredQueryString(UriInterface $uri, array $keys): array { $current = $uri->getQuery(); @@ -636,27 +647,21 @@ private static function getFilteredQueryString(UriInterface $uri, array $keys) }); } - /** - * @param string $key - * @param string|null $value - * - * @return string - */ - private static function generateQueryString($key, $value) + private static function generateQueryString(string $key, ?string $value): string { // Query string separators ("=", "&") within the key or value need to be encoded // (while preventing double-encoding) before setting the query string. All other // chars that need percent-encoding will be encoded by withQuery(). - $queryString = strtr($key, self::$replaceQuery); + $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT); if ($value !== null) { - $queryString .= '=' . strtr($value, self::$replaceQuery); + $queryString .= '=' . strtr($value, self::QUERY_SEPARATORS_REPLACEMENT); } return $queryString; } - private function removeDefaultPort() + private function removeDefaultPort(): void { if ($this->port !== null && self::isDefaultPort($this)) { $this->port = null; @@ -666,20 +671,18 @@ private function removeDefaultPort() /** * Filters the path of a URI * - * @param string $path - * - * @return string + * @param mixed $path * * @throws \InvalidArgumentException If the path is invalid. */ - private function filterPath($path) + private function filterPath($path): string { if (!is_string($path)) { throw new \InvalidArgumentException('Path must be a string'); } return preg_replace_callback( - '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', + '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $path ); @@ -688,31 +691,29 @@ private function filterPath($path) /** * Filters the query string or fragment of a URI. * - * @param string $str - * - * @return string + * @param mixed $str * * @throws \InvalidArgumentException If the query or fragment is invalid. */ - private function filterQueryAndFragment($str) + private function filterQueryAndFragment($str): string { if (!is_string($str)) { throw new \InvalidArgumentException('Query and fragment must be a string'); } return preg_replace_callback( - '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', + '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $str ); } - private function rawurlencodeMatchZero(array $match) + private function rawurlencodeMatchZero(array $match): string { return rawurlencode($match[0]); } - private function validateState() + private function validateState(): void { if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { $this->host = self::HTTP_DEFAULT_HOST; @@ -720,19 +721,13 @@ private function validateState() if ($this->getAuthority() === '') { if (0 === strpos($this->path, '//')) { - throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"'); + throw new MalformedUriException('The path of a URI without an authority must not start with two slashes "//"'); } if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) { - throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon'); + throw new MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon'); } } elseif (isset($this->path[0]) && $this->path[0] !== '/') { - @trigger_error( - 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' . - 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.', - E_USER_DEPRECATED - ); - $this->path = '/'. $this->path; - //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty'); + throw new MalformedUriException('The path of a URI with an authority must start with a slash "/" or be empty'); } } } diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/tests/integration/vendor/guzzlehttp/psr7/src/UriNormalizer.php index 384c29e50..e12971edd 100644 --- a/tests/integration/vendor/guzzlehttp/psr7/src/UriNormalizer.php +++ b/tests/integration/vendor/guzzlehttp/psr7/src/UriNormalizer.php @@ -1,4 +1,7 @@ getScheme() !== '' && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') @@ -171,6 +162,7 @@ public static function relativize(UriInterface $base, UriInterface $target) // inherit the base query component when resolving. if ($target->getQuery() === '') { $segments = explode('/', $target->getPath()); + /** @var string $lastSegment */ $lastSegment = end($segments); return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); @@ -179,7 +171,7 @@ public static function relativize(UriInterface $base, UriInterface $target) return $emptyPathUri; } - private static function getRelativePath(UriInterface $base, UriInterface $target) + private static function getRelativePath(UriInterface $base, UriInterface $target): string { $sourceSegments = explode('/', $base->getPath()); $targetSegments = explode('/', $target->getPath()); diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/Utils.php b/tests/integration/vendor/guzzlehttp/psr7/src/Utils.php new file mode 100644 index 000000000..6cc04ee4f --- /dev/null +++ b/tests/integration/vendor/guzzlehttp/psr7/src/Utils.php @@ -0,0 +1,412 @@ + $v) { + if (!is_string($k) || !in_array(strtolower($k), $keys)) { + $result[$k] = $v; + } + } + + return $result; + } + + /** + * Copy the contents of a stream into another stream until the given number + * of bytes have been read. + * + * @param StreamInterface $source Stream to read from + * @param StreamInterface $dest Stream to write to + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * + * @throws \RuntimeException on error. + */ + public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void + { + $bufferSize = 8192; + + if ($maxLen === -1) { + while (!$source->eof()) { + if (!$dest->write($source->read($bufferSize))) { + break; + } + } + } else { + $remaining = $maxLen; + while ($remaining > 0 && !$source->eof()) { + $buf = $source->read(min($bufferSize, $remaining)); + $len = strlen($buf); + if (!$len) { + break; + } + $remaining -= $len; + $dest->write($buf); + } + } + } + + /** + * Copy the contents of a stream into a string until the given number of + * bytes have been read. + * + * @param StreamInterface $stream Stream to read + * @param int $maxLen Maximum number of bytes to read. Pass -1 + * to read the entire stream. + * + * @throws \RuntimeException on error. + */ + public static function copyToString(StreamInterface $stream, int $maxLen = -1): string + { + $buffer = ''; + + if ($maxLen === -1) { + while (!$stream->eof()) { + $buf = $stream->read(1048576); + if ($buf === '') { + break; + } + $buffer .= $buf; + } + return $buffer; + } + + $len = 0; + while (!$stream->eof() && $len < $maxLen) { + $buf = $stream->read($maxLen - $len); + if ($buf === '') { + break; + } + $buffer .= $buf; + $len = strlen($buffer); + } + + return $buffer; + } + + /** + * Calculate a hash of a stream. + * + * This method reads the entire stream to calculate a rolling hash, based + * on PHP's `hash_init` functions. + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @throws \RuntimeException on error. + */ + public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string + { + $pos = $stream->tell(); + + if ($pos > 0) { + $stream->rewind(); + } + + $ctx = hash_init($algo); + while (!$stream->eof()) { + hash_update($ctx, $stream->read(1048576)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + /** + * Clone and modify a request with the given changes. + * + * This method is useful for reducing the number of clones needed to mutate + * a message. + * + * The changes can be one of: + * - method: (string) Changes the HTTP method. + * - set_headers: (array) Sets the given headers. + * - remove_headers: (array) Remove the given headers. + * - body: (mixed) Sets the given body. + * - uri: (UriInterface) Set the URI. + * - query: (string) Set the query string value of the URI. + * - version: (string) Set the protocol version. + * + * @param RequestInterface $request Request to clone and modify. + * @param array $changes Changes to apply. + */ + public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface + { + if (!$changes) { + return $request; + } + + $headers = $request->getHeaders(); + + if (!isset($changes['uri'])) { + $uri = $request->getUri(); + } else { + // Remove the host header if one is on the URI + if ($host = $changes['uri']->getHost()) { + $changes['set_headers']['Host'] = $host; + + if ($port = $changes['uri']->getPort()) { + $standardPorts = ['http' => 80, 'https' => 443]; + $scheme = $changes['uri']->getScheme(); + if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { + $changes['set_headers']['Host'] .= ':' . $port; + } + } + } + $uri = $changes['uri']; + } + + if (!empty($changes['remove_headers'])) { + $headers = self::caselessRemove($changes['remove_headers'], $headers); + } + + if (!empty($changes['set_headers'])) { + $headers = self::caselessRemove(array_keys($changes['set_headers']), $headers); + $headers = $changes['set_headers'] + $headers; + } + + if (isset($changes['query'])) { + $uri = $uri->withQuery($changes['query']); + } + + if ($request instanceof ServerRequestInterface) { + $new = (new ServerRequest( + $changes['method'] ?? $request->getMethod(), + $uri, + $headers, + $changes['body'] ?? $request->getBody(), + $changes['version'] ?? $request->getProtocolVersion(), + $request->getServerParams() + )) + ->withParsedBody($request->getParsedBody()) + ->withQueryParams($request->getQueryParams()) + ->withCookieParams($request->getCookieParams()) + ->withUploadedFiles($request->getUploadedFiles()); + + foreach ($request->getAttributes() as $key => $value) { + $new = $new->withAttribute($key, $value); + } + + return $new; + } + + return new Request( + $changes['method'] ?? $request->getMethod(), + $uri, + $headers, + $changes['body'] ?? $request->getBody(), + $changes['version'] ?? $request->getProtocolVersion() + ); + } + + /** + * Read a line from the stream up to the maximum allowed buffer length. + * + * @param StreamInterface $stream Stream to read from + * @param int|null $maxLength Maximum buffer length + */ + public static function readLine(StreamInterface $stream, ?int $maxLength = null): string + { + $buffer = ''; + $size = 0; + + while (!$stream->eof()) { + if ('' === ($byte = $stream->read(1))) { + return $buffer; + } + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte === "\n" || ++$size === $maxLength - 1) { + break; + } + } + + return $buffer; + } + + /** + * Create a new stream based on the input type. + * + * Options is an associative array that can contain the following keys: + * - metadata: Array of custom metadata. + * - size: Size of the stream. + * + * This method accepts the following `$resource` types: + * - `Psr\Http\Message\StreamInterface`: Returns the value as-is. + * - `string`: Creates a stream object that uses the given string as the contents. + * - `resource`: Creates a stream object that wraps the given PHP stream resource. + * - `Iterator`: If the provided value implements `Iterator`, then a read-only + * stream object will be created that wraps the given iterable. Each time the + * stream is read from, data from the iterator will fill a buffer and will be + * continuously called until the buffer is equal to the requested read size. + * Subsequent read calls will first read from the buffer and then call `next` + * on the underlying iterator until it is exhausted. + * - `object` with `__toString()`: If the object has the `__toString()` method, + * the object will be cast to a string and then a stream will be returned that + * uses the string value. + * - `NULL`: When `null` is passed, an empty stream object is returned. + * - `callable` When a callable is passed, a read-only stream object will be + * created that invokes the given callable. The callable is invoked with the + * number of suggested bytes to read. The callable can return any number of + * bytes, but MUST return `false` when there is no more data to return. The + * stream object that wraps the callable will invoke the callable until the + * number of requested bytes are available. Any additional bytes will be + * buffered and used in subsequent reads. + * + * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data + * @param array{size?: int, metadata?: array} $options Additional options + * + * @throws \InvalidArgumentException if the $resource arg is not valid. + */ + public static function streamFor($resource = '', array $options = []): StreamInterface + { + if (is_scalar($resource)) { + $stream = self::tryFopen('php://temp', 'r+'); + if ($resource !== '') { + fwrite($stream, (string) $resource); + fseek($stream, 0); + } + return new Stream($stream, $options); + } + + switch (gettype($resource)) { + case 'resource': + /* + * The 'php://input' is a special stream with quirks and inconsistencies. + * We avoid using that stream by reading it into php://temp + */ + + /** @var resource $resource */ + if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') { + $stream = self::tryFopen('php://temp', 'w+'); + fwrite($stream, stream_get_contents($resource)); + fseek($stream, 0); + $resource = $stream; + } + return new Stream($resource, $options); + case 'object': + /** @var object $resource */ + if ($resource instanceof StreamInterface) { + return $resource; + } elseif ($resource instanceof \Iterator) { + return new PumpStream(function () use ($resource) { + if (!$resource->valid()) { + return false; + } + $result = $resource->current(); + $resource->next(); + return $result; + }, $options); + } elseif (method_exists($resource, '__toString')) { + return self::streamFor((string) $resource, $options); + } + break; + case 'NULL': + return new Stream(self::tryFopen('php://temp', 'r+'), $options); + } + + if (is_callable($resource)) { + return new PumpStream($resource, $options); + } + + throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); + } + + /** + * Safely opens a PHP stream resource using a filename. + * + * When fopen fails, PHP normally raises a warning. This function adds an + * error handler that checks for errors and throws an exception instead. + * + * @param string $filename File to open + * @param string $mode Mode used to open the file + * + * @return resource + * + * @throws \RuntimeException if the file cannot be opened + */ + public static function tryFopen(string $filename, string $mode) + { + $ex = null; + set_error_handler(static function (int $errno, string $errstr) use ($filename, $mode, &$ex): bool { + $ex = new \RuntimeException(sprintf( + 'Unable to open "%s" using mode "%s": %s', + $filename, + $mode, + $errstr + )); + + return true; + }); + + try { + /** @var resource $handle */ + $handle = fopen($filename, $mode); + } catch (\Throwable $e) { + $ex = new \RuntimeException(sprintf( + 'Unable to open "%s" using mode "%s": %s', + $filename, + $mode, + $e->getMessage() + ), 0, $e); + } + + restore_error_handler(); + + if ($ex) { + /** @var $ex \RuntimeException */ + throw $ex; + } + + return $handle; + } + + /** + * Returns a UriInterface for the given value. + * + * This function accepts a string or UriInterface and returns a + * UriInterface for the given value. If the value is already a + * UriInterface, it is returned as-is. + * + * @param string|UriInterface $uri + * + * @throws \InvalidArgumentException + */ + public static function uriFor($uri): UriInterface + { + if ($uri instanceof UriInterface) { + return $uri; + } + + if (is_string($uri)) { + return new Uri($uri); + } + + throw new \InvalidArgumentException('URI must be a string or UriInterface'); + } +} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/functions.php b/tests/integration/vendor/guzzlehttp/psr7/src/functions.php deleted file mode 100644 index 957bfb42a..000000000 --- a/tests/integration/vendor/guzzlehttp/psr7/src/functions.php +++ /dev/null @@ -1,898 +0,0 @@ -getMethod() . ' ' - . $message->getRequestTarget()) - . ' HTTP/' . $message->getProtocolVersion(); - if (!$message->hasHeader('host')) { - $msg .= "\r\nHost: " . $message->getUri()->getHost(); - } - } elseif ($message instanceof ResponseInterface) { - $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' - . $message->getStatusCode() . ' ' - . $message->getReasonPhrase(); - } else { - throw new \InvalidArgumentException('Unknown message type'); - } - - foreach ($message->getHeaders() as $name => $values) { - $msg .= "\r\n{$name}: " . implode(', ', $values); - } - - return "{$msg}\r\n\r\n" . $message->getBody(); -} - -/** - * Returns a UriInterface for the given value. - * - * This function accepts a string or {@see Psr\Http\Message\UriInterface} and - * returns a UriInterface for the given value. If the value is already a - * `UriInterface`, it is returned as-is. - * - * @param string|UriInterface $uri - * - * @return UriInterface - * @throws \InvalidArgumentException - */ -function uri_for($uri) -{ - if ($uri instanceof UriInterface) { - return $uri; - } elseif (is_string($uri)) { - return new Uri($uri); - } - - throw new \InvalidArgumentException('URI must be a string or UriInterface'); -} - -/** - * Create a new stream based on the input type. - * - * Options is an associative array that can contain the following keys: - * - metadata: Array of custom metadata. - * - size: Size of the stream. - * - * @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data - * @param array $options Additional options - * - * @return StreamInterface - * @throws \InvalidArgumentException if the $resource arg is not valid. - */ -function stream_for($resource = '', array $options = []) -{ - if (is_scalar($resource)) { - $stream = fopen('php://temp', 'r+'); - if ($resource !== '') { - fwrite($stream, $resource); - fseek($stream, 0); - } - return new Stream($stream, $options); - } - - switch (gettype($resource)) { - case 'resource': - return new Stream($resource, $options); - case 'object': - if ($resource instanceof StreamInterface) { - return $resource; - } elseif ($resource instanceof \Iterator) { - return new PumpStream(function () use ($resource) { - if (!$resource->valid()) { - return false; - } - $result = $resource->current(); - $resource->next(); - return $result; - }, $options); - } elseif (method_exists($resource, '__toString')) { - return stream_for((string) $resource, $options); - } - break; - case 'NULL': - return new Stream(fopen('php://temp', 'r+'), $options); - } - - if (is_callable($resource)) { - return new PumpStream($resource, $options); - } - - throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); -} - -/** - * Parse an array of header values containing ";" separated data into an - * array of associative arrays representing the header key value pair - * data of the header. When a parameter does not contain a value, but just - * contains a key, this function will inject a key with a '' string value. - * - * @param string|array $header Header to parse into components. - * - * @return array Returns the parsed header values. - */ -function parse_header($header) -{ - static $trimmed = "\"' \n\t\r"; - $params = $matches = []; - - foreach (normalize_header($header) as $val) { - $part = []; - foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { - if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { - $m = $matches[0]; - if (isset($m[1])) { - $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); - } else { - $part[] = trim($m[0], $trimmed); - } - } - } - if ($part) { - $params[] = $part; - } - } - - return $params; -} - -/** - * Converts an array of header values that may contain comma separated - * headers into an array of headers with no comma separated values. - * - * @param string|array $header Header to normalize. - * - * @return array Returns the normalized header field values. - */ -function normalize_header($header) -{ - if (!is_array($header)) { - return array_map('trim', explode(',', $header)); - } - - $result = []; - foreach ($header as $value) { - foreach ((array) $value as $v) { - if (strpos($v, ',') === false) { - $result[] = $v; - continue; - } - foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { - $result[] = trim($vv); - } - } - } - - return $result; -} - -/** - * Clone and modify a request with the given changes. - * - * The changes can be one of: - * - method: (string) Changes the HTTP method. - * - set_headers: (array) Sets the given headers. - * - remove_headers: (array) Remove the given headers. - * - body: (mixed) Sets the given body. - * - uri: (UriInterface) Set the URI. - * - query: (string) Set the query string value of the URI. - * - version: (string) Set the protocol version. - * - * @param RequestInterface $request Request to clone and modify. - * @param array $changes Changes to apply. - * - * @return RequestInterface - */ -function modify_request(RequestInterface $request, array $changes) -{ - if (!$changes) { - return $request; - } - - $headers = $request->getHeaders(); - - if (!isset($changes['uri'])) { - $uri = $request->getUri(); - } else { - // Remove the host header if one is on the URI - if ($host = $changes['uri']->getHost()) { - $changes['set_headers']['Host'] = $host; - - if ($port = $changes['uri']->getPort()) { - $standardPorts = ['http' => 80, 'https' => 443]; - $scheme = $changes['uri']->getScheme(); - if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { - $changes['set_headers']['Host'] .= ':'.$port; - } - } - } - $uri = $changes['uri']; - } - - if (!empty($changes['remove_headers'])) { - $headers = _caseless_remove($changes['remove_headers'], $headers); - } - - if (!empty($changes['set_headers'])) { - $headers = _caseless_remove(array_keys($changes['set_headers']), $headers); - $headers = $changes['set_headers'] + $headers; - } - - if (isset($changes['query'])) { - $uri = $uri->withQuery($changes['query']); - } - - if ($request instanceof ServerRequestInterface) { - return (new ServerRequest( - isset($changes['method']) ? $changes['method'] : $request->getMethod(), - $uri, - $headers, - isset($changes['body']) ? $changes['body'] : $request->getBody(), - isset($changes['version']) - ? $changes['version'] - : $request->getProtocolVersion(), - $request->getServerParams() - )) - ->withParsedBody($request->getParsedBody()) - ->withQueryParams($request->getQueryParams()) - ->withCookieParams($request->getCookieParams()) - ->withUploadedFiles($request->getUploadedFiles()); - } - - return new Request( - isset($changes['method']) ? $changes['method'] : $request->getMethod(), - $uri, - $headers, - isset($changes['body']) ? $changes['body'] : $request->getBody(), - isset($changes['version']) - ? $changes['version'] - : $request->getProtocolVersion() - ); -} - -/** - * Attempts to rewind a message body and throws an exception on failure. - * - * The body of the message will only be rewound if a call to `tell()` returns a - * value other than `0`. - * - * @param MessageInterface $message Message to rewind - * - * @throws \RuntimeException - */ -function rewind_body(MessageInterface $message) -{ - $body = $message->getBody(); - - if ($body->tell()) { - $body->rewind(); - } -} - -/** - * Safely opens a PHP stream resource using a filename. - * - * When fopen fails, PHP normally raises a warning. This function adds an - * error handler that checks for errors and throws an exception instead. - * - * @param string $filename File to open - * @param string $mode Mode used to open the file - * - * @return resource - * @throws \RuntimeException if the file cannot be opened - */ -function try_fopen($filename, $mode) -{ - $ex = null; - set_error_handler(function () use ($filename, $mode, &$ex) { - $ex = new \RuntimeException(sprintf( - 'Unable to open %s using mode %s: %s', - $filename, - $mode, - func_get_args()[1] - )); - }); - - $handle = fopen($filename, $mode); - restore_error_handler(); - - if ($ex) { - /** @var $ex \RuntimeException */ - throw $ex; - } - - return $handle; -} - -/** - * Copy the contents of a stream into a string until the given number of - * bytes have been read. - * - * @param StreamInterface $stream Stream to read - * @param int $maxLen Maximum number of bytes to read. Pass -1 - * to read the entire stream. - * @return string - * @throws \RuntimeException on error. - */ -function copy_to_string(StreamInterface $stream, $maxLen = -1) -{ - $buffer = ''; - - if ($maxLen === -1) { - while (!$stream->eof()) { - $buf = $stream->read(1048576); - // Using a loose equality here to match on '' and false. - if ($buf == null) { - break; - } - $buffer .= $buf; - } - return $buffer; - } - - $len = 0; - while (!$stream->eof() && $len < $maxLen) { - $buf = $stream->read($maxLen - $len); - // Using a loose equality here to match on '' and false. - if ($buf == null) { - break; - } - $buffer .= $buf; - $len = strlen($buffer); - } - - return $buffer; -} - -/** - * Copy the contents of a stream into another stream until the given number - * of bytes have been read. - * - * @param StreamInterface $source Stream to read from - * @param StreamInterface $dest Stream to write to - * @param int $maxLen Maximum number of bytes to read. Pass -1 - * to read the entire stream. - * - * @throws \RuntimeException on error. - */ -function copy_to_stream( - StreamInterface $source, - StreamInterface $dest, - $maxLen = -1 -) { - $bufferSize = 8192; - - if ($maxLen === -1) { - while (!$source->eof()) { - if (!$dest->write($source->read($bufferSize))) { - break; - } - } - } else { - $remaining = $maxLen; - while ($remaining > 0 && !$source->eof()) { - $buf = $source->read(min($bufferSize, $remaining)); - $len = strlen($buf); - if (!$len) { - break; - } - $remaining -= $len; - $dest->write($buf); - } - } -} - -/** - * Calculate a hash of a Stream - * - * @param StreamInterface $stream Stream to calculate the hash for - * @param string $algo Hash algorithm (e.g. md5, crc32, etc) - * @param bool $rawOutput Whether or not to use raw output - * - * @return string Returns the hash of the stream - * @throws \RuntimeException on error. - */ -function hash( - StreamInterface $stream, - $algo, - $rawOutput = false -) { - $pos = $stream->tell(); - - if ($pos > 0) { - $stream->rewind(); - } - - $ctx = hash_init($algo); - while (!$stream->eof()) { - hash_update($ctx, $stream->read(1048576)); - } - - $out = hash_final($ctx, (bool) $rawOutput); - $stream->seek($pos); - - return $out; -} - -/** - * Read a line from the stream up to the maximum allowed buffer length - * - * @param StreamInterface $stream Stream to read from - * @param int $maxLength Maximum buffer length - * - * @return string - */ -function readline(StreamInterface $stream, $maxLength = null) -{ - $buffer = ''; - $size = 0; - - while (!$stream->eof()) { - // Using a loose equality here to match on '' and false. - if (null == ($byte = $stream->read(1))) { - return $buffer; - } - $buffer .= $byte; - // Break when a new line is found or the max length - 1 is reached - if ($byte === "\n" || ++$size === $maxLength - 1) { - break; - } - } - - return $buffer; -} - -/** - * Parses a request message string into a request object. - * - * @param string $message Request message string. - * - * @return Request - */ -function parse_request($message) -{ - $data = _parse_message($message); - $matches = []; - if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { - throw new \InvalidArgumentException('Invalid request string'); - } - $parts = explode(' ', $data['start-line'], 3); - $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; - - $request = new Request( - $parts[0], - $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1], - $data['headers'], - $data['body'], - $version - ); - - return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); -} - -/** - * Parses a response message string into a response object. - * - * @param string $message Response message string. - * - * @return Response - */ -function parse_response($message) -{ - $data = _parse_message($message); - // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space - // between status-code and reason-phrase is required. But browsers accept - // responses without space and reason as well. - if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { - throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']); - } - $parts = explode(' ', $data['start-line'], 3); - - return new Response( - $parts[1], - $data['headers'], - $data['body'], - explode('/', $parts[0])[1], - isset($parts[2]) ? $parts[2] : null - ); -} - -/** - * Parse a query string into an associative array. - * - * If multiple values are found for the same key, the value of that key - * value pair will become an array. This function does not parse nested - * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will - * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']). - * - * @param string $str Query string to parse - * @param int|bool $urlEncoding How the query string is encoded - * - * @return array - */ -function parse_query($str, $urlEncoding = true) -{ - $result = []; - - if ($str === '') { - return $result; - } - - if ($urlEncoding === true) { - $decoder = function ($value) { - return rawurldecode(str_replace('+', ' ', $value)); - }; - } elseif ($urlEncoding === PHP_QUERY_RFC3986) { - $decoder = 'rawurldecode'; - } elseif ($urlEncoding === PHP_QUERY_RFC1738) { - $decoder = 'urldecode'; - } else { - $decoder = function ($str) { return $str; }; - } - - foreach (explode('&', $str) as $kvp) { - $parts = explode('=', $kvp, 2); - $key = $decoder($parts[0]); - $value = isset($parts[1]) ? $decoder($parts[1]) : null; - if (!isset($result[$key])) { - $result[$key] = $value; - } else { - if (!is_array($result[$key])) { - $result[$key] = [$result[$key]]; - } - $result[$key][] = $value; - } - } - - return $result; -} - -/** - * Build a query string from an array of key value pairs. - * - * This function can use the return value of parse_query() to build a query - * string. This function does not modify the provided keys when an array is - * encountered (like http_build_query would). - * - * @param array $params Query string parameters. - * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 - * to encode using RFC3986, or PHP_QUERY_RFC1738 - * to encode using RFC1738. - * @return string - */ -function build_query(array $params, $encoding = PHP_QUERY_RFC3986) -{ - if (!$params) { - return ''; - } - - if ($encoding === false) { - $encoder = function ($str) { return $str; }; - } elseif ($encoding === PHP_QUERY_RFC3986) { - $encoder = 'rawurlencode'; - } elseif ($encoding === PHP_QUERY_RFC1738) { - $encoder = 'urlencode'; - } else { - throw new \InvalidArgumentException('Invalid type'); - } - - $qs = ''; - foreach ($params as $k => $v) { - $k = $encoder($k); - if (!is_array($v)) { - $qs .= $k; - if ($v !== null) { - $qs .= '=' . $encoder($v); - } - $qs .= '&'; - } else { - foreach ($v as $vv) { - $qs .= $k; - if ($vv !== null) { - $qs .= '=' . $encoder($vv); - } - $qs .= '&'; - } - } - } - - return $qs ? (string) substr($qs, 0, -1) : ''; -} - -/** - * Determines the mimetype of a file by looking at its extension. - * - * @param $filename - * - * @return null|string - */ -function mimetype_from_filename($filename) -{ - return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION)); -} - -/** - * Maps a file extensions to a mimetype. - * - * @param $extension string The file extension. - * - * @return string|null - * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types - */ -function mimetype_from_extension($extension) -{ - static $mimetypes = [ - '3gp' => 'video/3gpp', - '7z' => 'application/x-7z-compressed', - 'aac' => 'audio/x-aac', - 'ai' => 'application/postscript', - 'aif' => 'audio/x-aiff', - 'asc' => 'text/plain', - 'asf' => 'video/x-ms-asf', - 'atom' => 'application/atom+xml', - 'avi' => 'video/x-msvideo', - 'bmp' => 'image/bmp', - 'bz2' => 'application/x-bzip2', - 'cer' => 'application/pkix-cert', - 'crl' => 'application/pkix-crl', - 'crt' => 'application/x-x509-ca-cert', - 'css' => 'text/css', - 'csv' => 'text/csv', - 'cu' => 'application/cu-seeme', - 'deb' => 'application/x-debian-package', - 'doc' => 'application/msword', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dvi' => 'application/x-dvi', - 'eot' => 'application/vnd.ms-fontobject', - 'eps' => 'application/postscript', - 'epub' => 'application/epub+zip', - 'etx' => 'text/x-setext', - 'flac' => 'audio/flac', - 'flv' => 'video/x-flv', - 'gif' => 'image/gif', - 'gz' => 'application/gzip', - 'htm' => 'text/html', - 'html' => 'text/html', - 'ico' => 'image/x-icon', - 'ics' => 'text/calendar', - 'ini' => 'text/plain', - 'iso' => 'application/x-iso9660-image', - 'jar' => 'application/java-archive', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'js' => 'text/javascript', - 'json' => 'application/json', - 'latex' => 'application/x-latex', - 'log' => 'text/plain', - 'm4a' => 'audio/mp4', - 'm4v' => 'video/mp4', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mov' => 'video/quicktime', - 'mkv' => 'video/x-matroska', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mp4a' => 'audio/mp4', - 'mp4v' => 'video/mp4', - 'mpe' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpg4' => 'video/mp4', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', - 'pbm' => 'image/x-portable-bitmap', - 'pdf' => 'application/pdf', - 'pgm' => 'image/x-portable-graymap', - 'png' => 'image/png', - 'pnm' => 'image/x-portable-anymap', - 'ppm' => 'image/x-portable-pixmap', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'ps' => 'application/postscript', - 'qt' => 'video/quicktime', - 'rar' => 'application/x-rar-compressed', - 'ras' => 'image/x-cmu-raster', - 'rss' => 'application/rss+xml', - 'rtf' => 'application/rtf', - 'sgm' => 'text/sgml', - 'sgml' => 'text/sgml', - 'svg' => 'image/svg+xml', - 'swf' => 'application/x-shockwave-flash', - 'tar' => 'application/x-tar', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'torrent' => 'application/x-bittorrent', - 'ttf' => 'application/x-font-ttf', - 'txt' => 'text/plain', - 'wav' => 'audio/x-wav', - 'webm' => 'video/webm', - 'wma' => 'audio/x-ms-wma', - 'wmv' => 'video/x-ms-wmv', - 'woff' => 'application/x-font-woff', - 'wsdl' => 'application/wsdl+xml', - 'xbm' => 'image/x-xbitmap', - 'xls' => 'application/vnd.ms-excel', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xml' => 'application/xml', - 'xpm' => 'image/x-xpixmap', - 'xwd' => 'image/x-xwindowdump', - 'yaml' => 'text/yaml', - 'yml' => 'text/yaml', - 'zip' => 'application/zip', - ]; - - $extension = strtolower($extension); - - return isset($mimetypes[$extension]) - ? $mimetypes[$extension] - : null; -} - -/** - * Parses an HTTP message into an associative array. - * - * The array contains the "start-line" key containing the start line of - * the message, "headers" key containing an associative array of header - * array values, and a "body" key containing the body of the message. - * - * @param string $message HTTP request or response to parse. - * - * @return array - * @internal - */ -function _parse_message($message) -{ - if (!$message) { - throw new \InvalidArgumentException('Invalid message'); - } - - $message = ltrim($message, "\r\n"); - - $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); - - if ($messageParts === false || count($messageParts) !== 2) { - throw new \InvalidArgumentException('Invalid message: Missing header delimiter'); - } - - list($rawHeaders, $body) = $messageParts; - $rawHeaders .= "\r\n"; // Put back the delimiter we split previously - $headerParts = preg_split("/\r?\n/", $rawHeaders, 2); - - if ($headerParts === false || count($headerParts) !== 2) { - throw new \InvalidArgumentException('Invalid message: Missing status line'); - } - - list($startLine, $rawHeaders) = $headerParts; - - if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') { - // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0 - $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); - } - - /** @var array[] $headerLines */ - $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER); - - // If these aren't the same, then one line didn't match and there's an invalid header. - if ($count !== substr_count($rawHeaders, "\n")) { - // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4 - if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { - throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); - } - - throw new \InvalidArgumentException('Invalid header syntax'); - } - - $headers = []; - - foreach ($headerLines as $headerLine) { - $headers[$headerLine[1]][] = $headerLine[2]; - } - - return [ - 'start-line' => $startLine, - 'headers' => $headers, - 'body' => $body, - ]; -} - -/** - * Constructs a URI for an HTTP request message. - * - * @param string $path Path from the start-line - * @param array $headers Array of headers (each value an array). - * - * @return string - * @internal - */ -function _parse_request_uri($path, array $headers) -{ - $hostKey = array_filter(array_keys($headers), function ($k) { - return strtolower($k) === 'host'; - }); - - // If no host is found, then a full URI cannot be constructed. - if (!$hostKey) { - return $path; - } - - $host = $headers[reset($hostKey)][0]; - $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; - - return $scheme . '://' . $host . '/' . ltrim($path, '/'); -} - -/** - * Get a short summary of the message body - * - * Will return `null` if the response is not printable. - * - * @param MessageInterface $message The message to get the body summary - * @param int $truncateAt The maximum allowed size of the summary - * - * @return null|string - */ -function get_message_body_summary(MessageInterface $message, $truncateAt = 120) -{ - $body = $message->getBody(); - - if (!$body->isSeekable() || !$body->isReadable()) { - return null; - } - - $size = $body->getSize(); - - if ($size === 0) { - return null; - } - - $summary = $body->read($truncateAt); - $body->rewind(); - - if ($size > $truncateAt) { - $summary .= ' (truncated...)'; - } - - // Matches any printable character, including unicode characters: - // letters, marks, numbers, punctuation, spacing, and separators. - if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) { - return null; - } - - return $summary; -} - -/** @internal */ -function _caseless_remove($keys, array $data) -{ - $result = []; - - foreach ($keys as &$key) { - $key = strtolower($key); - } - - foreach ($data as $k => $v) { - if (!in_array(strtolower($k), $keys)) { - $result[$k] = $v; - } - } - - return $result; -} diff --git a/tests/integration/vendor/guzzlehttp/psr7/src/functions_include.php b/tests/integration/vendor/guzzlehttp/psr7/src/functions_include.php deleted file mode 100644 index 96a4a83a0..000000000 --- a/tests/integration/vendor/guzzlehttp/psr7/src/functions_include.php +++ /dev/null @@ -1,6 +0,0 @@ -=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/tests/integration/vendor/psr/container/src/ContainerExceptionInterface.php b/tests/integration/vendor/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 000000000..d35c6b4d8 --- /dev/null +++ b/tests/integration/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,13 @@ +=7.2.0" + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/tests/integration/vendor/psr/event-dispatcher/src/EventDispatcherInterface.php b/tests/integration/vendor/psr/event-dispatcher/src/EventDispatcherInterface.php new file mode 100644 index 000000000..4306fa915 --- /dev/null +++ b/tests/integration/vendor/psr/event-dispatcher/src/EventDispatcherInterface.php @@ -0,0 +1,21 @@ +=7.0.0", + "psr/http-message": "^1.0" + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/tests/integration/vendor/psr/http-factory/src/RequestFactoryInterface.php b/tests/integration/vendor/psr/http-factory/src/RequestFactoryInterface.php new file mode 100644 index 000000000..cb39a08bf --- /dev/null +++ b/tests/integration/vendor/psr/http-factory/src/RequestFactoryInterface.php @@ -0,0 +1,18 @@ +log(LogLevel::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function alert($message, array $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function critical($message, array $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function error($message, array $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function warning($message, array $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function notice($message, array $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function info($message, array $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function debug($message, array $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } -} diff --git a/tests/integration/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/tests/integration/vendor/psr/log/Psr/Log/InvalidArgumentException.php deleted file mode 100644 index 67f852d1d..000000000 --- a/tests/integration/vendor/psr/log/Psr/Log/InvalidArgumentException.php +++ /dev/null @@ -1,7 +0,0 @@ -logger = $logger; - } -} diff --git a/tests/integration/vendor/psr/log/Psr/Log/LoggerInterface.php b/tests/integration/vendor/psr/log/Psr/Log/LoggerInterface.php deleted file mode 100644 index 5ea72438b..000000000 --- a/tests/integration/vendor/psr/log/Psr/Log/LoggerInterface.php +++ /dev/null @@ -1,123 +0,0 @@ -log(LogLevel::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function alert($message, array $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function critical($message, array $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function error($message, array $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function warning($message, array $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function notice($message, array $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function info($message, array $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function debug($message, array $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } - - /** - * Logs with an arbitrary level. - * - * @param mixed $level - * @param string $message - * @param array $context - * - * @return void - */ - abstract public function log($level, $message, array $context = array()); -} diff --git a/tests/integration/vendor/psr/log/Psr/Log/NullLogger.php b/tests/integration/vendor/psr/log/Psr/Log/NullLogger.php deleted file mode 100644 index d8cd682c8..000000000 --- a/tests/integration/vendor/psr/log/Psr/Log/NullLogger.php +++ /dev/null @@ -1,28 +0,0 @@ -logger) { }` - * blocks. - */ -class NullLogger extends AbstractLogger -{ - /** - * Logs with an arbitrary level. - * - * @param mixed $level - * @param string $message - * @param array $context - * - * @return void - */ - public function log($level, $message, array $context = array()) - { - // noop - } -} diff --git a/tests/integration/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/tests/integration/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php deleted file mode 100644 index a0391a52b..000000000 --- a/tests/integration/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php +++ /dev/null @@ -1,140 +0,0 @@ - ". - * - * Example ->error('Foo') would yield "error Foo". - * - * @return string[] - */ - abstract public function getLogs(); - - public function testImplements() - { - $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); - } - - /** - * @dataProvider provideLevelsAndMessages - */ - public function testLogsAtAllLevels($level, $message) - { - $logger = $this->getLogger(); - $logger->{$level}($message, array('user' => 'Bob')); - $logger->log($level, $message, array('user' => 'Bob')); - - $expected = array( - $level.' message of level '.$level.' with context: Bob', - $level.' message of level '.$level.' with context: Bob', - ); - $this->assertEquals($expected, $this->getLogs()); - } - - public function provideLevelsAndMessages() - { - return array( - LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), - LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), - LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), - LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), - LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), - LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), - LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), - LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), - ); - } - - /** - * @expectedException \Psr\Log\InvalidArgumentException - */ - public function testThrowsOnInvalidLevel() - { - $logger = $this->getLogger(); - $logger->log('invalid level', 'Foo'); - } - - public function testContextReplacement() - { - $logger = $this->getLogger(); - $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); - - $expected = array('info {Message {nothing} Bob Bar a}'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testObjectCastToString() - { - if (method_exists($this, 'createPartialMock')) { - $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); - } else { - $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); - } - $dummy->expects($this->once()) - ->method('__toString') - ->will($this->returnValue('DUMMY')); - - $this->getLogger()->warning($dummy); - - $expected = array('warning DUMMY'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextCanContainAnything() - { - $context = array( - 'bool' => true, - 'null' => null, - 'string' => 'Foo', - 'int' => 0, - 'float' => 0.5, - 'nested' => array('with object' => new DummyTest), - 'object' => new \DateTime, - 'resource' => fopen('php://memory', 'r'), - ); - - $this->getLogger()->warning('Crazy context data', $context); - - $expected = array('warning Crazy context data'); - $this->assertEquals($expected, $this->getLogs()); - } - - public function testContextExceptionKeyCanBeExceptionOrOtherValues() - { - $logger = $this->getLogger(); - $logger->warning('Random message', array('exception' => 'oops')); - $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); - - $expected = array( - 'warning Random message', - 'critical Uncaught Exception!' - ); - $this->assertEquals($expected, $this->getLogs()); - } -} - -class DummyTest -{ - public function __toString() - { - } -} diff --git a/tests/integration/vendor/psr/log/README.md b/tests/integration/vendor/psr/log/README.md deleted file mode 100644 index 574bc1cb2..000000000 --- a/tests/integration/vendor/psr/log/README.md +++ /dev/null @@ -1,45 +0,0 @@ -PSR Log -======= - -This repository holds all interfaces/classes/traits related to -[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). - -Note that this is not a logger of its own. It is merely an interface that -describes a logger. See the specification for more details. - -Usage ------ - -If you need a logger, you can use the interface like this: - -```php -logger = $logger; - } - - public function doSomething() - { - if ($this->logger) { - $this->logger->info('Doing work'); - } - - // do something useful - } -} -``` - -You can then pick one of the implementations of the interface to get a logger. - -If you want to implement the interface, you can require this package and -implement `Psr\Log\LoggerInterface` in your code. Please read the -[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) -for details. diff --git a/tests/integration/vendor/psr/log/composer.json b/tests/integration/vendor/psr/log/composer.json deleted file mode 100644 index 87934d707..000000000 --- a/tests/integration/vendor/psr/log/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "psr/log", - "description": "Common interface for logging libraries", - "keywords": ["psr", "psr-3", "log"], - "homepage": "https://github.com/php-fig/log", - "license": "MIT", - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "require": { - "php": ">=5.3.0" - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - } -} diff --git a/tests/integration/vendor/ralouphie/getallheaders/.gitignore b/tests/integration/vendor/ralouphie/getallheaders/.gitignore deleted file mode 100644 index 1324e7d3e..000000000 --- a/tests/integration/vendor/ralouphie/getallheaders/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea -.DS_store -/vendor/ -composer.phar -composer.lock diff --git a/tests/integration/vendor/ralouphie/getallheaders/.travis.yml b/tests/integration/vendor/ralouphie/getallheaders/.travis.yml deleted file mode 100644 index f45b55fa0..000000000 --- a/tests/integration/vendor/ralouphie/getallheaders/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - -before_script: - - composer install - -script: - - mkdir -p build/logs - - php vendor/bin/phpunit -c phpunit.xml - -after_script: - - php vendor/bin/coveralls -v \ No newline at end of file diff --git a/tests/integration/vendor/ralouphie/getallheaders/README.md b/tests/integration/vendor/ralouphie/getallheaders/README.md index f3329d663..9430d76bb 100644 --- a/tests/integration/vendor/ralouphie/getallheaders/README.md +++ b/tests/integration/vendor/ralouphie/getallheaders/README.md @@ -14,6 +14,14 @@ This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/f ## Install +For PHP version **`>= 5.6`**: + ``` composer require ralouphie/getallheaders ``` + +For PHP version **`< 5.6`**: + +``` +composer require ralouphie/getallheaders "^2" +``` diff --git a/tests/integration/vendor/ralouphie/getallheaders/composer.json b/tests/integration/vendor/ralouphie/getallheaders/composer.json index 5a0d595c9..de8ce62e4 100644 --- a/tests/integration/vendor/ralouphie/getallheaders/composer.json +++ b/tests/integration/vendor/ralouphie/getallheaders/composer.json @@ -9,13 +9,18 @@ } ], "require": { - "php": ">=5.3" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "~3.7.0", - "satooshi/php-coveralls": ">=1.0" + "phpunit/phpunit": "^5 || ^6.5", + "php-coveralls/php-coveralls": "^2.1" }, "autoload": { "files": ["src/getallheaders.php"] + }, + "autoload-dev": { + "psr-4": { + "getallheaders\\Tests\\": "tests/" + } } -} \ No newline at end of file +} diff --git a/tests/integration/vendor/ralouphie/getallheaders/phpunit.xml b/tests/integration/vendor/ralouphie/getallheaders/phpunit.xml deleted file mode 100644 index 7255b23d9..000000000 --- a/tests/integration/vendor/ralouphie/getallheaders/phpunit.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - ./tests - - - - - src - - - - - - - - \ No newline at end of file diff --git a/tests/integration/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php b/tests/integration/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php deleted file mode 100644 index 8e3d1790a..000000000 --- a/tests/integration/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php +++ /dev/null @@ -1,121 +0,0 @@ - $val) { - $_SERVER[$key] = $val; - } - $result = getallheaders(); - $this->assertEquals($expected, $result, "Error testing $test_type works."); - } - - public function testWorksData() - { - return array( - array( - 'normal case', - array( - 'Key-One' => 'foo', - 'Key-Two' => 'bar', - 'Another-Key-For-Testing' => 'baz' - ), - array( - 'HTTP_KEY_ONE' => 'foo', - 'HTTP_KEY_TWO' => 'bar', - 'HTTP_ANOTHER_KEY_FOR_TESTING' => 'baz' - ) - ), - array( - 'Content-Type', - array( - 'Content-Type' => 'two' - ), - array( - 'HTTP_CONTENT_TYPE' => 'one', - 'CONTENT_TYPE' => 'two' - ) - ), - array( - 'Content-Length', - array( - 'Content-Length' => '222' - ), - array( - 'CONTENT_LENGTH' => '222', - 'HTTP_CONTENT_LENGTH' => '111' - ) - ), - array( - 'Content-Length (HTTP_CONTENT_LENGTH only)', - array( - 'Content-Length' => '111' - ), - array( - 'HTTP_CONTENT_LENGTH' => '111' - ) - ), - array( - 'Content-MD5', - array( - 'Content-Md5' => 'aef123' - ), - array( - 'CONTENT_MD5' => 'aef123', - 'HTTP_CONTENT_MD5' => 'fea321' - ) - ), - array( - 'Content-MD5 (HTTP_CONTENT_MD5 only)', - array( - 'Content-Md5' => 'f123' - ), - array( - 'HTTP_CONTENT_MD5' => 'f123' - ) - ), - array( - 'Authorization (normal)', - array( - 'Authorization' => 'testing' - ), - array( - 'HTTP_AUTHORIZATION' => 'testing', - ) - ), - array( - 'Authorization (redirect)', - array( - 'Authorization' => 'testing redirect' - ), - array( - 'REDIRECT_HTTP_AUTHORIZATION' => 'testing redirect', - ) - ), - array( - 'Authorization (PHP_AUTH_USER + PHP_AUTH_PW)', - array( - 'Authorization' => 'Basic ' . base64_encode('foo:bar') - ), - array( - 'PHP_AUTH_USER' => 'foo', - 'PHP_AUTH_PW' => 'bar' - ) - ), - array( - 'Authorization (PHP_AUTH_DIGEST)', - array( - 'Authorization' => 'example-digest' - ), - array( - 'PHP_AUTH_DIGEST' => 'example-digest' - ) - ) - ); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/ApcClassLoader.php b/tests/integration/vendor/symfony/class-loader/ApcClassLoader.php deleted file mode 100644 index 5500e3628..000000000 --- a/tests/integration/vendor/symfony/class-loader/ApcClassLoader.php +++ /dev/null @@ -1,141 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3. - * - * It expects an object implementing a findFile method to find the file. This - * allows using it as a wrapper around the other loaders of the component (the - * ClassLoader for instance) but also around any other autoloaders following - * this convention (the Composer one for instance). - * - * // with a Symfony autoloader - * use Symfony\Component\ClassLoader\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // or with a Composer autoloader - * use Composer\Autoload\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * $cachedLoader = new ApcClassLoader('my_prefix', $loader); - * - * // activate the cached autoloader - * $cachedLoader->register(); - * - * // eventually deactivate the non-cached loader if it was registered previously - * // to be sure to use the cached one. - * $loader->unregister(); - * - * @author Fabien Potencier - * @author Kris Wallsmith - */ -class ApcClassLoader -{ - private $prefix; - - /** - * A class loader object that implements the findFile() method. - * - * @var object - */ - protected $decorated; - - /** - * Constructor. - * - * @param string $prefix The APC namespace prefix to use - * @param object $decorated A class loader object that implements the findFile() method - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - */ - public function __construct($prefix, $decorated) - { - if (!function_exists('apcu_fetch')) { - throw new \RuntimeException('Unable to use ApcClassLoader as APC is not installed.'); - } - - if (!method_exists($decorated, 'findFile')) { - throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); - } - - $this->prefix = $prefix; - $this->decorated = $decorated; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds a file by class name while caching lookups to APC. - * - * @param string $class A class name to resolve to file - * - * @return string|null - */ - public function findFile($class) - { - $file = apcu_fetch($this->prefix.$class, $success); - - if (!$success) { - apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null); - } - - return $file; - } - - /** - * Passes through all unknown calls onto the decorated object. - */ - public function __call($method, $args) - { - return call_user_func_array(array($this->decorated, $method), $args); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/CHANGELOG.md b/tests/integration/vendor/symfony/class-loader/CHANGELOG.md deleted file mode 100644 index 64ef8d9c9..000000000 --- a/tests/integration/vendor/symfony/class-loader/CHANGELOG.md +++ /dev/null @@ -1,36 +0,0 @@ -CHANGELOG -========= - -3.0.0 ------ - - * The DebugClassLoader class has been removed - * The DebugUniversalClassLoader class has been removed - * The UniversalClassLoader class has been removed - * The ApcUniversalClassLoader class has been removed - -2.4.0 ------ - - * deprecated the UniversalClassLoader in favor of the ClassLoader class instead - * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead - * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component - * deprecated the DebugClassLoader as it has been moved to the Debug component instead - -2.3.0 ------ - - * added a WinCacheClassLoader for WinCache - -2.1.0 ------ - - * added a DebugClassLoader able to wrap any autoloader providing a findFile - method - * added a new ApcClassLoader and XcacheClassLoader using composition to wrap - other loaders - * added a new ClassLoader which does not distinguish between namespaced and - pear-like classes (as the PEAR convention is a subset of PSR-0) and - supports using Composer's namespace maps - * added a class map generator - * added support for loading globally-installed PEAR packages diff --git a/tests/integration/vendor/symfony/class-loader/ClassCollectionLoader.php b/tests/integration/vendor/symfony/class-loader/ClassCollectionLoader.php deleted file mode 100644 index 9f6e33edb..000000000 --- a/tests/integration/vendor/symfony/class-loader/ClassCollectionLoader.php +++ /dev/null @@ -1,438 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * ClassCollectionLoader. - * - * @author Fabien Potencier - */ -class ClassCollectionLoader -{ - private static $loaded; - private static $seen; - private static $useTokenizer = true; - - /** - * Loads a list of classes and caches them in one big file. - * - * @param array $classes An array of classes to load - * @param string $cacheDir A cache directory - * @param string $name The cache name prefix - * @param bool $autoReload Whether to flush the cache when the cache is stale or not - * @param bool $adaptive Whether to remove already declared classes or not - * @param string $extension File extension of the resulting file - * - * @throws \InvalidArgumentException When class can't be loaded - */ - public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') - { - // each $name can only be loaded once per PHP process - if (isset(self::$loaded[$name])) { - return; - } - - self::$loaded[$name] = true; - - if ($adaptive) { - $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); - - // don't include already declared classes - $classes = array_diff($classes, $declared); - - // the cache is different depending on which classes are already declared - $name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5); - } - - $classes = array_unique($classes); - - // cache the core classes - if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { - throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); - } - $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.DIRECTORY_SEPARATOR); - $cache = $cacheDir.'/'.$name.$extension; - - // auto-reload - $reload = false; - if ($autoReload) { - $metadata = $cache.'.meta'; - if (!is_file($metadata) || !is_file($cache)) { - $reload = true; - } else { - $time = filemtime($cache); - $meta = unserialize(file_get_contents($metadata)); - - sort($meta[1]); - sort($classes); - - if ($meta[1] != $classes) { - $reload = true; - } else { - foreach ($meta[0] as $resource) { - if (!is_file($resource) || filemtime($resource) > $time) { - $reload = true; - - break; - } - } - } - } - } - - if (!$reload && file_exists($cache)) { - require_once $cache; - - return; - } - if (!$adaptive) { - $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); - } - - $files = self::inline($classes, $cache, $declared); - - if ($autoReload) { - // save the resources - self::writeCacheFile($metadata, serialize(array(array_values($files), $classes))); - } - } - - /** - * Generates a file where classes and their parents are inlined. - * - * @param array $classes An array of classes to load - * @param string $cache The file where classes are inlined - * @param array $excluded An array of classes that won't be inlined - * - * @return array The source map of inlined classes, with classes as keys and files as values - * - * @throws \RuntimeException When class can't be loaded - */ - public static function inline($classes, $cache, array $excluded) - { - $declared = array(); - foreach (self::getOrderedClasses($excluded) as $class) { - $declared[$class->getName()] = true; - } - - // cache the core classes - $cacheDir = dirname($cache); - if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { - throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); - } - - $spacesRegex = '(?:\s*+(?:(?:\#|//)[^\n]*+\n|/\*(?:(?getName()])) { - continue; - } - $declared[$class->getName()] = true; - - $files[$class->getName()] = $file = $class->getFileName(); - $c = file_get_contents($file); - - if (preg_match($dontInlineRegex, $c)) { - $file = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $file)); - - for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) { - if ($file[$i] !== $cacheDir[$i]) { - break; - } - } - if (1 >= $i) { - $file = var_export(implode('/', $file), true); - } else { - $file = array_slice($file, $i); - $file = str_repeat('../', count($cacheDir) - $i).implode('/', $file); - $file = '__DIR__.'.var_export('/'.$file, true); - } - - $c = "\nnamespace {require $file;}"; - } else { - $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', $c); - - // fakes namespace declaration for global code - if (!$class->inNamespace()) { - $c = "\nnamespace\n{\n".$c."\n}\n"; - } - - $c = self::fixNamespaceDeclarations('= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - unset($tokens, $rawChunk); - gc_mem_caches(); - } - - return $output; - } - - /** - * This method is only useful for testing. - */ - public static function enableTokenizer($bool) - { - self::$useTokenizer = (bool) $bool; - } - - /** - * Strips leading & trailing ws, multiple EOL, multiple ws. - * - * @param string $code Original PHP code - * - * @return string compressed code - */ - private static function compressCode($code) - { - return preg_replace( - array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'), - array('', '', "\n", ' '), - $code - ); - } - - /** - * Writes a cache file. - * - * @param string $file Filename - * @param string $content Temporary file content - * - * @throws \RuntimeException when a cache file cannot be written - */ - private static function writeCacheFile($file, $content) - { - $tmpFile = tempnam(dirname($file), basename($file)); - if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { - @chmod($file, 0666 & ~umask()); - - return; - } - - throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file)); - } - - /** - * Gets an ordered array of passed classes including all their dependencies. - * - * @param array $classes - * - * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed) - * - * @throws \InvalidArgumentException When a class can't be loaded - */ - private static function getOrderedClasses(array $classes) - { - $map = array(); - self::$seen = array(); - foreach ($classes as $class) { - try { - $reflectionClass = new \ReflectionClass($class); - } catch (\ReflectionException $e) { - throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); - } - - $map = array_merge($map, self::getClassHierarchy($reflectionClass)); - } - - return $map; - } - - private static function getClassHierarchy(\ReflectionClass $class) - { - if (isset(self::$seen[$class->getName()])) { - return array(); - } - - self::$seen[$class->getName()] = true; - - $classes = array($class); - $parent = $class; - while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { - self::$seen[$parent->getName()] = true; - - array_unshift($classes, $parent); - } - - $traits = array(); - - foreach ($classes as $c) { - foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) { - if ($trait !== $c) { - $traits[] = $trait; - } - } - } - - return array_merge(self::getInterfaces($class), $traits, $classes); - } - - private static function getInterfaces(\ReflectionClass $class) - { - $classes = array(); - - foreach ($class->getInterfaces() as $interface) { - $classes = array_merge($classes, self::getInterfaces($interface)); - } - - if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) { - self::$seen[$class->getName()] = true; - - $classes[] = $class; - } - - return $classes; - } - - private static function computeTraitDeps(\ReflectionClass $class) - { - $traits = $class->getTraits(); - $deps = array($class->getName() => $traits); - while ($trait = array_pop($traits)) { - if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { - self::$seen[$trait->getName()] = true; - $traitDeps = $trait->getTraits(); - $deps[$trait->getName()] = $traitDeps; - $traits = array_merge($traits, $traitDeps); - } - } - - return $deps; - } - - /** - * Dependencies resolution. - * - * This function does not check for circular dependencies as it should never - * occur with PHP traits. - * - * @param array $tree The dependency tree - * @param \ReflectionClass $node The node - * @param \ArrayObject $resolved An array of already resolved dependencies - * @param \ArrayObject $unresolved An array of dependencies to be resolved - * - * @return \ArrayObject The dependencies for the given node - * - * @throws \RuntimeException if a circular dependency is detected - */ - private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null) - { - if (null === $resolved) { - $resolved = new \ArrayObject(); - } - if (null === $unresolved) { - $unresolved = new \ArrayObject(); - } - $nodeName = $node->getName(); - - if (isset($tree[$nodeName])) { - $unresolved[$nodeName] = $node; - foreach ($tree[$nodeName] as $dependency) { - if (!$resolved->offsetExists($dependency->getName())) { - self::resolveDependencies($tree, $dependency, $resolved, $unresolved); - } - } - $resolved[$nodeName] = $node; - unset($unresolved[$nodeName]); - } - - return $resolved; - } -} diff --git a/tests/integration/vendor/symfony/class-loader/ClassLoader.php b/tests/integration/vendor/symfony/class-loader/ClassLoader.php deleted file mode 100644 index a506dc094..000000000 --- a/tests/integration/vendor/symfony/class-loader/ClassLoader.php +++ /dev/null @@ -1,203 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * ClassLoader implements an PSR-0 class loader. - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md - * - * $loader = new ClassLoader(); - * - * // register classes with namespaces - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (e.g. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * @author Fabien Potencier - * @author Jordi Boggiano - */ -class ClassLoader -{ - private $prefixes = array(); - private $fallbackDirs = array(); - private $useIncludePath = false; - - /** - * Returns prefixes. - * - * @return array - */ - public function getPrefixes() - { - return $this->prefixes; - } - - /** - * Returns fallback directories. - * - * @return array - */ - public function getFallbackDirs() - { - return $this->fallbackDirs; - } - - /** - * Adds prefixes. - * - * @param array $prefixes Prefixes to add - */ - public function addPrefixes(array $prefixes) - { - foreach ($prefixes as $prefix => $path) { - $this->addPrefix($prefix, $path); - } - } - - /** - * Registers a set of classes. - * - * @param string $prefix The classes prefix - * @param array|string $paths The location(s) of the classes - */ - public function addPrefix($prefix, $paths) - { - if (!$prefix) { - foreach ((array) $paths as $path) { - $this->fallbackDirs[] = $path; - } - - return; - } - if (isset($this->prefixes[$prefix])) { - if (is_array($paths)) { - $this->prefixes[$prefix] = array_unique(array_merge( - $this->prefixes[$prefix], - $paths - )); - } elseif (!in_array($paths, $this->prefixes[$prefix])) { - $this->prefixes[$prefix][] = $paths; - } - } else { - $this->prefixes[$prefix] = array_unique((array) $paths); - } - } - - /** - * Turns on searching the include for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = (bool) $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|null The path, if found - */ - public function findFile($class) - { - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)).DIRECTORY_SEPARATOR; - $className = substr($class, $pos + 1); - } else { - // PEAR-like class name - $classPath = null; - $className = $class; - } - - $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; - - foreach ($this->prefixes as $prefix => $dirs) { - if ($class === strstr($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) { - return $dir.DIRECTORY_SEPARATOR.$classPath; - } - } - } - } - - foreach ($this->fallbackDirs as $dir) { - if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) { - return $dir.DIRECTORY_SEPARATOR.$classPath; - } - } - - if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { - return $file; - } - } -} diff --git a/tests/integration/vendor/symfony/class-loader/ClassMapGenerator.php b/tests/integration/vendor/symfony/class-loader/ClassMapGenerator.php deleted file mode 100644 index 810a70b6d..000000000 --- a/tests/integration/vendor/symfony/class-loader/ClassMapGenerator.php +++ /dev/null @@ -1,156 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * ClassMapGenerator. - * - * @author Gyula Sallai - */ -class ClassMapGenerator -{ - /** - * Generate a class map file. - * - * @param array|string $dirs Directories or a single path to search in - * @param string $file The name of the class map file - */ - public static function dump($dirs, $file) - { - $dirs = (array) $dirs; - $maps = array(); - - foreach ($dirs as $dir) { - $maps = array_merge($maps, static::createMap($dir)); - } - - file_put_contents($file, sprintf('isFile()) { - continue; - } - - $path = $file->getRealPath() ?: $file->getPathname(); - - if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { - continue; - } - - $classes = self::findClasses($path); - - if (PHP_VERSION_ID >= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - gc_mem_caches(); - } - - foreach ($classes as $class) { - $map[$class] = $path; - } - } - - return $map; - } - - /** - * Extract the classes in the given file. - * - * @param string $path The file to check - * - * @return array The found classes - */ - private static function findClasses($path) - { - $contents = file_get_contents($path); - $tokens = token_get_all($contents); - - $classes = array(); - - $namespace = ''; - for ($i = 0; isset($tokens[$i]); ++$i) { - $token = $tokens[$i]; - - if (!isset($token[1])) { - continue; - } - - $class = ''; - - switch ($token[0]) { - case T_NAMESPACE: - $namespace = ''; - // If there is a namespace, extract it - while (isset($tokens[++$i][1])) { - if (in_array($tokens[$i][0], array(T_STRING, T_NS_SEPARATOR))) { - $namespace .= $tokens[$i][1]; - } - } - $namespace .= '\\'; - break; - case T_CLASS: - case T_INTERFACE: - case T_TRAIT: - // Skip usage of ::class constant - $isClassConstant = false; - for ($j = $i - 1; $j > 0; --$j) { - if (!isset($tokens[$j][1])) { - break; - } - - if (T_DOUBLE_COLON === $tokens[$j][0]) { - $isClassConstant = true; - break; - } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) { - break; - } - } - - if ($isClassConstant) { - break; - } - - // Find the classname - while (isset($tokens[++$i][1])) { - $t = $tokens[$i]; - if (T_STRING === $t[0]) { - $class .= $t[1]; - } elseif ('' !== $class && T_WHITESPACE === $t[0]) { - break; - } - } - - $classes[] = ltrim($namespace.$class, '\\'); - break; - default: - break; - } - } - - return $classes; - } -} diff --git a/tests/integration/vendor/symfony/class-loader/LICENSE b/tests/integration/vendor/symfony/class-loader/LICENSE deleted file mode 100644 index 12a74531e..000000000 --- a/tests/integration/vendor/symfony/class-loader/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2016 Fabien Potencier - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. diff --git a/tests/integration/vendor/symfony/class-loader/MapClassLoader.php b/tests/integration/vendor/symfony/class-loader/MapClassLoader.php deleted file mode 100644 index 1d8bcc2a0..000000000 --- a/tests/integration/vendor/symfony/class-loader/MapClassLoader.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * A class loader that uses a mapping file to look up paths. - * - * @author Fabien Potencier - */ -class MapClassLoader -{ - private $map = array(); - - /** - * Constructor. - * - * @param array $map A map where keys are classes and values the absolute file path - */ - public function __construct(array $map) - { - $this->map = $map; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - */ - public function loadClass($class) - { - if (isset($this->map[$class])) { - require $this->map[$class]; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|null The path, if found - */ - public function findFile($class) - { - if (isset($this->map[$class])) { - return $this->map[$class]; - } - } -} diff --git a/tests/integration/vendor/symfony/class-loader/Psr4ClassLoader.php b/tests/integration/vendor/symfony/class-loader/Psr4ClassLoader.php deleted file mode 100644 index 84647b758..000000000 --- a/tests/integration/vendor/symfony/class-loader/Psr4ClassLoader.php +++ /dev/null @@ -1,93 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * A PSR-4 compatible class loader. - * - * See http://www.php-fig.org/psr/psr-4/ - * - * @author Alexander M. Turek - */ -class Psr4ClassLoader -{ - /** - * @var array - */ - private $prefixes = array(); - - /** - * @param string $prefix - * @param string $baseDir - */ - public function addPrefix($prefix, $baseDir) - { - $prefix = trim($prefix, '\\').'\\'; - $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; - $this->prefixes[] = array($prefix, $baseDir); - } - - /** - * @param string $class - * - * @return string|null - */ - public function findFile($class) - { - $class = ltrim($class, '\\'); - - foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) { - if (0 === strpos($class, $currentPrefix)) { - $classWithoutPrefix = substr($class, strlen($currentPrefix)); - $file = $currentBaseDir.str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php'; - if (file_exists($file)) { - return $file; - } - } - } - } - - /** - * @param string $class - * - * @return bool - */ - public function loadClass($class) - { - $file = $this->findFile($class); - if (null !== $file) { - require $file; - - return true; - } - - return false; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Removes this instance from the registered autoloaders. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/README.md b/tests/integration/vendor/symfony/class-loader/README.md deleted file mode 100644 index d61992b6a..000000000 --- a/tests/integration/vendor/symfony/class-loader/README.md +++ /dev/null @@ -1,14 +0,0 @@ -ClassLoader Component -===================== - -The ClassLoader component provides tools to autoload your classes and cache -their locations for performance. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/class_loader/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/tests/integration/vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php b/tests/integration/vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php deleted file mode 100644 index e96ba9540..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php +++ /dev/null @@ -1,196 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use Symfony\Component\ClassLoader\ApcClassLoader; -use Symfony\Component\ClassLoader\ClassLoader; - -class ApcClassLoaderTest extends \PHPUnit_Framework_TestCase -{ - protected function setUp() - { - if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) { - $this->markTestSkipped('The apc extension is not enabled.'); - } else { - apcu_clear_cache(); - } - } - - protected function tearDown() - { - if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) { - apcu_clear_cache(); - } - } - - public function testConstructor() - { - $loader = new ClassLoader(); - $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - - $loader = new ApcClassLoader('test.prefix.', $loader); - - $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apcu_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument'); - } - - /** - * @dataProvider getLoadClassTests - */ - public function testLoadClass($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - - $loader = new ApcClassLoader('test.prefix.', $loader); - $loader->loadClass($testClassName); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassTests() - { - return array( - array('\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'), - array('Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'), - ); - } - - /** - * @dataProvider getLoadClassFromFallbackTests - */ - public function testLoadClassFromFallback($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback')); - - $loader = new ApcClassLoader('test.prefix.fallback', $loader); - $loader->loadClass($testClassName); - - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassFromFallbackTests() - { - return array( - array('\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'), - array('Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'), - array('\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'), - array('Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'), - ); - } - - /** - * @dataProvider getLoadClassNamespaceCollisionTests - */ - public function testLoadClassNamespaceCollision($namespaces, $className, $message) - { - $loader = new ClassLoader(); - $loader->addPrefixes($namespaces); - - $loader = new ApcClassLoader('test.prefix.collision.', $loader); - $loader->loadClass($className); - - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassNamespaceCollisionTests() - { - return array( - array( - array( - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - ), - 'Apc\NamespaceCollision\A\Foo', - '->loadClass() loads NamespaceCollision\A\Foo from alpha.', - ), - array( - array( - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - ), - 'Apc\NamespaceCollision\A\Bar', - '->loadClass() loads NamespaceCollision\A\Bar from alpha.', - ), - array( - array( - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - ), - 'Apc\NamespaceCollision\A\B\Foo', - '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', - ), - array( - array( - 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', - 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', - ), - 'Apc\NamespaceCollision\A\B\Bar', - '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', - ), - ); - } - - /** - * @dataProvider getLoadClassPrefixCollisionTests - */ - public function testLoadClassPrefixCollision($prefixes, $className, $message) - { - $loader = new ClassLoader(); - $loader->addPrefixes($prefixes); - - $loader = new ApcClassLoader('test.prefix.collision.', $loader); - $loader->loadClass($className); - - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassPrefixCollisionTests() - { - return array( - array( - array( - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - ), - 'ApcPrefixCollision_A_Foo', - '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.', - ), - array( - array( - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - ), - 'ApcPrefixCollision_A_Bar', - '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.', - ), - array( - array( - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - ), - 'ApcPrefixCollision_A_B_Foo', - '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.', - ), - array( - array( - 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', - 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', - ), - 'ApcPrefixCollision_A_B_Bar', - '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.', - ), - ); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php b/tests/integration/vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php deleted file mode 100644 index 4d0234148..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php +++ /dev/null @@ -1,315 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use Symfony\Component\ClassLoader\ClassCollectionLoader; -use Symfony\Component\ClassLoader\Tests\Fixtures\DeclaredClass; -use Symfony\Component\ClassLoader\Tests\Fixtures\WarmedClass; - -require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php'; -require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; -require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; -require_once __DIR__.'/Fixtures/ClassesWithParents/A.php'; - -class ClassCollectionLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testTraitDependencies() - { - require_once __DIR__.'/Fixtures/deps/traits.php'; - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, array('CTFoo')); - - $this->assertEquals( - array('TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'), - array_map(function ($class) { return $class->getName(); }, $ordered) - ); - - $ordered = $m->invoke(null, array('CTBar')); - - $this->assertEquals( - array('TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'), - array_map(function ($class) { return $class->getName(); }, $ordered) - ); - } - - /** - * @dataProvider getDifferentOrders - */ - public function testClassReordering(array $classes) - { - $expected = array( - 'ClassesWithParents\\GInterface', - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - ); - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, $classes); - - $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); - } - - public function getDifferentOrders() - { - return array( - array(array( - 'ClassesWithParents\\A', - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\GInterface', - 'ClassesWithParents\\B', - )), - array(array( - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - 'ClassesWithParents\\CInterface', - )), - array(array( - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - )), - array(array( - 'ClassesWithParents\\A', - )), - ); - } - - /** - * @dataProvider getDifferentOrdersForTraits - */ - public function testClassWithTraitsReordering(array $classes) - { - require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; - - $expected = array( - 'ClassesWithParents\\GInterface', - 'ClassesWithParents\\CInterface', - 'ClassesWithParents\\ATrait', - 'ClassesWithParents\\BTrait', - 'ClassesWithParents\\CTrait', - 'ClassesWithParents\\B', - 'ClassesWithParents\\A', - 'ClassesWithParents\\D', - 'ClassesWithParents\\E', - ); - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, $classes); - - $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); - } - - public function getDifferentOrdersForTraits() - { - return array( - array(array( - 'ClassesWithParents\\E', - 'ClassesWithParents\\ATrait', - )), - array(array( - 'ClassesWithParents\\E', - )), - ); - } - - public function testFixClassWithTraitsOrdering() - { - require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/F.php'; - require_once __DIR__.'/Fixtures/ClassesWithParents/G.php'; - - $classes = array( - 'ClassesWithParents\\F', - 'ClassesWithParents\\G', - ); - - $expected = array( - 'ClassesWithParents\\CTrait', - 'ClassesWithParents\\F', - 'ClassesWithParents\\G', - ); - - $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); - $m = $r->getMethod('getOrderedClasses'); - $m->setAccessible(true); - - $ordered = $m->invoke(null, $classes); - - $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); - } - - /** - * @dataProvider getFixNamespaceDeclarationsData - */ - public function testFixNamespaceDeclarations($source, $expected) - { - $this->assertEquals('assertEquals('assertEquals(<<<'EOF' -namespace Namespaced -{ -class WithComments -{ -public static $loaded = true; -} -$string ='string should not be modified {$string}'; -$heredoc = (<<assertTrue(class_exists(WarmedClass::class, true)); - - @unlink($cache = sys_get_temp_dir().'/inline.php'); - - $classes = array(WarmedClass::class); - $excluded = array(DeclaredClass::class); - - ClassCollectionLoader::inline($classes, $cache, $excluded); - - $this->assertSame(<<<'EOTXT' - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use Symfony\Component\ClassLoader\ClassLoader; - -class ClassLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testGetPrefixes() - { - $loader = new ClassLoader(); - $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Bar', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Bas', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertArrayNotHasKey('Foo1', $prefixes); - $this->assertArrayHasKey('Bar', $prefixes); - $this->assertArrayHasKey('Bas', $prefixes); - } - - public function testGetFallbackDirs() - { - $loader = new ClassLoader(); - $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $fallback_dirs = $loader->getFallbackDirs(); - $this->assertCount(2, $fallback_dirs); - } - - /** - * @dataProvider getLoadClassTests - */ - public function testLoadClass($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->loadClass($testClassName); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassTests() - { - return array( - array('\\Namespaced2\\Foo', 'Namespaced2\\Foo', '->loadClass() loads Namespaced2\Foo class'), - array('\\Pearlike2_Foo', 'Pearlike2_Foo', '->loadClass() loads Pearlike2_Foo class'), - ); - } - - /** - * @dataProvider getLoadNonexistentClassTests - */ - public function testLoadNonexistentClass($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->loadClass($testClassName); - $this->assertFalse(class_exists($className), $message); - } - - public function getLoadNonexistentClassTests() - { - return array( - array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'), - ); - } - - public function testAddPrefixSingle() - { - $loader = new ClassLoader(); - $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertCount(1, $prefixes['Foo']); - } - - public function testAddPrefixesSingle() - { - $loader = new ClassLoader(); - $loader->addPrefixes(array('Foo' => array('foo', 'foo'))); - $loader->addPrefixes(array('Foo' => array('foo'))); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true)); - } - - public function testAddPrefixMulti() - { - $loader = new ClassLoader(); - $loader->addPrefix('Foo', 'foo'); - $loader->addPrefix('Foo', 'bar'); - $prefixes = $loader->getPrefixes(); - $this->assertArrayHasKey('Foo', $prefixes); - $this->assertCount(2, $prefixes['Foo']); - $this->assertContains('foo', $prefixes['Foo']); - $this->assertContains('bar', $prefixes['Foo']); - } - - public function testUseIncludePath() - { - $loader = new ClassLoader(); - $this->assertFalse($loader->getUseIncludePath()); - - $this->assertNull($loader->findFile('Foo')); - - $includePath = get_include_path(); - - $loader->setUseIncludePath(true); - $this->assertTrue($loader->getUseIncludePath()); - - set_include_path(__DIR__.'/Fixtures/includepath'.PATH_SEPARATOR.$includePath); - - $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'includepath'.DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo')); - - set_include_path($includePath); - } - - /** - * @dataProvider getLoadClassFromFallbackTests - */ - public function testLoadClassFromFallback($className, $testClassName, $message) - { - $loader = new ClassLoader(); - $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); - $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback')); - $loader->loadClass($testClassName); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassFromFallbackTests() - { - return array( - array('\\Namespaced2\\Baz', 'Namespaced2\\Baz', '->loadClass() loads Namespaced2\Baz class'), - array('\\Pearlike2_Baz', 'Pearlike2_Baz', '->loadClass() loads Pearlike2_Baz class'), - array('\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'), - array('\\Pearlike2_FooBar', 'Pearlike2_FooBar', '->loadClass() loads Pearlike2_Baz class from fallback dir'), - ); - } - - /** - * @dataProvider getLoadClassNamespaceCollisionTests - */ - public function testLoadClassNamespaceCollision($namespaces, $className, $message) - { - $loader = new ClassLoader(); - $loader->addPrefixes($namespaces); - - $loader->loadClass($className); - $this->assertTrue(class_exists($className), $message); - } - - public function getLoadClassNamespaceCollisionTests() - { - return array( - array( - array( - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'NamespaceCollision\C\Foo', - '->loadClass() loads NamespaceCollision\C\Foo from alpha.', - ), - array( - array( - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'NamespaceCollision\C\Bar', - '->loadClass() loads NamespaceCollision\C\Bar from alpha.', - ), - array( - array( - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'NamespaceCollision\C\B\Foo', - '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', - ), - array( - array( - 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'NamespaceCollision\C\B\Bar', - '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', - ), - array( - array( - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'PrefixCollision_C_Foo', - '->loadClass() loads PrefixCollision_C_Foo from alpha.', - ), - array( - array( - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'PrefixCollision_C_Bar', - '->loadClass() loads PrefixCollision_C_Bar from alpha.', - ), - array( - array( - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - ), - 'PrefixCollision_C_B_Foo', - '->loadClass() loads PrefixCollision_C_B_Foo from beta.', - ), - array( - array( - 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', - 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', - ), - 'PrefixCollision_C_B_Bar', - '->loadClass() loads PrefixCollision_C_B_Bar from beta.', - ), - ); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php b/tests/integration/vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php deleted file mode 100644 index 5de49d24b..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php +++ /dev/null @@ -1,147 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use Symfony\Component\ClassLoader\ClassMapGenerator; - -class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var string|null - */ - private $workspace = null; - - public function prepare_workspace() - { - $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); - mkdir($this->workspace, 0777, true); - $this->workspace = realpath($this->workspace); - } - - /** - * @param string $file - */ - private function clean($file) - { - if (is_dir($file) && !is_link($file)) { - $dir = new \FilesystemIterator($file); - foreach ($dir as $childFile) { - $this->clean($childFile); - } - - rmdir($file); - } else { - unlink($file); - } - } - - /** - * @dataProvider getTestCreateMapTests - */ - public function testDump($directory) - { - $this->prepare_workspace(); - - $file = $this->workspace.'/file'; - - $generator = new ClassMapGenerator(); - $generator->dump($directory, $file); - $this->assertFileExists($file); - - $this->clean($this->workspace); - } - - /** - * @dataProvider getTestCreateMapTests - */ - public function testCreateMap($directory, $expected) - { - $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); - } - - public function getTestCreateMapTests() - { - $data = array( - array(__DIR__.'/Fixtures/Namespaced', array( - 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', - 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', - 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', - 'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php', - 'Namespaced\\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php', - 'Namespaced\\WithHaltCompiler' => realpath(__DIR__).'/Fixtures/Namespaced/WithHaltCompiler.php', - 'Namespaced\\WithDirMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithDirMagic.php', - 'Namespaced\\WithFileMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithFileMagic.php', - )), - array(__DIR__.'/Fixtures/beta/NamespaceCollision', array( - 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', - 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', - 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', - 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', - )), - array(__DIR__.'/Fixtures/Pearlike', array( - 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', - 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', - 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', - 'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php', - )), - array(__DIR__.'/Fixtures/classmap', array( - 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', - 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', - 'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', - 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', - 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', - 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', - )), - array(__DIR__.'/Fixtures/php5.4', array( - 'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php', - 'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php', - 'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', - )), - array(__DIR__.'/Fixtures/php5.5', array( - 'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php', - )), - ); - - return $data; - } - - public function testCreateMapFinderSupport() - { - $finder = new \Symfony\Component\Finder\Finder(); - $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision'); - - $this->assertEqualsNormalized(array( - 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', - 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', - 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', - 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', - ), ClassMapGenerator::createMap($finder)); - } - - protected function assertEqualsNormalized($expected, $actual, $message = null) - { - foreach ($expected as $ns => $path) { - $expected[$ns] = str_replace('\\', '/', $path); - } - foreach ($actual as $ns => $path) { - $actual[$ns] = str_replace('\\', '/', $path); - } - $this->assertEquals($expected, $actual, $message); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php deleted file mode 100644 index 4259f1451..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class Bar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php deleted file mode 100644 index 3ddb595e2..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class Baz -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php deleted file mode 100644 index cf0a4b741..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class Foo -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php deleted file mode 100644 index bbbc81515..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class FooBar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php deleted file mode 100644 index e774cb9bf..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A; - -class Bar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php deleted file mode 100644 index 184a1b1da..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A; - -class Foo -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php deleted file mode 100644 index 3892f7068..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A\B; - -class Bar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php deleted file mode 100644 index 450eeb50b..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\NamespaceCollision\A\B; - -class Foo -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php deleted file mode 100644 index 96f2f76c6..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Apc\Namespaced; - -class FooBar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php deleted file mode 100644 index b0f942595..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php +++ /dev/null @@ -1,7 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class Bar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php deleted file mode 100644 index 0b0bbd057..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class Baz -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php deleted file mode 100644 index df5e1f4ce..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class Foo -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php deleted file mode 100644 index 361e53de1..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class WithComments -{ - /** @Boolean */ - public static $loaded = true; -} - -$string = 'string should not be modified {$string}'; - -$heredoc = (<< - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -class Pearlike_WithComments -{ - /** @Boolean */ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php deleted file mode 100644 index 7f5f79773..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php +++ /dev/null @@ -1,6 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A; - -class Bar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php deleted file mode 100644 index aee6a080d..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A; - -class Foo -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php deleted file mode 100644 index c1b8dd65d..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php +++ /dev/null @@ -1,8 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A\B; - -class Bar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php deleted file mode 100644 index f5f2d727e..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace NamespaceCollision\A\B; - -class Foo -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php deleted file mode 100644 index 4bb03dc7f..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php +++ /dev/null @@ -1,8 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ClassMap; - -class SomeClass extends SomeParent implements SomeInterface -{ -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php deleted file mode 100644 index 1fe5e09aa..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ClassMap; - -interface SomeInterface -{ -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php deleted file mode 100644 index ce2f9fc6c..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ClassMap; - -abstract class SomeParent -{ -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php deleted file mode 100644 index c7cec646f..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Foo\Bar; - -class A -{ -} -class B -{ -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/deps/traits.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/deps/traits.php deleted file mode 100644 index 82b30a6f9..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/deps/traits.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Namespaced; - -class FooBar -{ - public static $loaded = true; -} diff --git a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php b/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php deleted file mode 100644 index 1036d4359..000000000 --- a/tests/integration/vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php +++ /dev/null @@ -1,8 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader\Tests; - -use Symfony\Component\ClassLoader\Psr4ClassLoader; - -class Psr4ClassLoaderTest extends \PHPUnit_Framework_TestCase -{ - /** - * @param string $className - * @dataProvider getLoadClassTests - */ - public function testLoadClass($className) - { - $loader = new Psr4ClassLoader(); - $loader->addPrefix( - 'Acme\\DemoLib', - __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4' - ); - $loader->loadClass($className); - $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); - } - - /** - * @return array - */ - public function getLoadClassTests() - { - return array( - array('Acme\\DemoLib\\Foo'), - array('Acme\\DemoLib\\Class_With_Underscores'), - array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'), - array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'), - ); - } - - /** - * @param string $className - * @dataProvider getLoadNonexistentClassTests - */ - public function testLoadNonexistentClass($className) - { - $loader = new Psr4ClassLoader(); - $loader->addPrefix( - 'Acme\\DemoLib', - __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4' - ); - $loader->loadClass($className); - $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); - } - - /** - * @return array - */ - public function getLoadNonexistentClassTests() - { - return array( - array('Acme\\DemoLib\\I_Do_Not_Exist'), - array('UnknownVendor\\SomeLib\\I_Do_Not_Exist'), - ); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/WinCacheClassLoader.php b/tests/integration/vendor/symfony/class-loader/WinCacheClassLoader.php deleted file mode 100644 index b95a1d798..000000000 --- a/tests/integration/vendor/symfony/class-loader/WinCacheClassLoader.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * WinCacheClassLoader implements a wrapping autoloader cached in WinCache. - * - * It expects an object implementing a findFile method to find the file. This - * allow using it as a wrapper around the other loaders of the component (the - * ClassLoader for instance) but also around any other autoloaders following - * this convention (the Composer one for instance). - * - * // with a Symfony autoloader - * $loader = new ClassLoader(); - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // or with a Composer autoloader - * use Composer\Autoload\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * $cachedLoader = new WinCacheClassLoader('my_prefix', $loader); - * - * // activate the cached autoloader - * $cachedLoader->register(); - * - * // eventually deactivate the non-cached loader if it was registered previously - * // to be sure to use the cached one. - * $loader->unregister(); - * - * @author Fabien Potencier - * @author Kris Wallsmith - * @author Artem Ryzhkov - */ -class WinCacheClassLoader -{ - private $prefix; - - /** - * A class loader object that implements the findFile() method. - * - * @var object - */ - protected $decorated; - - /** - * Constructor. - * - * @param string $prefix The WinCache namespace prefix to use - * @param object $decorated A class loader object that implements the findFile() method - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - */ - public function __construct($prefix, $decorated) - { - if (!extension_loaded('wincache')) { - throw new \RuntimeException('Unable to use WinCacheClassLoader as WinCache is not enabled.'); - } - - if (!method_exists($decorated, 'findFile')) { - throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); - } - - $this->prefix = $prefix; - $this->decorated = $decorated; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds a file by class name while caching lookups to WinCache. - * - * @param string $class A class name to resolve to file - * - * @return string|null - */ - public function findFile($class) - { - $file = wincache_ucache_get($this->prefix.$class, $success); - - if (!$success) { - wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null, 0); - } - - return $file; - } - - /** - * Passes through all unknown calls onto the decorated object. - */ - public function __call($method, $args) - { - return call_user_func_array(array($this->decorated, $method), $args); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/XcacheClassLoader.php b/tests/integration/vendor/symfony/class-loader/XcacheClassLoader.php deleted file mode 100644 index bf309a692..000000000 --- a/tests/integration/vendor/symfony/class-loader/XcacheClassLoader.php +++ /dev/null @@ -1,141 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\ClassLoader; - -/** - * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3. - * - * It expects an object implementing a findFile method to find the file. This - * allows using it as a wrapper around the other loaders of the component (the - * ClassLoader for instance) but also around any other autoloaders following - * this convention (the Composer one for instance). - * - * // with a Symfony autoloader - * $loader = new ClassLoader(); - * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); - * $loader->addPrefix('Symfony', __DIR__.'/framework'); - * - * // or with a Composer autoloader - * use Composer\Autoload\ClassLoader; - * - * $loader = new ClassLoader(); - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * $cachedLoader = new XcacheClassLoader('my_prefix', $loader); - * - * // activate the cached autoloader - * $cachedLoader->register(); - * - * // eventually deactivate the non-cached loader if it was registered previously - * // to be sure to use the cached one. - * $loader->unregister(); - * - * @author Fabien Potencier - * @author Kris Wallsmith - * @author Kim Hemsø Rasmussen - */ -class XcacheClassLoader -{ - private $prefix; - - /** - * A class loader object that implements the findFile() method. - * - * @var object - */ - private $decorated; - - /** - * Constructor. - * - * @param string $prefix The XCache namespace prefix to use - * @param object $decorated A class loader object that implements the findFile() method - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - */ - public function __construct($prefix, $decorated) - { - if (!extension_loaded('xcache')) { - throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.'); - } - - if (!method_exists($decorated, 'findFile')) { - throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); - } - - $this->prefix = $prefix; - $this->decorated = $decorated; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - require $file; - - return true; - } - } - - /** - * Finds a file by class name while caching lookups to Xcache. - * - * @param string $class A class name to resolve to file - * - * @return string|null - */ - public function findFile($class) - { - if (xcache_isset($this->prefix.$class)) { - $file = xcache_get($this->prefix.$class); - } else { - $file = $this->decorated->findFile($class) ?: null; - xcache_set($this->prefix.$class, $file); - } - - return $file; - } - - /** - * Passes through all unknown calls onto the decorated object. - */ - public function __call($method, $args) - { - return call_user_func_array(array($this->decorated, $method), $args); - } -} diff --git a/tests/integration/vendor/symfony/class-loader/composer.json b/tests/integration/vendor/symfony/class-loader/composer.json deleted file mode 100644 index 634c647ce..000000000 --- a/tests/integration/vendor/symfony/class-loader/composer.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "symfony/class-loader", - "type": "library", - "description": "Symfony ClassLoader Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "minimum-stability": "dev", - "require": { - "php": ">=5.5.9" - }, - "require-dev": { - "symfony/finder": "~2.8|~3.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\ClassLoader\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } -} diff --git a/tests/integration/vendor/symfony/class-loader/phpunit.xml.dist b/tests/integration/vendor/symfony/class-loader/phpunit.xml.dist deleted file mode 100644 index 4856db5be..000000000 --- a/tests/integration/vendor/symfony/class-loader/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/tests/integration/vendor/symfony/config/Builder/ClassBuilder.php b/tests/integration/vendor/symfony/config/Builder/ClassBuilder.php new file mode 100644 index 000000000..26fcab400 --- /dev/null +++ b/tests/integration/vendor/symfony/config/Builder/ClassBuilder.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * Build PHP classes to generate config. + * + * @internal + * + * @author Tobias Nyholm + */ +class ClassBuilder +{ + /** @var string */ + private $namespace; + + /** @var string */ + private $name; + + /** @var Property[] */ + private $properties = []; + + /** @var Method[] */ + private $methods = []; + private $require = []; + private $use = []; + private $implements = []; + private $allowExtraKeys = false; + + public function __construct(string $namespace, string $name) + { + $this->namespace = $namespace; + $this->name = ucfirst($this->camelCase($name)).'Config'; + } + + public function getDirectory(): string + { + return str_replace('\\', \DIRECTORY_SEPARATOR, $this->namespace); + } + + public function getFilename(): string + { + return $this->name.'.php'; + } + + public function build(): string + { + $rootPath = explode(\DIRECTORY_SEPARATOR, $this->getDirectory()); + $require = ''; + foreach ($this->require as $class) { + // figure out relative path. + $path = explode(\DIRECTORY_SEPARATOR, $class->getDirectory()); + $path[] = $class->getFilename(); + foreach ($rootPath as $key => $value) { + if ($path[$key] !== $value) { + break; + } + unset($path[$key]); + } + $require .= sprintf('require_once __DIR__.\DIRECTORY_SEPARATOR.\'%s\';', implode('\'.\DIRECTORY_SEPARATOR.\'', $path))."\n"; + } + $use = ''; + foreach (array_keys($this->use) as $statement) { + $use .= sprintf('use %s;', $statement)."\n"; + } + + $implements = [] === $this->implements ? '' : 'implements '.implode(', ', $this->implements); + $body = ''; + foreach ($this->properties as $property) { + $body .= ' '.$property->getContent()."\n"; + } + foreach ($this->methods as $method) { + $lines = explode("\n", $method->getContent()); + foreach ($lines as $line) { + $body .= ' '.$line."\n"; + } + } + + $content = strtr(' $this->namespace, 'REQUIRE' => $require, 'USE' => $use, 'CLASS' => $this->getName(), 'IMPLEMENTS' => $implements, 'BODY' => $body]); + + return $content; + } + + public function addRequire(self $class): void + { + $this->require[] = $class; + } + + public function addUse(string $class): void + { + $this->use[$class] = true; + } + + public function addImplements(string $interface): void + { + $this->implements[] = '\\'.ltrim($interface, '\\'); + } + + public function addMethod(string $name, string $body, array $params = []): void + { + $this->methods[] = new Method(strtr($body, ['NAME' => $this->camelCase($name)] + $params)); + } + + public function addProperty(string $name, string $classType = null): Property + { + $property = new Property($name, '_' !== $name[0] ? $this->camelCase($name) : $name); + if (null !== $classType) { + $property->setType($classType); + } + $this->properties[] = $property; + $property->setContent(sprintf('private $%s;', $property->getName())); + + return $property; + } + + public function getProperties(): array + { + return $this->properties; + } + + private function camelCase(string $input): string + { + $output = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $input)))); + + return preg_replace('#\W#', '', $output); + } + + public function getName(): string + { + return $this->name; + } + + public function getNamespace(): string + { + return $this->namespace; + } + + public function getFqcn(): string + { + return '\\'.$this->namespace.'\\'.$this->name; + } + + public function setAllowExtraKeys(bool $allowExtraKeys): void + { + $this->allowExtraKeys = $allowExtraKeys; + } + + public function shouldAllowExtraKeys(): bool + { + return $this->allowExtraKeys; + } +} diff --git a/tests/integration/vendor/symfony/config/Builder/ConfigBuilderGenerator.php b/tests/integration/vendor/symfony/config/Builder/ConfigBuilderGenerator.php new file mode 100644 index 000000000..979c95522 --- /dev/null +++ b/tests/integration/vendor/symfony/config/Builder/ConfigBuilderGenerator.php @@ -0,0 +1,458 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\BooleanNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\FloatNode; +use Symfony\Component\Config\Definition\IntegerNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Config\Definition\VariableNode; +use Symfony\Component\Config\Loader\ParamConfigurator; + +/** + * Generate ConfigBuilders to help create valid config. + * + * @author Tobias Nyholm + */ +class ConfigBuilderGenerator implements ConfigBuilderGeneratorInterface +{ + private $classes; + private $outputDir; + + public function __construct(string $outputDir) + { + $this->outputDir = $outputDir; + } + + /** + * @return \Closure that will return the root config class + */ + public function build(ConfigurationInterface $configuration): \Closure + { + $this->classes = []; + + $rootNode = $configuration->getConfigTreeBuilder()->buildTree(); + $rootClass = new ClassBuilder('Symfony\\Config', $rootNode->getName()); + + $path = $this->getFullPath($rootClass); + if (!is_file($path)) { + // Generate the class if the file not exists + $this->classes[] = $rootClass; + $this->buildNode($rootNode, $rootClass, $this->getSubNamespace($rootClass)); + $rootClass->addImplements(ConfigBuilderInterface::class); + $rootClass->addMethod('getExtensionAlias', ' +public function NAME(): string +{ + return \'ALIAS\'; +}', ['ALIAS' => $rootNode->getPath()]); + + $this->writeClasses(); + } + + $loader = \Closure::fromCallable(function () use ($path, $rootClass) { + require_once $path; + $className = $rootClass->getFqcn(); + + return new $className(); + }); + + return $loader; + } + + private function getFullPath(ClassBuilder $class): string + { + $directory = $this->outputDir.\DIRECTORY_SEPARATOR.$class->getDirectory(); + if (!is_dir($directory)) { + @mkdir($directory, 0777, true); + } + + return $directory.\DIRECTORY_SEPARATOR.$class->getFilename(); + } + + private function writeClasses(): void + { + foreach ($this->classes as $class) { + $this->buildConstructor($class); + $this->buildToArray($class); + $this->buildSetExtraKey($class); + + file_put_contents($this->getFullPath($class), $class->build()); + } + + $this->classes = []; + } + + private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace): void + { + if (!$node instanceof ArrayNode) { + throw new \LogicException('The node was expected to be an ArrayNode. This Configuration includes an edge case not supported yet.'); + } + + foreach ($node->getChildren() as $child) { + switch (true) { + case $child instanceof ScalarNode: + $this->handleScalarNode($child, $class); + break; + case $child instanceof PrototypedArrayNode: + $this->handlePrototypedArrayNode($child, $class, $namespace); + break; + case $child instanceof VariableNode: + $this->handleVariableNode($child, $class); + break; + case $child instanceof ArrayNode: + $this->handleArrayNode($child, $class, $namespace); + break; + default: + throw new \RuntimeException(sprintf('Unknown node "%s".', \get_class($child))); + } + } + } + + private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace): void + { + $childClass = new ClassBuilder($namespace, $node->getName()); + $childClass->setAllowExtraKeys($node->shouldIgnoreExtraKeys()); + $class->addRequire($childClass); + $this->classes[] = $childClass; + + $property = $class->addProperty($node->getName(), $childClass->getFqcn()); + $body = ' +public function NAME(array $value = []): CLASS +{ + if (null === $this->PROPERTY) { + $this->PROPERTY = new CLASS($value); + } elseif ([] !== $value) { + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); + } + + return $this->PROPERTY; +}'; + $class->addUse(InvalidConfigurationException::class); + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + + $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); + } + + private function handleVariableNode(VariableNode $node, ClassBuilder $class): void + { + $comment = $this->getComment($node); + $property = $class->addProperty($node->getName()); + $class->addUse(ParamConfigurator::class); + + $body = ' +/** +COMMENT * @return $this + */ +public function NAME($valueDEFAULT): self +{ + $this->PROPERTY = $value; + + return $this; +}'; + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment, 'DEFAULT' => $node->hasDefaultValue() ? ' = '.var_export($node->getDefaultValue(), true) : '']); + } + + private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace): void + { + $name = $this->getSingularName($node); + $prototype = $node->getPrototype(); + $methodName = $name; + + $parameterType = $this->getParameterType($prototype); + if (null !== $parameterType || $prototype instanceof ScalarNode) { + $class->addUse(ParamConfigurator::class); + $property = $class->addProperty($node->getName()); + if (null === $key = $node->getKeyAttribute()) { + // This is an array of values; don't use singular name + $body = ' +/** + * @param ParamConfigurator|list $value + * @return $this + */ +public function NAME($value): self +{ + $this->PROPERTY = $value; + + return $this; +}'; + + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : $parameterType]); + } else { + $body = ' +/** + * @param ParamConfigurator|TYPE $value + * @return $this + */ +public function NAME(string $VAR, $VALUE): self +{ + $this->PROPERTY[$VAR] = $VALUE; + + return $this; +}'; + + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : $parameterType, 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']); + } + + return; + } + + $childClass = new ClassBuilder($namespace, $name); + if ($prototype instanceof ArrayNode) { + $childClass->setAllowExtraKeys($prototype->shouldIgnoreExtraKeys()); + } + $class->addRequire($childClass); + $this->classes[] = $childClass; + $property = $class->addProperty($node->getName(), $childClass->getFqcn().'[]'); + + if (null === $key = $node->getKeyAttribute()) { + $body = ' +public function NAME(array $value = []): CLASS +{ + return $this->PROPERTY[] = new CLASS($value); +}'; + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + } else { + $body = ' +public function NAME(string $VAR, array $VALUE = []): CLASS +{ + if (!isset($this->PROPERTY[$VAR])) { + return $this->PROPERTY[$VAR] = new CLASS($value); + } + if ([] === $value) { + return $this->PROPERTY[$VAR]; + } + + throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); +}'; + $class->addUse(InvalidConfigurationException::class); + $class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']); + } + + $this->buildNode($prototype, $childClass, $namespace.'\\'.$childClass->getName()); + } + + private function handleScalarNode(ScalarNode $node, ClassBuilder $class): void + { + $comment = $this->getComment($node); + $property = $class->addProperty($node->getName()); + $class->addUse(ParamConfigurator::class); + + $body = ' +/** +COMMENT * @return $this + */ +public function NAME($value): self +{ + $this->PROPERTY = $value; + + return $this; +}'; + + $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]); + } + + private function getParameterType(NodeInterface $node): ?string + { + if ($node instanceof BooleanNode) { + return 'bool'; + } + + if ($node instanceof IntegerNode) { + return 'int'; + } + + if ($node instanceof FloatNode) { + return 'float'; + } + + if ($node instanceof EnumNode) { + return ''; + } + + if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) { + // This is just an array of variables + return 'array'; + } + + if ($node instanceof VariableNode) { + // mixed + return ''; + } + + return null; + } + + private function getComment(VariableNode $node): string + { + $comment = ''; + if ('' !== $info = (string) $node->getInfo()) { + $comment .= ' * '.$info."\n"; + } + + foreach ((array) ($node->getExample() ?? []) as $example) { + $comment .= ' * @example '.$example."\n"; + } + + if ('' !== $default = $node->getDefaultValue()) { + $comment .= ' * @default '.(null === $default ? 'null' : var_export($default, true))."\n"; + } + + if ($node instanceof EnumNode) { + $comment .= sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_map(function ($a) { + return var_export($a, true); + }, $node->getValues())))."\n"; + } else { + $parameterType = $this->getParameterType($node); + if (null === $parameterType || '' === $parameterType) { + $parameterType = 'mixed'; + } + $comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n"; + } + + if ($node->isDeprecated()) { + $comment .= ' * @deprecated '.$node->getDeprecation($node->getName(), $node->getParent()->getName())['message']."\n"; + } + + return $comment; + } + + /** + * Pick a good singular name. + */ + private function getSingularName(PrototypedArrayNode $node): string + { + $name = $node->getName(); + if ('s' !== substr($name, -1)) { + return $name; + } + + $parent = $node->getParent(); + $mappings = $parent instanceof ArrayNode ? $parent->getXmlRemappings() : []; + foreach ($mappings as $map) { + if ($map[1] === $name) { + $name = $map[0]; + break; + } + } + + return $name; + } + + private function buildToArray(ClassBuilder $class): void + { + $body = '$output = [];'; + foreach ($class->getProperties() as $p) { + $code = '$this->PROPERTY'; + if (null !== $p->getType()) { + if ($p->isArray()) { + $code = 'array_map(function ($v) { return $v->toArray(); }, $this->PROPERTY)'; + } else { + $code = '$this->PROPERTY->toArray()'; + } + } + + $body .= strtr(' + if (null !== $this->PROPERTY) { + $output[\'ORG_NAME\'] = '.$code.'; + }', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]); + } + + $extraKeys = $class->shouldAllowExtraKeys() ? ' + $this->_extraKeys' : ''; + + $class->addMethod('toArray', ' +public function NAME(): array +{ + '.$body.' + + return $output'.$extraKeys.'; +}'); + } + + private function buildConstructor(ClassBuilder $class): void + { + $body = ''; + foreach ($class->getProperties() as $p) { + $code = '$value[\'ORG_NAME\']'; + if (null !== $p->getType()) { + if ($p->isArray()) { + $code = 'array_map(function ($v) { return new '.$p->getType().'($v); }, $value[\'ORG_NAME\'])'; + } else { + $code = 'new '.$p->getType().'($value[\'ORG_NAME\'])'; + } + } + + $body .= strtr(' + if (isset($value[\'ORG_NAME\'])) { + $this->PROPERTY = '.$code.'; + unset($value[\'ORG_NAME\']); + } +', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]); + } + + if ($class->shouldAllowExtraKeys()) { + $body .= ' + $this->_extraKeys = $value; +'; + } else { + $body .= ' + if ([] !== $value) { + throw new InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__).implode(\', \', array_keys($value))); + }'; + + $class->addUse(InvalidConfigurationException::class); + } + + $class->addMethod('__construct', ' +public function __construct(array $value = []) +{ +'.$body.' +}'); + } + + private function buildSetExtraKey(ClassBuilder $class): void + { + if (!$class->shouldAllowExtraKeys()) { + return; + } + + $class->addUse(ParamConfigurator::class); + + $class->addProperty('_extraKeys'); + + $class->addMethod('set', ' +/** + * @param ParamConfigurator|mixed $value + * @return $this + */ +public function NAME(string $key, $value): self +{ + if (null === $value) { + unset($this->_extraKeys[$key]); + } else { + $this->_extraKeys[$key] = $value; + } + + return $this; +}'); + } + + private function getSubNamespace(ClassBuilder $rootClass): string + { + return sprintf('%s\\%s', $rootClass->getNamespace(), substr($rootClass->getName(), 0, -6)); + } +} diff --git a/tests/integration/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php b/tests/integration/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php new file mode 100644 index 000000000..c52c9e5d5 --- /dev/null +++ b/tests/integration/vendor/symfony/config/Builder/ConfigBuilderGeneratorInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * Generates ConfigBuilders to help create valid config. + * + * @author Tobias Nyholm + */ +interface ConfigBuilderGeneratorInterface +{ + /** + * @return \Closure that will return the root config class + */ + public function build(ConfigurationInterface $configuration): \Closure; +} diff --git a/tests/integration/vendor/symfony/config/Builder/ConfigBuilderInterface.php b/tests/integration/vendor/symfony/config/Builder/ConfigBuilderInterface.php new file mode 100644 index 000000000..fd3129c58 --- /dev/null +++ b/tests/integration/vendor/symfony/config/Builder/ConfigBuilderInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * A ConfigBuilder provides helper methods to build a large complex array. + * + * @author Tobias Nyholm + */ +interface ConfigBuilderInterface +{ + /** + * Gets all configuration represented as an array. + */ + public function toArray(): array; + + /** + * Gets the alias for the extension which config we are building. + */ + public function getExtensionAlias(): string; +} diff --git a/tests/integration/vendor/symfony/config/Builder/Method.php b/tests/integration/vendor/symfony/config/Builder/Method.php new file mode 100644 index 000000000..3577e3d7a --- /dev/null +++ b/tests/integration/vendor/symfony/config/Builder/Method.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * Represents a method when building classes. + * + * @internal + * + * @author Tobias Nyholm + */ +class Method +{ + private $content; + + public function __construct(string $content) + { + $this->content = $content; + } + + public function getContent(): string + { + return $this->content; + } +} diff --git a/tests/integration/vendor/symfony/config/Builder/Property.php b/tests/integration/vendor/symfony/config/Builder/Property.php new file mode 100644 index 000000000..1b24c47cc --- /dev/null +++ b/tests/integration/vendor/symfony/config/Builder/Property.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Builder; + +/** + * Represents a property when building classes. + * + * @internal + * + * @author Tobias Nyholm + */ +class Property +{ + private $name; + private $originalName; + private $array = false; + private $type = null; + private $content; + + public function __construct(string $originalName, string $name) + { + $this->name = $name; + $this->originalName = $originalName; + } + + public function getName(): string + { + return $this->name; + } + + public function getOriginalName(): string + { + return $this->originalName; + } + + public function setType(string $type): void + { + $this->array = false; + $this->type = $type; + + if ('[]' === substr($type, -2)) { + $this->array = true; + $this->type = substr($type, 0, -2); + } + } + + public function getType(): ?string + { + return $this->type; + } + + public function getContent(): ?string + { + return $this->content; + } + + public function setContent(string $content): void + { + $this->content = $content; + } + + public function isArray(): bool + { + return $this->array; + } +} diff --git a/tests/integration/vendor/symfony/config/CHANGELOG.md b/tests/integration/vendor/symfony/config/CHANGELOG.md index b752df6fe..75ef8bc44 100644 --- a/tests/integration/vendor/symfony/config/CHANGELOG.md +++ b/tests/integration/vendor/symfony/config/CHANGELOG.md @@ -1,6 +1,73 @@ CHANGELOG ========= +5.3.0 +----- + + * Add support for generating `ConfigBuilder` for extensions + +5.1.0 +----- + + * updated the signature of method `NodeDefinition::setDeprecated()` to `NodeDefinition::setDeprecation(string $package, string $version, string $message)` + * updated the signature of method `BaseNode::setDeprecated()` to `BaseNode::setDeprecation(string $package, string $version, string $message)` + * deprecated passing a null message to `BaseNode::setDeprecated()` to un-deprecate a node + * deprecated `BaseNode::getDeprecationMessage()`, use `BaseNode::getDeprecation()` instead + +5.0.0 +----- + + * Dropped support for constructing a `TreeBuilder` without passing root node information. + * Removed the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead + * Added method `getChildNodeDefinitions()` to ParentNodeDefinitionInterface + * Removed `FileLoaderLoadException`, use `LoaderLoadException` instead + +4.4.0 +----- + + * added a way to exclude patterns of resources from being imported by the `import()` method + +4.3.0 +----- + + * deprecated using environment variables with `cannotBeEmpty()` if the value is validated with `validate()` + * made `Resource\*` classes final and not implement `Serializable` anymore + * deprecated the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead + +4.2.0 +----- + + * deprecated constructing a `TreeBuilder` without passing root node information + * renamed `FileLoaderLoadException` to `LoaderLoadException` + +4.1.0 +----- + + * added `setPathSeparator` method to `NodeBuilder` class + * added third `$pathSeparator` constructor argument to `BaseNode` + * the `Processor` class has been made final + +4.0.0 +----- + + * removed `ConfigCachePass` + +3.4.0 +----- + + * added `setDeprecated()` method to indicate a deprecated node + * added `XmlUtils::parse()` method to parse an XML string + * deprecated `ConfigCachePass` + +3.3.0 +----- + + * added `ReflectionClassResource` class + * added second `$exists` constructor argument to `ClassExistenceResource` + * made `ClassExistenceResource` work with interfaces and traits + * added `ConfigCachePass` (originally in FrameworkBundle) + * added `castToArray()` helper to turn any config value into an array + 3.0.0 ----- @@ -18,7 +85,7 @@ The edge case of defining just one value for nodes of type Enum is now allowed: $rootNode ->children() ->enumNode('variable') - ->values(array('value')) + ->values(['value']) ->end() ->end() ; @@ -28,7 +95,7 @@ Before: `InvalidArgumentException` (variable must contain at least two distinct elements). After: the code will work as expected and it will restrict the values of the `variable` option to just `value`. - + * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they can be validated that way, make them implement the new `SelfCheckingResourceInterface`. * deprecated the getResource() method in ResourceInterface. You can still call this method @@ -41,7 +108,7 @@ After: the code will work as expected and it will restrict the values of the * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` implementation to delegate creation of ConfigCache instances - + 2.2.0 ----- diff --git a/tests/integration/vendor/symfony/config/ConfigCache.php b/tests/integration/vendor/symfony/config/ConfigCache.php index 591c89bc4..3b090525e 100644 --- a/tests/integration/vendor/symfony/config/ConfigCache.php +++ b/tests/integration/vendor/symfony/config/ConfigCache.php @@ -31,13 +31,13 @@ class ConfigCache extends ResourceCheckerConfigCache * @param string $file The absolute cache path * @param bool $debug Whether debugging is enabled or not */ - public function __construct($file, $debug) + public function __construct(string $file, bool $debug) { - $this->debug = (bool) $debug; + $this->debug = $debug; - $checkers = array(); + $checkers = []; if (true === $this->debug) { - $checkers = array(new SelfCheckingResourceChecker()); + $checkers = [new SelfCheckingResourceChecker()]; } parent::__construct($file, $checkers); @@ -49,7 +49,7 @@ public function __construct($file, $debug) * This implementation always returns true when debug is off and the * cache file exists. * - * @return bool true if the cache is fresh, false otherwise + * @return bool */ public function isFresh() { diff --git a/tests/integration/vendor/symfony/config/ConfigCacheFactory.php b/tests/integration/vendor/symfony/config/ConfigCacheFactory.php index 396536e2d..11fd3cb3a 100644 --- a/tests/integration/vendor/symfony/config/ConfigCacheFactory.php +++ b/tests/integration/vendor/symfony/config/ConfigCacheFactory.php @@ -22,15 +22,12 @@ */ class ConfigCacheFactory implements ConfigCacheFactoryInterface { - /** - * @var bool Debug flag passed to the ConfigCache - */ private $debug; /** * @param bool $debug The debug flag to pass to ConfigCache */ - public function __construct($debug) + public function __construct(bool $debug) { $this->debug = $debug; } @@ -38,15 +35,11 @@ public function __construct($debug) /** * {@inheritdoc} */ - public function cache($file, $callback) + public function cache(string $file, callable $callback) { - if (!is_callable($callback)) { - throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback))); - } - $cache = new ConfigCache($file, $this->debug); if (!$cache->isFresh()) { - call_user_func($callback, $cache); + $callback($cache); } return $cache; diff --git a/tests/integration/vendor/symfony/config/ConfigCacheFactoryInterface.php b/tests/integration/vendor/symfony/config/ConfigCacheFactoryInterface.php index bd614c4b6..146ee9b56 100644 --- a/tests/integration/vendor/symfony/config/ConfigCacheFactoryInterface.php +++ b/tests/integration/vendor/symfony/config/ConfigCacheFactoryInterface.php @@ -26,7 +26,7 @@ interface ConfigCacheFactoryInterface * @param string $file The absolute cache file path * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback * - * @return ConfigCacheInterface $configCache The cache instance + * @return ConfigCacheInterface */ - public function cache($file, $callable); + public function cache(string $file, callable $callable); } diff --git a/tests/integration/vendor/symfony/config/ConfigCacheInterface.php b/tests/integration/vendor/symfony/config/ConfigCacheInterface.php index 7c47ad70a..3cd7a5cc0 100644 --- a/tests/integration/vendor/symfony/config/ConfigCacheInterface.php +++ b/tests/integration/vendor/symfony/config/ConfigCacheInterface.php @@ -23,7 +23,7 @@ interface ConfigCacheInterface /** * Gets the cache file path. * - * @return string The cache file path + * @return string */ public function getPath(); @@ -32,7 +32,7 @@ public function getPath(); * * This check should take the metadata passed to the write() method into consideration. * - * @return bool Whether the cache is still fresh + * @return bool */ public function isFresh(); @@ -45,5 +45,5 @@ public function isFresh(); * * @throws \RuntimeException When the cache file cannot be written */ - public function write($content, array $metadata = null); + public function write(string $content, array $metadata = null); } diff --git a/tests/integration/vendor/symfony/config/Definition/ArrayNode.php b/tests/integration/vendor/symfony/config/Definition/ArrayNode.php index 457e7a8c9..bd0eae9fd 100644 --- a/tests/integration/vendor/symfony/config/Definition/ArrayNode.php +++ b/tests/integration/vendor/symfony/config/Definition/ArrayNode.php @@ -22,8 +22,8 @@ */ class ArrayNode extends BaseNode implements PrototypeNodeInterface { - protected $xmlRemappings = array(); - protected $children = array(); + protected $xmlRemappings = []; + protected $children = []; protected $allowFalse = false; protected $allowNewKeys = true; protected $addIfNotSet = false; @@ -32,34 +32,30 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface protected $removeExtraKeys = true; protected $normalizeKeys = true; - public function setNormalizeKeys($normalizeKeys) + public function setNormalizeKeys(bool $normalizeKeys) { - $this->normalizeKeys = (bool) $normalizeKeys; + $this->normalizeKeys = $normalizeKeys; } /** - * Normalizes keys between the different configuration formats. + * {@inheritdoc} * * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. * After running this method, all keys are normalized to foo_bar. * * If you have a mixed key like foo-bar_moo, it will not be altered. * The key will also not be altered if the target key already exists. - * - * @param mixed $value - * - * @return array The value with normalized keys */ protected function preNormalize($value) { - if (!$this->normalizeKeys || !is_array($value)) { + if (!$this->normalizeKeys || !\is_array($value)) { return $value; } - $normalized = array(); + $normalized = []; foreach ($value as $k => $v) { - if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { + if (str_contains($k, '-') && !str_contains($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { $normalized[$normalizedKey] = $v; } else { $normalized[$k] = $v; @@ -72,7 +68,7 @@ protected function preNormalize($value) /** * Retrieves the children of this node. * - * @return array The children + * @return array */ public function getChildren() { @@ -82,7 +78,7 @@ public function getChildren() /** * Sets the xml remappings that should be performed. * - * @param array $remappings an array of the form array(array(string, string)) + * @param array $remappings An array of the form [[string, string]] */ public function setXmlRemappings(array $remappings) { @@ -92,7 +88,7 @@ public function setXmlRemappings(array $remappings) /** * Gets the xml remappings that should be performed. * - * @return array $remappings an array of the form array(array(string, string)) + * @return array an array of the form [[string, string]] */ public function getXmlRemappings() { @@ -102,70 +98,66 @@ public function getXmlRemappings() /** * Sets whether to add default values for this array if it has not been * defined in any of the configuration files. - * - * @param bool $boolean */ - public function setAddIfNotSet($boolean) + public function setAddIfNotSet(bool $boolean) { - $this->addIfNotSet = (bool) $boolean; + $this->addIfNotSet = $boolean; } /** * Sets whether false is allowed as value indicating that the array should be unset. - * - * @param bool $allow */ - public function setAllowFalse($allow) + public function setAllowFalse(bool $allow) { - $this->allowFalse = (bool) $allow; + $this->allowFalse = $allow; } /** * Sets whether new keys can be defined in subsequent configurations. - * - * @param bool $allow */ - public function setAllowNewKeys($allow) + public function setAllowNewKeys(bool $allow) { - $this->allowNewKeys = (bool) $allow; + $this->allowNewKeys = $allow; } /** * Sets if deep merging should occur. - * - * @param bool $boolean */ - public function setPerformDeepMerging($boolean) + public function setPerformDeepMerging(bool $boolean) { - $this->performDeepMerging = (bool) $boolean; + $this->performDeepMerging = $boolean; } /** - * Whether extra keys should just be ignore without an exception. + * Whether extra keys should just be ignored without an exception. * * @param bool $boolean To allow extra keys * @param bool $remove To remove extra keys */ - public function setIgnoreExtraKeys($boolean, $remove = true) + public function setIgnoreExtraKeys(bool $boolean, bool $remove = true) { - $this->ignoreExtraKeys = (bool) $boolean; + $this->ignoreExtraKeys = $boolean; $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; } /** - * Sets the node Name. - * - * @param string $name The node's name + * Returns true when extra keys should be ignored without an exception. + */ + public function shouldIgnoreExtraKeys(): bool + { + return $this->ignoreExtraKeys; + } + + /** + * {@inheritdoc} */ - public function setName($name) + public function setName(string $name) { $this->name = $name; } /** - * Checks if the node has a default value. - * - * @return bool + * {@inheritdoc} */ public function hasDefaultValue() { @@ -173,11 +165,7 @@ public function hasDefaultValue() } /** - * Retrieves the default value. - * - * @return array The default value - * - * @throws \RuntimeException if the node has no default value + * {@inheritdoc} */ public function getDefaultValue() { @@ -185,7 +173,7 @@ public function getDefaultValue() throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); } - $defaults = array(); + $defaults = []; foreach ($this->children as $name => $child) { if ($child->hasDefaultValue()) { $defaults[$name] = $child->getDefaultValue(); @@ -198,15 +186,13 @@ public function getDefaultValue() /** * Adds a child node. * - * @param NodeInterface $node The child node to add - * * @throws \InvalidArgumentException when the child node has no name * @throws \InvalidArgumentException when the child node's name is not unique */ public function addChild(NodeInterface $node) { $name = $node->getName(); - if (!strlen($name)) { + if ('' === $name) { throw new \InvalidArgumentException('Child nodes must be named.'); } if (isset($this->children[$name])) { @@ -217,11 +203,7 @@ public function addChild(NodeInterface $node) } /** - * Finalizes the value of this node. - * - * @param mixed $value - * - * @return mixed The finalised value + * {@inheritdoc} * * @throws UnsetKeyException * @throws InvalidConfigurationException if the node doesn't have enough children @@ -229,15 +211,19 @@ public function addChild(NodeInterface $node) protected function finalizeValue($value) { if (false === $value) { - $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)); - throw new UnsetKeyException($msg); + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s.', $this->getPath(), json_encode($value))); } foreach ($this->children as $name => $child) { - if (!array_key_exists($name, $value)) { + if (!\array_key_exists($name, $value)) { if ($child->isRequired()) { - $msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()); - $ex = new InvalidConfigurationException($msg); + $message = sprintf('The child config "%s" under "%s" must be configured', $name, $this->getPath()); + if ($child->getInfo()) { + $message .= sprintf(': %s', $child->getInfo()); + } else { + $message .= '.'; + } + $ex = new InvalidConfigurationException($message); $ex->setPath($this->getPath()); throw $ex; @@ -250,6 +236,11 @@ protected function finalizeValue($value) continue; } + if ($child->isDeprecated()) { + $deprecation = $child->getDeprecation($name, $this->getPath()); + trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); + } + try { $value[$name] = $child->finalize($value[$name]); } catch (UnsetKeyException $e) { @@ -261,20 +252,12 @@ protected function finalizeValue($value) } /** - * Validates the type of the value. - * - * @param mixed $value - * - * @throws InvalidTypeException + * {@inheritdoc} */ protected function validateType($value) { - if (!is_array($value) && (!$this->allowFalse || false !== $value)) { - $ex = new InvalidTypeException(sprintf( - 'Invalid type for path "%s". Expected array, but got %s', - $this->getPath(), - gettype($value) - )); + if (!\is_array($value) && (!$this->allowFalse || false !== $value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "array", but got "%s"', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } @@ -285,11 +268,7 @@ protected function validateType($value) } /** - * Normalizes the value. - * - * @param mixed $value The value to normalize - * - * @return mixed The normalized value + * {@inheritdoc} * * @throws InvalidConfigurationException */ @@ -301,10 +280,13 @@ protected function normalizeValue($value) $value = $this->remapXml($value); - $normalized = array(); + $normalized = []; foreach ($value as $name => $val) { if (isset($this->children[$name])) { - $normalized[$name] = $this->children[$name]->normalize($val); + try { + $normalized[$name] = $this->children[$name]->normalize($val); + } catch (UnsetKeyException $e) { + } unset($value[$name]); } elseif (!$this->removeExtraKeys) { $normalized[$name] = $val; @@ -312,8 +294,31 @@ protected function normalizeValue($value) } // if extra fields are present, throw exception - if (count($value) && !$this->ignoreExtraKeys) { - $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); + if (\count($value) && !$this->ignoreExtraKeys) { + $proposals = array_keys($this->children); + sort($proposals); + $guesses = []; + + foreach (array_keys($value) as $subject) { + $minScore = \INF; + foreach ($proposals as $proposal) { + $distance = levenshtein($subject, $proposal); + if ($distance <= $minScore && $distance < 3) { + $guesses[$proposal] = $distance; + $minScore = $distance; + } + } + } + + $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); + + if (\count($guesses)) { + asort($guesses); + $msg .= sprintf('. Did you mean "%s"?', implode('", "', array_keys($guesses))); + } else { + $msg .= sprintf('. Available option%s %s "%s".', 1 === \count($proposals) ? '' : 's', 1 === \count($proposals) ? 'is' : 'are', implode('", "', $proposals)); + } + $ex = new InvalidConfigurationException($msg); $ex->setPath($this->getPath()); @@ -326,13 +331,11 @@ protected function normalizeValue($value) /** * Remaps multiple singular values to a single plural value. * - * @param array $value The source values - * - * @return array The remapped values + * @return array */ - protected function remapXml($value) + protected function remapXml(array $value) { - foreach ($this->xmlRemappings as list($singular, $plural)) { + foreach ($this->xmlRemappings as [$singular, $plural]) { if (!isset($value[$singular])) { continue; } @@ -345,12 +348,7 @@ protected function remapXml($value) } /** - * Merges values together. - * - * @param mixed $leftSide The left side to merge - * @param mixed $rightSide The right side to merge - * - * @return mixed The merged values + * {@inheritdoc} * * @throws InvalidConfigurationException * @throws \RuntimeException @@ -369,15 +367,9 @@ protected function mergeValues($leftSide, $rightSide) foreach ($rightSide as $k => $v) { // no conflict - if (!array_key_exists($k, $leftSide)) { + if (!\array_key_exists($k, $leftSide)) { if (!$this->allowNewKeys) { - $ex = new InvalidConfigurationException(sprintf( - 'You are not allowed to define new elements for path "%s". ' - .'Please define all elements for this path in one config file. ' - .'If you are trying to overwrite an element, make sure you redefine it ' - .'with the same name.', - $this->getPath() - )); + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); $ex->setPath($this->getPath()); throw $ex; @@ -388,7 +380,12 @@ protected function mergeValues($leftSide, $rightSide) } if (!isset($this->children[$k])) { - throw new \RuntimeException('merge() expects a normalized config array.'); + if (!$this->ignoreExtraKeys || $this->removeExtraKeys) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $v; + continue; } $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); @@ -396,4 +393,12 @@ protected function mergeValues($leftSide, $rightSide) return $leftSide; } + + /** + * {@inheritdoc} + */ + protected function allowPlaceholders(): bool + { + return false; + } } diff --git a/tests/integration/vendor/symfony/config/Definition/BaseNode.php b/tests/integration/vendor/symfony/config/Definition/BaseNode.php index dbf36335b..5ca123925 100644 --- a/tests/integration/vendor/symfony/config/Definition/BaseNode.php +++ b/tests/integration/vendor/symfony/config/Definition/BaseNode.php @@ -15,6 +15,7 @@ use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; /** * The base node class. @@ -23,48 +24,103 @@ */ abstract class BaseNode implements NodeInterface { + public const DEFAULT_PATH_SEPARATOR = '.'; + + private static $placeholderUniquePrefixes = []; + private static $placeholders = []; + protected $name; protected $parent; - protected $normalizationClosures = array(); - protected $finalValidationClosures = array(); + protected $normalizationClosures = []; + protected $finalValidationClosures = []; protected $allowOverwrite = true; protected $required = false; - protected $equivalentValues = array(); - protected $attributes = array(); + protected $deprecation = []; + protected $equivalentValues = []; + protected $attributes = []; + protected $pathSeparator; + + private $handlingPlaceholder; /** - * Constructor. - * - * @param string $name The name of the node - * @param NodeInterface $parent The parent of this node - * - * @throws \InvalidArgumentException if the name contains a period. + * @throws \InvalidArgumentException if the name contains a period */ - public function __construct($name, NodeInterface $parent = null) + public function __construct(?string $name, NodeInterface $parent = null, string $pathSeparator = self::DEFAULT_PATH_SEPARATOR) { - if (false !== strpos($name, '.')) { - throw new \InvalidArgumentException('The name must not contain ".".'); + if (str_contains($name = (string) $name, $pathSeparator)) { + throw new \InvalidArgumentException('The name must not contain ".'.$pathSeparator.'".'); } $this->name = $name; $this->parent = $parent; + $this->pathSeparator = $pathSeparator; } - public function setAttribute($key, $value) + /** + * Register possible (dummy) values for a dynamic placeholder value. + * + * Matching configuration values will be processed with a provided value, one by one. After a provided value is + * successfully processed the configuration value is returned as is, thus preserving the placeholder. + * + * @internal + */ + public static function setPlaceholder(string $placeholder, array $values): void + { + if (!$values) { + throw new \InvalidArgumentException('At least one value must be provided.'); + } + + self::$placeholders[$placeholder] = $values; + } + + /** + * Adds a common prefix for dynamic placeholder values. + * + * Matching configuration values will be skipped from being processed and are returned as is, thus preserving the + * placeholder. An exact match provided by {@see setPlaceholder()} might take precedence. + * + * @internal + */ + public static function setPlaceholderUniquePrefix(string $prefix): void + { + self::$placeholderUniquePrefixes[] = $prefix; + } + + /** + * Resets all current placeholders available. + * + * @internal + */ + public static function resetPlaceholders(): void + { + self::$placeholderUniquePrefixes = []; + self::$placeholders = []; + } + + public function setAttribute(string $key, $value) { $this->attributes[$key] = $value; } - public function getAttribute($key, $default = null) + /** + * @return mixed + */ + public function getAttribute(string $key, $default = null) { - return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + return $this->attributes[$key] ?? $default; } - public function hasAttribute($key) + /** + * @return bool + */ + public function hasAttribute(string $key) { return isset($this->attributes[$key]); } + /** + * @return array + */ public function getAttributes() { return $this->attributes; @@ -75,17 +131,15 @@ public function setAttributes(array $attributes) $this->attributes = $attributes; } - public function removeAttribute($key) + public function removeAttribute(string $key) { unset($this->attributes[$key]); } /** * Sets an info message. - * - * @param string $info */ - public function setInfo($info) + public function setInfo(string $info) { $this->setAttribute('info', $info); } @@ -93,7 +147,7 @@ public function setInfo($info) /** * Returns info message. * - * @return string The info text + * @return string|null */ public function getInfo() { @@ -113,7 +167,7 @@ public function setExample($example) /** * Retrieves the example configuration for this node. * - * @return string|array The example + * @return string|array|null */ public function getExample() { @@ -128,27 +182,63 @@ public function getExample() */ public function addEquivalentValue($originalValue, $equivalentValue) { - $this->equivalentValues[] = array($originalValue, $equivalentValue); + $this->equivalentValues[] = [$originalValue, $equivalentValue]; } /** * Set this node as required. + */ + public function setRequired(bool $boolean) + { + $this->required = $boolean; + } + + /** + * Sets this node as deprecated. * - * @param bool $boolean Required node + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message the deprecation message to use + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path */ - public function setRequired($boolean) + public function setDeprecated(?string $package/*, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.' */) { - $this->required = (bool) $boolean; + $args = \func_get_args(); + + if (\func_num_args() < 2) { + trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + if (!isset($args[0])) { + trigger_deprecation('symfony/config', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); + + $this->deprecation = []; + + return; + } + + $message = (string) $args[0]; + $package = $version = ''; + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); + } + + $this->deprecation = [ + 'package' => $package, + 'version' => $version, + 'message' => $message, + ]; } /** * Sets if this node can be overridden. - * - * @param bool $allow */ - public function setAllowOverwrite($allow) + public function setAllowOverwrite(bool $allow) { - $this->allowOverwrite = (bool) $allow; + $this->allowOverwrite = $allow; } /** @@ -172,9 +262,7 @@ public function setFinalValidationClosures(array $closures) } /** - * Checks if this node is required. - * - * @return bool + * {@inheritdoc} */ public function isRequired() { @@ -182,9 +270,47 @@ public function isRequired() } /** - * Returns the name of this node. + * Checks if this node is deprecated. * - * @return string The Node's name + * @return bool + */ + public function isDeprecated() + { + return (bool) $this->deprecation; + } + + /** + * Returns the deprecated message. + * + * @param string $node the configuration node name + * @param string $path the path of the node + * + * @return string + * + * @deprecated since Symfony 5.1, use "getDeprecation()" instead. + */ + public function getDeprecationMessage(string $node, string $path) + { + trigger_deprecation('symfony/config', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); + + return $this->getDeprecation($node, $path)['message']; + } + + /** + * @param string $node The configuration node name + * @param string $path The path of the node + */ + public function getDeprecation(string $node, string $path): array + { + return [ + 'package' => $this->deprecation['package'] ?? '', + 'version' => $this->deprecation['version'] ?? '', + 'message' => strtr($this->deprecation['message'] ?? '', ['%node%' => $node, '%path%' => $path]), + ]; + } + + /** + * {@inheritdoc} */ public function getName() { @@ -192,54 +318,60 @@ public function getName() } /** - * Retrieves the path of this node. - * - * @return string The Node's path + * {@inheritdoc} */ public function getPath() { - $path = $this->name; - if (null !== $this->parent) { - $path = $this->parent->getPath().'.'.$path; + return $this->parent->getPath().$this->pathSeparator.$this->name; } - return $path; + return $this->name; } /** - * Merges two values together. - * - * @param mixed $leftSide - * @param mixed $rightSide - * - * @return mixed The merged value - * - * @throws ForbiddenOverwriteException + * {@inheritdoc} */ final public function merge($leftSide, $rightSide) { if (!$this->allowOverwrite) { - throw new ForbiddenOverwriteException(sprintf( - 'Configuration path "%s" cannot be overwritten. You have to ' - .'define all options for this path, and any of its sub-paths in ' - .'one configuration section.', - $this->getPath() - )); + throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); } - $this->validateType($leftSide); - $this->validateType($rightSide); + if ($leftSide !== $leftPlaceholders = self::resolvePlaceholderValue($leftSide)) { + foreach ($leftPlaceholders as $leftPlaceholder) { + $this->handlingPlaceholder = $leftSide; + try { + $this->merge($leftPlaceholder, $rightSide); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $rightSide; + } + + if ($rightSide !== $rightPlaceholders = self::resolvePlaceholderValue($rightSide)) { + foreach ($rightPlaceholders as $rightPlaceholder) { + $this->handlingPlaceholder = $rightSide; + try { + $this->merge($leftSide, $rightPlaceholder); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $rightSide; + } + + $this->doValidateType($leftSide); + $this->doValidateType($rightSide); return $this->mergeValues($leftSide, $rightSide); } /** - * Normalizes a value, applying all normalization closures. - * - * @param mixed $value Value to normalize - * - * @return mixed The normalized value + * {@inheritdoc} */ final public function normalize($value) { @@ -250,6 +382,20 @@ final public function normalize($value) $value = $closure($value); } + // resolve placeholder value + if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { + foreach ($placeholders as $placeholder) { + $this->handlingPlaceholder = $value; + try { + $this->normalize($placeholder); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $value; + } + // replace value with their equivalent foreach ($this->equivalentValues as $data) { if ($data[0] === $value) { @@ -258,7 +404,7 @@ final public function normalize($value) } // validate type - $this->validateType($value); + $this->doValidateType($value); // normalize value return $this->normalizeValue($value); @@ -267,9 +413,9 @@ final public function normalize($value) /** * Normalizes the value before any other normalization is applied. * - * @param $value + * @param mixed $value * - * @return $value The normalized array value + * @return mixed */ protected function preNormalize($value) { @@ -287,18 +433,24 @@ public function getParent() } /** - * Finalizes a value, applying all finalization closures. - * - * @param mixed $value The value to finalize - * - * @return mixed The finalized value - * - * @throws Exception - * @throws InvalidConfigurationException + * {@inheritdoc} */ final public function finalize($value) { - $this->validateType($value); + if ($value !== $placeholders = self::resolvePlaceholderValue($value)) { + foreach ($placeholders as $placeholder) { + $this->handlingPlaceholder = $value; + try { + $this->finalize($placeholder); + } finally { + $this->handlingPlaceholder = null; + } + } + + return $value; + } + + $this->doValidateType($value); $value = $this->finalizeValue($value); @@ -308,9 +460,13 @@ final public function finalize($value) try { $value = $closure($value); } catch (Exception $e) { + if ($e instanceof UnsetKeyException && null !== $this->handlingPlaceholder) { + continue; + } + throw $e; } catch (\Exception $e) { - throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e); + throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": ', $this->getPath()).$e->getMessage(), $e->getCode(), $e); } } @@ -331,7 +487,7 @@ abstract protected function validateType($value); * * @param mixed $value The value to normalize * - * @return mixed The normalized value + * @return mixed */ abstract protected function normalizeValue($value); @@ -341,7 +497,7 @@ abstract protected function normalizeValue($value); * @param mixed $leftSide * @param mixed $rightSide * - * @return mixed The merged value + * @return mixed */ abstract protected function mergeValues($leftSide, $rightSide); @@ -350,7 +506,84 @@ abstract protected function mergeValues($leftSide, $rightSide); * * @param mixed $value The value to finalize * - * @return mixed The finalized value + * @return mixed */ abstract protected function finalizeValue($value); + + /** + * Tests if placeholder values are allowed for this node. + */ + protected function allowPlaceholders(): bool + { + return true; + } + + /** + * Tests if a placeholder is being handled currently. + */ + protected function isHandlingPlaceholder(): bool + { + return null !== $this->handlingPlaceholder; + } + + /** + * Gets allowed dynamic types for this node. + */ + protected function getValidPlaceholderTypes(): array + { + return []; + } + + private static function resolvePlaceholderValue($value) + { + if (\is_string($value)) { + if (isset(self::$placeholders[$value])) { + return self::$placeholders[$value]; + } + + foreach (self::$placeholderUniquePrefixes as $placeholderUniquePrefix) { + if (str_starts_with($value, $placeholderUniquePrefix)) { + return []; + } + } + } + + return $value; + } + + private function doValidateType($value): void + { + if (null !== $this->handlingPlaceholder && !$this->allowPlaceholders()) { + $e = new InvalidTypeException(sprintf('A dynamic value is not compatible with a "%s" node type at path "%s".', static::class, $this->getPath())); + $e->setPath($this->getPath()); + + throw $e; + } + + if (null === $this->handlingPlaceholder || null === $value) { + $this->validateType($value); + + return; + } + + $knownTypes = array_keys(self::$placeholders[$this->handlingPlaceholder]); + $validTypes = $this->getValidPlaceholderTypes(); + + if ($validTypes && array_diff($knownTypes, $validTypes)) { + $e = new InvalidTypeException(sprintf( + 'Invalid type for path "%s". Expected %s, but got %s.', + $this->getPath(), + 1 === \count($validTypes) ? '"'.reset($validTypes).'"' : 'one of "'.implode('", "', $validTypes).'"', + 1 === \count($knownTypes) ? '"'.reset($knownTypes).'"' : 'one of "'.implode('", "', $knownTypes).'"' + )); + if ($hint = $this->getInfo()) { + $e->addHint($hint); + } + $e->setPath($this->getPath()); + + throw $e; + } + + $this->validateType($value); + } } diff --git a/tests/integration/vendor/symfony/config/Definition/BooleanNode.php b/tests/integration/vendor/symfony/config/Definition/BooleanNode.php index 08e1a7730..c64ecb839 100644 --- a/tests/integration/vendor/symfony/config/Definition/BooleanNode.php +++ b/tests/integration/vendor/symfony/config/Definition/BooleanNode.php @@ -25,12 +25,8 @@ class BooleanNode extends ScalarNode */ protected function validateType($value) { - if (!is_bool($value)) { - $ex = new InvalidTypeException(sprintf( - 'Invalid type for path "%s". Expected boolean, but got %s.', - $this->getPath(), - gettype($value) - )); + if (!\is_bool($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected "bool", but got "%s".', $this->getPath(), get_debug_type($value))); if ($hint = $this->getInfo()) { $ex->addHint($hint); } @@ -48,4 +44,12 @@ protected function isValueEmpty($value) // a boolean value cannot be empty return false; } + + /** + * {@inheritdoc} + */ + protected function getValidPlaceholderTypes(): array + { + return ['bool']; + } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php index dc1c2fd8d..eb5b04021 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Config\Definition\Builder; use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\PrototypedArrayNode; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\PrototypedArrayNode; /** * This class provides a fluent interface for defining an array node. @@ -25,7 +25,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition protected $performDeepMerging = true; protected $ignoreExtraKeys = false; protected $removeExtraKeys = true; - protected $children = array(); + protected $children = []; protected $prototype; protected $atLeastOne = false; protected $allowNewKeys = true; @@ -39,18 +39,16 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition /** * {@inheritdoc} */ - public function __construct($name, NodeParentInterface $parent = null) + public function __construct(?string $name, NodeParentInterface $parent = null) { parent::__construct($name, $parent); - $this->nullEquivalent = array(); - $this->trueEquivalent = array(); + $this->nullEquivalent = []; + $this->trueEquivalent = []; } /** - * Sets a custom children builder. - * - * @param NodeBuilder $builder A custom NodeBuilder + * {@inheritdoc} */ public function setBuilder(NodeBuilder $builder) { @@ -58,9 +56,7 @@ public function setBuilder(NodeBuilder $builder) } /** - * Returns a builder to add children nodes. - * - * @return NodeBuilder + * {@inheritdoc} */ public function children() { @@ -70,15 +66,69 @@ public function children() /** * Sets a prototype for child nodes. * - * @param string $type the type of node - * * @return NodeDefinition */ - public function prototype($type) + public function prototype(string $type) { return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); } + /** + * @return VariableNodeDefinition + */ + public function variablePrototype() + { + return $this->prototype('variable'); + } + + /** + * @return ScalarNodeDefinition + */ + public function scalarPrototype() + { + return $this->prototype('scalar'); + } + + /** + * @return BooleanNodeDefinition + */ + public function booleanPrototype() + { + return $this->prototype('boolean'); + } + + /** + * @return IntegerNodeDefinition + */ + public function integerPrototype() + { + return $this->prototype('integer'); + } + + /** + * @return FloatNodeDefinition + */ + public function floatPrototype() + { + return $this->prototype('float'); + } + + /** + * @return ArrayNodeDefinition + */ + public function arrayPrototype() + { + return $this->prototype('array'); + } + + /** + * @return EnumNodeDefinition + */ + public function enumPrototype() + { + return $this->prototype('enum'); + } + /** * Adds the default value if the node is not set in the configuration. * @@ -86,7 +136,7 @@ public function prototype($type) * If this function has been called and the node is not set during the finalization * phase, it's default value will be derived from its children default values. * - * @return ArrayNodeDefinition + * @return $this */ public function addDefaultsIfNotSet() { @@ -98,11 +148,11 @@ public function addDefaultsIfNotSet() /** * Adds children with a default value when none are defined. * - * @param int|string|array|null $children The number of children|The child name|The children names to be added - * * This method is applicable to prototype nodes only. * - * @return ArrayNodeDefinition + * @param int|string|array|null $children The number of children|The child name|The children names to be added + * + * @return $this */ public function addDefaultChildrenIfNoneSet($children = null) { @@ -116,7 +166,7 @@ public function addDefaultChildrenIfNoneSet($children = null) * * This method is applicable to prototype nodes only. * - * @return ArrayNodeDefinition + * @return $this */ public function requiresAtLeastOneElement() { @@ -130,7 +180,7 @@ public function requiresAtLeastOneElement() * * If used all keys have to be defined in the same configuration file. * - * @return ArrayNodeDefinition + * @return $this */ public function disallowNewKeysInSubsequentConfigs() { @@ -142,12 +192,12 @@ public function disallowNewKeysInSubsequentConfigs() /** * Sets a normalization rule for XML configurations. * - * @param string $singular The key to remap - * @param string $plural The plural of the key for irregular plurals + * @param string $singular The key to remap + * @param string|null $plural The plural of the key for irregular plurals * - * @return ArrayNodeDefinition + * @return $this */ - public function fixXmlConfig($singular, $plural = null) + public function fixXmlConfig(string $singular, string $plural = null) { $this->normalization()->remap($singular, $plural); @@ -162,15 +212,15 @@ public function fixXmlConfig($singular, $plural = null) * to be the key of the particular item. For example, if "id" is the * "key", then: * - * array( - * array('id' => 'my_name', 'foo' => 'bar'), - * ); + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; * * becomes * - * array( - * 'my_name' => array('foo' => 'bar'), - * ); + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; * * If you'd like "'id' => 'my_name'" to still be present in the resulting * array, then you can set the second argument of this method to false. @@ -180,9 +230,9 @@ public function fixXmlConfig($singular, $plural = null) * @param string $name The name of the key * @param bool $removeKeyItem Whether or not the key item should be removed * - * @return ArrayNodeDefinition + * @return $this */ - public function useAttributeAsKey($name, $removeKeyItem = true) + public function useAttributeAsKey(string $name, bool $removeKeyItem = true) { $this->key = $name; $this->removeKeyItem = $removeKeyItem; @@ -193,11 +243,9 @@ public function useAttributeAsKey($name, $removeKeyItem = true) /** * Sets whether the node can be unset. * - * @param bool $allow - * - * @return ArrayNodeDefinition + * @return $this */ - public function canBeUnset($allow = true) + public function canBeUnset(bool $allow = true) { $this->merge()->allowUnset($allow); @@ -217,19 +265,19 @@ public function canBeUnset($allow = true) * enableableArrayNode: {enabled: false, ...} # The config is disabled * enableableArrayNode: false # The config is disabled * - * @return ArrayNodeDefinition + * @return $this */ public function canBeEnabled() { $this ->addDefaultsIfNotSet() - ->treatFalseLike(array('enabled' => false)) - ->treatTrueLike(array('enabled' => true)) - ->treatNullLike(array('enabled' => true)) + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) ->beforeNormalization() ->ifArray() - ->then(function ($v) { - $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; + ->then(function (array $v) { + $v['enabled'] = $v['enabled'] ?? true; return $v; }) @@ -247,15 +295,15 @@ public function canBeEnabled() * * By default, the section is enabled. * - * @return ArrayNodeDefinition + * @return $this */ public function canBeDisabled() { $this ->addDefaultsIfNotSet() - ->treatFalseLike(array('enabled' => false)) - ->treatTrueLike(array('enabled' => true)) - ->treatNullLike(array('enabled' => true)) + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) ->children() ->booleanNode('enabled') ->defaultTrue() @@ -267,7 +315,7 @@ public function canBeDisabled() /** * Disables the deep merging of the node. * - * @return ArrayNodeDefinition + * @return $this */ public function performNoDeepMerging() { @@ -280,16 +328,16 @@ public function performNoDeepMerging() * Allows extra config keys to be specified under an array without * throwing an exception. * - * Those config values are simply ignored and removed from the - * resulting array. This should be used only in special cases where - * you want to send an entire configuration array through a special - * tree that processes only part of the array. + * Those config values are ignored and removed from the resulting + * array. This should be used only in special cases where you want + * to send an entire configuration array through a special tree that + * processes only part of the array. * * @param bool $remove Whether to remove the extra keys * - * @return ArrayNodeDefinition + * @return $this */ - public function ignoreExtraKeys($remove = true) + public function ignoreExtraKeys(bool $remove = true) { $this->ignoreExtraKeys = true; $this->removeExtraKeys = $remove; @@ -298,33 +346,19 @@ public function ignoreExtraKeys($remove = true) } /** - * Sets key normalization. - * - * @param bool $bool Whether to enable key normalization + * Sets whether to enable key normalization. * - * @return ArrayNodeDefinition + * @return $this */ - public function normalizeKeys($bool) + public function normalizeKeys(bool $bool) { - $this->normalizeKeys = (bool) $bool; + $this->normalizeKeys = $bool; return $this; } /** - * Appends a node definition. - * - * $node = new ArrayNodeDefinition() - * ->children() - * ->scalarNode('foo')->end() - * ->scalarNode('baz')->end() - * ->end() - * ->append($this->getBarNodeDefinition()) - * ; - * - * @param NodeDefinition $node A NodeDefinition instance - * - * @return ArrayNodeDefinition This node + * {@inheritdoc} */ public function append(NodeDefinition $node) { @@ -336,7 +370,7 @@ public function append(NodeDefinition $node) /** * Returns a node builder to be used to add children and prototype. * - * @return NodeBuilder The node builder + * @return NodeBuilder */ protected function getNodeBuilder() { @@ -353,7 +387,7 @@ protected function getNodeBuilder() protected function createNode() { if (null === $this->prototype) { - $node = new ArrayNode($this->name, $this->parent); + $node = new ArrayNode($this->name, $this->parent, $this->pathSeparator); $this->validateConcreteNode($node); @@ -364,7 +398,7 @@ protected function createNode() $node->addChild($child->getNode()); } } else { - $node = new PrototypedArrayNode($this->name, $this->parent); + $node = new PrototypedArrayNode($this->name, $this->parent, $this->pathSeparator); $this->validatePrototypeNode($node); @@ -372,11 +406,15 @@ protected function createNode() $node->setKeyAttribute($this->key, $this->removeKeyItem); } - if (true === $this->atLeastOne) { + if (true === $this->atLeastOne || false === $this->allowEmptyValue) { $node->setMinNumberOfElements(1); } if ($this->default) { + if (!\is_array($this->defaultValue)) { + throw new \InvalidArgumentException(sprintf('%s: the default value of an array node has to be an array.', $node->getPath())); + } + $node->setDefaultValue($this->defaultValue); } @@ -400,6 +438,10 @@ protected function createNode() $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); $node->setNormalizeKeys($this->normalizeKeys); + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } + if (null !== $this->normalization) { $node->setNormalizationClosures($this->normalization->before); $node->setXmlRemappings($this->normalization->remappings); @@ -420,8 +462,6 @@ protected function createNode() /** * Validate the configuration of a concrete node. * - * @param ArrayNode $node The related node - * * @throws InvalidDefinitionException */ protected function validateConcreteNode(ArrayNode $node) @@ -429,35 +469,29 @@ protected function validateConcreteNode(ArrayNode $node) $path = $node->getPath(); if (null !== $this->key) { - throw new InvalidDefinitionException( - sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path) - ); + throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s".', $path)); + } + + if (false === $this->allowEmptyValue) { + throw new InvalidDefinitionException(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s".', $path)); } if (true === $this->atLeastOne) { - throw new InvalidDefinitionException( - sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path) - ); + throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s".', $path)); } if ($this->default) { - throw new InvalidDefinitionException( - sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path) - ); + throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s".', $path)); } if (false !== $this->addDefaultChildren) { - throw new InvalidDefinitionException( - sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path) - ); + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s".', $path)); } } /** * Validate the configuration of a prototype node. * - * @param PrototypedArrayNode $node The related node - * * @throws InvalidDefinitionException */ protected function validatePrototypeNode(PrototypedArrayNode $node) @@ -465,29 +499,51 @@ protected function validatePrototypeNode(PrototypedArrayNode $node) $path = $node->getPath(); if ($this->addDefaults) { - throw new InvalidDefinitionException( - sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path) - ); + throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s".', $path)); } if (false !== $this->addDefaultChildren) { if ($this->default) { - throw new InvalidDefinitionException( - sprintf('A default value and default children might not be used together at path "%s"', $path) - ); + throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s".', $path)); } - if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { - throw new InvalidDefinitionException( - sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path) - ); + if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s".', $path)); } - if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) { - throw new InvalidDefinitionException( - sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path) - ); + if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s".', $path)); } } } + + /** + * @return NodeDefinition[] + */ + public function getChildNodeDefinitions() + { + return $this->children; + } + + /** + * Finds a node defined by the given $nodePath. + * + * @param string $nodePath The path of the node to find. e.g "doctrine.orm.mappings" + */ + public function find(string $nodePath): NodeDefinition + { + $firstPathSegment = (false === $pathSeparatorPos = strpos($nodePath, $this->pathSeparator)) + ? $nodePath + : substr($nodePath, 0, $pathSeparatorPos); + + if (null === $node = ($this->children[$firstPathSegment] ?? null)) { + throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the current node "%s".', $firstPathSegment, $this->name)); + } + + if (false === $pathSeparatorPos) { + return $node; + } + + return $node->find(substr($nodePath, $pathSeparatorPos + \strlen($this->pathSeparator))); + } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php index 28e56579a..ace0b34a2 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php @@ -24,7 +24,7 @@ class BooleanNodeDefinition extends ScalarNodeDefinition /** * {@inheritdoc} */ - public function __construct($name, NodeParentInterface $parent = null) + public function __construct(?string $name, NodeParentInterface $parent = null) { parent::__construct($name, $parent); @@ -34,11 +34,11 @@ public function __construct($name, NodeParentInterface $parent = null) /** * Instantiate a Node. * - * @return BooleanNode The node + * @return BooleanNode */ protected function instantiateNode() { - return new BooleanNode($this->name, $this->parent); + return new BooleanNode($this->name, $this->parent, $this->pathSeparator); } /** diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php b/tests/integration/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php new file mode 100644 index 000000000..f30b8736c --- /dev/null +++ b/tests/integration/vendor/symfony/config/Definition/Builder/BuilderAwareInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that can be implemented by nodes which build other nodes. + * + * @author Roland Franssen + */ +interface BuilderAwareInterface +{ + /** + * Sets a custom children builder. + */ + public function setBuilder(NodeBuilder $builder); +} diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php index 5d3ff014f..52e2fd111 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php @@ -23,9 +23,7 @@ class EnumNodeDefinition extends ScalarNodeDefinition private $values; /** - * @param array $values - * - * @return EnumNodeDefinition|$this + * @return $this */ public function values(array $values) { @@ -43,7 +41,7 @@ public function values(array $values) /** * Instantiate a Node. * - * @return EnumNode The node + * @return EnumNode * * @throws \RuntimeException */ @@ -53,6 +51,6 @@ protected function instantiateNode() throw new \RuntimeException('You must call ->values() on enum nodes.'); } - return new EnumNode($this->name, $this->parent, $this->values); + return new EnumNode($this->name, $this->parent, $this->values, $this->pathSeparator); } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/ExprBuilder.php b/tests/integration/vendor/symfony/config/Definition/Builder/ExprBuilder.php index 94f6e2075..14387b51b 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/ExprBuilder.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/ExprBuilder.php @@ -25,11 +25,6 @@ class ExprBuilder public $ifPart; public $thenPart; - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ public function __construct(NodeDefinition $node) { $this->node = $node; @@ -38,13 +33,11 @@ public function __construct(NodeDefinition $node) /** * Marks the expression as being always used. * - * @param \Closure $then - * - * @return ExprBuilder + * @return $this */ public function always(\Closure $then = null) { - $this->ifPart = function ($v) { return true; }; + $this->ifPart = function () { return true; }; if (null !== $then) { $this->thenPart = $then; @@ -58,9 +51,7 @@ public function always(\Closure $then = null) * * The default one tests if the value is true. * - * @param \Closure $closure - * - * @return ExprBuilder + * @return $this */ public function ifTrue(\Closure $closure = null) { @@ -76,11 +67,11 @@ public function ifTrue(\Closure $closure = null) /** * Tests if the value is a string. * - * @return ExprBuilder + * @return $this */ public function ifString() { - $this->ifPart = function ($v) { return is_string($v); }; + $this->ifPart = function ($v) { return \is_string($v); }; return $this; } @@ -88,7 +79,7 @@ public function ifString() /** * Tests if the value is null. * - * @return ExprBuilder + * @return $this */ public function ifNull() { @@ -100,7 +91,7 @@ public function ifNull() /** * Tests if the value is empty. * - * @return ExprBuilder + * @return $this */ public function ifEmpty() { @@ -112,11 +103,11 @@ public function ifEmpty() /** * Tests if the value is an array. * - * @return ExprBuilder + * @return $this */ public function ifArray() { - $this->ifPart = function ($v) { return is_array($v); }; + $this->ifPart = function ($v) { return \is_array($v); }; return $this; } @@ -124,13 +115,11 @@ public function ifArray() /** * Tests if the value is in an array. * - * @param array $array - * - * @return ExprBuilder + * @return $this */ public function ifInArray(array $array) { - $this->ifPart = function ($v) use ($array) { return in_array($v, $array, true); }; + $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); }; return $this; } @@ -138,23 +127,32 @@ public function ifInArray(array $array) /** * Tests if the value is not in an array. * - * @param array $array - * - * @return ExprBuilder + * @return $this */ public function ifNotInArray(array $array) { - $this->ifPart = function ($v) use ($array) { return !in_array($v, $array, true); }; + $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); }; return $this; } /** - * Sets the closure to run if the test pass. + * Transforms variables of any type into an array. * - * @param \Closure $closure + * @return $this + */ + public function castToArray() + { + $this->ifPart = function ($v) { return !\is_array($v); }; + $this->thenPart = function ($v) { return [$v]; }; + + return $this; + } + + /** + * Sets the closure to run if the test pass. * - * @return ExprBuilder + * @return $this */ public function then(\Closure $closure) { @@ -166,43 +164,41 @@ public function then(\Closure $closure) /** * Sets a closure returning an empty array. * - * @return ExprBuilder + * @return $this */ public function thenEmptyArray() { - $this->thenPart = function ($v) { return array(); }; + $this->thenPart = function () { return []; }; return $this; } /** - * Sets a closure marking the value as invalid at validation time. + * Sets a closure marking the value as invalid at processing time. * * if you want to add the value of the node in your message just use a %s placeholder. * - * @param string $message - * - * @return ExprBuilder + * @return $this * * @throws \InvalidArgumentException */ - public function thenInvalid($message) + public function thenInvalid(string $message) { - $this->thenPart = function ($v) use ($message) {throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; return $this; } /** - * Sets a closure unsetting this key of the array at validation time. + * Sets a closure unsetting this key of the array at processing time. * - * @return ExprBuilder + * @return $this * * @throws UnsetKeyException */ public function thenUnset() { - $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); }; + $this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); }; return $this; } @@ -210,7 +206,7 @@ public function thenUnset() /** * Returns the related node. * - * @return NodeDefinition + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition * * @throws \RuntimeException */ diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php index c0bed462b..f50f190f8 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php @@ -23,10 +23,10 @@ class FloatNodeDefinition extends NumericNodeDefinition /** * Instantiates a Node. * - * @return FloatNode The node + * @return FloatNode */ protected function instantiateNode() { - return new FloatNode($this->name, $this->parent, $this->min, $this->max); + return new FloatNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php index f6c3c147f..d28e5aecb 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php @@ -23,10 +23,10 @@ class IntegerNodeDefinition extends NumericNodeDefinition /** * Instantiates a Node. * - * @return IntegerNode The node + * @return IntegerNode */ protected function instantiateNode() { - return new IntegerNode($this->name, $this->parent, $this->min, $this->max); + return new IntegerNode($this->name, $this->parent, $this->min, $this->max, $this->pathSeparator); } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/MergeBuilder.php b/tests/integration/vendor/symfony/config/Definition/Builder/MergeBuilder.php index f908a499c..a88d49ba9 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/MergeBuilder.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/MergeBuilder.php @@ -22,11 +22,6 @@ class MergeBuilder public $allowFalse = false; public $allowOverwrite = true; - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ public function __construct(NodeDefinition $node) { $this->node = $node; @@ -35,11 +30,9 @@ public function __construct(NodeDefinition $node) /** * Sets whether the node can be unset. * - * @param bool $allow - * - * @return MergeBuilder + * @return $this */ - public function allowUnset($allow = true) + public function allowUnset(bool $allow = true) { $this->allowFalse = $allow; @@ -49,11 +42,9 @@ public function allowUnset($allow = true) /** * Sets whether the node can be overwritten. * - * @param bool $deny Whether the overwriting is forbidden or not - * - * @return MergeBuilder + * @return $this */ - public function denyOverwrite($deny = true) + public function denyOverwrite(bool $deny = true) { $this->allowOverwrite = !$deny; @@ -63,7 +54,7 @@ public function denyOverwrite($deny = true) /** * Returns the related node. * - * @return NodeDefinition + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition */ public function end() { diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/NodeBuilder.php b/tests/integration/vendor/symfony/config/Definition/Builder/NodeBuilder.php index 2a063f1bd..245e97277 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/NodeBuilder.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/NodeBuilder.php @@ -21,28 +21,23 @@ class NodeBuilder implements NodeParentInterface protected $parent; protected $nodeMapping; - /** - * Constructor. - */ public function __construct() { - $this->nodeMapping = array( - 'variable' => __NAMESPACE__.'\\VariableNodeDefinition', - 'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition', - 'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition', - 'integer' => __NAMESPACE__.'\\IntegerNodeDefinition', - 'float' => __NAMESPACE__.'\\FloatNodeDefinition', - 'array' => __NAMESPACE__.'\\ArrayNodeDefinition', - 'enum' => __NAMESPACE__.'\\EnumNodeDefinition', - ); + $this->nodeMapping = [ + 'variable' => VariableNodeDefinition::class, + 'scalar' => ScalarNodeDefinition::class, + 'boolean' => BooleanNodeDefinition::class, + 'integer' => IntegerNodeDefinition::class, + 'float' => FloatNodeDefinition::class, + 'array' => ArrayNodeDefinition::class, + 'enum' => EnumNodeDefinition::class, + ]; } /** * Set the parent node. * - * @param ParentNodeDefinitionInterface $parent The parent node - * - * @return NodeBuilder This node builder + * @return $this */ public function setParent(ParentNodeDefinitionInterface $parent = null) { @@ -54,11 +49,9 @@ public function setParent(ParentNodeDefinitionInterface $parent = null) /** * Creates a child array node. * - * @param string $name The name of the node - * - * @return ArrayNodeDefinition The child node + * @return ArrayNodeDefinition */ - public function arrayNode($name) + public function arrayNode(string $name) { return $this->node($name, 'array'); } @@ -66,11 +59,9 @@ public function arrayNode($name) /** * Creates a child scalar node. * - * @param string $name the name of the node - * - * @return ScalarNodeDefinition The child node + * @return ScalarNodeDefinition */ - public function scalarNode($name) + public function scalarNode(string $name) { return $this->node($name, 'scalar'); } @@ -78,11 +69,9 @@ public function scalarNode($name) /** * Creates a child Boolean node. * - * @param string $name The name of the node - * - * @return BooleanNodeDefinition The child node + * @return BooleanNodeDefinition */ - public function booleanNode($name) + public function booleanNode(string $name) { return $this->node($name, 'boolean'); } @@ -90,11 +79,9 @@ public function booleanNode($name) /** * Creates a child integer node. * - * @param string $name the name of the node - * - * @return IntegerNodeDefinition The child node + * @return IntegerNodeDefinition */ - public function integerNode($name) + public function integerNode(string $name) { return $this->node($name, 'integer'); } @@ -102,11 +89,9 @@ public function integerNode($name) /** * Creates a child float node. * - * @param string $name the name of the node - * - * @return FloatNodeDefinition The child node + * @return FloatNodeDefinition */ - public function floatNode($name) + public function floatNode(string $name) { return $this->node($name, 'float'); } @@ -114,11 +99,9 @@ public function floatNode($name) /** * Creates a child EnumNode. * - * @param string $name - * * @return EnumNodeDefinition */ - public function enumNode($name) + public function enumNode(string $name) { return $this->node($name, 'enum'); } @@ -126,11 +109,9 @@ public function enumNode($name) /** * Creates a child variable node. * - * @param string $name The name of the node - * - * @return VariableNodeDefinition The builder of the child node + * @return VariableNodeDefinition */ - public function variableNode($name) + public function variableNode(string $name) { return $this->node($name, 'variable'); } @@ -138,7 +119,7 @@ public function variableNode($name) /** * Returns the parent node. * - * @return ParentNodeDefinitionInterface|NodeDefinition The parent node + * @return NodeDefinition&ParentNodeDefinitionInterface */ public function end() { @@ -148,15 +129,12 @@ public function end() /** * Creates a child node. * - * @param string $name The name of the node - * @param string $type The type of the node - * - * @return NodeDefinition The child node + * @return NodeDefinition * * @throws \RuntimeException When the node type is not registered * @throws \RuntimeException When the node class is not found */ - public function node($name, $type) + public function node(?string $name, string $type) { $class = $this->getNodeClass($type); @@ -180,13 +158,11 @@ public function node($name, $type) * ->end() * ; * - * @param NodeDefinition $node - * - * @return NodeBuilder This node builder + * @return $this */ public function append(NodeDefinition $node) { - if ($node instanceof ParentNodeDefinitionInterface) { + if ($node instanceof BuilderAwareInterface) { $builder = clone $this; $builder->setParent(null); $node->setBuilder($builder); @@ -207,9 +183,9 @@ public function append(NodeDefinition $node) * @param string $type The name of the type * @param string $class The fully qualified name the node definition class * - * @return NodeBuilder This node builder + * @return $this */ - public function setNodeClass($type, $class) + public function setNodeClass(string $type, string $class) { $this->nodeMapping[strtolower($type)] = $class; @@ -219,14 +195,12 @@ public function setNodeClass($type, $class) /** * Returns the class name of the node definition. * - * @param string $type The node type - * - * @return string The node definition class name + * @return string * * @throws \RuntimeException When the node type is not registered * @throws \RuntimeException When the node class is not found */ - protected function getNodeClass($type) + protected function getNodeClass(string $type) { $type = strtolower($type); diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/NodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/NodeDefinition.php index 4633dc7d2..cf153f01c 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/NodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/NodeDefinition.php @@ -11,8 +11,9 @@ namespace Symfony\Component\Config\Definition\Builder; -use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\NodeInterface; /** * This class provides a fluent interface for defining a node. @@ -27,25 +28,17 @@ abstract class NodeDefinition implements NodeParentInterface protected $defaultValue; protected $default = false; protected $required = false; + protected $deprecation = []; protected $merge; protected $allowEmptyValue = true; protected $nullEquivalent; protected $trueEquivalent = true; protected $falseEquivalent = false; - - /** - * @var NodeParentInterface|null - */ + protected $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR; protected $parent; - protected $attributes = array(); + protected $attributes = []; - /** - * Constructor. - * - * @param string $name The name of the node - * @param NodeParentInterface|null $parent The parent - */ - public function __construct($name, NodeParentInterface $parent = null) + public function __construct(?string $name, NodeParentInterface $parent = null) { $this->parent = $parent; $this->name = $name; @@ -54,9 +47,7 @@ public function __construct($name, NodeParentInterface $parent = null) /** * Sets the parent node. * - * @param NodeParentInterface $parent The parent - * - * @return NodeDefinition|$this + * @return $this */ public function setParent(NodeParentInterface $parent) { @@ -68,11 +59,9 @@ public function setParent(NodeParentInterface $parent) /** * Sets info message. * - * @param string $info The info text - * - * @return NodeDefinition|$this + * @return $this */ - public function info($info) + public function info(string $info) { return $this->attribute('info', $info); } @@ -82,7 +71,7 @@ public function info($info) * * @param string|array $example * - * @return NodeDefinition|$this + * @return $this */ public function example($example) { @@ -92,12 +81,11 @@ public function example($example) /** * Sets an attribute on the node. * - * @param string $key - * @param mixed $value + * @param mixed $value * - * @return NodeDefinition|$this + * @return $this */ - public function attribute($key, $value) + public function attribute(string $key, $value) { $this->attributes[$key] = $value; @@ -107,7 +95,7 @@ public function attribute($key, $value) /** * Returns the parent node. * - * @return NodeParentInterface|NodeBuilder|NodeDefinition|null The builder of the parent node + * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null */ public function end() { @@ -117,11 +105,9 @@ public function end() /** * Creates the node. * - * @param bool $forceRootNode Whether to force this node as the root node - * * @return NodeInterface */ - public function getNode($forceRootNode = false) + public function getNode(bool $forceRootNode = false) { if ($forceRootNode) { $this->parent = null; @@ -136,7 +122,9 @@ public function getNode($forceRootNode = false) } $node = $this->createNode(); - $node->setAttributes($this->attributes); + if ($node instanceof BaseNode) { + $node->setAttributes($this->attributes); + } return $node; } @@ -146,7 +134,7 @@ public function getNode($forceRootNode = false) * * @param mixed $value The default value * - * @return NodeDefinition|$this + * @return $this */ public function defaultValue($value) { @@ -159,7 +147,7 @@ public function defaultValue($value) /** * Sets the node as required. * - * @return NodeDefinition|$this + * @return $this */ public function isRequired() { @@ -168,12 +156,48 @@ public function isRequired() return $this; } + /** + * Sets the node as deprecated. + * + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message the deprecation message to use + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path + * + * @return $this + */ + public function setDeprecated(/* string $package, string $version, string $message = 'The child node "%node%" at path "%path%" is deprecated.' */) + { + $args = \func_get_args(); + + if (\func_num_args() < 2) { + trigger_deprecation('symfony/config', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + $message = $args[0] ?? 'The child node "%node%" at path "%path%" is deprecated.'; + $package = $version = ''; + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) ($args[2] ?? 'The child node "%node%" at path "%path%" is deprecated.'); + } + + $this->deprecation = [ + 'package' => $package, + 'version' => $version, + 'message' => $message, + ]; + + return $this; + } + /** * Sets the equivalent value used when the node contains null. * * @param mixed $value * - * @return NodeDefinition|$this + * @return $this */ public function treatNullLike($value) { @@ -187,7 +211,7 @@ public function treatNullLike($value) * * @param mixed $value * - * @return NodeDefinition|$this + * @return $this */ public function treatTrueLike($value) { @@ -201,7 +225,7 @@ public function treatTrueLike($value) * * @param mixed $value * - * @return NodeDefinition|$this + * @return $this */ public function treatFalseLike($value) { @@ -213,7 +237,7 @@ public function treatFalseLike($value) /** * Sets null as the default value. * - * @return NodeDefinition|$this + * @return $this */ public function defaultNull() { @@ -223,7 +247,7 @@ public function defaultNull() /** * Sets true as the default value. * - * @return NodeDefinition|$this + * @return $this */ public function defaultTrue() { @@ -233,7 +257,7 @@ public function defaultTrue() /** * Sets false as the default value. * - * @return NodeDefinition|$this + * @return $this */ public function defaultFalse() { @@ -253,7 +277,7 @@ public function beforeNormalization() /** * Denies the node value being empty. * - * @return NodeDefinition|$this + * @return $this */ public function cannotBeEmpty() { @@ -279,11 +303,9 @@ public function validate() /** * Sets whether the node can be overwritten. * - * @param bool $deny Whether the overwriting is forbidden or not - * - * @return NodeDefinition|$this + * @return $this */ - public function cannotBeOverwritten($deny = true) + public function cannotBeOverwritten(bool $deny = true) { $this->merge()->denyOverwrite($deny); @@ -335,9 +357,27 @@ protected function normalization() /** * Instantiate and configure the node according to this definition. * - * @return NodeInterface $node The node instance + * @return NodeInterface * * @throws InvalidDefinitionException When the definition is invalid */ abstract protected function createNode(); + + /** + * Set PathSeparator to use. + * + * @return $this + */ + public function setPathSeparator(string $separator) + { + if ($this instanceof ParentNodeDefinitionInterface) { + foreach ($this->getChildNodeDefinitions() as $child) { + $child->setPathSeparator($separator); + } + } + + $this->pathSeparator = $separator; + + return $this; + } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php b/tests/integration/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php index 748c9f28c..06cbbd434 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php @@ -19,14 +19,9 @@ class NormalizationBuilder { protected $node; - public $before = array(); - public $remappings = array(); + public $before = []; + public $remappings = []; - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ public function __construct(NodeDefinition $node) { $this->node = $node; @@ -35,14 +30,14 @@ public function __construct(NodeDefinition $node) /** * Registers a key to remap to its plural form. * - * @param string $key The key to remap - * @param string $plural The plural of the key in case of irregular plural + * @param string $key The key to remap + * @param string|null $plural The plural of the key in case of irregular plural * - * @return NormalizationBuilder + * @return $this */ - public function remap($key, $plural = null) + public function remap(string $key, string $plural = null) { - $this->remappings[] = array($key, null === $plural ? $key.'s' : $plural); + $this->remappings[] = [$key, null === $plural ? $key.'s' : $plural]; return $this; } @@ -50,9 +45,7 @@ public function remap($key, $plural = null) /** * Registers a closure to run before the normalization or an expression builder to build it if null is provided. * - * @param \Closure $closure - * - * @return ExprBuilder|NormalizationBuilder + * @return ExprBuilder|$this */ public function before(\Closure $closure = null) { diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php index 8451e75b2..c4bff1756 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php @@ -26,16 +26,16 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition /** * Ensures that the value is smaller than the given reference. * - * @param mixed $max + * @param int|float $max * - * @return NumericNodeDefinition + * @return $this * * @throws \InvalidArgumentException when the constraint is inconsistent */ public function max($max) { if (isset($this->min) && $this->min > $max) { - throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min)); + throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s).', $max, $this->min)); } $this->max = $max; @@ -45,16 +45,16 @@ public function max($max) /** * Ensures that the value is bigger than the given reference. * - * @param mixed $min + * @param int|float $min * - * @return NumericNodeDefinition + * @return $this * * @throws \InvalidArgumentException when the constraint is inconsistent */ public function min($min) { if (isset($this->max) && $this->max < $min) { - throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max)); + throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s).', $min, $this->max)); } $this->min = $min; diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php b/tests/integration/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php index 575495bb6..449b91afd 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -16,11 +16,36 @@ * * @author Victor Berchet */ -interface ParentNodeDefinitionInterface +interface ParentNodeDefinitionInterface extends BuilderAwareInterface { + /** + * Returns a builder to add children nodes. + * + * @return NodeBuilder + */ public function children(); + /** + * Appends a node definition. + * + * Usage: + * + * $node = $parentNode + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ public function append(NodeDefinition $node); - public function setBuilder(NodeBuilder $builder); + /** + * Gets the child node definitions. + * + * @return NodeDefinition[] + */ + public function getChildNodeDefinitions(); } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php index 6170555cc..076f74b34 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php @@ -23,10 +23,10 @@ class ScalarNodeDefinition extends VariableNodeDefinition /** * Instantiate a Node. * - * @return ScalarNode The node + * @return ScalarNode */ protected function instantiateNode() { - return new ScalarNode($this->name, $this->parent); + return new ScalarNode($this->name, $this->parent, $this->pathSeparator); } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/TreeBuilder.php b/tests/integration/vendor/symfony/config/Definition/Builder/TreeBuilder.php index 5d02848a0..f3c3c2109 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/TreeBuilder.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/TreeBuilder.php @@ -22,24 +22,19 @@ class TreeBuilder implements NodeParentInterface { protected $tree; protected $root; - protected $builder; + + public function __construct(string $name, string $type = 'array', NodeBuilder $builder = null) + { + $builder = $builder ?? new NodeBuilder(); + $this->root = $builder->node($name, $type)->setParent($this); + } /** - * Creates the root node. - * - * @param string $name The name of the root node - * @param string $type The type of the root node - * @param NodeBuilder $builder A custom node builder instance - * - * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') - * - * @throws \RuntimeException When the node type is not supported + * @return NodeDefinition|ArrayNodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') */ - public function root($name, $type = 'array', NodeBuilder $builder = null) + public function getRootNode(): NodeDefinition { - $builder = $builder ?: new NodeBuilder(); - - return $this->root = $builder->node($name, $type)->setParent($this); + return $this->root; } /** @@ -51,13 +46,18 @@ public function root($name, $type = 'array', NodeBuilder $builder = null) */ public function buildTree() { - if (null === $this->root) { - throw new \RuntimeException('The configuration tree has no root node.'); - } if (null !== $this->tree) { return $this->tree; } return $this->tree = $this->root->getNode(true); } + + public function setPathSeparator(string $separator) + { + // unset last built as changing path separator changes all nodes + $this->tree = null; + + $this->root->setPathSeparator($separator); + } } diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/ValidationBuilder.php b/tests/integration/vendor/symfony/config/Definition/Builder/ValidationBuilder.php index e88582389..4efc726c0 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/ValidationBuilder.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/ValidationBuilder.php @@ -19,13 +19,8 @@ class ValidationBuilder { protected $node; - public $rules = array(); + public $rules = []; - /** - * Constructor. - * - * @param NodeDefinition $node The related node - */ public function __construct(NodeDefinition $node) { $this->node = $node; @@ -34,9 +29,7 @@ public function __construct(NodeDefinition $node) /** * Registers a closure to run as normalization or an expression builder to build it if null is provided. * - * @param \Closure $closure - * - * @return ExprBuilder|ValidationBuilder + * @return ExprBuilder|$this */ public function rule(\Closure $closure = null) { diff --git a/tests/integration/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php b/tests/integration/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php index a46b7ea61..eea16cc32 100644 --- a/tests/integration/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php +++ b/tests/integration/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php @@ -23,11 +23,11 @@ class VariableNodeDefinition extends NodeDefinition /** * Instantiate a Node. * - * @return VariableNode The node + * @return VariableNode */ protected function instantiateNode() { - return new VariableNode($this->name, $this->parent); + return new VariableNode($this->name, $this->parent, $this->pathSeparator); } /** @@ -55,6 +55,10 @@ protected function createNode() $node->addEquivalentValue(false, $this->falseEquivalent); $node->setRequired($this->required); + if ($this->deprecation) { + $node->setDeprecated($this->deprecation['package'], $this->deprecation['version'], $this->deprecation['message']); + } + if (null !== $this->validation) { $node->setFinalValidationClosures($this->validation->rules); } diff --git a/tests/integration/vendor/symfony/config/Definition/ConfigurationInterface.php b/tests/integration/vendor/symfony/config/Definition/ConfigurationInterface.php index d6456edb8..7b5d443fe 100644 --- a/tests/integration/vendor/symfony/config/Definition/ConfigurationInterface.php +++ b/tests/integration/vendor/symfony/config/Definition/ConfigurationInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Config\Definition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + /** * Configuration interface. * @@ -21,7 +23,7 @@ interface ConfigurationInterface /** * Generates the configuration tree builder. * - * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + * @return TreeBuilder */ public function getConfigTreeBuilder(); } diff --git a/tests/integration/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/tests/integration/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php index ec5460f2f..a8b18a023 100644 --- a/tests/integration/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php +++ b/tests/integration/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php @@ -11,14 +11,15 @@ namespace Symfony\Component\Config\Definition\Dumper; -use Symfony\Component\Config\Definition\ConfigurationInterface; -use Symfony\Component\Config\Definition\NodeInterface; use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\BaseNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; use Symfony\Component\Config\Definition\PrototypedArrayNode; /** - * Dumps a XML reference configuration for the given configuration/node instance. + * Dumps an XML reference configuration for the given configuration/node instance. * * @author Wouter J */ @@ -26,12 +27,12 @@ class XmlReferenceDumper { private $reference; - public function dump(ConfigurationInterface $configuration, $namespace = null) + public function dump(ConfigurationInterface $configuration, string $namespace = null) { return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); } - public function dumpNode(NodeInterface $node, $namespace = null) + public function dumpNode(NodeInterface $node, string $namespace = null) { $this->reference = ''; $this->writeNode($node, 0, true, $namespace); @@ -41,34 +42,28 @@ public function dumpNode(NodeInterface $node, $namespace = null) return $ref; } - /** - * @param NodeInterface $node - * @param int $depth - * @param bool $root If the node is the root node - * @param string $namespace The namespace of the node - */ - private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null) + private function writeNode(NodeInterface $node, int $depth = 0, bool $root = false, string $namespace = null) { $rootName = ($root ? 'config' : $node->getName()); $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); // xml remapping if ($node->getParent()) { - $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) { + $remapping = array_filter($node->getParent()->getXmlRemappings(), function (array $mapping) use ($rootName) { return $rootName === $mapping[1]; }); - if (count($remapping)) { - list($singular) = current($remapping); + if (\count($remapping)) { + [$singular] = current($remapping); $rootName = $singular; } } $rootName = str_replace('_', '-', $rootName); - $rootAttributes = array(); - $rootAttributeComments = array(); - $rootChildren = array(); - $rootComments = array(); + $rootAttributes = []; + $rootAttributeComments = []; + $rootChildren = []; + $rootComments = []; if ($node instanceof ArrayNode) { $children = $node->getChildren(); @@ -97,15 +92,15 @@ private function writeNode(NodeInterface $node, $depth = 0, $root = false, $name } if ($prototype instanceof PrototypedArrayNode) { - $prototype->setName($key); - $children = array($key => $prototype); + $prototype->setName($key ?? ''); + $children = [$key => $prototype]; } elseif ($prototype instanceof ArrayNode) { $children = $prototype->getChildren(); } else { if ($prototype->hasDefaultValue()) { $prototypeValue = $prototype->getDefaultValue(); } else { - switch (get_class($prototype)) { + switch (\get_class($prototype)) { case 'Symfony\Component\Config\Definition\ScalarNode': $prototypeValue = 'scalar value'; break; @@ -132,65 +127,72 @@ private function writeNode(NodeInterface $node, $depth = 0, $root = false, $name // get attributes and elements foreach ($children as $child) { - if (!$child instanceof ArrayNode) { - // get attributes + if ($child instanceof ArrayNode) { + // get elements + $rootChildren[] = $child; - // metadata - $name = str_replace('_', '-', $child->getName()); - $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world + continue; + } - // comments - $comments = array(); - if ($info = $child->getInfo()) { - $comments[] = $info; - } + // get attributes - if ($example = $child->getExample()) { - $comments[] = 'Example: '.$example; - } + // metadata + $name = str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world - if ($child->isRequired()) { - $comments[] = 'Required'; - } + // comments + $comments = []; + if ($child instanceof BaseNode && $info = $child->getInfo()) { + $comments[] = $info; + } - if ($child instanceof EnumNode) { - $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); - } + if ($child instanceof BaseNode && $example = $child->getExample()) { + $comments[] = 'Example: '.$example; + } - if (count($comments)) { - $rootAttributeComments[$name] = implode(";\n", $comments); - } + if ($child->isRequired()) { + $comments[] = 'Required'; + } - // default values - if ($child->hasDefaultValue()) { - $value = $child->getDefaultValue(); - } + if ($child instanceof BaseNode && $child->isDeprecated()) { + $deprecation = $child->getDeprecation($child->getName(), $node->getPath()); + $comments[] = sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); + } - // append attribute - $rootAttributes[$name] = $value; - } else { - // get elements - $rootChildren[] = $child; + if ($child instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); } + + if (\count($comments)) { + $rootAttributeComments[$name] = implode(";\n", $comments); + } + + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + + // append attribute + $rootAttributes[$name] = $value; } } // render comments // root node comment - if (count($rootComments)) { + if (\count($rootComments)) { foreach ($rootComments as $comment) { $this->writeLine('', $depth); } } // attribute comments - if (count($rootAttributeComments)) { + if (\count($rootAttributeComments)) { foreach ($rootAttributeComments as $attrName => $comment) { - $commentDepth = $depth + 4 + strlen($attrName) + 2; + $commentDepth = $depth + 4 + \strlen($attrName) + 2; $commentLines = explode("\n", $comment); - $multiline = (count($commentLines) > 1); - $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); + $multiline = (\count($commentLines) > 1); + $comment = implode(\PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); if ($multiline) { $this->writeLine(' - - - - - - - - - - - scalar value - - - scalar value - - - - - - - - - - - - - - - - - - - - - - - - -EOL - ); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php deleted file mode 100644 index ede5a19d0..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Dumper; - -use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; -use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; - -class YamlReferenceDumperTest extends \PHPUnit_Framework_TestCase -{ - public function testDumper() - { - $configuration = new ExampleConfiguration(); - - $dumper = new YamlReferenceDumper(); - - $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); - } - - private function getConfigurationAsString() - { - return <<<'EOL' -acme_root: - boolean: true - scalar_empty: ~ - scalar_null: null - scalar_true: true - scalar_false: false - scalar_default: default - scalar_array_empty: [] - scalar_array_defaults: - - # Defaults: - - elem1 - - elem2 - scalar_required: ~ # Required - node_with_a_looong_name: ~ - enum_with_default: this # One of "this"; "that" - enum: ~ # One of "this"; "that" - - # some info - array: - child1: ~ - child2: ~ - - # this is a long - # multi-line info text - # which should be indented - child3: ~ # Example: example setting - scalar_prototyped: [] - parameters: - - # Prototype: Parameter name - name: ~ - connections: - - # Prototype - - - user: ~ - pass: ~ - cms_pages: - - # Prototype - page: - - # Prototype - locale: - title: ~ # Required - path: ~ # Required - pipou: - - # Prototype - name: [] - -EOL; - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/EnumNodeTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/EnumNodeTest.php deleted file mode 100644 index 654d5050d..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/EnumNodeTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\EnumNode; - -class EnumNodeTest extends \PHPUnit_Framework_TestCase -{ - public function testFinalizeValue() - { - $node = new EnumNode('foo', null, array('foo', 'bar')); - $this->assertSame('foo', $node->finalize('foo')); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage $values must contain at least one element. - */ - public function testConstructionWithNoValues() - { - new EnumNode('foo', null, array()); - } - - public function testConstructionWithOneValue() - { - $node = new EnumNode('foo', null, array('foo')); - $this->assertSame('foo', $node->finalize('foo')); - } - - public function testConstructionWithOneDistinctValue() - { - $node = new EnumNode('foo', null, array('foo', 'foo')); - $this->assertSame('foo', $node->finalize('foo')); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar" - */ - public function testFinalizeWithInvalidValue() - { - $node = new EnumNode('foo', null, array('foo', 'bar')); - $node->finalize('foobar'); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/FinalizationTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/FinalizationTest.php deleted file mode 100644 index 19fc347d8..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/FinalizationTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\Config\Definition\Processor; -use Symfony\Component\Config\Definition\NodeInterface; - -class FinalizationTest extends \PHPUnit_Framework_TestCase -{ - public function testUnsetKeyWithDeepHierarchy() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('config', 'array') - ->children() - ->node('level1', 'array') - ->canBeUnset() - ->children() - ->node('level2', 'array') - ->canBeUnset() - ->children() - ->node('somevalue', 'scalar')->end() - ->node('anothervalue', 'scalar')->end() - ->end() - ->end() - ->node('level1_scalar', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'level1' => array( - 'level2' => array( - 'somevalue' => 'foo', - 'anothervalue' => 'bar', - ), - 'level1_scalar' => 'foo', - ), - ); - - $b = array( - 'level1' => array( - 'level2' => false, - ), - ); - - $this->assertEquals(array( - 'level1' => array( - 'level1_scalar' => 'foo', - ), - ), $this->process($tree, array($a, $b))); - } - - protected function process(NodeInterface $tree, array $configs) - { - $processor = new Processor(); - - return $processor->process($tree, $configs); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/FloatNodeTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/FloatNodeTest.php deleted file mode 100644 index 84afd6c10..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/FloatNodeTest.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\FloatNode; - -class FloatNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new FloatNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - /** - * @dataProvider getValidValues - * - * @param int $value - */ - public function testValidNonEmptyValues($value) - { - $node = new FloatNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidValues() - { - return array( - array(1798.0), - array(-678.987), - array(12.56E45), - array(0.0), - // Integer are accepted too, they will be cast - array(17), - array(-10), - array(0), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new FloatNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(null), - array(''), - array('foo'), - array(true), - array(false), - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php deleted file mode 100644 index 58d214859..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\IntegerNode; - -class IntegerNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new IntegerNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - /** - * @dataProvider getValidValues - * - * @param int $value - */ - public function testValidNonEmptyValues($value) - { - $node = new IntegerNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidValues() - { - return array( - array(1798), - array(-678), - array(0), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new IntegerNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(null), - array(''), - array('foo'), - array(true), - array(false), - array(0.0), - array(0.1), - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/MergeTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/MergeTest.php deleted file mode 100644 index 08ddc3209..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/MergeTest.php +++ /dev/null @@ -1,195 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -class MergeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException - */ - public function testForbiddenOverwrite() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('foo', 'scalar') - ->cannotBeOverwritten() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'foo' => 'bar', - ); - - $b = array( - 'foo' => 'moo', - ); - - $tree->merge($a, $b); - } - - public function testUnsetKey() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('foo', 'scalar')->end() - ->node('bar', 'scalar')->end() - ->node('unsettable', 'array') - ->canBeUnset() - ->children() - ->node('foo', 'scalar')->end() - ->node('bar', 'scalar')->end() - ->end() - ->end() - ->node('unsetted', 'array') - ->canBeUnset() - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'foo' => 'bar', - 'unsettable' => array( - 'foo' => 'a', - 'bar' => 'b', - ), - 'unsetted' => false, - ); - - $b = array( - 'foo' => 'moo', - 'bar' => 'b', - 'unsettable' => false, - 'unsetted' => array('a', 'b'), - ); - - $this->assertEquals(array( - 'foo' => 'moo', - 'bar' => 'b', - 'unsettable' => false, - 'unsetted' => array('a', 'b'), - ), $tree->merge($a, $b)); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ - public function testDoesNotAllowNewKeysInSubsequentConfigs() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('config', 'array') - ->children() - ->node('test', 'array') - ->disallowNewKeysInSubsequentConfigs() - ->useAttributeAsKey('key') - ->prototype('array') - ->children() - ->node('value', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree(); - - $a = array( - 'test' => array( - 'a' => array('value' => 'foo'), - ), - ); - - $b = array( - 'test' => array( - 'b' => array('value' => 'foo'), - ), - ); - - $tree->merge($a, $b); - } - - public function testPerformsNoDeepMerging() - { - $tb = new TreeBuilder(); - - $tree = $tb - ->root('config', 'array') - ->children() - ->node('no_deep_merging', 'array') - ->performNoDeepMerging() - ->children() - ->node('foo', 'scalar')->end() - ->node('bar', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'no_deep_merging' => array( - 'foo' => 'a', - 'bar' => 'b', - ), - ); - - $b = array( - 'no_deep_merging' => array( - 'c' => 'd', - ), - ); - - $this->assertEquals(array( - 'no_deep_merging' => array( - 'c' => 'd', - ), - ), $tree->merge($a, $b)); - } - - public function testPrototypeWithoutAKeyAttribute() - { - $tb = new TreeBuilder(); - - $tree = $tb - ->root('config', 'array') - ->children() - ->arrayNode('append_elements') - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $a = array( - 'append_elements' => array('a', 'b'), - ); - - $b = array( - 'append_elements' => array('c', 'd'), - ); - - $this->assertEquals(array('append_elements' => array('a', 'b', 'c', 'd')), $tree->merge($a, $b)); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/NormalizationTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/NormalizationTest.php deleted file mode 100644 index a896f9622..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/NormalizationTest.php +++ /dev/null @@ -1,229 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\NodeInterface; -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -class NormalizationTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getEncoderTests - */ - public function testNormalizeEncoders($denormalized) - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root_name', 'array') - ->fixXmlConfig('encoder') - ->children() - ->node('encoders', 'array') - ->useAttributeAsKey('class') - ->prototype('array') - ->beforeNormalization()->ifString()->then(function ($v) { return array('algorithm' => $v); })->end() - ->children() - ->node('algorithm', 'scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $normalized = array( - 'encoders' => array( - 'foo' => array('algorithm' => 'plaintext'), - ), - ); - - $this->assertNormalized($tree, $denormalized, $normalized); - } - - public function getEncoderTests() - { - $configs = array(); - - // XML - $configs[] = array( - 'encoder' => array( - array('class' => 'foo', 'algorithm' => 'plaintext'), - ), - ); - - // XML when only one element of this type - $configs[] = array( - 'encoder' => array('class' => 'foo', 'algorithm' => 'plaintext'), - ); - - // YAML/PHP - $configs[] = array( - 'encoders' => array( - array('class' => 'foo', 'algorithm' => 'plaintext'), - ), - ); - - // YAML/PHP - $configs[] = array( - 'encoders' => array( - 'foo' => 'plaintext', - ), - ); - - // YAML/PHP - $configs[] = array( - 'encoders' => array( - 'foo' => array('algorithm' => 'plaintext'), - ), - ); - - return array_map(function ($v) { - return array($v); - }, $configs); - } - - /** - * @dataProvider getAnonymousKeysTests - */ - public function testAnonymousKeysArray($denormalized) - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('logout', 'array') - ->fixXmlConfig('handler') - ->children() - ->node('handlers', 'array') - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $normalized = array('logout' => array('handlers' => array('a', 'b', 'c'))); - - $this->assertNormalized($tree, $denormalized, $normalized); - } - - public function getAnonymousKeysTests() - { - $configs = array(); - - $configs[] = array( - 'logout' => array( - 'handlers' => array('a', 'b', 'c'), - ), - ); - - $configs[] = array( - 'logout' => array( - 'handler' => array('a', 'b', 'c'), - ), - ); - - return array_map(function ($v) { return array($v); }, $configs); - } - - /** - * @dataProvider getNumericKeysTests - */ - public function testNumericKeysAsAttributes($denormalized) - { - $normalized = array( - 'thing' => array(42 => array('foo', 'bar'), 1337 => array('baz', 'qux')), - ); - - $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, $normalized); - } - - public function getNumericKeysTests() - { - $configs = array(); - - $configs[] = array( - 'thing' => array( - 42 => array('foo', 'bar'), 1337 => array('baz', 'qux'), - ), - ); - - $configs[] = array( - 'thing' => array( - array('foo', 'bar', 'id' => 42), array('baz', 'qux', 'id' => 1337), - ), - ); - - return array_map(function ($v) { return array($v); }, $configs); - } - - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage The attribute "id" must be set for path "root.thing". - */ - public function testNonAssociativeArrayThrowsExceptionIfAttributeNotSet() - { - $denormalized = array( - 'thing' => array( - array('foo', 'bar'), array('baz', 'qux'), - ), - ); - - $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, array()); - } - - public function testAssociativeArrayPreserveKeys() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->prototype('array') - ->children() - ->node('foo', 'scalar')->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - $data = array('first' => array('foo' => 'bar')); - - $this->assertNormalized($tree, $data, $data); - } - - public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized) - { - self::assertSame($normalized, $tree->normalize($denormalized)); - } - - private function getNumericKeysTestTree() - { - $tb = new TreeBuilder(); - $tree = $tb - ->root('root', 'array') - ->children() - ->node('thing', 'array') - ->useAttributeAsKey('id') - ->prototype('array') - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->buildTree() - ; - - return $tree; - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php deleted file mode 100644 index c343fcfd9..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php +++ /dev/null @@ -1,180 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\PrototypedArrayNode; -use Symfony\Component\Config\Definition\ArrayNode; -use Symfony\Component\Config\Definition\ScalarNode; - -class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase -{ - public function testGetDefaultValueReturnsAnEmptyArrayForPrototypes() - { - $node = new PrototypedArrayNode('root'); - $prototype = new ArrayNode(null, $node); - $node->setPrototype($prototype); - $this->assertEmpty($node->getDefaultValue()); - } - - public function testGetDefaultValueReturnsDefaultValueForPrototypes() - { - $node = new PrototypedArrayNode('root'); - $prototype = new ArrayNode(null, $node); - $node->setPrototype($prototype); - $node->setDefaultValue(array('test')); - $this->assertEquals(array('test'), $node->getDefaultValue()); - } - - // a remapped key (e.g. "mapping" -> "mappings") should be unset after being used - public function testRemappedKeysAreUnset() - { - $node = new ArrayNode('root'); - $mappingsNode = new PrototypedArrayNode('mappings'); - $node->addChild($mappingsNode); - - // each item under mappings is just a scalar - $prototype = new ScalarNode(null, $mappingsNode); - $mappingsNode->setPrototype($prototype); - - $remappings = array(); - $remappings[] = array('mapping', 'mappings'); - $node->setXmlRemappings($remappings); - - $normalized = $node->normalize(array('mapping' => array('foo', 'bar'))); - $this->assertEquals(array('mappings' => array('foo', 'bar')), $normalized); - } - - /** - * Tests that when a key attribute is mapped, that key is removed from the array. - * - * - * - * - * The above should finally be mapped to an array that looks like this - * (because "id" is the key attribute). - * - * array( - * 'things' => array( - * 'option1' => 'foo', - * 'option2' => 'bar', - * ) - * ) - */ - public function testMappedAttributeKeyIsRemoved() - { - $node = new PrototypedArrayNode('root'); - $node->setKeyAttribute('id', true); - - // each item under the root is an array, with one scalar item - $prototype = new ArrayNode(null, $node); - $prototype->addChild(new ScalarNode('foo')); - $node->setPrototype($prototype); - - $children = array(); - $children[] = array('id' => 'item_name', 'foo' => 'bar'); - $normalized = $node->normalize($children); - - $expected = array(); - $expected['item_name'] = array('foo' => 'bar'); - $this->assertEquals($expected, $normalized); - } - - /** - * Tests the opposite of the testMappedAttributeKeyIsRemoved because - * the removal can be toggled with an option. - */ - public function testMappedAttributeKeyNotRemoved() - { - $node = new PrototypedArrayNode('root'); - $node->setKeyAttribute('id', false); - - // each item under the root is an array, with two scalar items - $prototype = new ArrayNode(null, $node); - $prototype->addChild(new ScalarNode('foo')); - $prototype->addChild(new ScalarNode('id')); // the key attribute will remain - $node->setPrototype($prototype); - - $children = array(); - $children[] = array('id' => 'item_name', 'foo' => 'bar'); - $normalized = $node->normalize($children); - - $expected = array(); - $expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar'); - $this->assertEquals($expected, $normalized); - } - - public function testAddDefaultChildren() - { - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet(); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('defaults' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet('defaultkey'); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet(array('defaultkey')); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setKeyAttribute('foobar'); - $node->setAddChildrenIfNoneSet(array('dk1', 'dk2')); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array('dk1' => array('foo' => 'bar'), 'dk2' => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(array(5, 6)); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(0 => array('foo' => 'bar'), 1 => array('foo' => 'bar')), $node->getDefaultValue()); - - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(2); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(array('foo' => 'bar'), array('foo' => 'bar')), $node->getDefaultValue()); - } - - public function testDefaultChildrenWinsOverDefaultValue() - { - $node = $this->getPrototypeNodeWithDefaultChildren(); - $node->setAddChildrenIfNoneSet(); - $node->setDefaultValue(array('bar' => 'foo')); - $this->assertTrue($node->hasDefaultValue()); - $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); - } - - protected function getPrototypeNodeWithDefaultChildren() - { - $node = new PrototypedArrayNode('root'); - $prototype = new ArrayNode(null, $node); - $child = new ScalarNode('foo'); - $child->setDefaultValue('bar'); - $prototype->addChild($child); - $prototype->setAddIfNotSet(true); - $node->setPrototype($prototype); - - return $node; - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php b/tests/integration/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php deleted file mode 100644 index 86c180301..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition; - -use Symfony\Component\Config\Definition\ScalarNode; - -class ScalarNodeTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getValidValues - */ - public function testNormalize($value) - { - $node = new ScalarNode('test'); - $this->assertSame($value, $node->normalize($value)); - } - - public function getValidValues() - { - return array( - array(false), - array(true), - array(null), - array(''), - array('foo'), - array(0), - array(1), - array(0.0), - array(0.1), - ); - } - - /** - * @dataProvider getInvalidValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException - */ - public function testNormalizeThrowsExceptionOnInvalidValues($value) - { - $node = new ScalarNode('test'); - $node->normalize($value); - } - - public function getInvalidValues() - { - return array( - array(array()), - array(array('foo' => 'bar')), - array(new \stdClass()), - ); - } - - public function testNormalizeThrowsExceptionWithoutHint() - { - $node = new ScalarNode('test'); - - $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', 'Invalid type for path "test". Expected scalar, but got array.'); - - $node->normalize(array()); - } - - public function testNormalizeThrowsExceptionWithErrorMessage() - { - $node = new ScalarNode('test'); - $node->setInfo('"the test value"'); - - $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', "Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); - - $node->normalize(array()); - } - - /** - * @dataProvider getValidNonEmptyValues - * - * @param mixed $value - */ - public function testValidNonEmptyValues($value) - { - $node = new ScalarNode('test'); - $node->setAllowEmptyValue(false); - - $this->assertSame($value, $node->finalize($value)); - } - - public function getValidNonEmptyValues() - { - return array( - array(false), - array(true), - array('foo'), - array(0), - array(1), - array(0.0), - array(0.1), - ); - } - - /** - * @dataProvider getEmptyValues - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * - * @param mixed $value - */ - public function testNotAllowedEmptyValuesThrowException($value) - { - $node = new ScalarNode('test'); - $node->setAllowEmptyValue(false); - $node->finalize($value); - } - - public function getEmptyValues() - { - return array( - array(null), - array(''), - ); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php b/tests/integration/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php deleted file mode 100644 index c3d050c75..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Exception; - -use Symfony\Component\Config\Exception\FileLoaderLoadException; - -class FileLoaderLoadExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testMessageCannotLoadResource() - { - $exception = new FileLoaderLoadException('resource', null); - $this->assertEquals('Cannot load resource "resource".', $exception->getMessage()); - } - - public function testMessageCannotImportResourceFromSource() - { - $exception = new FileLoaderLoadException('resource', 'sourceResource'); - $this->assertEquals('Cannot import resource "resource" from "sourceResource".', $exception->getMessage()); - } - - public function testMessageCannotImportBundleResource() - { - $exception = new FileLoaderLoadException('@resource', 'sourceResource'); - $this->assertEquals( - 'Cannot import resource "@resource" from "sourceResource". '. - 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. - 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', - $exception->getMessage() - ); - } - - public function testMessageHasPreviousErrorWithDotAndUnableToLoad() - { - $exception = new FileLoaderLoadException( - 'resource', - null, - null, - new \Exception('There was a previous error with an ending dot.') - ); - $this->assertEquals( - 'There was a previous error with an ending dot in resource (which is loaded in resource "resource").', - $exception->getMessage() - ); - } - - public function testMessageHasPreviousErrorWithoutDotAndUnableToLoad() - { - $exception = new FileLoaderLoadException( - 'resource', - null, - null, - new \Exception('There was a previous error with no ending dot') - ); - $this->assertEquals( - 'There was a previous error with no ending dot in resource (which is loaded in resource "resource").', - $exception->getMessage() - ); - } - - public function testMessageHasPreviousErrorAndUnableToLoadBundle() - { - $exception = new FileLoaderLoadException( - '@resource', - null, - null, - new \Exception('There was a previous error with an ending dot.') - ); - $this->assertEquals( - 'There was a previous error with an ending dot in @resource '. - '(which is loaded in resource "@resource"). '. - 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. - 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', - $exception->getMessage() - ); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/FileLocatorTest.php b/tests/integration/vendor/symfony/config/Tests/FileLocatorTest.php deleted file mode 100644 index 0a03adf12..000000000 --- a/tests/integration/vendor/symfony/config/Tests/FileLocatorTest.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests; - -use Symfony\Component\Config\FileLocator; - -class FileLocatorTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getIsAbsolutePathTests - */ - public function testIsAbsolutePath($path) - { - $loader = new FileLocator(array()); - $r = new \ReflectionObject($loader); - $m = $r->getMethod('isAbsolutePath'); - $m->setAccessible(true); - - $this->assertTrue($m->invoke($loader, $path), '->isAbsolutePath() returns true for an absolute path'); - } - - public function getIsAbsolutePathTests() - { - return array( - array('/foo.xml'), - array('c:\\\\foo.xml'), - array('c:/foo.xml'), - array('\\server\\foo.xml'), - array('https://server/foo.xml'), - array('phar://server/foo.xml'), - ); - } - - public function testLocate() - { - $loader = new FileLocator(__DIR__.'/Fixtures'); - - $this->assertEquals( - __DIR__.DIRECTORY_SEPARATOR.'FileLocatorTest.php', - $loader->locate('FileLocatorTest.php', __DIR__), - '->locate() returns the absolute filename if the file exists in the given path' - ); - - $this->assertEquals( - __DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', - $loader->locate('foo.xml', __DIR__), - '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' - ); - - $this->assertEquals( - __DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', - $loader->locate(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__), - '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' - ); - - $loader = new FileLocator(array(__DIR__.'/Fixtures', __DIR__.'/Fixtures/Again')); - - $this->assertEquals( - array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), - $loader->locate('foo.xml', __DIR__, false), - '->locate() returns an array of absolute filenames' - ); - - $this->assertEquals( - array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), - $loader->locate('foo.xml', __DIR__.'/Fixtures', false), - '->locate() returns an array of absolute filenames' - ); - - $loader = new FileLocator(__DIR__.'/Fixtures/Again'); - - $this->assertEquals( - array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), - $loader->locate('foo.xml', __DIR__.'/Fixtures', false), - '->locate() returns an array of absolute filenames' - ); - } - - /** - * @expectedException \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException - * @expectedExceptionMessage The file "foobar.xml" does not exist - */ - public function testLocateThrowsAnExceptionIfTheFileDoesNotExists() - { - $loader = new FileLocator(array(__DIR__.'/Fixtures')); - - $loader->locate('foobar.xml', __DIR__); - } - - /** - * @expectedException \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException - */ - public function testLocateThrowsAnExceptionIfTheFileDoesNotExistsInAbsolutePath() - { - $loader = new FileLocator(array(__DIR__.'/Fixtures')); - - $loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage An empty file name is not valid to be located. - */ - public function testLocateEmpty() - { - $loader = new FileLocator(array(__DIR__.'/Fixtures')); - - $loader->locate(null, __DIR__); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Again/foo.xml b/tests/integration/vendor/symfony/config/Tests/Fixtures/Again/foo.xml deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php b/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php deleted file mode 100644 index 47701c1b2..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\NodeDefinition; - -class BarNodeDefinition extends NodeDefinition -{ - protected function createNode() - { - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php b/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php deleted file mode 100644 index aa5986311..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; - -class NodeBuilder extends BaseNodeBuilder -{ - public function barNode($name) - { - return $this->node($name, 'bar'); - } - - protected function getNodeClass($type) - { - switch ($type) { - case 'variable': - return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; - case 'bar': - return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; - default: - return parent::getNodeClass($type); - } - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php b/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php deleted file mode 100644 index 1017880c1..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Definition\Builder; - -use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; - -class VariableNodeDefinition extends BaseVariableNodeDefinition -{ -} diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/tests/integration/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php deleted file mode 100644 index 3a34f906a..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Fixtures\Configuration; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\Config\Definition\ConfigurationInterface; - -class ExampleConfiguration implements ConfigurationInterface -{ - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('acme_root'); - - $rootNode - ->fixXmlConfig('parameter') - ->fixXmlConfig('connection') - ->fixXmlConfig('cms_page') - ->children() - ->booleanNode('boolean')->defaultTrue()->end() - ->scalarNode('scalar_empty')->end() - ->scalarNode('scalar_null')->defaultNull()->end() - ->scalarNode('scalar_true')->defaultTrue()->end() - ->scalarNode('scalar_false')->defaultFalse()->end() - ->scalarNode('scalar_default')->defaultValue('default')->end() - ->scalarNode('scalar_array_empty')->defaultValue(array())->end() - ->scalarNode('scalar_array_defaults')->defaultValue(array('elem1', 'elem2'))->end() - ->scalarNode('scalar_required')->isRequired()->end() - ->scalarNode('node_with_a_looong_name')->end() - ->enumNode('enum_with_default')->values(array('this', 'that'))->defaultValue('this')->end() - ->enumNode('enum')->values(array('this', 'that'))->end() - ->arrayNode('array') - ->info('some info') - ->canBeUnset() - ->children() - ->scalarNode('child1')->end() - ->scalarNode('child2')->end() - ->scalarNode('child3') - ->info( - "this is a long\n". - "multi-line info text\n". - 'which should be indented' - ) - ->example('example setting') - ->end() - ->end() - ->end() - ->arrayNode('scalar_prototyped') - ->prototype('scalar')->end() - ->end() - ->arrayNode('parameters') - ->useAttributeAsKey('name') - ->prototype('scalar')->info('Parameter name')->end() - ->end() - ->arrayNode('connections') - ->prototype('array') - ->children() - ->scalarNode('user')->end() - ->scalarNode('pass')->end() - ->end() - ->end() - ->end() - ->arrayNode('cms_pages') - ->useAttributeAsKey('page') - ->prototype('array') - ->useAttributeAsKey('locale') - ->prototype('array') - ->children() - ->scalarNode('title')->isRequired()->end() - ->scalarNode('path')->isRequired()->end() - ->end() - ->end() - ->end() - ->end() - ->arrayNode('pipou') - ->useAttributeAsKey('name') - ->prototype('array') - ->prototype('array') - ->children() - ->scalarNode('didou') - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ; - - return $treeBuilder; - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/document_type.xml b/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/document_type.xml deleted file mode 100644 index 4c2522826..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/document_type.xml +++ /dev/null @@ -1,3 +0,0 @@ - -]> - diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml b/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml deleted file mode 100644 index a07af9fd8..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml b/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml deleted file mode 100644 index e2725a2c2..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd b/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd deleted file mode 100644 index e56820f69..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/valid.xml b/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/valid.xml deleted file mode 100644 index a96bb3826..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Fixtures/Util/valid.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tests/integration/vendor/symfony/config/Tests/Fixtures/foo.xml b/tests/integration/vendor/symfony/config/Tests/Fixtures/foo.xml deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php b/tests/integration/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php deleted file mode 100644 index cdb098030..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\Config\Loader\DelegatingLoader; - -class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $loader = new DelegatingLoader($resolver = new LoaderResolver()); - $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); - } - - public function testGetSetResolver() - { - $resolver = new LoaderResolver(); - $loader = new DelegatingLoader($resolver); - $this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader'); - $loader->setResolver($resolver = new LoaderResolver()); - $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); - } - - public function testSupports() - { - $loader1 = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader1->expects($this->once())->method('supports')->will($this->returnValue(true)); - $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); - $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); - - $loader1 = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader1->expects($this->once())->method('supports')->will($this->returnValue(false)); - $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); - } - - public function testLoad() - { - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); - $loader->expects($this->once())->method('load'); - $resolver = new LoaderResolver(array($loader)); - $loader = new DelegatingLoader($resolver); - - $loader->load('foo'); - } - - /** - * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException - */ - public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded() - { - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader->expects($this->once())->method('supports')->will($this->returnValue(false)); - $resolver = new LoaderResolver(array($loader)); - $loader = new DelegatingLoader($resolver); - - $loader->load('foo'); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Loader/FileLoaderTest.php b/tests/integration/vendor/symfony/config/Tests/Loader/FileLoaderTest.php deleted file mode 100644 index 1e8e91eda..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Loader/FileLoaderTest.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\FileLoader; -use Symfony\Component\Config\Loader\LoaderResolver; - -class FileLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testImportWithFileLocatorDelegation() - { - $locatorMock = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); - - $locatorMockForAdditionalLoader = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); - $locatorMockForAdditionalLoader->expects($this->any())->method('locate')->will($this->onConsecutiveCalls( - array('path/to/file1'), // Default - array('path/to/file1', 'path/to/file2'), // First is imported - array('path/to/file1', 'path/to/file2'), // Second is imported - array('path/to/file1'), // Exception - array('path/to/file1', 'path/to/file2') // Exception - )); - - $fileLoader = new TestFileLoader($locatorMock); - $fileLoader->setSupports(false); - $fileLoader->setCurrentDir('.'); - - $additionalLoader = new TestFileLoader($locatorMockForAdditionalLoader); - $additionalLoader->setCurrentDir('.'); - - $fileLoader->setResolver($loaderResolver = new LoaderResolver(array($fileLoader, $additionalLoader))); - - // Default case - $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); - - // Check first file is imported if not already loading - $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); - - // Check second file is imported if first is already loading - $fileLoader->addLoading('path/to/file1'); - $this->assertSame('path/to/file2', $fileLoader->import('my_resource')); - - // Check exception throws if first (and only available) file is already loading - try { - $fileLoader->import('my_resource'); - $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } - - // Check exception throws if all files are already loading - try { - $fileLoader->addLoading('path/to/file2'); - $fileLoader->import('my_resource'); - $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); - } - } -} - -class TestFileLoader extends FileLoader -{ - private $supports = true; - - public function load($resource, $type = null) - { - return $resource; - } - - public function supports($resource, $type = null) - { - return $this->supports; - } - - public function addLoading($resource) - { - self::$loading[$resource] = true; - } - - public function removeLoading($resource) - { - unset(self::$loading[$resource]); - } - - public function clearLoading() - { - self::$loading = array(); - } - - public function setSupports($supports) - { - $this->supports = $supports; - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php b/tests/integration/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php deleted file mode 100644 index 03db21be4..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\LoaderResolver; - -class LoaderResolverTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $resolver = new LoaderResolver(array( - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'), - )); - - $this->assertEquals(array($loader), $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument'); - } - - public function testResolve() - { - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $resolver = new LoaderResolver(array($loader)); - $this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource'); - - $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); - $resolver = new LoaderResolver(array($loader)); - $this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource'); - } - - public function testLoaders() - { - $resolver = new LoaderResolver(); - $resolver->addLoader($loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface')); - - $this->assertEquals(array($loader), $resolver->getLoaders(), 'addLoader() adds a loader'); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Loader/LoaderTest.php b/tests/integration/vendor/symfony/config/Tests/Loader/LoaderTest.php deleted file mode 100644 index e938a4b07..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Loader/LoaderTest.php +++ /dev/null @@ -1,117 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Loader; - -use Symfony\Component\Config\Loader\Loader; - -class LoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testGetSetResolver() - { - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); - } - - public function testResolve() - { - $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('foo.xml') - ->will($this->returnValue($resolvedLoader)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader'); - $this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader'); - } - - /** - * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException - */ - public function testResolveWhenResolverCannotFindLoader() - { - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('FOOBAR') - ->will($this->returnValue(false)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $loader->resolve('FOOBAR'); - } - - public function testImport() - { - $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $resolvedLoader->expects($this->once()) - ->method('load') - ->with('foo') - ->will($this->returnValue('yes')); - - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('foo') - ->will($this->returnValue($resolvedLoader)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertEquals('yes', $loader->import('foo')); - } - - public function testImportWithType() - { - $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); - $resolvedLoader->expects($this->once()) - ->method('load') - ->with('foo', 'bar') - ->will($this->returnValue('yes')); - - $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); - $resolver->expects($this->once()) - ->method('resolve') - ->with('foo', 'bar') - ->will($this->returnValue($resolvedLoader)); - - $loader = new ProjectLoader1(); - $loader->setResolver($resolver); - - $this->assertEquals('yes', $loader->import('foo', 'bar')); - } -} - -class ProjectLoader1 extends Loader -{ - public function load($resource, $type = null) - { - } - - public function supports($resource, $type = null) - { - return is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION); - } - - public function getType() - { - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php b/tests/integration/vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php deleted file mode 100644 index 20d5b3308..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\ClassExistenceResource; - -class ClassExistenceResourceTest extends \PHPUnit_Framework_TestCase -{ - public function testToString() - { - $res = new ClassExistenceResource('BarClass'); - $this->assertSame('BarClass', (string) $res); - } - - public function testGetResource() - { - $res = new ClassExistenceResource('BarClass'); - $this->assertSame('BarClass', $res->getResource()); - } - - public function testIsFreshWhenClassDoesNotExist() - { - $res = new ClassExistenceResource('Symfony\Component\Config\Tests\Fixtures\BarClass'); - - $this->assertTrue($res->isFresh(time())); - - eval(<<assertFalse($res->isFresh(time())); - } - - public function testIsFreshWhenClassExists() - { - $res = new ClassExistenceResource('Symfony\Component\Config\Tests\Resource\ClassExistenceResourceTest'); - - $this->assertTrue($res->isFresh(time())); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php b/tests/integration/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php deleted file mode 100644 index a22209dd8..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php +++ /dev/null @@ -1,180 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\DirectoryResource; - -class DirectoryResourceTest extends \PHPUnit_Framework_TestCase -{ - protected $directory; - - protected function setUp() - { - $this->directory = sys_get_temp_dir().DIRECTORY_SEPARATOR.'symfonyDirectoryIterator'; - if (!file_exists($this->directory)) { - mkdir($this->directory); - } - touch($this->directory.'/tmp.xml'); - } - - protected function tearDown() - { - if (!is_dir($this->directory)) { - return; - } - $this->removeDirectory($this->directory); - } - - protected function removeDirectory($directory) - { - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::CHILD_FIRST); - foreach ($iterator as $path) { - if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) { - continue; - } - if ($path->isDir()) { - rmdir($path->__toString()); - } else { - unlink($path->__toString()); - } - } - rmdir($directory); - } - - public function testGetResource() - { - $resource = new DirectoryResource($this->directory); - $this->assertSame(realpath($this->directory), $resource->getResource(), '->getResource() returns the path to the resource'); - } - - public function testGetPattern() - { - $resource = new DirectoryResource($this->directory, 'bar'); - $this->assertEquals('bar', $resource->getPattern()); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessageRegExp /The directory ".*" does not exist./ - */ - public function testResourceDoesNotExist() - { - $resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); - } - - public function testIsFresh() - { - $resource = new DirectoryResource($this->directory); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed'); - $this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated'); - } - - public function testIsFreshForDeletedResources() - { - $resource = new DirectoryResource($this->directory); - $this->removeDirectory($this->directory); - - $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist'); - } - - public function testIsFreshUpdateFile() - { - $resource = new DirectoryResource($this->directory); - touch($this->directory.'/tmp.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an existing file is modified'); - } - - public function testIsFreshNewFile() - { - $resource = new DirectoryResource($this->directory); - touch($this->directory.'/new.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file is added'); - } - - public function testIsFreshNewFileWithDifferentPattern() - { - $resource = new DirectoryResource($this->directory, '/.xml$/'); - touch($this->directory.'/new.yaml', time() + 20); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file with a non-matching pattern is added'); - } - - public function testIsFreshDeleteFile() - { - $resource = new DirectoryResource($this->directory); - unlink($this->directory.'/tmp.xml'); - $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if an existing file is removed'); - } - - public function testIsFreshDeleteDirectory() - { - $resource = new DirectoryResource($this->directory); - $this->removeDirectory($this->directory); - $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the whole resource is removed'); - } - - public function testIsFreshCreateFileInSubdirectory() - { - $subdirectory = $this->directory.'/subdirectory'; - mkdir($subdirectory); - - $resource = new DirectoryResource($this->directory); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if an unmodified subdirectory exists'); - - touch($subdirectory.'/newfile.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file in a subdirectory is added'); - } - - public function testIsFreshModifySubdirectory() - { - $resource = new DirectoryResource($this->directory); - - $subdirectory = $this->directory.'/subdirectory'; - mkdir($subdirectory); - touch($subdirectory, time() + 20); - - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a subdirectory is modified (e.g. a file gets deleted)'); - } - - public function testFilterRegexListNoMatch() - { - $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - - touch($this->directory.'/new.bar', time() + 20); - $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file not matching the filter regex is created'); - } - - public function testFilterRegexListMatch() - { - $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - - touch($this->directory.'/new.xml', time() + 20); - $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an new file matching the filter regex is created '); - } - - public function testSerializeUnserialize() - { - $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); - - $unserialized = unserialize(serialize($resource)); - - $this->assertSame(realpath($this->directory), $resource->getResource()); - $this->assertSame('/\.(foo|xml)$/', $resource->getPattern()); - } - - public function testResourcesWithDifferentPatternsAreDifferent() - { - $resourceA = new DirectoryResource($this->directory, '/.xml$/'); - $resourceB = new DirectoryResource($this->directory, '/.yaml$/'); - - $this->assertEquals(2, count(array_unique(array($resourceA, $resourceB)))); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php b/tests/integration/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php deleted file mode 100644 index 56ee584b1..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\FileExistenceResource; - -class FileExistenceResourceTest extends \PHPUnit_Framework_TestCase -{ - protected $resource; - protected $file; - protected $time; - - protected function setUp() - { - $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; - $this->time = time(); - $this->resource = new FileExistenceResource($this->file); - } - - protected function tearDown() - { - if (file_exists($this->file)) { - unlink($this->file); - } - } - - public function testToString() - { - $this->assertSame($this->file, (string) $this->resource); - } - - public function testGetResource() - { - $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); - } - - public function testIsFreshWithExistingResource() - { - touch($this->file, $this->time); - $serialized = serialize(new FileExistenceResource($this->file)); - - $resource = unserialize($serialized); - $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); - - unlink($this->file); - $resource = unserialize($serialized); - $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); - } - - public function testIsFreshWithAbsentResource() - { - $serialized = serialize(new FileExistenceResource($this->file)); - - $resource = unserialize($serialized); - $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); - - touch($this->file, $this->time); - $resource = unserialize($serialized); - $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Resource/FileResourceTest.php b/tests/integration/vendor/symfony/config/Tests/Resource/FileResourceTest.php deleted file mode 100644 index 6a168e635..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Resource/FileResourceTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\FileResource; - -class FileResourceTest extends \PHPUnit_Framework_TestCase -{ - protected $resource; - protected $file; - protected $time; - - protected function setUp() - { - $this->file = sys_get_temp_dir().'/tmp.xml'; - $this->time = time(); - touch($this->file, $this->time); - $this->resource = new FileResource($this->file); - } - - protected function tearDown() - { - if (!file_exists($this->file)) { - return; - } - - unlink($this->file); - } - - public function testGetResource() - { - $this->assertSame(realpath($this->file), $this->resource->getResource(), '->getResource() returns the path to the resource'); - } - - public function testGetResourceWithScheme() - { - $resource = new FileResource('file://'.$this->file); - $this->assertSame('file://'.$this->file, $resource->getResource(), '->getResource() returns the path to the schemed resource'); - } - - public function testToString() - { - $this->assertSame(realpath($this->file), (string) $this->resource); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessageRegExp /The file ".*" does not exist./ - */ - public function testResourceDoesNotExist() - { - $resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999)); - } - - public function testIsFresh() - { - $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); - $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); - $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); - } - - public function testIsFreshForDeletedResources() - { - unlink($this->file); - - $this->assertFalse($this->resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist'); - } - - public function testSerializeUnserialize() - { - $unserialized = unserialize(serialize($this->resource)); - - $this->assertSame(realpath($this->file), $this->resource->getResource()); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Resource/ResourceStub.php b/tests/integration/vendor/symfony/config/Tests/Resource/ResourceStub.php deleted file mode 100644 index b01729cbf..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Resource/ResourceStub.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Resource; - -use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; - -class ResourceStub implements SelfCheckingResourceInterface -{ - private $fresh = true; - - public function setFresh($isFresh) - { - $this->fresh = $isFresh; - } - - public function __toString() - { - return 'stub'; - } - - public function isFresh($timestamp) - { - return $this->fresh; - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php b/tests/integration/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php deleted file mode 100644 index c76c0dd0a..000000000 --- a/tests/integration/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php +++ /dev/null @@ -1,131 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests; - -use Symfony\Component\Config\Tests\Resource\ResourceStub; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\Config\ResourceCheckerConfigCache; - -class ResourceCheckerConfigCacheTest extends \PHPUnit_Framework_TestCase -{ - private $cacheFile = null; - - protected function setUp() - { - $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); - } - - protected function tearDown() - { - $files = array($this->cacheFile, "{$this->cacheFile}.meta"); - - foreach ($files as $file) { - if (file_exists($file)) { - unlink($file); - } - } - } - - public function testGetPath() - { - $cache = new ResourceCheckerConfigCache($this->cacheFile); - - $this->assertSame($this->cacheFile, $cache->getPath()); - } - - public function testCacheIsNotFreshIfEmpty() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface') - ->expects($this->never())->method('supports'); - - /* If there is nothing in the cache, it needs to be filled (and thus it's not fresh). - It does not matter if you provide checkers or not. */ - - unlink($this->cacheFile); // remove tempnam() side effect - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - - $this->assertFalse($cache->isFresh()); - } - - public function testCacheIsFreshIfNocheckerProvided() - { - /* For example in prod mode, you may choose not to run any checkers - at all. In that case, the cache should always be considered fresh. */ - $cache = new ResourceCheckerConfigCache($this->cacheFile); - $this->assertTrue($cache->isFresh()); - } - - public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh() - { - /* As in the previous test, but this time we have a resource. */ - $cache = new ResourceCheckerConfigCache($this->cacheFile); - $cache->write('', array(new ResourceStub())); - - $this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed - } - - public function testIsFreshWithchecker() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); - - $checker->expects($this->once()) - ->method('supports') - ->willReturn(true); - - $checker->expects($this->once()) - ->method('isFresh') - ->willReturn(true); - - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - $cache->write('', array(new ResourceStub())); - - $this->assertTrue($cache->isFresh()); - } - - public function testIsNotFreshWithchecker() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); - - $checker->expects($this->once()) - ->method('supports') - ->willReturn(true); - - $checker->expects($this->once()) - ->method('isFresh') - ->willReturn(false); - - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - $cache->write('', array(new ResourceStub())); - - $this->assertFalse($cache->isFresh()); - } - - public function testCacheIsNotFreshWhenUnserializeFails() - { - $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); - $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); - $cache->write('foo', array(new FileResource(__FILE__))); - - $metaFile = "{$this->cacheFile}.meta"; - file_put_contents($metaFile, str_replace('FileResource', 'ClassNotHere', file_get_contents($metaFile))); - - $this->assertFalse($cache->isFresh()); - } - - public function testCacheKeepsContent() - { - $cache = new ResourceCheckerConfigCache($this->cacheFile); - $cache->write('FOOBAR'); - - $this->assertSame('FOOBAR', file_get_contents($cache->getPath())); - } -} diff --git a/tests/integration/vendor/symfony/config/Tests/Util/XmlUtilsTest.php b/tests/integration/vendor/symfony/config/Tests/Util/XmlUtilsTest.php deleted file mode 100644 index cefd4f295..000000000 --- a/tests/integration/vendor/symfony/config/Tests/Util/XmlUtilsTest.php +++ /dev/null @@ -1,195 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Config\Tests\Util; - -use Symfony\Component\Config\Util\XmlUtils; - -class XmlUtilsTest extends \PHPUnit_Framework_TestCase -{ - public function testLoadFile() - { - $fixtures = __DIR__.'/../Fixtures/Util/'; - - try { - XmlUtils::loadFile($fixtures.'invalid.xml'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('ERROR 77', $e->getMessage()); - } - - try { - XmlUtils::loadFile($fixtures.'document_type.xml'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('Document types are not allowed', $e->getMessage()); - } - - try { - XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('ERROR 1845', $e->getMessage()); - } - - try { - XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file'); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('XSD file or callable', $e->getMessage()); - } - - $mock = $this->getMock(__NAMESPACE__.'\Validator'); - $mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true)); - - try { - XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertContains('is not valid', $e->getMessage()); - } - - $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'))); - $this->assertSame(array(), libxml_get_errors()); - } - - public function testLoadFileWithInternalErrorsEnabled() - { - $internalErrors = libxml_use_internal_errors(true); - - $this->assertSame(array(), libxml_get_errors()); - $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/invalid_schema.xml')); - $this->assertSame(array(), libxml_get_errors()); - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - } - - /** - * @dataProvider getDataForConvertDomToArray - */ - public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true) - { - $dom = new \DOMDocument(); - $dom->loadXML($root ? $xml : ''.$xml.''); - - $this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix)); - } - - public function getDataForConvertDomToArray() - { - return array( - array(null, ''), - array('bar', 'bar'), - array(array('bar' => 'foobar'), '', true), - array(array('foo' => null), ''), - array(array('foo' => 'bar'), 'bar'), - array(array('foo' => array('foo' => 'bar')), ''), - array(array('foo' => array('foo' => 0)), '0'), - array(array('foo' => array('foo' => 'bar')), 'bar'), - array(array('foo' => array('foo' => 'bar', 'value' => 'text')), 'text'), - array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), 'text'), - array(array('foo' => array('bar', 'text')), 'bartext'), - array(array('foo' => array(array('foo' => 'bar'), array('foo' => 'text'))), ''), - array(array('foo' => array('foo' => array('bar', 'text'))), 'text'), - array(array('foo' => 'bar'), 'bar'), - array(array('foo' => 'text'), 'text'), - array(array('foo' => array('bar' => 'bar', 'value' => 'text')), 'text', false, false), - array(array('attr' => 1, 'b' => 'hello'), 'hello2', true), - ); - } - - /** - * @dataProvider getDataForPhpize - */ - public function testPhpize($expected, $value) - { - $this->assertSame($expected, XmlUtils::phpize($value)); - } - - public function getDataForPhpize() - { - return array( - array('', ''), - array(null, 'null'), - array(true, 'true'), - array(false, 'false'), - array(null, 'Null'), - array(true, 'True'), - array(false, 'False'), - array(0, '0'), - array(1, '1'), - array(-1, '-1'), - array(0777, '0777'), - array(255, '0xFF'), - array(100.0, '1e2'), - array(-120.0, '-1.2E2'), - array(-10100.1, '-10100.1'), - array('-10,100.1', '-10,100.1'), - array('1234 5678 9101 1121 3141', '1234 5678 9101 1121 3141'), - array('1,2,3,4', '1,2,3,4'), - array('11,22,33,44', '11,22,33,44'), - array('11,222,333,4', '11,222,333,4'), - array('1,222,333,444', '1,222,333,444'), - array('11,222,333,444', '11,222,333,444'), - array('111,222,333,444', '111,222,333,444'), - array('1111,2222,3333,4444,5555', '1111,2222,3333,4444,5555'), - array('foo', 'foo'), - array(6, '0b0110'), - ); - } - - public function testLoadEmptyXmlFile() - { - $file = __DIR__.'/../Fixtures/foo.xml'; - $this->setExpectedException('InvalidArgumentException', sprintf('File %s does not contain valid XML, it is empty.', $file)); - XmlUtils::loadFile($file); - } - - // test for issue https://github.com/symfony/symfony/issues/9731 - public function testLoadWrongEmptyXMLWithErrorHandler() - { - $originalDisableEntities = libxml_disable_entity_loader(false); - $errorReporting = error_reporting(-1); - - set_error_handler(function ($errno, $errstr) { - throw new \Exception($errstr, $errno); - }); - - $file = __DIR__.'/../Fixtures/foo.xml'; - try { - try { - XmlUtils::loadFile($file); - $this->fail('An exception should have been raised'); - } catch (\InvalidArgumentException $e) { - $this->assertEquals(sprintf('File %s does not contain valid XML, it is empty.', $file), $e->getMessage()); - } - } finally { - restore_error_handler(); - error_reporting($errorReporting); - } - - $disableEntities = libxml_disable_entity_loader(true); - libxml_disable_entity_loader($disableEntities); - - libxml_disable_entity_loader($originalDisableEntities); - - $this->assertFalse($disableEntities); - - // should not throw an exception - XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd'); - } -} - -interface Validator -{ - public function validate(); -} diff --git a/tests/integration/vendor/symfony/config/Util/Exception/InvalidXmlException.php b/tests/integration/vendor/symfony/config/Util/Exception/InvalidXmlException.php new file mode 100644 index 000000000..a335bbd2e --- /dev/null +++ b/tests/integration/vendor/symfony/config/Util/Exception/InvalidXmlException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated + * to the actual XML parsing. + * + * @author Ole Rößner + */ +class InvalidXmlException extends XmlParsingException +{ +} diff --git a/tests/integration/vendor/symfony/config/Util/Exception/XmlParsingException.php b/tests/integration/vendor/symfony/config/Util/Exception/XmlParsingException.php new file mode 100644 index 000000000..9bceed660 --- /dev/null +++ b/tests/integration/vendor/symfony/config/Util/Exception/XmlParsingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML cannot be parsed properly. + * + * @author Ole Rößner + */ +class XmlParsingException extends \InvalidArgumentException +{ +} diff --git a/tests/integration/vendor/symfony/config/Util/XmlUtils.php b/tests/integration/vendor/symfony/config/Util/XmlUtils.php index 25d9b0a0a..8258a0627 100644 --- a/tests/integration/vendor/symfony/config/Util/XmlUtils.php +++ b/tests/integration/vendor/symfony/config/Util/XmlUtils.php @@ -11,6 +11,9 @@ namespace Symfony\Component\Config\Util; +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; + /** * XMLUtils is a bunch of utility methods to XML operations. * @@ -18,6 +21,7 @@ * * @author Fabien Potencier * @author Martin Hasoň + * @author Ole Rößner */ class XmlUtils { @@ -29,42 +33,49 @@ private function __construct() } /** - * Loads an XML file. + * Parses an XML string. * - * @param string $file An XML file path + * @param string $content An XML string * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation * * @return \DOMDocument * - * @throws \InvalidArgumentException When loading of XML file returns error + * @throws XmlParsingException When parsing of XML file returns error + * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself + * @throws \RuntimeException When DOM extension is missing */ - public static function loadFile($file, $schemaOrCallable = null) + public static function parse(string $content, $schemaOrCallable = null) { - $content = @file_get_contents($file); - if ('' === trim($content)) { - throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); + if (!\extension_loaded('dom')) { + throw new \LogicException('Extension DOM is required.'); } $internalErrors = libxml_use_internal_errors(true); - $disableEntities = libxml_disable_entity_loader(true); + if (\LIBXML_VERSION < 20900) { + $disableEntities = libxml_disable_entity_loader(true); + } libxml_clear_errors(); $dom = new \DOMDocument(); $dom->validateOnParse = true; - if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { - libxml_disable_entity_loader($disableEntities); + if (!$dom->loadXML($content, \LIBXML_NONET | (\defined('LIBXML_COMPACT') ? \LIBXML_COMPACT : 0))) { + if (\LIBXML_VERSION < 20900) { + libxml_disable_entity_loader($disableEntities); + } - throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors))); + throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors))); } $dom->normalizeDocument(); libxml_use_internal_errors($internalErrors); - libxml_disable_entity_loader($disableEntities); + if (\LIBXML_VERSION < 20900) { + libxml_disable_entity_loader($disableEntities); + } foreach ($dom->childNodes as $child) { - if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { - throw new \InvalidArgumentException('Document types are not allowed.'); + if (\XML_DOCUMENT_TYPE_NODE === $child->nodeType) { + throw new XmlParsingException('Document types are not allowed.'); } } @@ -73,27 +84,27 @@ public static function loadFile($file, $schemaOrCallable = null) libxml_clear_errors(); $e = null; - if (is_callable($schemaOrCallable)) { + if (\is_callable($schemaOrCallable)) { try { - $valid = call_user_func($schemaOrCallable, $dom, $internalErrors); + $valid = $schemaOrCallable($dom, $internalErrors); } catch (\Exception $e) { $valid = false; } - } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { + } elseif (!\is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { $schemaSource = file_get_contents((string) $schemaOrCallable); $valid = @$dom->schemaValidateSource($schemaSource); } else { libxml_use_internal_errors($internalErrors); - throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); + throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); } if (!$valid) { $messages = static::getXmlErrors($internalErrors); if (empty($messages)) { - $messages = array(sprintf('The XML file "%s" is not valid.', $file)); + throw new InvalidXmlException('The XML is not valid.', 0, $e); } - throw new \InvalidArgumentException(implode("\n", $messages), 0, $e); + throw new XmlParsingException(implode("\n", $messages), 0, $e); } } @@ -104,7 +115,42 @@ public static function loadFile($file, $schemaOrCallable = null) } /** - * Converts a \DomElement object to a PHP array. + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + * @throws XmlParsingException When XML parsing returns any errors + * @throws \RuntimeException When DOM extension is missing + */ + public static function loadFile(string $file, $schemaOrCallable = null) + { + if (!is_file($file)) { + throw new \InvalidArgumentException(sprintf('Resource "%s" is not a file.', $file)); + } + + if (!is_readable($file)) { + throw new \InvalidArgumentException(sprintf('File "%s" is not readable.', $file)); + } + + $content = @file_get_contents($file); + + if ('' === trim($content)) { + throw new \InvalidArgumentException(sprintf('File "%s" does not contain valid XML, it is empty.', $file)); + } + + try { + return static::parse($content, $schemaOrCallable); + } catch (InvalidXmlException $e) { + throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); + } + } + + /** + * Converts a \DOMElement object to a PHP array. * * The following rules applies during the conversion: * @@ -118,18 +164,18 @@ public static function loadFile($file, $schemaOrCallable = null) * * * The nested-tags are converted to keys (bar) * - * @param \DomElement $element A \DomElement instance + * @param \DOMElement $element A \DOMElement instance * @param bool $checkPrefix Check prefix in an element or an attribute name * - * @return array A PHP array + * @return mixed */ - public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true) + public static function convertDomElementToArray(\DOMElement $element, bool $checkPrefix = true) { $prefix = (string) $element->prefix; $empty = true; - $config = array(); + $config = []; foreach ($element->attributes as $name => $node) { - if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) { + if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], true)) { continue; } $config[$name] = static::phpize($node->value); @@ -150,8 +196,8 @@ public static function convertDomElementToArray(\DOMElement $element, $checkPref $key = $node->localName; if (isset($config[$key])) { - if (!is_array($config[$key]) || !is_int(key($config[$key]))) { - $config[$key] = array($config[$key]); + if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) { + $config[$key] = [$config[$key]]; } $config[$key][] = $value; } else { @@ -164,7 +210,7 @@ public static function convertDomElementToArray(\DOMElement $element, $checkPref if (false !== $nodeValue) { $value = static::phpize($nodeValue); - if (count($config)) { + if (\count($config)) { $config['value'] = $value; } else { $config = $value; @@ -188,40 +234,36 @@ public static function phpize($value) switch (true) { case 'null' === $lowercaseValue: - return; + return null; case ctype_digit($value): - $raw = $value; - $cast = (int) $value; - - return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): $raw = $value; $cast = (int) $value; - return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + return self::isOctal($value) ? \intval($value, 8) : (($raw === (string) $cast) ? $cast : $raw); case 'true' === $lowercaseValue: return true; case 'false' === $lowercaseValue: return false; - case isset($value[1]) && '0b' == $value[0].$value[1]: + case isset($value[1]) && '0b' == $value[0].$value[1] && preg_match('/^0b[01]*$/', $value): return bindec($value); case is_numeric($value): return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; case preg_match('/^0x[0-9a-f]++$/i', $value): return hexdec($value); - case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value): + case preg_match('/^[+-]?[0-9]+(\.[0-9]+)?$/', $value): return (float) $value; default: return $value; } } - protected static function getXmlErrors($internalErrors) + protected static function getXmlErrors(bool $internalErrors) { - $errors = array(); + $errors = []; foreach (libxml_get_errors() as $error) { $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', - LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, trim($error->message), $error->file ?: 'n/a', @@ -235,4 +277,13 @@ protected static function getXmlErrors($internalErrors) return $errors; } + + private static function isOctal(string $str): bool + { + if ('-' === $str[0]) { + $str = substr($str, 1); + } + + return $str === '0'.decoct(\intval($str, 8)); + } } diff --git a/tests/integration/vendor/symfony/config/composer.json b/tests/integration/vendor/symfony/config/composer.json index 2de34d92e..b357ed317 100644 --- a/tests/integration/vendor/symfony/config/composer.json +++ b/tests/integration/vendor/symfony/config/composer.json @@ -1,7 +1,7 @@ { "name": "symfony/config", "type": "library", - "description": "Symfony Config Component", + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", @@ -16,11 +16,22 @@ } ], "require": { - "php": ">=5.5.9", - "symfony/filesystem": "~2.8|~3.0" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php81": "^1.22" }, "require-dev": { - "symfony/yaml": "~3.0" + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "conflict": { + "symfony/finder": "<4.4" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -31,10 +42,5 @@ "/Tests/" ] }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } + "minimum-stability": "dev" } diff --git a/tests/integration/vendor/symfony/config/phpunit.xml.dist b/tests/integration/vendor/symfony/config/phpunit.xml.dist deleted file mode 100644 index 3fe6fd87c..000000000 --- a/tests/integration/vendor/symfony/config/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/tests/integration/vendor/symfony/console/Application.php b/tests/integration/vendor/symfony/console/Application.php index f2746c896..a81cfdcbb 100644 --- a/tests/integration/vendor/symfony/console/Application.php +++ b/tests/integration/vendor/symfony/console/Application.php @@ -11,33 +11,47 @@ namespace Symfony\Component\Console; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\CompleteCommand; +use Symfony\Component\Console\Command\DumpCompletionCommand; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\LazyCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Command\SignalableCommandInterface; +use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleSignalEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Exception\NamespaceNotFoundException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\ProcessHelper; use Symfony\Component\Console\Helper\QuestionHelper; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\StreamableInputInterface; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputAwareInterface; -use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Command\HelpCommand; -use Symfony\Component\Console\Command\ListCommand; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Helper\FormatterHelper; -use Symfony\Component\Console\Event\ConsoleCommandEvent; -use Symfony\Component\Console\Event\ConsoleExceptionEvent; -use Symfony\Component\Console\Event\ConsoleTerminateEvent; -use Symfony\Component\Console\Exception\CommandNotFoundException; -use Symfony\Component\Console\Exception\LogicException; -use Symfony\Component\Debug\Exception\FatalThrowableError; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\SignalRegistry\SignalRegistry; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\ErrorHandler\ErrorHandler; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Service\ResetInterface; /** * An Application is the container for a collection of commands. @@ -54,13 +68,14 @@ * * @author Fabien Potencier */ -class Application +class Application implements ResetInterface { - private $commands = array(); + private $commands = []; private $wantHelps = false; private $runningCommand; private $name; private $version; + private $commandLoader; private $catchExceptions = true; private $autoExit = true; private $definition; @@ -68,45 +83,63 @@ class Application private $dispatcher; private $terminal; private $defaultCommand; - private $singleCommand; + private $singleCommand = false; + private $initialized; + private $signalRegistry; + private $signalsToDispatchEvent = []; - /** - * @param string $name The name of the application - * @param string $version The version of the application - */ - public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') { $this->name = $name; $this->version = $version; $this->terminal = new Terminal(); $this->defaultCommand = 'list'; - $this->helperSet = $this->getDefaultHelperSet(); - $this->definition = $this->getDefaultInputDefinition(); - - foreach ($this->getDefaultCommands() as $command) { - $this->add($command); + if (\defined('SIGINT') && SignalRegistry::isSupported()) { + $this->signalRegistry = new SignalRegistry(); + $this->signalsToDispatchEvent = [\SIGINT, \SIGTERM, \SIGUSR1, \SIGUSR2]; } } + /** + * @final + */ public function setDispatcher(EventDispatcherInterface $dispatcher) { $this->dispatcher = $dispatcher; } + public function setCommandLoader(CommandLoaderInterface $commandLoader) + { + $this->commandLoader = $commandLoader; + } + + public function getSignalRegistry(): SignalRegistry + { + if (!$this->signalRegistry) { + throw new RuntimeException('Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); + } + + return $this->signalRegistry; + } + + public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent) + { + $this->signalsToDispatchEvent = $signalsToDispatchEvent; + } + /** * Runs the current application. * - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * * @return int 0 if everything went fine, or an error code * - * @throws \Exception When doRun returns Exception + * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. */ public function run(InputInterface $input = null, OutputInterface $output = null) { - putenv('LINES='.$this->terminal->getHeight()); - putenv('COLUMNS='.$this->terminal->getWidth()); + if (\function_exists('putenv')) { + @putenv('LINES='.$this->terminal->getHeight()); + @putenv('COLUMNS='.$this->terminal->getWidth()); + } if (null === $input) { $input = new ArgvInput(); @@ -116,6 +149,22 @@ public function run(InputInterface $input = null, OutputInterface $output = null $output = new ConsoleOutput(); } + $renderException = function (\Throwable $e) use ($output) { + if ($output instanceof ConsoleOutputInterface) { + $this->renderThrowable($e, $output->getErrorOutput()); + } else { + $this->renderThrowable($e, $output); + } + }; + if ($phpHandler = set_exception_handler($renderException)) { + restore_exception_handler(); + if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { + $errorHandler = true; + } elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) { + $phpHandler[0]->setExceptionHandler($errorHandler); + } + } + $this->configureIO($input, $output); try { @@ -125,11 +174,7 @@ public function run(InputInterface $input = null, OutputInterface $output = null throw $e; } - if ($output instanceof ConsoleOutputInterface) { - $this->renderException($e, $output->getErrorOutput()); - } else { - $this->renderException($e, $output); - } + $renderException($e); $exitCode = $e->getCode(); if (is_numeric($exitCode)) { @@ -140,6 +185,20 @@ public function run(InputInterface $input = null, OutputInterface $output = null } else { $exitCode = 1; } + } finally { + // if the exception handler changed, keep it + // otherwise, unregister $renderException + if (!$phpHandler) { + if (set_exception_handler($renderException) === $renderException) { + restore_exception_handler(); + } + restore_exception_handler(); + } elseif (!$errorHandler) { + $finalHandler = $phpHandler[0]->setExceptionHandler(null); + if ($finalHandler !== $renderException) { + $phpHandler[0]->setExceptionHandler($finalHandler); + } + } } if ($this->autoExit) { @@ -156,24 +215,28 @@ public function run(InputInterface $input = null, OutputInterface $output = null /** * Runs the current application. * - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * * @return int 0 if everything went fine, or an error code */ public function doRun(InputInterface $input, OutputInterface $output) { - if (true === $input->hasParameterOption(array('--version', '-V'), true)) { + if (true === $input->hasParameterOption(['--version', '-V'], true)) { $output->writeln($this->getLongVersion()); return 0; } + try { + // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. + $input->bind($this->getDefinition()); + } catch (ExceptionInterface $e) { + // Errors must be ignored, full binding/validation happens later when the command is known. + } + $name = $this->getCommandName($input); - if (true === $input->hasParameterOption(array('--help', '-h'), true)) { + if (true === $input->hasParameterOption(['--help', '-h'], true)) { if (!$name) { $name = 'help'; - $input = new ArrayInput(array('command_name' => $this->defaultCommand)); + $input = new ArrayInput(['command_name' => $this->defaultCommand]); } else { $this->wantHelps = true; } @@ -181,11 +244,56 @@ public function doRun(InputInterface $input, OutputInterface $output) if (!$name) { $name = $this->defaultCommand; - $input = new ArrayInput(array('command' => $this->defaultCommand)); + $definition = $this->getDefinition(); + $definition->setArguments(array_merge( + $definition->getArguments(), + [ + 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), + ] + )); } - // the command name MUST be the first element of the input - $command = $this->find($name); + try { + $this->runningCommand = null; + // the command name MUST be the first element of the input + $command = $this->find($name); + } catch (\Throwable $e) { + if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + + if (0 === $event->getExitCode()) { + return 0; + } + + $e = $event->getError(); + } + + throw $e; + } + + $alternative = $alternatives[0]; + + $style = new SymfonyStyle($input, $output); + $style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error'); + if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + + return $event->getExitCode(); + } + + return 1; + } + + $command = $this->find($alternative); + } + + if ($command instanceof LazyCommand) { + $command = $command->getCommand(); + } $this->runningCommand = $command; $exitCode = $this->doRunCommand($command, $input, $output); @@ -195,10 +303,12 @@ public function doRun(InputInterface $input, OutputInterface $output) } /** - * Set a helper set to be used with the command. - * - * @param HelperSet $helperSet The helper set + * {@inheritdoc} */ + public function reset() + { + } + public function setHelperSet(HelperSet $helperSet) { $this->helperSet = $helperSet; @@ -207,18 +317,17 @@ public function setHelperSet(HelperSet $helperSet) /** * Get the helper set associated with the command. * - * @return HelperSet The HelperSet instance associated with this command + * @return HelperSet */ public function getHelperSet() { + if (!$this->helperSet) { + $this->helperSet = $this->getDefaultHelperSet(); + } + return $this->helperSet; } - /** - * Set an input definition to be used with this application. - * - * @param InputDefinition $definition The input definition - */ public function setDefinition(InputDefinition $definition) { $this->definition = $definition; @@ -227,10 +336,14 @@ public function setDefinition(InputDefinition $definition) /** * Gets the InputDefinition related to this Application. * - * @return InputDefinition The InputDefinition instance + * @return InputDefinition */ public function getDefinition() { + if (!$this->definition) { + $this->definition = $this->getDefaultInputDefinition(); + } + if ($this->singleCommand) { $inputDefinition = $this->definition; $inputDefinition->setArguments(); @@ -241,10 +354,33 @@ public function getDefinition() return $this->definition; } + /** + * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ( + CompletionInput::TYPE_ARGUMENT_VALUE === $input->getCompletionType() + && 'command' === $input->getCompletionName() + ) { + $suggestions->suggestValues(array_filter(array_map(function (Command $command) { + return $command->isHidden() ? null : $command->getName(); + }, $this->all()))); + + return; + } + + if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) { + $suggestions->suggestOptions($this->getDefinition()->getOptions()); + + return; + } + } + /** * Gets the help message. * - * @return string A help message + * @return string */ public function getHelp() { @@ -254,7 +390,7 @@ public function getHelp() /** * Gets whether to catch exceptions or not during commands execution. * - * @return bool Whether to catch exceptions or not during commands execution + * @return bool */ public function areExceptionsCaught() { @@ -263,18 +399,16 @@ public function areExceptionsCaught() /** * Sets whether to catch exceptions or not during commands execution. - * - * @param bool $boolean Whether to catch exceptions or not during commands execution */ - public function setCatchExceptions($boolean) + public function setCatchExceptions(bool $boolean) { - $this->catchExceptions = (bool) $boolean; + $this->catchExceptions = $boolean; } /** * Gets whether to automatically exit after a command execution or not. * - * @return bool Whether to automatically exit after a command execution or not + * @return bool */ public function isAutoExitEnabled() { @@ -283,18 +417,16 @@ public function isAutoExitEnabled() /** * Sets whether to automatically exit after a command execution or not. - * - * @param bool $boolean Whether to automatically exit after a command execution or not */ - public function setAutoExit($boolean) + public function setAutoExit(bool $boolean) { - $this->autoExit = (bool) $boolean; + $this->autoExit = $boolean; } /** * Gets the name of the application. * - * @return string The application name + * @return string */ public function getName() { @@ -303,10 +435,8 @@ public function getName() /** * Sets the application name. - * - * @param string $name The application name - */ - public function setName($name) + **/ + public function setName(string $name) { $this->name = $name; } @@ -314,7 +444,7 @@ public function setName($name) /** * Gets the application version. * - * @return string The application version + * @return string */ public function getVersion() { @@ -323,10 +453,8 @@ public function getVersion() /** * Sets the application version. - * - * @param string $version The application version */ - public function setVersion($version) + public function setVersion(string $version) { $this->version = $version; } @@ -334,7 +462,7 @@ public function setVersion($version) /** * Returns the long version of the application. * - * @return string The long application version + * @return string */ public function getLongVersion() { @@ -352,11 +480,9 @@ public function getLongVersion() /** * Registers a new command. * - * @param string $name The command name - * - * @return Command The newly created command + * @return Command */ - public function register($name) + public function register(string $name) { return $this->add(new Command($name)); } @@ -381,22 +507,27 @@ public function addCommands(array $commands) * If a command with the same name already exists, it will be overridden. * If the command is not enabled it will not be added. * - * @param Command $command A Command object - * - * @return Command|null The registered command if enabled or null + * @return Command|null */ public function add(Command $command) { + $this->init(); + $command->setApplication($this); if (!$command->isEnabled()) { $command->setApplication(null); - return; + return null; } - if (null === $command->getDefinition()) { - throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); + if (!$command instanceof LazyCommand) { + // Will throw if the command is not correctly initialized. + $command->getDefinition(); + } + + if (!$command->getName()) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command))); } $this->commands[$command->getName()] = $command; @@ -411,18 +542,23 @@ public function add(Command $command) /** * Returns a registered command by name or alias. * - * @param string $name The command name or alias - * - * @return Command A Command object + * @return Command * - * @throws CommandNotFoundException When command name given does not exist + * @throws CommandNotFoundException When given command name does not exist */ - public function get($name) + public function get(string $name) { - if (!isset($this->commands[$name])) { + $this->init(); + + if (!$this->has($name)) { throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); } + // When the command has a different name than the one used at the command loader level + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name)); + } + $command = $this->commands[$name]; if ($this->wantHelps) { @@ -440,13 +576,13 @@ public function get($name) /** * Returns true if the command exists, false otherwise. * - * @param string $name The command name or alias - * - * @return bool true if the command exists, false otherwise + * @return bool */ - public function has($name) + public function has(string $name) { - return isset($this->commands[$name]); + $this->init(); + + return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name))); } /** @@ -454,42 +590,44 @@ public function has($name) * * It does not return the global namespace which always exists. * - * @return string[] An array of namespaces + * @return string[] */ public function getNamespaces() { - $namespaces = array(); + $namespaces = []; foreach ($this->all() as $command) { - $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + if ($command->isHidden()) { + continue; + } + + $namespaces[] = $this->extractAllNamespaces($command->getName()); foreach ($command->getAliases() as $alias) { - $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + $namespaces[] = $this->extractAllNamespaces($alias); } } - return array_values(array_unique(array_filter($namespaces))); + return array_values(array_unique(array_filter(array_merge([], ...$namespaces)))); } /** * Finds a registered namespace by a name or an abbreviation. * - * @param string $namespace A namespace or abbreviation to search for + * @return string * - * @return string A registered namespace - * - * @throws CommandNotFoundException When namespace is incorrect or ambiguous + * @throws NamespaceNotFoundException When namespace is incorrect or ambiguous */ - public function findNamespace($namespace) + public function findNamespace(string $namespace) { $allNamespaces = $this->getNamespaces(); - $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*'; $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); if (empty($namespaces)) { $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { - if (1 == count($alternatives)) { + if (1 == \count($alternatives)) { $message .= "\n\nDid you mean this?\n "; } else { $message .= "\n\nDid you mean one of these?\n "; @@ -498,12 +636,12 @@ public function findNamespace($namespace) $message .= implode("\n ", $alternatives); } - throw new CommandNotFoundException($message, $alternatives); + throw new NamespaceNotFoundException($message, $alternatives); } - $exact = in_array($namespace, $namespaces, true); - if (count($namespaces) > 1 && !$exact) { - throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + $exact = \in_array($namespace, $namespaces, true); + if (\count($namespaces) > 1 && !$exact) { + throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); } return $exact ? $namespace : reset($namespaces); @@ -515,19 +653,38 @@ public function findNamespace($namespace) * Contrary to get, this command tries to find the best * match if you give it an abbreviation of a name or alias. * - * @param string $name A command name or a command alias - * - * @return Command A Command instance + * @return Command * * @throws CommandNotFoundException When command name is incorrect or ambiguous */ - public function find($name) + public function find(string $name) { - $allCommands = array_keys($this->commands); - $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $this->init(); + + $aliases = []; + + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if (!$this->has($alias)) { + $this->commands[$alias] = $command; + } + } + } + + if ($this->has($name)) { + return $this->get($name); + } + + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); + $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*'; $commands = preg_grep('{^'.$expr.'}', $allCommands); - if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { + if (empty($commands)) { + $commands = preg_grep('{^'.$expr.'}i', $allCommands); + } + + // if no commands matched or we just matched namespaces + if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { if (false !== $pos = strrpos($name, ':')) { // check if a namespace exists and contains commands $this->findNamespace(substr($name, 0, $pos)); @@ -536,7 +693,12 @@ public function find($name) $message = sprintf('Command "%s" is not defined.', $name); if ($alternatives = $this->findAlternatives($name, $allCommands)) { - if (1 == count($alternatives)) { + // remove hidden commands + $alternatives = array_filter($alternatives, function ($name) { + return !$this->get($name)->isHidden(); + }); + + if (1 == \count($alternatives)) { $message .= "\n\nDid you mean this?\n "; } else { $message .= "\n\nDid you mean one of these?\n "; @@ -544,27 +706,58 @@ public function find($name) $message .= implode("\n ", $alternatives); } - throw new CommandNotFoundException($message, $alternatives); + throw new CommandNotFoundException($message, array_values($alternatives)); } // filter out aliases for commands which are already on the list - if (count($commands) > 1) { - $commandList = $this->commands; - $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + if (\count($commands) > 1) { + $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; + $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) { + if (!$commandList[$nameOrAlias] instanceof Command) { + $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); + } + $commandName = $commandList[$nameOrAlias]->getName(); - return $commandName === $nameOrAlias || !in_array($commandName, $commands); - }); + $aliases[$nameOrAlias] = $commandName; + + return $commandName === $nameOrAlias || !\in_array($commandName, $commands); + })); } - $exact = in_array($name, $commands, true); - if (count($commands) > 1 && !$exact) { - $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + if (\count($commands) > 1) { + $usableWidth = $this->terminal->getWidth() - 10; + $abbrevs = array_values($commands); + $maxLen = 0; + foreach ($abbrevs as $abbrev) { + $maxLen = max(Helper::width($abbrev), $maxLen); + } + $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen, &$commands) { + if ($commandList[$cmd]->isHidden()) { + unset($commands[array_search($cmd, $commands)]); + + return false; + } - throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); + $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription(); + + return Helper::width($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev; + }, array_values($commands)); + + if (\count($commands) > 1) { + $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs)); + + throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands)); + } } - return $this->get($exact ? $name : reset($commands)); + $command = $this->get(reset($commands)); + + if ($command->isHidden()) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + return $command; } /** @@ -572,38 +765,55 @@ public function find($name) * * The array keys are the full names and the values the command instances. * - * @param string $namespace A namespace name - * - * @return Command[] An array of Command instances + * @return Command[] */ - public function all($namespace = null) + public function all(string $namespace = null) { + $this->init(); + if (null === $namespace) { - return $this->commands; + if (!$this->commandLoader) { + return $this->commands; + } + + $commands = $this->commands; + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + + return $commands; } - $commands = array(); + $commands = []; foreach ($this->commands as $name => $command) { if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { $commands[$name] = $command; } } + if ($this->commandLoader) { + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + } + return $commands; } /** * Returns an array of possible abbreviations given a set of names. * - * @param array $names An array of names - * - * @return array An array of abbreviations + * @return string[][] */ - public static function getAbbreviations($names) + public static function getAbbreviations(array $names) { - $abbrevs = array(); + $abbrevs = []; foreach ($names as $name) { - for ($len = strlen($name); $len > 0; --$len) { + for ($len = \strlen($name); $len > 0; --$len) { $abbrev = substr($name, 0, $len); $abbrevs[$abbrev][] = $name; } @@ -612,195 +822,140 @@ public static function getAbbreviations($names) return $abbrevs; } - /** - * Renders a caught exception. - * - * @param \Exception $e An exception instance - * @param OutputInterface $output An OutputInterface instance - */ - public function renderException(\Exception $e, OutputInterface $output) + public function renderThrowable(\Throwable $e, OutputInterface $output): void { $output->writeln('', OutputInterface::VERBOSITY_QUIET); + $this->doRenderThrowable($e, $output); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void + { do { - $title = sprintf( - ' [%s%s] ', - get_class($e), - $output->isVerbose() && 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '' - ); - - $len = $this->stringWidth($title); - - $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX; - // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 - if (defined('HHVM_VERSION') && $width > 1 << 31) { - $width = 1 << 31; + $message = trim($e->getMessage()); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $class = get_debug_type($e); + $title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : ''); + $len = Helper::width($title); + } else { + $len = 0; } - $formatter = $output->getFormatter(); - $lines = array(); - foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + + if (str_contains($message, "@anonymous\0")) { + $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { + return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0]; + }, $message); + } + + $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX; + $lines = []; + foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) { foreach ($this->splitStringByWidth($line, $width - 4) as $line) { // pre-format lines to get the right string length - $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; - $lines[] = array($line, $lineLength); + $lineLength = Helper::width($line) + 4; + $lines[] = [$line, $lineLength]; $len = max($lineLength, $len); } } - $messages = array(); - $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len))); - $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + $messages = []; + if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = sprintf('%s', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); + } + $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::width($title)))); + } foreach ($lines as $line) { - $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]))); + $messages[] = sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); } $messages[] = $emptyLine; $messages[] = ''; - $output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET); + $output->writeln($messages, OutputInterface::VERBOSITY_QUIET); if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); // exception related properties $trace = $e->getTrace(); - array_unshift($trace, array( + + array_unshift($trace, [ 'function' => '', - 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', - 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', - 'args' => array(), - )); - - for ($i = 0, $count = count($trace); $i < $count; ++$i) { - $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; - $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; - $function = $trace[$i]['function']; - $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; - $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; - - $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + 'file' => $e->getFile() ?: 'n/a', + 'line' => $e->getLine() ?: 'n/a', + 'args' => [], + ]); + + for ($i = 0, $count = \count($trace); $i < $count; ++$i) { + $class = $trace[$i]['class'] ?? ''; + $type = $trace[$i]['type'] ?? ''; + $function = $trace[$i]['function'] ?? ''; + $file = $trace[$i]['file'] ?? 'n/a'; + $line = $trace[$i]['line'] ?? 'n/a'; + + $output->writeln(sprintf(' %s%s at %s:%s', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); } $output->writeln('', OutputInterface::VERBOSITY_QUIET); } } while ($e = $e->getPrevious()); - - if (null !== $this->runningCommand) { - $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); - $output->writeln('', OutputInterface::VERBOSITY_QUIET); - } - } - - /** - * Tries to figure out the terminal width in which this application runs. - * - * @return int|null - * - * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. - */ - protected function getTerminalWidth() - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); - - return $this->terminal->getWidth(); - } - - /** - * Tries to figure out the terminal height in which this application runs. - * - * @return int|null - * - * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. - */ - protected function getTerminalHeight() - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); - - return $this->terminal->getHeight(); - } - - /** - * Tries to figure out the terminal dimensions based on the current environment. - * - * @return array Array containing width and height - * - * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. - */ - public function getTerminalDimensions() - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); - - return array($this->terminal->getWidth(), $this->terminal->getHeight()); - } - - /** - * Sets terminal dimensions. - * - * Can be useful to force terminal dimensions for functional tests. - * - * @param int $width The width - * @param int $height The height - * - * @return Application The current application - * - * @deprecated since version 3.2, to be removed in 4.0. Set the COLUMNS and LINES env vars instead. - */ - public function setTerminalDimensions($width, $height) - { - @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Set the COLUMNS and LINES env vars instead.', __METHOD__), E_USER_DEPRECATED); - - putenv('COLUMNS='.$width); - putenv('LINES='.$height); - - return $this; } /** * Configures the input and output instances based on the user arguments and options. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance */ protected function configureIO(InputInterface $input, OutputInterface $output) { - if (true === $input->hasParameterOption(array('--ansi'), true)) { + if (true === $input->hasParameterOption(['--ansi'], true)) { $output->setDecorated(true); - } elseif (true === $input->hasParameterOption(array('--no-ansi'), true)) { + } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) { $output->setDecorated(false); } - if (true === $input->hasParameterOption(array('--no-interaction', '-n'), true)) { + if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) { $input->setInteractive(false); - } elseif (function_exists('posix_isatty')) { - $inputStream = null; - - if ($input instanceof StreamableInputInterface) { - $inputStream = $input->getStream(); - } - - // This check ensures that calling QuestionHelper::setInputStream() works - // To be removed in 4.0 (in the same time as QuestionHelper::setInputStream) - if (!$inputStream && $this->getHelperSet()->has('question')) { - $inputStream = $this->getHelperSet()->get('question')->getInputStream(false); - } + } - if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { - $input->setInteractive(false); - } + switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) { + case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break; + case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break; + case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break; + case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break; + default: $shellVerbosity = 0; break; } - if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) { + if (true === $input->hasParameterOption(['--quiet', '-q'], true)) { $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); - $input->setInteractive(false); + $shellVerbosity = -1; } else { - if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === 3) { + if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) { $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); - } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === 2) { + $shellVerbosity = 3; + } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) { $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + $shellVerbosity = 2; } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $shellVerbosity = 1; } } + + if (-1 === $shellVerbosity) { + $input->setInteractive(false); + } + + if (\function_exists('putenv')) { + @putenv('SHELL_VERBOSITY='.$shellVerbosity); + } + $_ENV['SHELL_VERBOSITY'] = $shellVerbosity; + $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity; } /** @@ -809,13 +964,7 @@ protected function configureIO(InputInterface $input, OutputInterface $output) * If an event dispatcher has been attached to the application, * events are also dispatched during the life-cycle of the command. * - * @param Command $command A Command instance - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * * @return int 0 if everything went fine, or an error code - * - * @throws \Exception when the command being run threw an exception */ protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) { @@ -825,16 +974,47 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI } } - if (null === $this->dispatcher) { - try { - return $command->run($input, $output); - } catch (\Exception $e) { - throw $e; - } catch (\Throwable $e) { - throw new FatalThrowableError($e); + if ($command instanceof SignalableCommandInterface && ($this->signalsToDispatchEvent || $command->getSubscribedSignals())) { + if (!$this->signalRegistry) { + throw new RuntimeException('Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); + } + + if (Terminal::hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + foreach ([\SIGINT, \SIGTERM] as $signal) { + $this->signalRegistry->register($signal, static function () use ($sttyMode) { + shell_exec('stty '.$sttyMode); + }); + } + } + + if ($this->dispatcher) { + foreach ($this->signalsToDispatchEvent as $signal) { + $event = new ConsoleSignalEvent($command, $input, $output, $signal); + + $this->signalRegistry->register($signal, function ($signal, $hasNext) use ($event) { + $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL); + + // No more handlers, we try to simulate PHP default behavior + if (!$hasNext) { + if (!\in_array($signal, [\SIGUSR1, \SIGUSR2], true)) { + exit(0); + } + } + }); + } + } + + foreach ($command->getSubscribedSignals() as $signal) { + $this->signalRegistry->register($signal, [$command, 'handleSignal']); } } + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + // bind before the console.command event, so the listeners have access to input options/arguments try { $command->mergeApplicationDefinition(); @@ -844,36 +1024,32 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI } $event = new ConsoleCommandEvent($command, $input, $output); - $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + $e = null; - if ($event->commandShouldRun()) { - try { - $e = null; + try { + $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND); + + if ($event->commandShouldRun()) { $exitCode = $command->run($input, $output); - } catch (\Exception $x) { - $e = $x; - } catch (\Throwable $x) { - $e = new FatalThrowableError($x); + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; } - if (null !== $e) { - $event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode()); - $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + } catch (\Throwable $e) { + $event = new ConsoleErrorEvent($input, $output, $e, $command); + $this->dispatcher->dispatch($event, ConsoleEvents::ERROR); + $e = $event->getError(); - if ($e !== $event->getException()) { - $x = $e = $event->getException(); - } - - $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); - $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); - - throw $x; + if (0 === $exitCode = $event->getExitCode()) { + $e = null; } - } else { - $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; } $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); - $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE); + + if (null !== $e) { + throw $e; + } return $event->getExitCode(); } @@ -881,9 +1057,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI /** * Gets the name of the command based on input. * - * @param InputInterface $input The input interface - * - * @return string The command name + * @return string|null */ protected function getCommandName(InputInterface $input) { @@ -893,58 +1067,52 @@ protected function getCommandName(InputInterface $input) /** * Gets the default input definition. * - * @return InputDefinition An InputDefinition instance + * @return InputDefinition */ protected function getDefaultInputDefinition() { - return new InputDefinition(array( + return new InputDefinition([ new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), - - new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the '.$this->defaultCommand.' command'), new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), - new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), - new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null), new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), - )); + ]); } /** * Gets the default commands that should always be available. * - * @return Command[] An array of default Command instances + * @return Command[] */ protected function getDefaultCommands() { - return array(new HelpCommand(), new ListCommand()); + return [new HelpCommand(), new ListCommand(), new CompleteCommand(), new DumpCompletionCommand()]; } /** * Gets the default helper set with the helpers that should always be available. * - * @return HelperSet A HelperSet instance + * @return HelperSet */ protected function getDefaultHelperSet() { - return new HelperSet(array( + return new HelperSet([ new FormatterHelper(), new DebugFormatterHelper(), new ProcessHelper(), new QuestionHelper(), - )); + ]); } /** * Returns abbreviated suggestions in string format. - * - * @param array $abbrevs Abbreviated suggestions to convert - * - * @return string A formatted string of abbreviated suggestions */ - private function getAbbreviationSuggestions($abbrevs) + private function getAbbreviationSuggestions(array $abbrevs): string { - return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + return ' '.implode("\n ", $abbrevs); } /** @@ -952,34 +1120,27 @@ private function getAbbreviationSuggestions($abbrevs) * * This method is not part of public API and should not be used directly. * - * @param string $name The full name of the command - * @param string $limit The maximum number of parts of the namespace - * - * @return string The namespace of the command + * @return string */ - public function extractNamespace($name, $limit = null) + public function extractNamespace(string $name, int $limit = null) { - $parts = explode(':', $name); - array_pop($parts); + $parts = explode(':', $name, -1); - return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit)); } /** * Finds alternative of $name among $collection, * if nothing is found in $collection, try in $abbrevs. * - * @param string $name The string - * @param array|\Traversable $collection The collection - * - * @return string[] A sorted array of similar string + * @return string[] */ - private function findAlternatives($name, $collection) + private function findAlternatives(string $name, iterable $collection): array { $threshold = 1e3; - $alternatives = array(); + $alternatives = []; - $collectionParts = array(); + $collectionParts = []; foreach ($collection as $item) { $collectionParts[$item] = explode(':', $item); } @@ -995,7 +1156,7 @@ private function findAlternatives($name, $collection) } $lev = levenshtein($subname, $parts[$i]); - if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + if ($lev <= \strlen($subname) / 3 || '' !== $subname && str_contains($parts[$i], $subname)) { $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; } elseif ($exists) { $alternatives[$collectionName] += $threshold; @@ -1005,13 +1166,13 @@ private function findAlternatives($name, $collection) foreach ($collection as $item) { $lev = levenshtein($name, $item); - if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) { $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; } } $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); - asort($alternatives); + ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE); return array_keys($alternatives); } @@ -1019,14 +1180,11 @@ private function findAlternatives($name, $collection) /** * Sets the default Command name. * - * @param string $commandName The Command name - * @param bool $isSingleCommand Set to true if there is only one command in this application - * - * @return self + * @return $this */ - public function setDefaultCommand($commandName, $isSingleCommand = false) + public function setDefaultCommand(string $commandName, bool $isSingleCommand = false) { - $this->defaultCommand = $commandName; + $this->defaultCommand = explode('|', ltrim($commandName, '|'))[0]; if ($isSingleCommand) { // Ensure the command exist @@ -1038,16 +1196,15 @@ public function setDefaultCommand($commandName, $isSingleCommand = false) return $this; } - private function stringWidth($string) + /** + * @internal + */ + public function isSingleCommand(): bool { - if (false === $encoding = mb_detect_encoding($string, null, true)) { - return strlen($string); - } - - return mb_strwidth($string, $encoding); + return $this->singleCommand; } - private function splitStringByWidth($string, $width) + private function splitStringByWidth(string $string, int $width): array { // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. // additionally, array_slice() is not enough as some character has doubled width. @@ -1057,22 +1214,27 @@ private function splitStringByWidth($string, $width) } $utf8String = mb_convert_encoding($string, 'utf8', $encoding); - $lines = array(); + $lines = []; $line = ''; - foreach (preg_split('//u', $utf8String) as $char) { - // test if $char could be appended to current line - if (mb_strwidth($line.$char, 'utf8') <= $width) { - $line .= $char; - continue; + + $offset = 0; + while (preg_match('/.{1,10000}/u', $utf8String, $m, 0, $offset)) { + $offset += \strlen($m[0]); + + foreach (preg_split('//u', $m[0]) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; } - // if not, push current line to array and make new line - $lines[] = str_pad($line, $width); - $line = $char; - } - if ('' !== $line) { - $lines[] = count($lines) ? str_pad($line, $width) : $line; } + $lines[] = \count($lines) ? str_pad($line, $width) : $line; + mb_convert_variables($encoding, 'utf8', $lines); return $lines; @@ -1081,18 +1243,16 @@ private function splitStringByWidth($string, $width) /** * Returns all namespaces of the command name. * - * @param string $name The full name of the command - * - * @return string[] The namespaces of the command + * @return string[] */ - private function extractAllNamespaces($name) + private function extractAllNamespaces(string $name): array { // -1 as third argument is needed to skip the command short name when exploding $parts = explode(':', $name, -1); - $namespaces = array(); + $namespaces = []; foreach ($parts as $part) { - if (count($namespaces)) { + if (\count($namespaces)) { $namespaces[] = end($namespaces).':'.$part; } else { $namespaces[] = $part; @@ -1101,4 +1261,16 @@ private function extractAllNamespaces($name) return $namespaces; } + + private function init() + { + if ($this->initialized) { + return; + } + $this->initialized = true; + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } } diff --git a/tests/integration/vendor/symfony/console/Attribute/AsCommand.php b/tests/integration/vendor/symfony/console/Attribute/AsCommand.php new file mode 100644 index 000000000..b337f548f --- /dev/null +++ b/tests/integration/vendor/symfony/console/Attribute/AsCommand.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Attribute; + +/** + * Service tag to autoconfigure commands. + */ +#[\Attribute(\Attribute::TARGET_CLASS)] +class AsCommand +{ + public function __construct( + public string $name, + public ?string $description = null, + array $aliases = [], + bool $hidden = false, + ) { + if (!$hidden && !$aliases) { + return; + } + + $name = explode('|', $name); + $name = array_merge($name, $aliases); + + if ($hidden && '' !== $name[0]) { + array_unshift($name, ''); + } + + $this->name = implode('|', $name); + } +} diff --git a/tests/integration/vendor/symfony/console/CHANGELOG.md b/tests/integration/vendor/symfony/console/CHANGELOG.md index a97a4a7ad..6662dd1eb 100644 --- a/tests/integration/vendor/symfony/console/CHANGELOG.md +++ b/tests/integration/vendor/symfony/console/CHANGELOG.md @@ -1,19 +1,149 @@ CHANGELOG ========= +5.4 +--- + + * Add `TesterTrait::assertCommandIsSuccessful()` to test command + * Deprecate `HelperSet::setCommand()` and `getCommand()` without replacement + +5.3 +--- + + * Add `GithubActionReporter` to render annotations in a Github Action + * Add `InputOption::VALUE_NEGATABLE` flag to handle `--foo`/`--no-foo` options + * Add the `Command::$defaultDescription` static property and the `description` attribute + on the `console.command` tag to allow the `list` command to instantiate commands lazily + * Add option `--short` to the `list` command + * Add support for bright colors + * Add `#[AsCommand]` attribute for declaring commands on PHP 8 + * Add `Helper::width()` and `Helper::length()` + * The `--ansi` and `--no-ansi` options now default to `null`. + +5.2.0 +----- + + * Added `SingleCommandApplication::setAutoExit()` to allow testing via `CommandTester` + * added support for multiline responses to questions through `Question::setMultiline()` + and `Question::isMultiline()` + * Added `SignalRegistry` class to stack signals handlers + * Added support for signals: + * Added `Application::getSignalRegistry()` and `Application::setSignalsToDispatchEvent()` methods + * Added `SignalableCommandInterface` interface + * Added `TableCellStyle` class to customize table cell + * Removed `php ` prefix invocation from help messages. + +5.1.0 +----- + + * `Command::setHidden()` is final since Symfony 5.1 + * Add `SingleCommandApplication` + * Add `Cursor` class + +5.0.0 +----- + + * removed support for finding hidden commands using an abbreviation, use the full name instead + * removed `TableStyle::setCrossingChar()` method in favor of `TableStyle::setDefaultCrossingChar()` + * removed `TableStyle::setHorizontalBorderChar()` method in favor of `TableStyle::setDefaultCrossingChars()` + * removed `TableStyle::getHorizontalBorderChar()` method in favor of `TableStyle::getBorderChars()` + * removed `TableStyle::setVerticalBorderChar()` method in favor of `TableStyle::setVerticalBorderChars()` + * removed `TableStyle::getVerticalBorderChar()` method in favor of `TableStyle::getBorderChars()` + * removed support for returning `null` from `Command::execute()`, return `0` instead + * `ProcessHelper::run()` accepts only `array|Symfony\Component\Process\Process` for its `command` argument + * `Application::setDispatcher` accepts only `Symfony\Contracts\EventDispatcher\EventDispatcherInterface` + for its `dispatcher` argument + * renamed `Application::renderException()` and `Application::doRenderException()` + to `renderThrowable()` and `doRenderThrowable()` respectively. + +4.4.0 +----- + + * deprecated finding hidden commands using an abbreviation, use the full name instead + * added `Question::setTrimmable` default to true to allow the answer to be trimmed + * added method `minSecondsBetweenRedraws()` and `maxSecondsBetweenRedraws()` on `ProgressBar` + * `Application` implements `ResetInterface` + * marked all dispatched event classes as `@final` + * added support for displaying table horizontally + * deprecated returning `null` from `Command::execute()`, return `0` instead + * Deprecated the `Application::renderException()` and `Application::doRenderException()` methods, + use `renderThrowable()` and `doRenderThrowable()` instead. + * added support for the `NO_COLOR` env var (https://no-color.org/) + +4.3.0 +----- + + * added support for hyperlinks + * added `ProgressBar::iterate()` method that simplify updating the progress bar when iterating + * added `Question::setAutocompleterCallback()` to provide a callback function + that dynamically generates suggestions as the user types + +4.2.0 +----- + + * allowed passing commands as `[$process, 'ENV_VAR' => 'value']` to + `ProcessHelper::run()` to pass environment variables + * deprecated passing a command as a string to `ProcessHelper::run()`, + pass it the command as an array of its arguments instead + * made the `ProcessHelper` class final + * added `WrappableOutputFormatterInterface::formatAndWrap()` (implemented in `OutputFormatter`) + * added `capture_stderr_separately` option to `CommandTester::execute()` + +4.1.0 +----- + + * added option to run suggested command if command is not found and only 1 alternative is available + * added option to modify console output and print multiple modifiable sections + * added support for iterable messages in output `write` and `writeln` methods + +4.0.0 +----- + + * `OutputFormatter` throws an exception when unknown options are used + * removed `QuestionHelper::setInputStream()/getInputStream()` + * removed `Application::getTerminalWidth()/getTerminalHeight()` and + `Application::setTerminalDimensions()/getTerminalDimensions()` + * removed `ConsoleExceptionEvent` + * removed `ConsoleEvents::EXCEPTION` + +3.4.0 +----- + + * added `SHELL_VERBOSITY` env var to control verbosity + * added `CommandLoaderInterface`, `FactoryCommandLoader` and PSR-11 + `ContainerCommandLoader` for commands lazy-loading + * added a case-insensitive command name matching fallback + * added static `Command::$defaultName/getDefaultName()`, allowing for + commands to be registered at compile time in the application command loader. + Setting the `$defaultName` property avoids the need for filling the `command` + attribute on the `console.command` tag when using `AddConsoleCommandPass`. + +3.3.0 +----- + + * added `ExceptionListener` + * added `AddConsoleCommandPass` (originally in FrameworkBundle) + * [BC BREAK] `Input::getOption()` no longer returns the default value for options + with value optional explicitly passed empty + * added console.error event to catch exceptions thrown by other listeners + * deprecated console.exception event in favor of console.error + * added ability to handle `CommandNotFoundException` through the + `console.error` event + * deprecated default validation in `SymfonyQuestionHelper::ask` + 3.2.0 ------ -* added `setInputs()` method to CommandTester for ease testing of commands expecting inputs -* added `setStream()` and `getStream()` methods to Input (implement StreamableInputInterface) -* added StreamableInputInterface -* added LockableTrait + * added `setInputs()` method to CommandTester for ease testing of commands expecting inputs + * added `setStream()` and `getStream()` methods to Input (implement StreamableInputInterface) + * added StreamableInputInterface + * added LockableTrait 3.1.0 ----- * added truncate method to FormatterHelper - * added setColumnWidth(s) method to Table + * added setColumnWidth(s) method to Table 2.8.3 ----- diff --git a/tests/integration/vendor/symfony/console/CI/GithubActionReporter.php b/tests/integration/vendor/symfony/console/CI/GithubActionReporter.php new file mode 100644 index 000000000..a15c1ff18 --- /dev/null +++ b/tests/integration/vendor/symfony/console/CI/GithubActionReporter.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CI; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Utility class for Github actions. + * + * @author Maxime Steinhausser + */ +class GithubActionReporter +{ + private $output; + + /** + * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L80-L85 + */ + private const ESCAPED_DATA = [ + '%' => '%25', + "\r" => '%0D', + "\n" => '%0A', + ]; + + /** + * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L87-L94 + */ + private const ESCAPED_PROPERTIES = [ + '%' => '%25', + "\r" => '%0D', + "\n" => '%0A', + ':' => '%3A', + ',' => '%2C', + ]; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + public static function isGithubActionEnvironment(): bool + { + return false !== getenv('GITHUB_ACTIONS'); + } + + /** + * Output an error using the Github annotations format. + * + * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + */ + public function error(string $message, string $file = null, int $line = null, int $col = null): void + { + $this->log('error', $message, $file, $line, $col); + } + + /** + * Output a warning using the Github annotations format. + * + * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message + */ + public function warning(string $message, string $file = null, int $line = null, int $col = null): void + { + $this->log('warning', $message, $file, $line, $col); + } + + /** + * Output a debug log using the Github annotations format. + * + * @see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message + */ + public function debug(string $message, string $file = null, int $line = null, int $col = null): void + { + $this->log('debug', $message, $file, $line, $col); + } + + private function log(string $type, string $message, string $file = null, int $line = null, int $col = null): void + { + // Some values must be encoded. + $message = strtr($message, self::ESCAPED_DATA); + + if (!$file) { + // No file provided, output the message solely: + $this->output->writeln(sprintf('::%s::%s', $type, $message)); + + return; + } + + $this->output->writeln(sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message)); + } +} diff --git a/tests/integration/vendor/symfony/console/Color.php b/tests/integration/vendor/symfony/console/Color.php new file mode 100644 index 000000000..22a4ce9ff --- /dev/null +++ b/tests/integration/vendor/symfony/console/Color.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Fabien Potencier + */ +final class Color +{ + private const COLORS = [ + 'black' => 0, + 'red' => 1, + 'green' => 2, + 'yellow' => 3, + 'blue' => 4, + 'magenta' => 5, + 'cyan' => 6, + 'white' => 7, + 'default' => 9, + ]; + + private const BRIGHT_COLORS = [ + 'gray' => 0, + 'bright-red' => 1, + 'bright-green' => 2, + 'bright-yellow' => 3, + 'bright-blue' => 4, + 'bright-magenta' => 5, + 'bright-cyan' => 6, + 'bright-white' => 7, + ]; + + private const AVAILABLE_OPTIONS = [ + 'bold' => ['set' => 1, 'unset' => 22], + 'underscore' => ['set' => 4, 'unset' => 24], + 'blink' => ['set' => 5, 'unset' => 25], + 'reverse' => ['set' => 7, 'unset' => 27], + 'conceal' => ['set' => 8, 'unset' => 28], + ]; + + private $foreground; + private $background; + private $options = []; + + public function __construct(string $foreground = '', string $background = '', array $options = []) + { + $this->foreground = $this->parseColor($foreground); + $this->background = $this->parseColor($background, true); + + foreach ($options as $option) { + if (!isset(self::AVAILABLE_OPTIONS[$option])) { + throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS)))); + } + + $this->options[$option] = self::AVAILABLE_OPTIONS[$option]; + } + } + + public function apply(string $text): string + { + return $this->set().$text.$this->unset(); + } + + public function set(): string + { + $setCodes = []; + if ('' !== $this->foreground) { + $setCodes[] = $this->foreground; + } + if ('' !== $this->background) { + $setCodes[] = $this->background; + } + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + } + if (0 === \count($setCodes)) { + return ''; + } + + return sprintf("\033[%sm", implode(';', $setCodes)); + } + + public function unset(): string + { + $unsetCodes = []; + if ('' !== $this->foreground) { + $unsetCodes[] = 39; + } + if ('' !== $this->background) { + $unsetCodes[] = 49; + } + foreach ($this->options as $option) { + $unsetCodes[] = $option['unset']; + } + if (0 === \count($unsetCodes)) { + return ''; + } + + return sprintf("\033[%sm", implode(';', $unsetCodes)); + } + + private function parseColor(string $color, bool $background = false): string + { + if ('' === $color) { + return ''; + } + + if ('#' === $color[0]) { + $color = substr($color, 1); + + if (3 === \strlen($color)) { + $color = $color[0].$color[0].$color[1].$color[1].$color[2].$color[2]; + } + + if (6 !== \strlen($color)) { + throw new InvalidArgumentException(sprintf('Invalid "%s" color.', $color)); + } + + return ($background ? '4' : '3').$this->convertHexColorToAnsi(hexdec($color)); + } + + if (isset(self::COLORS[$color])) { + return ($background ? '4' : '3').self::COLORS[$color]; + } + + if (isset(self::BRIGHT_COLORS[$color])) { + return ($background ? '10' : '9').self::BRIGHT_COLORS[$color]; + } + + throw new InvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS))))); + } + + private function convertHexColorToAnsi(int $color): string + { + $r = ($color >> 16) & 255; + $g = ($color >> 8) & 255; + $b = $color & 255; + + // see https://github.com/termstandard/colors/ for more information about true color support + if ('truecolor' !== getenv('COLORTERM')) { + return (string) $this->degradeHexColorToAnsi($r, $g, $b); + } + + return sprintf('8;2;%d;%d;%d', $r, $g, $b); + } + + private function degradeHexColorToAnsi(int $r, int $g, int $b): int + { + if (0 === round($this->getSaturation($r, $g, $b) / 50)) { + return 0; + } + + return (round($b / 255) << 2) | (round($g / 255) << 1) | round($r / 255); + } + + private function getSaturation(int $r, int $g, int $b): int + { + $r = $r / 255; + $g = $g / 255; + $b = $b / 255; + $v = max($r, $g, $b); + + if (0 === $diff = $v - min($r, $g, $b)) { + return 0; + } + + return (int) $diff * 100 / $v; + } +} diff --git a/tests/integration/vendor/symfony/console/Command/Command.php b/tests/integration/vendor/symfony/console/Command/Command.php index ecb2da745..146f6017b 100644 --- a/tests/integration/vendor/symfony/console/Command/Command.php +++ b/tests/integration/vendor/symfony/console/Command/Command.php @@ -11,16 +11,19 @@ namespace Symfony\Component\Console\Command; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\ExceptionInterface; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\LogicException; /** * Base class for all commands. @@ -29,42 +32,94 @@ */ class Command { + // see https://tldp.org/LDP/abs/html/exitcodes.html + public const SUCCESS = 0; + public const FAILURE = 1; + public const INVALID = 2; + + /** + * @var string|null The default command name + */ + protected static $defaultName; + + /** + * @var string|null The default command description + */ + protected static $defaultDescription; + private $application; private $name; private $processTitle; - private $aliases = array(); + private $aliases = []; private $definition; private $hidden = false; - private $help; - private $description; + private $help = ''; + private $description = ''; + private $fullDefinition; private $ignoreValidationErrors = false; - private $applicationDefinitionMerged = false; - private $applicationDefinitionMergedWithArgs = false; private $code; - private $synopsis = array(); - private $usages = array(); + private $synopsis = []; + private $usages = []; private $helperSet; /** - * Constructor. - * + * @return string|null + */ + public static function getDefaultName() + { + $class = static::class; + + if (\PHP_VERSION_ID >= 80000 && $attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { + return $attribute[0]->newInstance()->name; + } + + $r = new \ReflectionProperty($class, 'defaultName'); + + return $class === $r->class ? static::$defaultName : null; + } + + public static function getDefaultDescription(): ?string + { + $class = static::class; + + if (\PHP_VERSION_ID >= 80000 && $attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { + return $attribute[0]->newInstance()->description; + } + + $r = new \ReflectionProperty($class, 'defaultDescription'); + + return $class === $r->class ? static::$defaultDescription : null; + } + + /** * @param string|null $name The name of the command; passing null means it must be set in configure() * * @throws LogicException When the command name is empty */ - public function __construct($name = null) + public function __construct(string $name = null) { $this->definition = new InputDefinition(); + if (null === $name && null !== $name = static::getDefaultName()) { + $aliases = explode('|', $name); + + if ('' === $name = array_shift($aliases)) { + $this->setHidden(true); + $name = array_shift($aliases); + } + + $this->setAliases($aliases); + } + if (null !== $name) { $this->setName($name); } - $this->configure(); - - if (!$this->name) { - throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + if ('' === $this->description) { + $this->setDescription(static::getDefaultDescription() ?? ''); } + + $this->configure(); } /** @@ -77,11 +132,6 @@ public function ignoreValidationErrors() $this->ignoreValidationErrors = true; } - /** - * Sets the application instance for this command. - * - * @param Application $application An Application instance - */ public function setApplication(Application $application = null) { $this->application = $application; @@ -90,13 +140,10 @@ public function setApplication(Application $application = null) } else { $this->helperSet = null; } + + $this->fullDefinition = null; } - /** - * Sets the helper set. - * - * @param HelperSet $helperSet A HelperSet instance - */ public function setHelperSet(HelperSet $helperSet) { $this->helperSet = $helperSet; @@ -105,7 +152,7 @@ public function setHelperSet(HelperSet $helperSet) /** * Gets the helper set. * - * @return HelperSet A HelperSet instance + * @return HelperSet|null */ public function getHelperSet() { @@ -115,7 +162,7 @@ public function getHelperSet() /** * Gets the application instance for this command. * - * @return Application An Application instance + * @return Application|null */ public function getApplication() { @@ -125,7 +172,7 @@ public function getApplication() /** * Checks whether the command is enabled or not in the current environment. * - * Override this to check for x or y and return false if the command can not + * Override this to check for x or y and return false if the command cannot * run properly under the current conditions. * * @return bool @@ -150,10 +197,7 @@ protected function configure() * execute() method, you set the code to execute by passing * a Closure to the setCode() method. * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * - * @return null|int null or 0 if everything went fine, or an error code + * @return int 0 if everything went fine, or an exit code * * @throws LogicException When this abstract method is not implemented * @@ -170,22 +214,20 @@ protected function execute(InputInterface $input, OutputInterface $output) * This method is executed before the InputDefinition is validated. * This means that this is the only place where the command can * interactively ask for values of missing required arguments. - * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance */ protected function interact(InputInterface $input, OutputInterface $output) { } /** - * Initializes the command just after the input has been validated. + * Initializes the command after the input has been bound and before the input + * is validated. * * This is mainly useful when a lot of commands extends one main command * where some things need to be initialized based on the input arguments and options. * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance + * @see InputInterface::bind() + * @see InputInterface::validate() */ protected function initialize(InputInterface $input, OutputInterface $output) { @@ -198,26 +240,21 @@ protected function initialize(InputInterface $input, OutputInterface $output) * setCode() method or by overriding the execute() method * in a sub-class. * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * * @return int The command exit code * + * @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}. + * * @see setCode() * @see execute() */ public function run(InputInterface $input, OutputInterface $output) { - // force the creation of the synopsis before the merge with the app definition - $this->getSynopsis(true); - $this->getSynopsis(false); - // add the application arguments and options $this->mergeApplicationDefinition(); // bind the input against the command specific arguments/options try { - $input->bind($this->definition); + $input->bind($this->getDefinition()); } catch (ExceptionInterface $e) { if (!$this->ignoreValidationErrors) { throw $e; @@ -227,9 +264,15 @@ public function run(InputInterface $input, OutputInterface $output) $this->initialize($input, $output); if (null !== $this->processTitle) { - if (function_exists('cli_set_process_title')) { - cli_set_process_title($this->processTitle); - } elseif (function_exists('setproctitle')) { + if (\function_exists('cli_set_process_title')) { + if (!@cli_set_process_title($this->processTitle)) { + if ('Darwin' === \PHP_OS) { + $output->writeln('Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', OutputInterface::VERBOSITY_VERY_VERBOSE); + } else { + cli_set_process_title($this->processTitle); + } + } + } elseif (\function_exists('setproctitle')) { setproctitle($this->processTitle); } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { $output->writeln('Install the proctitle PECL to be able to change the process title.'); @@ -250,14 +293,25 @@ public function run(InputInterface $input, OutputInterface $output) $input->validate(); if ($this->code) { - $statusCode = call_user_func($this->code, $input, $output); + $statusCode = ($this->code)($input, $output); } else { $statusCode = $this->execute($input, $output); + + if (!\is_int($statusCode)) { + throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode))); + } } return is_numeric($statusCode) ? (int) $statusCode : 0; } + /** + * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). + */ + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + } + /** * Sets the code to execute when running this command. * @@ -266,7 +320,7 @@ public function run(InputInterface $input, OutputInterface $output) * * @param callable $code A callable(InputInterface $input, OutputInterface $output) * - * @return Command The current instance + * @return $this * * @throws InvalidArgumentException * @@ -277,7 +331,14 @@ public function setCode(callable $code) if ($code instanceof \Closure) { $r = new \ReflectionFunction($code); if (null === $r->getClosureThis()) { - $code = \Closure::bind($code, $this); + set_error_handler(static function () {}); + try { + if ($c = \Closure::bind($code, $this)) { + $code = $c; + } + } finally { + restore_error_handler(); + } } } @@ -292,24 +353,24 @@ public function setCode(callable $code) * This method is not part of public API and should not be used directly. * * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + * + * @internal */ - public function mergeApplicationDefinition($mergeArgs = true) + public function mergeApplicationDefinition(bool $mergeArgs = true) { - if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + if (null === $this->application) { return; } - $this->definition->addOptions($this->application->getDefinition()->getOptions()); - - if ($mergeArgs) { - $currentArguments = $this->definition->getArguments(); - $this->definition->setArguments($this->application->getDefinition()->getArguments()); - $this->definition->addArguments($currentArguments); - } + $this->fullDefinition = new InputDefinition(); + $this->fullDefinition->setOptions($this->definition->getOptions()); + $this->fullDefinition->addOptions($this->application->getDefinition()->getOptions()); - $this->applicationDefinitionMerged = true; if ($mergeArgs) { - $this->applicationDefinitionMergedWithArgs = true; + $this->fullDefinition->setArguments($this->application->getDefinition()->getArguments()); + $this->fullDefinition->addArguments($this->definition->getArguments()); + } else { + $this->fullDefinition->setArguments($this->definition->getArguments()); } } @@ -318,7 +379,7 @@ public function mergeApplicationDefinition($mergeArgs = true) * * @param array|InputDefinition $definition An array of argument and option instances or a definition instance * - * @return Command The current instance + * @return $this */ public function setDefinition($definition) { @@ -328,7 +389,7 @@ public function setDefinition($definition) $this->definition->setDefinition($definition); } - $this->applicationDefinitionMerged = false; + $this->fullDefinition = null; return $this; } @@ -336,11 +397,11 @@ public function setDefinition($definition) /** * Gets the InputDefinition attached to this Command. * - * @return InputDefinition An InputDefinition instance + * @return InputDefinition */ public function getDefinition() { - return $this->definition; + return $this->fullDefinition ?? $this->getNativeDefinition(); } /** @@ -351,26 +412,33 @@ public function getDefinition() * * This method is not part of public API and should not be used directly. * - * @return InputDefinition An InputDefinition instance + * @return InputDefinition */ public function getNativeDefinition() { - return $this->getDefinition(); + if (null === $this->definition) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); + } + + return $this->definition; } /** * Adds an argument. * - * @param string $name The argument name - * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid * - * @return Command The current instance + * @return $this */ - public function addArgument($name, $mode = null, $description = '', $default = null) + public function addArgument(string $name, int $mode = null, string $description = '', $default = null) { $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + if (null !== $this->fullDefinition) { + $this->fullDefinition->addArgument(new InputArgument($name, $mode, $description, $default)); + } return $this; } @@ -378,17 +446,20 @@ public function addArgument($name, $mode = null, $description = '', $default = n /** * Adds an option. * - * @param string $name The option name - * @param string $shortcut The shortcut (can be null) - * @param int $mode The option mode: One of the InputOption::VALUE_* constants - * @param string $description A description text - * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants + * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) * - * @return Command The current instance + * @throws InvalidArgumentException If option mode is invalid or incompatible + * + * @return $this */ - public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null) { $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + if (null !== $this->fullDefinition) { + $this->fullDefinition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + } return $this; } @@ -401,13 +472,11 @@ public function addOption($name, $shortcut = null, $mode = null, $description = * * $command->setName('foo:bar'); * - * @param string $name The command name - * - * @return Command The current instance + * @return $this * * @throws InvalidArgumentException When the name is invalid */ - public function setName($name) + public function setName(string $name) { $this->validateName($name); @@ -422,13 +491,9 @@ public function setName($name) * This feature should be used only when creating a long process command, * like a daemon. * - * PHP 5.5+ or the proctitle PECL library is required - * - * @param string $title The process title - * - * @return Command The current instance + * @return $this */ - public function setProcessTitle($title) + public function setProcessTitle(string $title) { $this->processTitle = $title; @@ -438,7 +503,7 @@ public function setProcessTitle($title) /** * Returns the command name. * - * @return string The command name + * @return string|null */ public function getName() { @@ -447,18 +512,21 @@ public function getName() /** * @param bool $hidden Whether or not the command should be hidden from the list of commands + * The default value will be true in Symfony 6.0 * - * @return Command The current instance + * @return $this + * + * @final since Symfony 5.1 */ - public function setHidden($hidden) + public function setHidden(bool $hidden /*= true*/) { - $this->hidden = (bool) $hidden; + $this->hidden = $hidden; return $this; } /** - * @return bool Whether the command should be publicly shown or not. + * @return bool whether the command should be publicly shown or not */ public function isHidden() { @@ -468,11 +536,9 @@ public function isHidden() /** * Sets the description for the command. * - * @param string $description The description for the command - * - * @return Command The current instance + * @return $this */ - public function setDescription($description) + public function setDescription(string $description) { $this->description = $description; @@ -482,7 +548,7 @@ public function setDescription($description) /** * Returns the description for the command. * - * @return string The description for the command + * @return string */ public function getDescription() { @@ -492,11 +558,9 @@ public function getDescription() /** * Sets the help for the command. * - * @param string $help The help for the command - * - * @return Command The current instance + * @return $this */ - public function setHelp($help) + public function setHelp(string $help) { $this->help = $help; @@ -506,7 +570,7 @@ public function setHelp($help) /** * Returns the help for the command. * - * @return string The help for the command + * @return string */ public function getHelp() { @@ -517,20 +581,21 @@ public function getHelp() * Returns the processed help for the command replacing the %command.name% and * %command.full_name% patterns with the real values dynamically. * - * @return string The processed help for the command + * @return string */ public function getProcessedHelp() { $name = $this->name; + $isSingleCommand = $this->application && $this->application->isSingleCommand(); - $placeholders = array( + $placeholders = [ '%command.name%', '%command.full_name%', - ); - $replacements = array( + ]; + $replacements = [ $name, - $_SERVER['PHP_SELF'].' '.$name, - ); + $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name, + ]; return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); } @@ -540,21 +605,20 @@ public function getProcessedHelp() * * @param string[] $aliases An array of aliases for the command * - * @return Command The current instance + * @return $this * * @throws InvalidArgumentException When an alias is invalid */ - public function setAliases($aliases) + public function setAliases(iterable $aliases) { - if (!is_array($aliases) && !$aliases instanceof \Traversable) { - throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); - } + $list = []; foreach ($aliases as $alias) { $this->validateName($alias); + $list[] = $alias; } - $this->aliases = $aliases; + $this->aliases = \is_array($aliases) ? $aliases : $list; return $this; } @@ -562,7 +626,7 @@ public function setAliases($aliases) /** * Returns the aliases for the command. * - * @return array An array of aliases for the command + * @return array */ public function getAliases() { @@ -574,9 +638,9 @@ public function getAliases() * * @param bool $short Whether to show the short version of the synopsis (with options folded) or not * - * @return string The synopsis + * @return string */ - public function getSynopsis($short = false) + public function getSynopsis(bool $short = false) { $key = $short ? 'short' : 'long'; @@ -588,15 +652,13 @@ public function getSynopsis($short = false) } /** - * Add a command usage example. - * - * @param string $usage The usage, it'll be prefixed with the command name + * Add a command usage example, it'll be prefixed with the command name. * - * @return Command The current instance + * @return $this */ - public function addUsage($usage) + public function addUsage(string $usage) { - if (0 !== strpos($usage, $this->name)) { + if (!str_starts_with($usage, $this->name)) { $usage = sprintf('%s %s', $this->name, $usage); } @@ -618,14 +680,12 @@ public function getUsages() /** * Gets a helper instance by name. * - * @param string $name The helper name - * - * @return mixed The helper value + * @return mixed * * @throws LogicException if no HelperSet is defined * @throws InvalidArgumentException if the helper is not defined */ - public function getHelper($name) + public function getHelper(string $name) { if (null === $this->helperSet) { throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); @@ -639,11 +699,9 @@ public function getHelper($name) * * It must be non-empty and parts can optionally be separated by ":". * - * @param string $name - * * @throws InvalidArgumentException When the name is invalid */ - private function validateName($name) + private function validateName(string $name) { if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); diff --git a/tests/integration/vendor/symfony/console/Command/CompleteCommand.php b/tests/integration/vendor/symfony/console/Command/CompleteCommand.php new file mode 100644 index 000000000..97357d673 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Command/CompleteCommand.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Completion\Output\BashCompletionOutput; +use Symfony\Component\Console\Completion\Output\CompletionOutputInterface; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Responsible for providing the values to the shell completion. + * + * @author Wouter de Jong + */ +final class CompleteCommand extends Command +{ + protected static $defaultName = '|_complete'; + protected static $defaultDescription = 'Internal command to provide shell completion suggestions'; + + private $completionOutputs; + + private $isDebug = false; + + /** + * @param array> $completionOutputs A list of additional completion outputs, with shell name as key and FQCN as value + */ + public function __construct(array $completionOutputs = []) + { + // must be set before the parent constructor, as the property value is used in configure() + $this->completionOutputs = $completionOutputs + ['bash' => BashCompletionOutput::class]; + + parent::__construct(); + } + + protected function configure(): void + { + $this + ->addOption('shell', 's', InputOption::VALUE_REQUIRED, 'The shell type ("'.implode('", "', array_keys($this->completionOutputs)).'")') + ->addOption('input', 'i', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'An array of input tokens (e.g. COMP_WORDS or argv)') + ->addOption('current', 'c', InputOption::VALUE_REQUIRED, 'The index of the "input" array that the cursor is in (e.g. COMP_CWORD)') + ->addOption('symfony', 'S', InputOption::VALUE_REQUIRED, 'The version of the completion script') + ; + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOLEAN); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + // uncomment when a bugfix or BC break has been introduced in the shell completion scripts + //$version = $input->getOption('symfony'); + //if ($version && version_compare($version, 'x.y', '>=')) { + // $message = sprintf('Completion script version is not supported ("%s" given, ">=x.y" required).', $version); + // $this->log($message); + + // $output->writeln($message.' Install the Symfony completion script again by using the "completion" command.'); + + // return 126; + //} + + $shell = $input->getOption('shell'); + if (!$shell) { + throw new \RuntimeException('The "--shell" option must be set.'); + } + + if (!$completionOutput = $this->completionOutputs[$shell] ?? false) { + throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs)))); + } + + $completionInput = $this->createCompletionInput($input); + $suggestions = new CompletionSuggestions(); + + $this->log([ + '', + ''.date('Y-m-d H:i:s').'', + 'Input: ("|" indicates the cursor position)', + ' '.(string) $completionInput, + 'Command:', + ' '.(string) implode(' ', $_SERVER['argv']), + 'Messages:', + ]); + + $command = $this->findCommand($completionInput, $output); + if (null === $command) { + $this->log(' No command found, completing using the Application class.'); + + $this->getApplication()->complete($completionInput, $suggestions); + } elseif ( + $completionInput->mustSuggestArgumentValuesFor('command') + && $command->getName() !== $completionInput->getCompletionValue() + ) { + $this->log(' No command found, completing using the Application class.'); + + // expand shortcut names ("cache:cl") into their full name ("cache:clear") + $suggestions->suggestValue($command->getName()); + } else { + $command->mergeApplicationDefinition(); + $completionInput->bind($command->getDefinition()); + + if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) { + $this->log(' Completing option names for the '.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).' command.'); + + $suggestions->suggestOptions($command->getDefinition()->getOptions()); + } else { + $this->log([ + ' Completing using the '.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).' class.', + ' Completing '.$completionInput->getCompletionType().' for '.$completionInput->getCompletionName().'', + ]); + if (null !== $compval = $completionInput->getCompletionValue()) { + $this->log(' Current value: '.$compval.''); + } + + $command->complete($completionInput, $suggestions); + } + } + + /** @var CompletionOutputInterface $completionOutput */ + $completionOutput = new $completionOutput(); + + $this->log('Suggestions:'); + if ($options = $suggestions->getOptionSuggestions()) { + $this->log(' --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options))); + } elseif ($values = $suggestions->getValueSuggestions()) { + $this->log(' '.implode(' ', $values)); + } else { + $this->log(' No suggestions were provided'); + } + + $completionOutput->write($suggestions, $output); + } catch (\Throwable $e) { + $this->log([ + 'Error!', + (string) $e, + ]); + + if ($output->isDebug()) { + throw $e; + } + + return self::FAILURE; + } + + return self::SUCCESS; + } + + private function createCompletionInput(InputInterface $input): CompletionInput + { + $currentIndex = $input->getOption('current'); + if (!$currentIndex || !ctype_digit($currentIndex)) { + throw new \RuntimeException('The "--current" option must be set and it must be an integer.'); + } + + $completionInput = CompletionInput::fromTokens($input->getOption('input'), (int) $currentIndex); + + try { + $completionInput->bind($this->getApplication()->getDefinition()); + } catch (ExceptionInterface $e) { + } + + return $completionInput; + } + + private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command + { + try { + $inputName = $completionInput->getFirstArgument(); + if (null === $inputName) { + return null; + } + + return $this->getApplication()->find($inputName); + } catch (CommandNotFoundException $e) { + } + + return null; + } + + private function log($messages): void + { + if (!$this->isDebug) { + return; + } + + $commandName = basename($_SERVER['argv'][0]); + file_put_contents(sys_get_temp_dir().'/sf_'.$commandName.'.log', implode(\PHP_EOL, (array) $messages).\PHP_EOL, \FILE_APPEND); + } +} diff --git a/tests/integration/vendor/symfony/console/Command/DumpCompletionCommand.php b/tests/integration/vendor/symfony/console/Command/DumpCompletionCommand.php new file mode 100644 index 000000000..64a800530 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Command/DumpCompletionCommand.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; + +/** + * Dumps the completion script for the current shell. + * + * @author Wouter de Jong + */ +final class DumpCompletionCommand extends Command +{ + protected static $defaultName = 'completion'; + protected static $defaultDescription = 'Dump the shell completion script'; + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('shell')) { + $suggestions->suggestValues($this->getSupportedShells()); + } + } + + protected function configure() + { + $fullCommand = $_SERVER['PHP_SELF']; + $commandName = basename($fullCommand); + $fullCommand = realpath($fullCommand) ?: $fullCommand; + + $this + ->setHelp(<<%command.name% command dumps the shell completion script required +to use shell autocompletion (currently only bash completion is supported). + +Static installation +------------------- + +Dump the script to a global completion file and restart your shell: + + %command.full_name% bash | sudo tee /etc/bash_completion.d/${commandName} + +Or dump the script to a local file and source it: + + %command.full_name% bash > completion.sh + + # source the file whenever you use the project + source completion.sh + + # or add this line at the end of your "~/.bashrc" file: + source /path/to/completion.sh + +Dynamic installation +-------------------- + +Add this to the end of your shell configuration file (e.g. "~/.bashrc"): + + eval "$(${fullCommand} completion bash)" +EOH + ) + ->addArgument('shell', InputArgument::OPTIONAL, 'The shell type (e.g. "bash"), the value of the "$SHELL" env var will be used if this is not given') + ->addOption('debug', null, InputOption::VALUE_NONE, 'Tail the completion debug log') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $commandName = basename($_SERVER['argv'][0]); + + if ($input->getOption('debug')) { + $this->tailDebugLog($commandName, $output); + + return self::SUCCESS; + } + + $shell = $input->getArgument('shell') ?? self::guessShell(); + $completionFile = __DIR__.'/../Resources/completion.'.$shell; + if (!file_exists($completionFile)) { + $supportedShells = $this->getSupportedShells(); + + ($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output) + ->writeln(sprintf('Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").', $shell, implode('", "', $supportedShells))); + + return self::INVALID; + } + + $output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, $this->getApplication()->getVersion()], file_get_contents($completionFile))); + + return self::SUCCESS; + } + + private static function guessShell(): string + { + return basename($_SERVER['SHELL'] ?? ''); + } + + private function tailDebugLog(string $commandName, OutputInterface $output): void + { + $debugFile = sys_get_temp_dir().'/sf_'.$commandName.'.log'; + if (!file_exists($debugFile)) { + touch($debugFile); + } + $process = new Process(['tail', '-f', $debugFile], null, null, null, 0); + $process->run(function (string $type, string $line) use ($output): void { + $output->write($line); + }); + } + + /** + * @return string[] + */ + private function getSupportedShells(): array + { + return array_map(function ($f) { + return pathinfo($f, \PATHINFO_EXTENSION); + }, glob(__DIR__.'/../Resources/completion.*')); + } +} diff --git a/tests/integration/vendor/symfony/console/Command/HelpCommand.php b/tests/integration/vendor/symfony/console/Command/HelpCommand.php index b8fd911ad..c66ef463e 100644 --- a/tests/integration/vendor/symfony/console/Command/HelpCommand.php +++ b/tests/integration/vendor/symfony/console/Command/HelpCommand.php @@ -11,10 +11,13 @@ namespace Symfony\Component\Console\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Descriptor\ApplicationDescription; use Symfony\Component\Console\Helper\DescriptorHelper; use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -35,20 +38,20 @@ protected function configure() $this ->setName('help') - ->setDefinition(array( + ->setDefinition([ new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), - )) - ->setDescription('Displays help for a command') + ]) + ->setDescription('Display help for a command') ->setHelp(<<<'EOF' The %command.name% command displays help for a given command: - php %command.full_name% list + %command.full_name% list You can also output the help in other formats by using the --format option: - php %command.full_name% --format=xml list + %command.full_name% --format=xml list To display the list of available commands, please use the list command. EOF @@ -56,11 +59,6 @@ protected function configure() ; } - /** - * Sets the command. - * - * @param Command $command The command to set - */ public function setCommand(Command $command) { $this->command = $command; @@ -76,11 +74,28 @@ protected function execute(InputInterface $input, OutputInterface $output) } $helper = new DescriptorHelper(); - $helper->describe($output, $this->command, array( + $helper->describe($output, $this->command, [ 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), - )); + ]); $this->command = null; + + return 0; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('command_name')) { + $descriptor = new ApplicationDescription($this->getApplication()); + $suggestions->suggestValues(array_keys($descriptor->getCommands())); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $helper = new DescriptorHelper(); + $suggestions->suggestValues($helper->getFormats()); + } } } diff --git a/tests/integration/vendor/symfony/console/Command/LazyCommand.php b/tests/integration/vendor/symfony/console/Command/LazyCommand.php new file mode 100644 index 000000000..e576ad03f --- /dev/null +++ b/tests/integration/vendor/symfony/console/Command/LazyCommand.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Nicolas Grekas + */ +final class LazyCommand extends Command +{ + private $command; + private $isEnabled; + + public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true) + { + $this->setName($name) + ->setAliases($aliases) + ->setHidden($isHidden) + ->setDescription($description); + + $this->command = $commandFactory; + $this->isEnabled = $isEnabled; + } + + public function ignoreValidationErrors(): void + { + $this->getCommand()->ignoreValidationErrors(); + } + + public function setApplication(Application $application = null): void + { + if ($this->command instanceof parent) { + $this->command->setApplication($application); + } + + parent::setApplication($application); + } + + public function setHelperSet(HelperSet $helperSet): void + { + if ($this->command instanceof parent) { + $this->command->setHelperSet($helperSet); + } + + parent::setHelperSet($helperSet); + } + + public function isEnabled(): bool + { + return $this->isEnabled ?? $this->getCommand()->isEnabled(); + } + + public function run(InputInterface $input, OutputInterface $output): int + { + return $this->getCommand()->run($input, $output); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + $this->getCommand()->complete($input, $suggestions); + } + + /** + * @return $this + */ + public function setCode(callable $code): self + { + $this->getCommand()->setCode($code); + + return $this; + } + + /** + * @internal + */ + public function mergeApplicationDefinition(bool $mergeArgs = true): void + { + $this->getCommand()->mergeApplicationDefinition($mergeArgs); + } + + /** + * @return $this + */ + public function setDefinition($definition): self + { + $this->getCommand()->setDefinition($definition); + + return $this; + } + + public function getDefinition(): InputDefinition + { + return $this->getCommand()->getDefinition(); + } + + public function getNativeDefinition(): InputDefinition + { + return $this->getCommand()->getNativeDefinition(); + } + + /** + * @return $this + */ + public function addArgument(string $name, int $mode = null, string $description = '', $default = null): self + { + $this->getCommand()->addArgument($name, $mode, $description, $default); + + return $this; + } + + /** + * @return $this + */ + public function addOption(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null): self + { + $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default); + + return $this; + } + + /** + * @return $this + */ + public function setProcessTitle(string $title): self + { + $this->getCommand()->setProcessTitle($title); + + return $this; + } + + /** + * @return $this + */ + public function setHelp(string $help): self + { + $this->getCommand()->setHelp($help); + + return $this; + } + + public function getHelp(): string + { + return $this->getCommand()->getHelp(); + } + + public function getProcessedHelp(): string + { + return $this->getCommand()->getProcessedHelp(); + } + + public function getSynopsis(bool $short = false): string + { + return $this->getCommand()->getSynopsis($short); + } + + /** + * @return $this + */ + public function addUsage(string $usage): self + { + $this->getCommand()->addUsage($usage); + + return $this; + } + + public function getUsages(): array + { + return $this->getCommand()->getUsages(); + } + + /** + * @return mixed + */ + public function getHelper(string $name) + { + return $this->getCommand()->getHelper($name); + } + + public function getCommand(): parent + { + if (!$this->command instanceof \Closure) { + return $this->command; + } + + $command = $this->command = ($this->command)(); + $command->setApplication($this->getApplication()); + + if (null !== $this->getHelperSet()) { + $command->setHelperSet($this->getHelperSet()); + } + + $command->setName($this->getName()) + ->setAliases($this->getAliases()) + ->setHidden($this->isHidden()) + ->setDescription($this->getDescription()); + + // Will throw if the command is not correctly initialized. + $command->getDefinition(); + + return $command; + } +} diff --git a/tests/integration/vendor/symfony/console/Command/ListCommand.php b/tests/integration/vendor/symfony/console/Command/ListCommand.php index 179ddea5d..f04a4ef67 100644 --- a/tests/integration/vendor/symfony/console/Command/ListCommand.php +++ b/tests/integration/vendor/symfony/console/Command/ListCommand.php @@ -11,12 +11,14 @@ namespace Symfony\Component\Console\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Descriptor\ApplicationDescription; use Symfony\Component\Console\Helper\DescriptorHelper; use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputDefinition; /** * ListCommand displays the list of all available commands for the application. @@ -32,59 +34,62 @@ protected function configure() { $this ->setName('list') - ->setDefinition($this->createDefinition()) - ->setDescription('Lists commands') + ->setDefinition([ + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'), + ]) + ->setDescription('List commands') ->setHelp(<<<'EOF' The %command.name% command lists all commands: - php %command.full_name% + %command.full_name% You can also display the commands for a specific namespace: - php %command.full_name% test + %command.full_name% test You can also output the information in other formats by using the --format option: - php %command.full_name% --format=xml + %command.full_name% --format=xml It's also possible to get raw list of commands (useful for embedding command runner): - php %command.full_name% --raw + %command.full_name% --raw EOF ) ; } - /** - * {@inheritdoc} - */ - public function getNativeDefinition() - { - return $this->createDefinition(); - } - /** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $helper = new DescriptorHelper(); - $helper->describe($output, $this->getApplication(), array( + $helper->describe($output, $this->getApplication(), [ 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), 'namespace' => $input->getArgument('namespace'), - )); + 'short' => $input->getOption('short'), + ]); + + return 0; } - /** - * {@inheritdoc} - */ - private function createDefinition() + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { - return new InputDefinition(array( - new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), - new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), - new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), - )); + if ($input->mustSuggestArgumentValuesFor('namespace')) { + $descriptor = new ApplicationDescription($this->getApplication()); + $suggestions->suggestValues(array_keys($descriptor->getNamespaces())); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $helper = new DescriptorHelper(); + $suggestions->suggestValues($helper->getFormats()); + } } } diff --git a/tests/integration/vendor/symfony/console/Command/LockableTrait.php b/tests/integration/vendor/symfony/console/Command/LockableTrait.php index 955977059..b1856dca7 100644 --- a/tests/integration/vendor/symfony/console/Command/LockableTrait.php +++ b/tests/integration/vendor/symfony/console/Command/LockableTrait.php @@ -12,8 +12,10 @@ namespace Symfony\Component\Console\Command; use Symfony\Component\Console\Exception\LogicException; -use Symfony\Component\Console\Exception\RuntimeException; -use Symfony\Component\Filesystem\LockHandler; +use Symfony\Component\Lock\LockFactory; +use Symfony\Component\Lock\LockInterface; +use Symfony\Component\Lock\Store\FlockStore; +use Symfony\Component\Lock\Store\SemaphoreStore; /** * Basic lock feature for commands. @@ -22,27 +24,31 @@ */ trait LockableTrait { - private $lockHandler; + /** @var LockInterface|null */ + private $lock; /** * Locks a command. - * - * @return bool */ - private function lock($name = null, $blocking = false) + private function lock(string $name = null, bool $blocking = false): bool { - if (!class_exists(LockHandler::class)) { - throw new RuntimeException('To enable the locking feature you must install the symfony/filesystem component.'); + if (!class_exists(SemaphoreStore::class)) { + throw new LogicException('To enable the locking feature you must install the symfony/lock component.'); } - if (null !== $this->lockHandler) { + if (null !== $this->lock) { throw new LogicException('A lock is already in place.'); } - $this->lockHandler = new LockHandler($name ?: $this->getName()); + if (SemaphoreStore::isSupported()) { + $store = new SemaphoreStore(); + } else { + $store = new FlockStore(); + } - if (!$this->lockHandler->lock($blocking)) { - $this->lockHandler = null; + $this->lock = (new LockFactory($store))->createLock($name ?: $this->getName()); + if (!$this->lock->acquire($blocking)) { + $this->lock = null; return false; } @@ -55,9 +61,9 @@ private function lock($name = null, $blocking = false) */ private function release() { - if ($this->lockHandler) { - $this->lockHandler->release(); - $this->lockHandler = null; + if ($this->lock) { + $this->lock->release(); + $this->lock = null; } } } diff --git a/tests/integration/vendor/symfony/console/Command/SignalableCommandInterface.php b/tests/integration/vendor/symfony/console/Command/SignalableCommandInterface.php new file mode 100644 index 000000000..d439728b6 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Command/SignalableCommandInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +/** + * Interface for command reacting to signal. + * + * @author Grégoire Pineau + */ +interface SignalableCommandInterface +{ + /** + * Returns the list of signals to subscribe. + */ + public function getSubscribedSignals(): array; + + /** + * The method will be called when the application is signaled. + */ + public function handleSignal(int $signal): void; +} diff --git a/tests/integration/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php b/tests/integration/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php new file mode 100644 index 000000000..0adaf886f --- /dev/null +++ b/tests/integration/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Robin Chalas + */ +interface CommandLoaderInterface +{ + /** + * Loads a command. + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function get(string $name); + + /** + * Checks if a command exists. + * + * @return bool + */ + public function has(string $name); + + /** + * @return string[] + */ + public function getNames(); +} diff --git a/tests/integration/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php b/tests/integration/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php new file mode 100644 index 000000000..ddccb3d45 --- /dev/null +++ b/tests/integration/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * Loads commands from a PSR-11 container. + * + * @author Robin Chalas + */ +class ContainerCommandLoader implements CommandLoaderInterface +{ + private $container; + private $commandMap; + + /** + * @param array $commandMap An array with command names as keys and service ids as values + */ + public function __construct(ContainerInterface $container, array $commandMap) + { + $this->container = $container; + $this->commandMap = $commandMap; + } + + /** + * {@inheritdoc} + */ + public function get(string $name) + { + if (!$this->has($name)) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + return $this->container->get($this->commandMap[$name]); + } + + /** + * {@inheritdoc} + */ + public function has(string $name) + { + return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); + } + + /** + * {@inheritdoc} + */ + public function getNames() + { + return array_keys($this->commandMap); + } +} diff --git a/tests/integration/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php b/tests/integration/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php new file mode 100644 index 000000000..7e2db3464 --- /dev/null +++ b/tests/integration/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * A simple command loader using factories to instantiate commands lazily. + * + * @author Maxime Steinhausser + */ +class FactoryCommandLoader implements CommandLoaderInterface +{ + private $factories; + + /** + * @param callable[] $factories Indexed by command names + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * {@inheritdoc} + */ + public function has(string $name) + { + return isset($this->factories[$name]); + } + + /** + * {@inheritdoc} + */ + public function get(string $name) + { + if (!isset($this->factories[$name])) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + $factory = $this->factories[$name]; + + return $factory(); + } + + /** + * {@inheritdoc} + */ + public function getNames() + { + return array_keys($this->factories); + } +} diff --git a/tests/integration/vendor/symfony/console/Completion/CompletionInput.php b/tests/integration/vendor/symfony/console/Completion/CompletionInput.php new file mode 100644 index 000000000..368b94507 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Completion/CompletionInput.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion; + +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * An input specialized for shell completion. + * + * This input allows unfinished option names or values and exposes what kind of + * completion is expected. + * + * @author Wouter de Jong + */ +final class CompletionInput extends ArgvInput +{ + public const TYPE_ARGUMENT_VALUE = 'argument_value'; + public const TYPE_OPTION_VALUE = 'option_value'; + public const TYPE_OPTION_NAME = 'option_name'; + public const TYPE_NONE = 'none'; + + private $tokens; + private $currentIndex; + private $completionType; + private $completionName = null; + private $completionValue = ''; + + /** + * Converts a terminal string into tokens. + * + * This is required for shell completions without COMP_WORDS support. + */ + public static function fromString(string $inputStr, int $currentIndex): self + { + preg_match_all('/(?<=^|\s)([\'"]?)(.+?)(?tokens = $tokens; + $input->currentIndex = $currentIndex; + + return $input; + } + + /** + * {@inheritdoc} + */ + public function bind(InputDefinition $definition): void + { + parent::bind($definition); + + $relevantToken = $this->getRelevantToken(); + if ('-' === $relevantToken[0]) { + // the current token is an input option: complete either option name or option value + [$optionToken, $optionValue] = explode('=', $relevantToken, 2) + ['', '']; + + $option = $this->getOptionFromToken($optionToken); + if (null === $option && !$this->isCursorFree()) { + $this->completionType = self::TYPE_OPTION_NAME; + $this->completionValue = $relevantToken; + + return; + } + + if (null !== $option && $option->acceptValue()) { + $this->completionType = self::TYPE_OPTION_VALUE; + $this->completionName = $option->getName(); + $this->completionValue = $optionValue ?: (!str_starts_with($optionToken, '--') ? substr($optionToken, 2) : ''); + + return; + } + } + + $previousToken = $this->tokens[$this->currentIndex - 1]; + if ('-' === $previousToken[0] && '' !== trim($previousToken, '-')) { + // check if previous option accepted a value + $previousOption = $this->getOptionFromToken($previousToken); + if (null !== $previousOption && $previousOption->acceptValue()) { + $this->completionType = self::TYPE_OPTION_VALUE; + $this->completionName = $previousOption->getName(); + $this->completionValue = $relevantToken; + + return; + } + } + + // complete argument value + $this->completionType = self::TYPE_ARGUMENT_VALUE; + + foreach ($this->definition->getArguments() as $argumentName => $argument) { + if (!isset($this->arguments[$argumentName])) { + break; + } + + $argumentValue = $this->arguments[$argumentName]; + $this->completionName = $argumentName; + if (\is_array($argumentValue)) { + $this->completionValue = $argumentValue ? $argumentValue[array_key_last($argumentValue)] : null; + } else { + $this->completionValue = $argumentValue; + } + } + + if ($this->currentIndex >= \count($this->tokens)) { + if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) { + $this->completionName = $argumentName; + $this->completionValue = ''; + } else { + // we've reached the end + $this->completionType = self::TYPE_NONE; + $this->completionName = null; + $this->completionValue = ''; + } + } + } + + /** + * Returns the type of completion required. + * + * TYPE_ARGUMENT_VALUE when completing the value of an input argument + * TYPE_OPTION_VALUE when completing the value of an input option + * TYPE_OPTION_NAME when completing the name of an input option + * TYPE_NONE when nothing should be completed + * + * @return string One of self::TYPE_* constants. TYPE_OPTION_NAME and TYPE_NONE are already implemented by the Console component + */ + public function getCompletionType(): string + { + return $this->completionType; + } + + /** + * The name of the input option or argument when completing a value. + * + * @return string|null returns null when completing an option name + */ + public function getCompletionName(): ?string + { + return $this->completionName; + } + + /** + * The value already typed by the user (or empty string). + */ + public function getCompletionValue(): string + { + return $this->completionValue; + } + + public function mustSuggestOptionValuesFor(string $optionName): bool + { + return self::TYPE_OPTION_VALUE === $this->getCompletionType() && $optionName === $this->getCompletionName(); + } + + public function mustSuggestArgumentValuesFor(string $argumentName): bool + { + return self::TYPE_ARGUMENT_VALUE === $this->getCompletionType() && $argumentName === $this->getCompletionName(); + } + + protected function parseToken(string $token, bool $parseOptions): bool + { + try { + return parent::parseToken($token, $parseOptions); + } catch (RuntimeException $e) { + // suppress errors, completed input is almost never valid + } + + return $parseOptions; + } + + private function getOptionFromToken(string $optionToken): ?InputOption + { + $optionName = ltrim($optionToken, '-'); + if (!$optionName) { + return null; + } + + if ('-' === ($optionToken[1] ?? ' ')) { + // long option name + return $this->definition->hasOption($optionName) ? $this->definition->getOption($optionName) : null; + } + + // short option name + return $this->definition->hasShortcut($optionName[0]) ? $this->definition->getOptionForShortcut($optionName[0]) : null; + } + + /** + * The token of the cursor, or the last token if the cursor is at the end of the input. + */ + private function getRelevantToken(): string + { + return $this->tokens[$this->isCursorFree() ? $this->currentIndex - 1 : $this->currentIndex]; + } + + /** + * Whether the cursor is "free" (i.e. at the end of the input preceded by a space). + */ + private function isCursorFree(): bool + { + $nrOfTokens = \count($this->tokens); + if ($this->currentIndex > $nrOfTokens) { + throw new \LogicException('Current index is invalid, it must be the number of input tokens or one more.'); + } + + return $this->currentIndex >= $nrOfTokens; + } + + public function __toString() + { + $str = ''; + foreach ($this->tokens as $i => $token) { + $str .= $token; + + if ($this->currentIndex === $i) { + $str .= '|'; + } + + $str .= ' '; + } + + if ($this->currentIndex > $i) { + $str .= '|'; + } + + return rtrim($str); + } +} diff --git a/tests/integration/vendor/symfony/console/Completion/CompletionSuggestions.php b/tests/integration/vendor/symfony/console/Completion/CompletionSuggestions.php new file mode 100644 index 000000000..d8905e5ee --- /dev/null +++ b/tests/integration/vendor/symfony/console/Completion/CompletionSuggestions.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion; + +use Symfony\Component\Console\Input\InputOption; + +/** + * Stores all completion suggestions for the current input. + * + * @author Wouter de Jong + */ +final class CompletionSuggestions +{ + private $valueSuggestions = []; + private $optionSuggestions = []; + + /** + * Add a suggested value for an input option or argument. + * + * @param string|Suggestion $value + * + * @return $this + */ + public function suggestValue($value): self + { + $this->valueSuggestions[] = !$value instanceof Suggestion ? new Suggestion($value) : $value; + + return $this; + } + + /** + * Add multiple suggested values at once for an input option or argument. + * + * @param list $values + * + * @return $this + */ + public function suggestValues(array $values): self + { + foreach ($values as $value) { + $this->suggestValue($value); + } + + return $this; + } + + /** + * Add a suggestion for an input option name. + * + * @return $this + */ + public function suggestOption(InputOption $option): self + { + $this->optionSuggestions[] = $option; + + return $this; + } + + /** + * Add multiple suggestions for input option names at once. + * + * @param InputOption[] $options + * + * @return $this + */ + public function suggestOptions(array $options): self + { + foreach ($options as $option) { + $this->suggestOption($option); + } + + return $this; + } + + /** + * @return InputOption[] + */ + public function getOptionSuggestions(): array + { + return $this->optionSuggestions; + } + + /** + * @return Suggestion[] + */ + public function getValueSuggestions(): array + { + return $this->valueSuggestions; + } +} diff --git a/tests/integration/vendor/symfony/console/Completion/Output/BashCompletionOutput.php b/tests/integration/vendor/symfony/console/Completion/Output/BashCompletionOutput.php new file mode 100644 index 000000000..8d5ffa6b6 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Completion/Output/BashCompletionOutput.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion\Output; + +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Wouter de Jong + */ +class BashCompletionOutput implements CompletionOutputInterface +{ + public function write(CompletionSuggestions $suggestions, OutputInterface $output): void + { + $values = $suggestions->getValueSuggestions(); + foreach ($suggestions->getOptionSuggestions() as $option) { + $values[] = '--'.$option->getName(); + } + $output->writeln(implode("\n", $values)); + } +} diff --git a/tests/integration/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php b/tests/integration/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php new file mode 100644 index 000000000..659e59655 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion\Output; + +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Transforms the {@see CompletionSuggestions} object into output readable by the shell completion. + * + * @author Wouter de Jong + */ +interface CompletionOutputInterface +{ + public function write(CompletionSuggestions $suggestions, OutputInterface $output): void; +} diff --git a/tests/integration/vendor/symfony/console/Completion/Suggestion.php b/tests/integration/vendor/symfony/console/Completion/Suggestion.php new file mode 100644 index 000000000..6c7bc4dc4 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Completion/Suggestion.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Completion; + +/** + * Represents a single suggested value. + * + * @author Wouter de Jong + */ +class Suggestion +{ + private $value; + + public function __construct(string $value) + { + $this->value = $value; + } + + public function getValue(): string + { + return $this->value; + } + + public function __toString(): string + { + return $this->getValue(); + } +} diff --git a/tests/integration/vendor/symfony/console/ConsoleEvents.php b/tests/integration/vendor/symfony/console/ConsoleEvents.php index b3571e9af..6ae8f32b8 100644 --- a/tests/integration/vendor/symfony/console/ConsoleEvents.php +++ b/tests/integration/vendor/symfony/console/ConsoleEvents.php @@ -11,6 +11,11 @@ namespace Symfony\Component\Console; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleSignalEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; + /** * Contains all events dispatched by an Application. * @@ -21,33 +26,47 @@ final class ConsoleEvents /** * The COMMAND event allows you to attach listeners before any command is * executed by the console. It also allows you to modify the command, input and output - * before they are handled to the command. + * before they are handed to the command. * * @Event("Symfony\Component\Console\Event\ConsoleCommandEvent") + */ + public const COMMAND = 'console.command'; + + /** + * The SIGNAL event allows you to perform some actions + * after the command execution was interrupted. * - * @var string + * @Event("Symfony\Component\Console\Event\ConsoleSignalEvent") */ - const COMMAND = 'console.command'; + public const SIGNAL = 'console.signal'; /** * The TERMINATE event allows you to attach listeners after a command is * executed by the console. * * @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent") - * - * @var string */ - const TERMINATE = 'console.terminate'; + public const TERMINATE = 'console.terminate'; /** - * The EXCEPTION event occurs when an uncaught exception appears. + * The ERROR event occurs when an uncaught exception or error appears. * - * This event allows you to deal with the exception or + * This event allows you to deal with the exception/error or * to modify the thrown exception. * - * @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent") + * @Event("Symfony\Component\Console\Event\ConsoleErrorEvent") + */ + public const ERROR = 'console.error'; + + /** + * Event aliases. * - * @var string + * These aliases can be consumed by RegisterListenersPass. */ - const EXCEPTION = 'console.exception'; + public const ALIASES = [ + ConsoleCommandEvent::class => self::COMMAND, + ConsoleErrorEvent::class => self::ERROR, + ConsoleSignalEvent::class => self::SIGNAL, + ConsoleTerminateEvent::class => self::TERMINATE, + ]; } diff --git a/tests/integration/vendor/symfony/console/Cursor.php b/tests/integration/vendor/symfony/console/Cursor.php new file mode 100644 index 000000000..0c4dafb6c --- /dev/null +++ b/tests/integration/vendor/symfony/console/Cursor.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Pierre du Plessis + */ +final class Cursor +{ + private $output; + private $input; + + /** + * @param resource|null $input + */ + public function __construct(OutputInterface $output, $input = null) + { + $this->output = $output; + $this->input = $input ?? (\defined('STDIN') ? \STDIN : fopen('php://input', 'r+')); + } + + /** + * @return $this + */ + public function moveUp(int $lines = 1): self + { + $this->output->write(sprintf("\x1b[%dA", $lines)); + + return $this; + } + + /** + * @return $this + */ + public function moveDown(int $lines = 1): self + { + $this->output->write(sprintf("\x1b[%dB", $lines)); + + return $this; + } + + /** + * @return $this + */ + public function moveRight(int $columns = 1): self + { + $this->output->write(sprintf("\x1b[%dC", $columns)); + + return $this; + } + + /** + * @return $this + */ + public function moveLeft(int $columns = 1): self + { + $this->output->write(sprintf("\x1b[%dD", $columns)); + + return $this; + } + + /** + * @return $this + */ + public function moveToColumn(int $column): self + { + $this->output->write(sprintf("\x1b[%dG", $column)); + + return $this; + } + + /** + * @return $this + */ + public function moveToPosition(int $column, int $row): self + { + $this->output->write(sprintf("\x1b[%d;%dH", $row + 1, $column)); + + return $this; + } + + /** + * @return $this + */ + public function savePosition(): self + { + $this->output->write("\x1b7"); + + return $this; + } + + /** + * @return $this + */ + public function restorePosition(): self + { + $this->output->write("\x1b8"); + + return $this; + } + + /** + * @return $this + */ + public function hide(): self + { + $this->output->write("\x1b[?25l"); + + return $this; + } + + /** + * @return $this + */ + public function show(): self + { + $this->output->write("\x1b[?25h\x1b[?0c"); + + return $this; + } + + /** + * Clears all the output from the current line. + * + * @return $this + */ + public function clearLine(): self + { + $this->output->write("\x1b[2K"); + + return $this; + } + + /** + * Clears all the output from the current line after the current position. + */ + public function clearLineAfter(): self + { + $this->output->write("\x1b[K"); + + return $this; + } + + /** + * Clears all the output from the cursors' current position to the end of the screen. + * + * @return $this + */ + public function clearOutput(): self + { + $this->output->write("\x1b[0J"); + + return $this; + } + + /** + * Clears the entire screen. + * + * @return $this + */ + public function clearScreen(): self + { + $this->output->write("\x1b[2J"); + + return $this; + } + + /** + * Returns the current cursor position as x,y coordinates. + */ + public function getCurrentPosition(): array + { + static $isTtySupported; + + if (null === $isTtySupported && \function_exists('proc_open')) { + $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes); + } + + if (!$isTtySupported) { + return [1, 1]; + } + + $sttyMode = shell_exec('stty -g'); + shell_exec('stty -icanon -echo'); + + @fwrite($this->input, "\033[6n"); + + $code = trim(fread($this->input, 1024)); + + shell_exec(sprintf('stty %s', $sttyMode)); + + sscanf($code, "\033[%d;%dR", $row, $col); + + return [$col, $row]; + } +} diff --git a/tests/integration/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php b/tests/integration/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php new file mode 100644 index 000000000..743e306d0 --- /dev/null +++ b/tests/integration/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\DependencyInjection; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\LazyCommand; +use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Registers console commands. + * + * @author Grégoire Pineau + */ +class AddConsoleCommandPass implements CompilerPassInterface +{ + private $commandLoaderServiceId; + private $commandTag; + private $noPreloadTag; + private $privateTagName; + + public function __construct(string $commandLoaderServiceId = 'console.command_loader', string $commandTag = 'console.command', string $noPreloadTag = 'container.no_preload', string $privateTagName = 'container.private') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/console', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->commandLoaderServiceId = $commandLoaderServiceId; + $this->commandTag = $commandTag; + $this->noPreloadTag = $noPreloadTag; + $this->privateTagName = $privateTagName; + } + + public function process(ContainerBuilder $container) + { + $commandServices = $container->findTaggedServiceIds($this->commandTag, true); + $lazyCommandMap = []; + $lazyCommandRefs = []; + $serviceIds = []; + + foreach ($commandServices as $id => $tags) { + $definition = $container->getDefinition($id); + $definition->addTag($this->noPreloadTag); + $class = $container->getParameterBag()->resolveValue($definition->getClass()); + + if (isset($tags[0]['command'])) { + $aliases = $tags[0]['command']; + } else { + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(Command::class)) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class)); + } + $aliases = $class::getDefaultName(); + } + + $aliases = explode('|', $aliases ?? ''); + $commandName = array_shift($aliases); + + if ($isHidden = '' === $commandName) { + $commandName = array_shift($aliases); + } + + if (null === $commandName) { + if (!$definition->isPublic() || $definition->isPrivate() || $definition->hasTag($this->privateTagName)) { + $commandId = 'console.command.public_alias.'.$id; + $container->setAlias($commandId, $id)->setPublic(true); + $id = $commandId; + } + $serviceIds[] = $id; + + continue; + } + + $description = $tags[0]['description'] ?? null; + + unset($tags[0]); + $lazyCommandMap[$commandName] = $id; + $lazyCommandRefs[$id] = new TypedReference($id, $class); + + foreach ($aliases as $alias) { + $lazyCommandMap[$alias] = $id; + } + + foreach ($tags as $tag) { + if (isset($tag['command'])) { + $aliases[] = $tag['command']; + $lazyCommandMap[$tag['command']] = $id; + } + + $description = $description ?? $tag['description'] ?? null; + } + + $definition->addMethodCall('setName', [$commandName]); + + if ($aliases) { + $definition->addMethodCall('setAliases', [$aliases]); + } + + if ($isHidden) { + $definition->addMethodCall('setHidden', [true]); + } + + if (!$description) { + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(Command::class)) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class)); + } + $description = $class::getDefaultDescription(); + } + + if ($description) { + $definition->addMethodCall('setDescription', [$description]); + + $container->register('.'.$id.'.lazy', LazyCommand::class) + ->setArguments([$commandName, $aliases, $description, $isHidden, new ServiceClosureArgument($lazyCommandRefs[$id])]); + + $lazyCommandRefs[$id] = new Reference('.'.$id.'.lazy'); + } + } + + $container + ->register($this->commandLoaderServiceId, ContainerCommandLoader::class) + ->setPublic(true) + ->addTag($this->noPreloadTag) + ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]); + + $container->setParameter('console.command.ids', $serviceIds); + } +} diff --git a/tests/integration/vendor/symfony/console/Descriptor/ApplicationDescription.php b/tests/integration/vendor/symfony/console/Descriptor/ApplicationDescription.php index 4c4a267fe..fac01ad37 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/ApplicationDescription.php +++ b/tests/integration/vendor/symfony/console/Descriptor/ApplicationDescription.php @@ -22,17 +22,11 @@ */ class ApplicationDescription { - const GLOBAL_NAMESPACE = '_global'; + public const GLOBAL_NAMESPACE = '_global'; - /** - * @var Application - */ private $application; - - /** - * @var null|string - */ private $namespace; + private $showHidden; /** * @var array @@ -40,31 +34,23 @@ class ApplicationDescription private $namespaces; /** - * @var Command[] + * @var array */ private $commands; /** - * @var Command[] + * @var array */ private $aliases; - /** - * Constructor. - * - * @param Application $application - * @param string|null $namespace - */ - public function __construct(Application $application, $namespace = null) + public function __construct(Application $application, string $namespace = null, bool $showHidden = false) { $this->application = $application; $this->namespace = $namespace; + $this->showHidden = $showHidden; } - /** - * @return array - */ - public function getNamespaces() + public function getNamespaces(): array { if (null === $this->namespaces) { $this->inspectApplication(); @@ -76,7 +62,7 @@ public function getNamespaces() /** * @return Command[] */ - public function getCommands() + public function getCommands(): array { if (null === $this->commands) { $this->inspectApplication(); @@ -86,33 +72,29 @@ public function getCommands() } /** - * @param string $name - * - * @return Command - * * @throws CommandNotFoundException */ - public function getCommand($name) + public function getCommand(string $name): Command { if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { - throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); } - return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + return $this->commands[$name] ?? $this->aliases[$name]; } private function inspectApplication() { - $this->commands = array(); - $this->namespaces = array(); + $this->commands = []; + $this->namespaces = []; $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); foreach ($this->sortCommands($all) as $namespace => $commands) { - $names = array(); + $names = []; /** @var Command $command */ foreach ($commands as $name => $command) { - if (!$command->getName() || $command->isHidden()) { + if (!$command->getName() || (!$this->showHidden && $command->isHidden())) { continue; } @@ -125,36 +107,37 @@ private function inspectApplication() $names[] = $name; } - $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names); + $this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names]; } } - /** - * @param array $commands - * - * @return array - */ - private function sortCommands(array $commands) + private function sortCommands(array $commands): array { - $namespacedCommands = array(); - $globalCommands = array(); + $namespacedCommands = []; + $globalCommands = []; + $sortedCommands = []; foreach ($commands as $name => $command) { $key = $this->application->extractNamespace($name, 1); - if (!$key) { - $globalCommands['_global'][$name] = $command; + if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) { + $globalCommands[$name] = $command; } else { $namespacedCommands[$key][$name] = $command; } } - ksort($namespacedCommands); - $namespacedCommands = array_merge($globalCommands, $namespacedCommands); - foreach ($namespacedCommands as &$commandsSet) { - ksort($commandsSet); + if ($globalCommands) { + ksort($globalCommands); + $sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands; + } + + if ($namespacedCommands) { + ksort($namespacedCommands); + foreach ($namespacedCommands as $key => $commandsSet) { + ksort($commandsSet); + $sortedCommands[$key] = $commandsSet; + } } - // unset reference to keep scope clear - unset($commandsSet); - return $namespacedCommands; + return $sortedCommands; } } diff --git a/tests/integration/vendor/symfony/console/Descriptor/Descriptor.php b/tests/integration/vendor/symfony/console/Descriptor/Descriptor.php index 50dd86ce2..a3648301f 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/Descriptor.php +++ b/tests/integration/vendor/symfony/console/Descriptor/Descriptor.php @@ -13,11 +13,11 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Exception\InvalidArgumentException; /** * @author Jean-François Simon @@ -34,7 +34,7 @@ abstract class Descriptor implements DescriptorInterface /** * {@inheritdoc} */ - public function describe(OutputInterface $output, $object, array $options = array()) + public function describe(OutputInterface $output, object $object, array $options = []) { $this->output = $output; @@ -55,68 +55,40 @@ public function describe(OutputInterface $output, $object, array $options = arra $this->describeApplication($object, $options); break; default: - throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))); } } /** * Writes content to output. - * - * @param string $content - * @param bool $decorated */ - protected function write($content, $decorated = false) + protected function write(string $content, bool $decorated = false) { $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); } /** * Describes an InputArgument instance. - * - * @param InputArgument $argument - * @param array $options - * - * @return string|mixed */ - abstract protected function describeInputArgument(InputArgument $argument, array $options = array()); + abstract protected function describeInputArgument(InputArgument $argument, array $options = []); /** * Describes an InputOption instance. - * - * @param InputOption $option - * @param array $options - * - * @return string|mixed */ - abstract protected function describeInputOption(InputOption $option, array $options = array()); + abstract protected function describeInputOption(InputOption $option, array $options = []); /** * Describes an InputDefinition instance. - * - * @param InputDefinition $definition - * @param array $options - * - * @return string|mixed */ - abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array()); + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []); /** * Describes a Command instance. - * - * @param Command $command - * @param array $options - * - * @return string|mixed */ - abstract protected function describeCommand(Command $command, array $options = array()); + abstract protected function describeCommand(Command $command, array $options = []); /** * Describes an Application instance. - * - * @param Application $application - * @param array $options - * - * @return string|mixed */ - abstract protected function describeApplication(Application $application, array $options = array()); + abstract protected function describeApplication(Application $application, array $options = []); } diff --git a/tests/integration/vendor/symfony/console/Descriptor/DescriptorInterface.php b/tests/integration/vendor/symfony/console/Descriptor/DescriptorInterface.php index 3929b6d9e..ebea30367 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/DescriptorInterface.php +++ b/tests/integration/vendor/symfony/console/Descriptor/DescriptorInterface.php @@ -20,12 +20,5 @@ */ interface DescriptorInterface { - /** - * Describes an InputArgument instance. - * - * @param OutputInterface $output - * @param object $object - * @param array $options - */ - public function describe(OutputInterface $output, $object, array $options = array()); + public function describe(OutputInterface $output, object $object, array $options = []); } diff --git a/tests/integration/vendor/symfony/console/Descriptor/JsonDescriptor.php b/tests/integration/vendor/symfony/console/Descriptor/JsonDescriptor.php index 87e38fdb8..1d2865941 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/JsonDescriptor.php +++ b/tests/integration/vendor/symfony/console/Descriptor/JsonDescriptor.php @@ -29,7 +29,7 @@ class JsonDescriptor extends Descriptor /** * {@inheritdoc} */ - protected function describeInputArgument(InputArgument $argument, array $options = array()) + protected function describeInputArgument(InputArgument $argument, array $options = []) { $this->writeData($this->getInputArgumentData($argument), $options); } @@ -37,15 +37,18 @@ protected function describeInputArgument(InputArgument $argument, array $options /** * {@inheritdoc} */ - protected function describeInputOption(InputOption $option, array $options = array()) + protected function describeInputOption(InputOption $option, array $options = []) { $this->writeData($this->getInputOptionData($option), $options); + if ($option->isNegatable()) { + $this->writeData($this->getInputOptionData($option, true), $options); + } } /** * {@inheritdoc} */ - protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + protected function describeInputDefinition(InputDefinition $definition, array $options = []) { $this->writeData($this->getInputDefinitionData($definition), $options); } @@ -53,114 +56,126 @@ protected function describeInputDefinition(InputDefinition $definition, array $o /** * {@inheritdoc} */ - protected function describeCommand(Command $command, array $options = array()) + protected function describeCommand(Command $command, array $options = []) { - $this->writeData($this->getCommandData($command), $options); + $this->writeData($this->getCommandData($command, $options['short'] ?? false), $options); } /** * {@inheritdoc} */ - protected function describeApplication(Application $application, array $options = array()) + protected function describeApplication(Application $application, array $options = []) { - $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; - $description = new ApplicationDescription($application, $describedNamespace); - $commands = array(); + $describedNamespace = $options['namespace'] ?? null; + $description = new ApplicationDescription($application, $describedNamespace, true); + $commands = []; foreach ($description->getCommands() as $command) { - $commands[] = $this->getCommandData($command); + $commands[] = $this->getCommandData($command, $options['short'] ?? false); + } + + $data = []; + if ('UNKNOWN' !== $application->getName()) { + $data['application']['name'] = $application->getName(); + if ('UNKNOWN' !== $application->getVersion()) { + $data['application']['version'] = $application->getVersion(); + } } - $data = $describedNamespace - ? array('commands' => $commands, 'namespace' => $describedNamespace) - : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + $data['commands'] = $commands; + + if ($describedNamespace) { + $data['namespace'] = $describedNamespace; + } else { + $data['namespaces'] = array_values($description->getNamespaces()); + } $this->writeData($data, $options); } /** * Writes data as json. - * - * @param array $data - * @param array $options - * - * @return array|string */ private function writeData(array $data, array $options) { - $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0)); + $flags = $options['json_encoding'] ?? 0; + + $this->write(json_encode($data, $flags)); } - /** - * @param InputArgument $argument - * - * @return array - */ - private function getInputArgumentData(InputArgument $argument) + private function getInputArgumentData(InputArgument $argument): array { - return array( + return [ 'name' => $argument->getName(), 'is_required' => $argument->isRequired(), 'is_array' => $argument->isArray(), 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), - 'default' => $argument->getDefault(), - ); + 'default' => \INF === $argument->getDefault() ? 'INF' : $argument->getDefault(), + ]; } - /** - * @param InputOption $option - * - * @return array - */ - private function getInputOptionData(InputOption $option) + private function getInputOptionData(InputOption $option, bool $negated = false): array { - return array( + return $negated ? [ + 'name' => '--no-'.$option->getName(), + 'shortcut' => '', + 'accept_value' => false, + 'is_value_required' => false, + 'is_multiple' => false, + 'description' => 'Negate the "--'.$option->getName().'" option', + 'default' => false, + ] : [ 'name' => '--'.$option->getName(), - 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '', + 'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '', 'accept_value' => $option->acceptValue(), 'is_value_required' => $option->isValueRequired(), 'is_multiple' => $option->isArray(), 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), - 'default' => $option->getDefault(), - ); + 'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(), + ]; } - /** - * @param InputDefinition $definition - * - * @return array - */ - private function getInputDefinitionData(InputDefinition $definition) + private function getInputDefinitionData(InputDefinition $definition): array { - $inputArguments = array(); + $inputArguments = []; foreach ($definition->getArguments() as $name => $argument) { $inputArguments[$name] = $this->getInputArgumentData($argument); } - $inputOptions = array(); + $inputOptions = []; foreach ($definition->getOptions() as $name => $option) { $inputOptions[$name] = $this->getInputOptionData($option); + if ($option->isNegatable()) { + $inputOptions['no-'.$name] = $this->getInputOptionData($option, true); + } } - return array('arguments' => $inputArguments, 'options' => $inputOptions); + return ['arguments' => $inputArguments, 'options' => $inputOptions]; } - /** - * @param Command $command - * - * @return array - */ - private function getCommandData(Command $command) + private function getCommandData(Command $command, bool $short = false): array { - $command->getSynopsis(); - $command->mergeApplicationDefinition(false); - - return array( + $data = [ 'name' => $command->getName(), - 'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()), 'description' => $command->getDescription(), - 'help' => $command->getProcessedHelp(), - 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), - ); + ]; + + if ($short) { + $data += [ + 'usage' => $command->getAliases(), + ]; + } else { + $command->mergeApplicationDefinition(false); + + $data += [ + 'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getDefinition()), + ]; + } + + $data['hidden'] = $command->isHidden(); + + return $data; } } diff --git a/tests/integration/vendor/symfony/console/Descriptor/MarkdownDescriptor.php b/tests/integration/vendor/symfony/console/Descriptor/MarkdownDescriptor.php index 2eb9944d6..21ceca6c2 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/MarkdownDescriptor.php +++ b/tests/integration/vendor/symfony/console/Descriptor/MarkdownDescriptor.php @@ -13,9 +13,11 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; /** * Markdown descriptor. @@ -29,14 +31,34 @@ class MarkdownDescriptor extends Descriptor /** * {@inheritdoc} */ - protected function describeInputArgument(InputArgument $argument, array $options = array()) + public function describe(OutputInterface $output, object $object, array $options = []) + { + $decorated = $output->isDecorated(); + $output->setDecorated(false); + + parent::describe($output, $object, $options); + + $output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + protected function write(string $content, bool $decorated = true) + { + parent::write($content, $decorated); + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) { $this->write( - '**'.$argument->getName().':**'."\n\n" - .'* Name: '.($argument->getName() ?: '')."\n" + '#### `'.($argument->getName() ?: '')."`\n\n" + .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '') .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" - .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $argument->getDescription() ?: '')."\n" .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' ); } @@ -44,16 +66,23 @@ protected function describeInputArgument(InputArgument $argument, array $options /** * {@inheritdoc} */ - protected function describeInputOption(InputOption $option, array $options = array()) + protected function describeInputOption(InputOption $option, array $options = []) { + $name = '--'.$option->getName(); + if ($option->isNegatable()) { + $name .= '|--no-'.$option->getName(); + } + if ($option->getShortcut()) { + $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).''; + } + $this->write( - '**'.$option->getName().':**'."\n\n" - .'* Name: `--'.$option->getName().'`'."\n" - .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '')."\n" + '#### `'.$name.'`'."\n\n" + .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '') .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" - .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $option->getDescription() ?: '')."\n" + .'* Is negatable: '.($option->isNegatable() ? 'yes' : 'no')."\n" .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' ); } @@ -61,25 +90,29 @@ protected function describeInputOption(InputOption $option, array $options = arr /** * {@inheritdoc} */ - protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + protected function describeInputDefinition(InputDefinition $definition, array $options = []) { - if ($showArguments = count($definition->getArguments()) > 0) { - $this->write('### Arguments:'); + if ($showArguments = \count($definition->getArguments()) > 0) { + $this->write('### Arguments'); foreach ($definition->getArguments() as $argument) { $this->write("\n\n"); - $this->write($this->describeInputArgument($argument)); + if (null !== $describeInputArgument = $this->describeInputArgument($argument)) { + $this->write($describeInputArgument); + } } } - if (count($definition->getOptions()) > 0) { + if (\count($definition->getOptions()) > 0) { if ($showArguments) { $this->write("\n\n"); } - $this->write('### Options:'); + $this->write('### Options'); foreach ($definition->getOptions() as $option) { $this->write("\n\n"); - $this->write($this->describeInputOption($option)); + if (null !== $describeInputOption = $this->describeInputOption($option)) { + $this->write($describeInputOption); + } } } } @@ -87,18 +120,31 @@ protected function describeInputDefinition(InputDefinition $definition, array $o /** * {@inheritdoc} */ - protected function describeCommand(Command $command, array $options = array()) + protected function describeCommand(Command $command, array $options = []) { - $command->getSynopsis(); + if ($options['short'] ?? false) { + $this->write( + '`'.$command->getName()."`\n" + .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + .'### Usage'."\n\n" + .array_reduce($command->getAliases(), function ($carry, $usage) { + return $carry.'* `'.$usage.'`'."\n"; + }) + ); + + return; + } + $command->mergeApplicationDefinition(false); $this->write( - $command->getName()."\n" - .str_repeat('-', strlen($command->getName()))."\n\n" - .'* Description: '.($command->getDescription() ?: '')."\n" - .'* Usage:'."\n\n" - .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) { - return $carry.' * `'.$usage.'`'."\n"; + '`'.$command->getName()."`\n" + .str_repeat('-', Helper::width($command->getName()) + 2)."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + .'### Usage'."\n\n" + .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry.'* `'.$usage.'`'."\n"; }) ); @@ -107,21 +153,23 @@ protected function describeCommand(Command $command, array $options = array()) $this->write($help); } - if ($command->getNativeDefinition()) { + $definition = $command->getDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { $this->write("\n\n"); - $this->describeInputDefinition($command->getNativeDefinition()); + $this->describeInputDefinition($definition); } } /** * {@inheritdoc} */ - protected function describeApplication(Application $application, array $options = array()) + protected function describeApplication(Application $application, array $options = []) { - $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $describedNamespace = $options['namespace'] ?? null; $description = new ApplicationDescription($application, $describedNamespace); + $title = $this->getApplicationTitle($application); - $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + $this->write($title."\n".str_repeat('=', Helper::width($title))); foreach ($description->getNamespaces() as $namespace) { if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { @@ -130,14 +178,29 @@ protected function describeApplication(Application $application, array $options } $this->write("\n\n"); - $this->write(implode("\n", array_map(function ($commandName) { - return '* '.$commandName; + $this->write(implode("\n", array_map(function ($commandName) use ($description) { + return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())); }, $namespace['commands']))); } foreach ($description->getCommands() as $command) { $this->write("\n\n"); - $this->write($this->describeCommand($command)); + if (null !== $describeCommand = $this->describeCommand($command, $options)) { + $this->write($describeCommand); + } } } + + private function getApplicationTitle(Application $application): string + { + if ('UNKNOWN' !== $application->getName()) { + if ('UNKNOWN' !== $application->getVersion()) { + return sprintf('%s %s', $application->getName(), $application->getVersion()); + } + + return $application->getName(); + } + + return 'Console Tool'; + } } diff --git a/tests/integration/vendor/symfony/console/Descriptor/TextDescriptor.php b/tests/integration/vendor/symfony/console/Descriptor/TextDescriptor.php index fe4fa502c..fbb140ae7 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/TextDescriptor.php +++ b/tests/integration/vendor/symfony/console/Descriptor/TextDescriptor.php @@ -13,6 +13,8 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputOption; @@ -29,16 +31,16 @@ class TextDescriptor extends Descriptor /** * {@inheritdoc} */ - protected function describeInputArgument(InputArgument $argument, array $options = array()) + protected function describeInputArgument(InputArgument $argument, array $options = []) { - if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) { $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); } else { $default = ''; } - $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); - $spacingWidth = $totalWidth - strlen($argument->getName()); + $totalWidth = $options['total_width'] ?? Helper::width($argument->getName()); + $spacingWidth = $totalWidth - \strlen($argument->getName()); $this->writeText(sprintf(' %s %s%s%s', $argument->getName(), @@ -52,9 +54,9 @@ protected function describeInputArgument(InputArgument $argument, array $options /** * {@inheritdoc} */ - protected function describeInputOption(InputOption $option, array $options = array()) + protected function describeInputOption(InputOption $option, array $options = []) { - if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) { $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); } else { $default = ''; @@ -69,13 +71,13 @@ protected function describeInputOption(InputOption $option, array $options = arr } } - $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $totalWidth = $options['total_width'] ?? $this->calculateTotalWidthForOptions([$option]); $synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', - sprintf('--%s%s', $option->getName(), $value) + sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value) ); - $spacingWidth = $totalWidth - strlen($synopsis); + $spacingWidth = $totalWidth - Helper::width($synopsis); $this->writeText(sprintf(' %s %s%s%s%s', $synopsis, @@ -90,18 +92,18 @@ protected function describeInputOption(InputOption $option, array $options = arr /** * {@inheritdoc} */ - protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + protected function describeInputDefinition(InputDefinition $definition, array $options = []) { $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); foreach ($definition->getArguments() as $argument) { - $totalWidth = max($totalWidth, strlen($argument->getName())); + $totalWidth = max($totalWidth, Helper::width($argument->getName())); } if ($definition->getArguments()) { $this->writeText('Arguments:', $options); $this->writeText("\n"); foreach ($definition->getArguments() as $argument) { - $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth))); + $this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth])); $this->writeText("\n"); } } @@ -111,20 +113,20 @@ protected function describeInputDefinition(InputDefinition $definition, array $o } if ($definition->getOptions()) { - $laterOptions = array(); + $laterOptions = []; $this->writeText('Options:', $options); foreach ($definition->getOptions() as $option) { - if (strlen($option->getShortcut()) > 1) { + if (\strlen($option->getShortcut() ?? '') > 1) { $laterOptions[] = $option; continue; } $this->writeText("\n"); - $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); } foreach ($laterOptions as $option) { $this->writeText("\n"); - $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); } } } @@ -132,27 +134,33 @@ protected function describeInputDefinition(InputDefinition $definition, array $o /** * {@inheritdoc} */ - protected function describeCommand(Command $command, array $options = array()) + protected function describeCommand(Command $command, array $options = []) { - $command->getSynopsis(true); - $command->getSynopsis(false); $command->mergeApplicationDefinition(false); + if ($description = $command->getDescription()) { + $this->writeText('Description:', $options); + $this->writeText("\n"); + $this->writeText(' '.$description); + $this->writeText("\n\n"); + } + $this->writeText('Usage:', $options); - foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) { + foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) { $this->writeText("\n"); - $this->writeText(' '.$usage, $options); + $this->writeText(' '.OutputFormatter::escape($usage), $options); } $this->writeText("\n"); - $definition = $command->getNativeDefinition(); + $definition = $command->getDefinition(); if ($definition->getOptions() || $definition->getArguments()) { $this->writeText("\n"); $this->describeInputDefinition($definition, $options); $this->writeText("\n"); } - if ($help = $command->getProcessedHelp()) { + $help = $command->getProcessedHelp(); + if ($help && $help !== $description) { $this->writeText("\n"); $this->writeText('Help:', $options); $this->writeText("\n"); @@ -164,9 +172,9 @@ protected function describeCommand(Command $command, array $options = array()) /** * {@inheritdoc} */ - protected function describeApplication(Application $application, array $options = array()) + protected function describeApplication(Application $application, array $options = []) { - $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $describedNamespace = $options['namespace'] ?? null; $description = new ApplicationDescription($application, $describedNamespace); if (isset($options['raw_text']) && $options['raw_text']) { @@ -189,7 +197,20 @@ protected function describeApplication(Application $application, array $options $this->writeText("\n"); $this->writeText("\n"); - $width = $this->getColumnWidth($description->getCommands()); + $commands = $description->getCommands(); + $namespaces = $description->getNamespaces(); + if ($describedNamespace && $namespaces) { + // make sure all alias commands are included when describing a specific namespace + $describedNamespaceInfo = reset($namespaces); + foreach ($describedNamespaceInfo['commands'] as $name) { + $commands[$name] = $description->getCommand($name); + } + } + + // calculate max. width based on available commands per namespace + $width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) { + return array_intersect($namespace['commands'], array_keys($commands)); + }, array_values($namespaces))))); if ($describedNamespace) { $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); @@ -197,23 +218,26 @@ protected function describeApplication(Application $application, array $options $this->writeText('Available commands:', $options); } - // add commands by namespace - $commands = $description->getCommands(); + foreach ($namespaces as $namespace) { + $namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) { + return isset($commands[$name]); + }); + + if (!$namespace['commands']) { + continue; + } - foreach ($description->getNamespaces() as $namespace) { if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { $this->writeText("\n"); $this->writeText(' '.$namespace['id'].'', $options); } foreach ($namespace['commands'] as $name) { - if (isset($commands[$name])) { - $this->writeText("\n"); - $spacingWidth = $width - strlen($name); - $command = $commands[$name]; - $commandAliases = $this->getCommandAliasesText($command); - $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); - } + $this->writeText("\n"); + $spacingWidth = $width - Helper::width($name); + $command = $commands[$name]; + $commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : ''; + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); } } @@ -224,7 +248,7 @@ protected function describeApplication(Application $application, array $options /** * {@inheritdoc} */ - private function writeText($content, array $options = array()) + private function writeText(string $content, array $options = []) { $this->write( isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, @@ -234,12 +258,8 @@ private function writeText($content, array $options = array()) /** * Formats command aliases to show them in the command description. - * - * @param Command $command - * - * @return string */ - private function getCommandAliasesText($command) + private function getCommandAliasesText(Command $command): string { $text = ''; $aliases = $command->getAliases(); @@ -255,47 +275,60 @@ private function getCommandAliasesText($command) * Formats input option/argument default value. * * @param mixed $default - * - * @return string */ - private function formatDefaultValue($default) + private function formatDefaultValue($default): string { - return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + if (\INF === $default) { + return 'INF'; + } + + if (\is_string($default)) { + $default = OutputFormatter::escape($default); + } elseif (\is_array($default)) { + foreach ($default as $key => $value) { + if (\is_string($value)) { + $default[$key] = OutputFormatter::escape($value); + } + } + } + + return str_replace('\\\\', '\\', json_encode($default, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE)); } /** - * @param Command[] $commands - * - * @return int + * @param array $commands */ - private function getColumnWidth(array $commands) + private function getColumnWidth(array $commands): int { - $widths = array(); + $widths = []; foreach ($commands as $command) { - $widths[] = strlen($command->getName()); - foreach ($command->getAliases() as $alias) { - $widths[] = strlen($alias); + if ($command instanceof Command) { + $widths[] = Helper::width($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = Helper::width($alias); + } + } else { + $widths[] = Helper::width($command); } } - return max($widths) + 2; + return $widths ? max($widths) + 2 : 0; } /** * @param InputOption[] $options - * - * @return int */ - private function calculateTotalWidthForOptions($options) + private function calculateTotalWidthForOptions(array $options): int { $totalWidth = 0; foreach ($options as $option) { // "-" + shortcut + ", --" + name - $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName()); - - if ($option->acceptValue()) { - $valueLength = 1 + strlen($option->getName()); // = + value + $nameLength = 1 + max(Helper::width($option->getShortcut()), 1) + 4 + Helper::width($option->getName()); + if ($option->isNegatable()) { + $nameLength += 6 + Helper::width($option->getName()); // |--no- + name + } elseif ($option->acceptValue()) { + $valueLength = 1 + Helper::width($option->getName()); // = + value $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] $nameLength += $valueLength; diff --git a/tests/integration/vendor/symfony/console/Descriptor/XmlDescriptor.php b/tests/integration/vendor/symfony/console/Descriptor/XmlDescriptor.php index b5676beb3..4f7cd8b3e 100644 --- a/tests/integration/vendor/symfony/console/Descriptor/XmlDescriptor.php +++ b/tests/integration/vendor/symfony/console/Descriptor/XmlDescriptor.php @@ -26,12 +26,7 @@ */ class XmlDescriptor extends Descriptor { - /** - * @param InputDefinition $definition - * - * @return \DOMDocument - */ - public function getInputDefinitionDocument(InputDefinition $definition) + public function getInputDefinitionDocument(InputDefinition $definition): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($definitionXML = $dom->createElement('definition')); @@ -49,68 +44,63 @@ public function getInputDefinitionDocument(InputDefinition $definition) return $dom; } - /** - * @param Command $command - * - * @return \DOMDocument - */ - public function getCommandDocument(Command $command) + public function getCommandDocument(Command $command, bool $short = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($commandXML = $dom->createElement('command')); - $command->getSynopsis(); - $command->mergeApplicationDefinition(false); - $commandXML->setAttribute('id', $command->getName()); $commandXML->setAttribute('name', $command->getName()); + $commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0); $commandXML->appendChild($usagesXML = $dom->createElement('usages')); - foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) { - $usagesXML->appendChild($dom->createElement('usage', $usage)); - } - $commandXML->appendChild($descriptionXML = $dom->createElement('description')); $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); - $commandXML->appendChild($helpXML = $dom->createElement('help')); - $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + if ($short) { + foreach ($command->getAliases() as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + } else { + $command->mergeApplicationDefinition(false); + + foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } - $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); - $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + } return $dom; } - /** - * @param Application $application - * @param string|null $namespace - * - * @return \DOMDocument - */ - public function getApplicationDocument(Application $application, $namespace = null) + public function getApplicationDocument(Application $application, string $namespace = null, bool $short = false): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($rootXml = $dom->createElement('symfony')); - if ($application->getName() !== 'UNKNOWN') { + if ('UNKNOWN' !== $application->getName()) { $rootXml->setAttribute('name', $application->getName()); - if ($application->getVersion() !== 'UNKNOWN') { + if ('UNKNOWN' !== $application->getVersion()) { $rootXml->setAttribute('version', $application->getVersion()); } } $rootXml->appendChild($commandsXML = $dom->createElement('commands')); - $description = new ApplicationDescription($application, $namespace); + $description = new ApplicationDescription($application, $namespace, true); if ($namespace) { $commandsXML->setAttribute('namespace', $namespace); } foreach ($description->getCommands() as $command) { - $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + $this->appendDocument($commandsXML, $this->getCommandDocument($command, $short)); } if (!$namespace) { @@ -133,7 +123,7 @@ public function getApplicationDocument(Application $application, $namespace = nu /** * {@inheritdoc} */ - protected function describeInputArgument(InputArgument $argument, array $options = array()) + protected function describeInputArgument(InputArgument $argument, array $options = []) { $this->writeDocument($this->getInputArgumentDocument($argument)); } @@ -141,7 +131,7 @@ protected function describeInputArgument(InputArgument $argument, array $options /** * {@inheritdoc} */ - protected function describeInputOption(InputOption $option, array $options = array()) + protected function describeInputOption(InputOption $option, array $options = []) { $this->writeDocument($this->getInputOptionDocument($option)); } @@ -149,7 +139,7 @@ protected function describeInputOption(InputOption $option, array $options = arr /** * {@inheritdoc} */ - protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + protected function describeInputDefinition(InputDefinition $definition, array $options = []) { $this->writeDocument($this->getInputDefinitionDocument($definition)); } @@ -157,24 +147,21 @@ protected function describeInputDefinition(InputDefinition $definition, array $o /** * {@inheritdoc} */ - protected function describeCommand(Command $command, array $options = array()) + protected function describeCommand(Command $command, array $options = []) { - $this->writeDocument($this->getCommandDocument($command)); + $this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false)); } /** * {@inheritdoc} */ - protected function describeApplication(Application $application, array $options = array()) + protected function describeApplication(Application $application, array $options = []) { - $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + $this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false)); } /** * Appends document children to parent node. - * - * @param \DOMNode $parentNode - * @param \DOMNode $importedParent */ private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) { @@ -185,10 +172,6 @@ private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) /** * Writes DOM document. - * - * @param \DOMDocument $dom - * - * @return \DOMDocument|string */ private function writeDocument(\DOMDocument $dom) { @@ -196,12 +179,7 @@ private function writeDocument(\DOMDocument $dom) $this->write($dom->saveXML()); } - /** - * @param InputArgument $argument - * - * @return \DOMDocument - */ - private function getInputArgumentDocument(InputArgument $argument) + private function getInputArgumentDocument(InputArgument $argument): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); @@ -213,7 +191,7 @@ private function getInputArgumentDocument(InputArgument $argument) $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); - $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + $defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : [])); foreach ($defaults as $default) { $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); $defaultXML->appendChild($dom->createTextNode($default)); @@ -222,21 +200,16 @@ private function getInputArgumentDocument(InputArgument $argument) return $dom; } - /** - * @param InputOption $option - * - * @return \DOMDocument - */ - private function getInputOptionDocument(InputOption $option) + private function getInputOptionDocument(InputOption $option): \DOMDocument { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->appendChild($objectXML = $dom->createElement('option')); $objectXML->setAttribute('name', '--'.$option->getName()); - $pos = strpos($option->getShortcut(), '|'); + $pos = strpos($option->getShortcut() ?? '', '|'); if (false !== $pos) { $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); - $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut()))); + $objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut())); } else { $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); } @@ -247,7 +220,7 @@ private function getInputOptionDocument(InputOption $option) $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); if ($option->acceptValue()) { - $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + $defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : [])); $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); if (!empty($defaults)) { @@ -258,6 +231,17 @@ private function getInputOptionDocument(InputOption $option) } } + if ($option->isNegatable()) { + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--no-'.$option->getName()); + $objectXML->setAttribute('shortcut', ''); + $objectXML->setAttribute('accept_value', 0); + $objectXML->setAttribute('is_value_required', 0); + $objectXML->setAttribute('is_multiple', 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode('Negate the "--'.$option->getName().'" option')); + } + return $dom; } } diff --git a/tests/integration/vendor/symfony/console/Event/ConsoleCommandEvent.php b/tests/integration/vendor/symfony/console/Event/ConsoleCommandEvent.php index 92adf1ef9..08bd18fd1 100644 --- a/tests/integration/vendor/symfony/console/Event/ConsoleCommandEvent.php +++ b/tests/integration/vendor/symfony/console/Event/ConsoleCommandEvent.php @@ -16,46 +16,35 @@ * * @author Fabien Potencier */ -class ConsoleCommandEvent extends ConsoleEvent +final class ConsoleCommandEvent extends ConsoleEvent { /** * The return code for skipped commands, this will also be passed into the terminate event. */ - const RETURN_CODE_DISABLED = 113; + public const RETURN_CODE_DISABLED = 113; /** * Indicates if the command should be run or skipped. - * - * @var bool */ private $commandShouldRun = true; /** * Disables the command, so it won't be run. - * - * @return bool */ - public function disableCommand() + public function disableCommand(): bool { return $this->commandShouldRun = false; } - /** - * Enables the command. - * - * @return bool - */ - public function enableCommand() + public function enableCommand(): bool { return $this->commandShouldRun = true; } /** * Returns true if the command is runnable, false otherwise. - * - * @return bool */ - public function commandShouldRun() + public function commandShouldRun(): bool { return $this->commandShouldRun; } diff --git a/tests/integration/vendor/symfony/console/Event/ConsoleErrorEvent.php b/tests/integration/vendor/symfony/console/Event/ConsoleErrorEvent.php new file mode 100644 index 000000000..57d9b38ba --- /dev/null +++ b/tests/integration/vendor/symfony/console/Event/ConsoleErrorEvent.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle throwables thrown while running a command. + * + * @author Wouter de Jong + */ +final class ConsoleErrorEvent extends ConsoleEvent +{ + private $error; + private $exitCode; + + public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, Command $command = null) + { + parent::__construct($command, $input, $output); + + $this->error = $error; + } + + public function getError(): \Throwable + { + return $this->error; + } + + public function setError(\Throwable $error): void + { + $this->error = $error; + } + + public function setExitCode(int $exitCode): void + { + $this->exitCode = $exitCode; + + $r = new \ReflectionProperty($this->error, 'code'); + $r->setAccessible(true); + $r->setValue($this->error, $this->exitCode); + } + + public function getExitCode(): int + { + return $this->exitCode ?? (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1); + } +} diff --git a/tests/integration/vendor/symfony/console/Event/ConsoleEvent.php b/tests/integration/vendor/symfony/console/Event/ConsoleEvent.php index ab620c460..be7937d51 100644 --- a/tests/integration/vendor/symfony/console/Event/ConsoleEvent.php +++ b/tests/integration/vendor/symfony/console/Event/ConsoleEvent.php @@ -14,7 +14,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * Allows to inspect input and output of a command. @@ -28,7 +28,7 @@ class ConsoleEvent extends Event private $input; private $output; - public function __construct(Command $command, InputInterface $input, OutputInterface $output) + public function __construct(?Command $command, InputInterface $input, OutputInterface $output) { $this->command = $command; $this->input = $input; @@ -38,7 +38,7 @@ public function __construct(Command $command, InputInterface $input, OutputInter /** * Gets the command that is executed. * - * @return Command A Command instance + * @return Command|null */ public function getCommand() { @@ -48,7 +48,7 @@ public function getCommand() /** * Gets the input instance. * - * @return InputInterface An InputInterface instance + * @return InputInterface */ public function getInput() { @@ -58,7 +58,7 @@ public function getInput() /** * Gets the output instance. * - * @return OutputInterface An OutputInterface instance + * @return OutputInterface */ public function getOutput() { diff --git a/tests/integration/vendor/symfony/console/Event/ConsoleExceptionEvent.php b/tests/integration/vendor/symfony/console/Event/ConsoleExceptionEvent.php deleted file mode 100644 index 603b7eed7..000000000 --- a/tests/integration/vendor/symfony/console/Event/ConsoleExceptionEvent.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Event; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Allows to handle exception thrown in a command. - * - * @author Fabien Potencier - */ -class ConsoleExceptionEvent extends ConsoleEvent -{ - private $exception; - private $exitCode; - - public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) - { - parent::__construct($command, $input, $output); - - $this->setException($exception); - $this->exitCode = (int) $exitCode; - } - - /** - * Returns the thrown exception. - * - * @return \Exception The thrown exception - */ - public function getException() - { - return $this->exception; - } - - /** - * Replaces the thrown exception. - * - * This exception will be thrown if no response is set in the event. - * - * @param \Exception $exception The thrown exception - */ - public function setException(\Exception $exception) - { - $this->exception = $exception; - } - - /** - * Gets the exit code. - * - * @return int The command exit code - */ - public function getExitCode() - { - return $this->exitCode; - } -} diff --git a/tests/integration/vendor/symfony/console/Event/ConsoleSignalEvent.php b/tests/integration/vendor/symfony/console/Event/ConsoleSignalEvent.php new file mode 100644 index 000000000..ef13ed2f5 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Event/ConsoleSignalEvent.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author marie + */ +final class ConsoleSignalEvent extends ConsoleEvent +{ + private $handlingSignal; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal) + { + parent::__construct($command, $input, $output); + $this->handlingSignal = $handlingSignal; + } + + public function getHandlingSignal(): int + { + return $this->handlingSignal; + } +} diff --git a/tests/integration/vendor/symfony/console/Event/ConsoleTerminateEvent.php b/tests/integration/vendor/symfony/console/Event/ConsoleTerminateEvent.php index b6a5d7c0d..190038d1a 100644 --- a/tests/integration/vendor/symfony/console/Event/ConsoleTerminateEvent.php +++ b/tests/integration/vendor/symfony/console/Event/ConsoleTerminateEvent.php @@ -20,38 +20,23 @@ * * @author Francesco Levorato */ -class ConsoleTerminateEvent extends ConsoleEvent +final class ConsoleTerminateEvent extends ConsoleEvent { - /** - * The exit code of the command. - * - * @var int - */ private $exitCode; - public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $exitCode) { parent::__construct($command, $input, $output); $this->setExitCode($exitCode); } - /** - * Sets the exit code. - * - * @param int $exitCode The command exit code - */ - public function setExitCode($exitCode) + public function setExitCode(int $exitCode): void { - $this->exitCode = (int) $exitCode; + $this->exitCode = $exitCode; } - /** - * Gets the exit code. - * - * @return int The command exit code - */ - public function getExitCode() + public function getExitCode(): int { return $this->exitCode; } diff --git a/tests/integration/vendor/symfony/console/EventListener/ErrorListener.php b/tests/integration/vendor/symfony/console/EventListener/ErrorListener.php new file mode 100644 index 000000000..897d9853f --- /dev/null +++ b/tests/integration/vendor/symfony/console/EventListener/ErrorListener.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author James Halsall + * @author Robin Chalas + */ +class ErrorListener implements EventSubscriberInterface +{ + private $logger; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + public function onConsoleError(ConsoleErrorEvent $event) + { + if (null === $this->logger) { + return; + } + + $error = $event->getError(); + + if (!$inputString = $this->getInputString($event)) { + $this->logger->critical('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]); + + return; + } + + $this->logger->critical('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]); + } + + public function onConsoleTerminate(ConsoleTerminateEvent $event) + { + if (null === $this->logger) { + return; + } + + $exitCode = $event->getExitCode(); + + if (0 === $exitCode) { + return; + } + + if (!$inputString = $this->getInputString($event)) { + $this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]); + + return; + } + + $this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]); + } + + public static function getSubscribedEvents() + { + return [ + ConsoleEvents::ERROR => ['onConsoleError', -128], + ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128], + ]; + } + + private static function getInputString(ConsoleEvent $event): ?string + { + $commandName = $event->getCommand() ? $event->getCommand()->getName() : null; + $input = $event->getInput(); + + if (method_exists($input, '__toString')) { + if ($commandName) { + return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input); + } + + return (string) $input; + } + + return $commandName; + } +} diff --git a/tests/integration/vendor/symfony/console/Exception/CommandNotFoundException.php b/tests/integration/vendor/symfony/console/Exception/CommandNotFoundException.php index 54f1a5b0c..910ae1928 100644 --- a/tests/integration/vendor/symfony/console/Exception/CommandNotFoundException.php +++ b/tests/integration/vendor/symfony/console/Exception/CommandNotFoundException.php @@ -21,12 +21,12 @@ class CommandNotFoundException extends \InvalidArgumentException implements Exce private $alternatives; /** - * @param string $message Exception message to throw - * @param array $alternatives List of similar defined names - * @param int $code Exception code - * @param Exception $previous previous exception used for the exception chaining + * @param string $message Exception message to throw + * @param string[] $alternatives List of similar defined names + * @param int $code Exception code + * @param \Throwable|null $previous Previous exception used for the exception chaining */ - public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null) { parent::__construct($message, $code, $previous); @@ -34,7 +34,7 @@ public function __construct($message, array $alternatives = array(), $code = 0, } /** - * @return array A list of similar defined names + * @return string[] */ public function getAlternatives() { diff --git a/tests/integration/vendor/symfony/console/Exception/ExceptionInterface.php b/tests/integration/vendor/symfony/console/Exception/ExceptionInterface.php index 491cc4c64..1624e13d0 100644 --- a/tests/integration/vendor/symfony/console/Exception/ExceptionInterface.php +++ b/tests/integration/vendor/symfony/console/Exception/ExceptionInterface.php @@ -16,6 +16,6 @@ * * @author Jérôme Tamarelle */ -interface ExceptionInterface +interface ExceptionInterface extends \Throwable { } diff --git a/tests/integration/vendor/symfony/console/Exception/MissingInputException.php b/tests/integration/vendor/symfony/console/Exception/MissingInputException.php new file mode 100644 index 000000000..04f02ade4 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Exception/MissingInputException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents failure to read input from stdin. + * + * @author Gabriel Ostrolucký + */ +class MissingInputException extends RuntimeException implements ExceptionInterface +{ +} diff --git a/tests/integration/vendor/symfony/console/Exception/NamespaceNotFoundException.php b/tests/integration/vendor/symfony/console/Exception/NamespaceNotFoundException.php new file mode 100644 index 000000000..dd16e4508 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Exception/NamespaceNotFoundException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect namespace typed in the console. + * + * @author Pierre du Plessis + */ +class NamespaceNotFoundException extends CommandNotFoundException +{ +} diff --git a/tests/integration/vendor/symfony/console/Formatter/NullOutputFormatter.php b/tests/integration/vendor/symfony/console/Formatter/NullOutputFormatter.php new file mode 100644 index 000000000..d770e1465 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Formatter/NullOutputFormatter.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * @author Tien Xuan Vo + */ +final class NullOutputFormatter implements OutputFormatterInterface +{ + private $style; + + /** + * {@inheritdoc} + */ + public function format(?string $message): ?string + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getStyle(string $name): OutputFormatterStyleInterface + { + // to comply with the interface we must return a OutputFormatterStyleInterface + return $this->style ?? $this->style = new NullOutputFormatterStyle(); + } + + /** + * {@inheritdoc} + */ + public function hasStyle(string $name): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDecorated(): bool + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setDecorated(bool $decorated): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setStyle(string $name, OutputFormatterStyleInterface $style): void + { + // do nothing + } +} diff --git a/tests/integration/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php b/tests/integration/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php new file mode 100644 index 000000000..bfd0afedd --- /dev/null +++ b/tests/integration/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * @author Tien Xuan Vo + */ +final class NullOutputFormatterStyle implements OutputFormatterStyleInterface +{ + /** + * {@inheritdoc} + */ + public function apply(string $text): string + { + return $text; + } + + /** + * {@inheritdoc} + */ + public function setBackground(string $color = null): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setForeground(string $color = null): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setOption(string $option): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function setOptions(array $options): void + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function unsetOption(string $option): void + { + // do nothing + } +} diff --git a/tests/integration/vendor/symfony/console/Formatter/OutputFormatter.php b/tests/integration/vendor/symfony/console/Formatter/OutputFormatter.php index b1a763d02..603e5dca0 100644 --- a/tests/integration/vendor/symfony/console/Formatter/OutputFormatter.php +++ b/tests/integration/vendor/symfony/console/Formatter/OutputFormatter.php @@ -17,28 +17,46 @@ * Formatter class for console output. * * @author Konstantin Kudryashov + * @author Roland Franssen */ -class OutputFormatter implements OutputFormatterInterface +class OutputFormatter implements WrappableOutputFormatterInterface { private $decorated; - private $styles = array(); + private $styles = []; private $styleStack; + public function __clone() + { + $this->styleStack = clone $this->styleStack; + foreach ($this->styles as $key => $value) { + $this->styles[$key] = clone $value; + } + } + /** - * Escapes "<" special char in given text. + * Escapes "<" and ">" special chars in given text. * - * @param string $text Text to escape - * - * @return string Escaped text + * @return string */ - public static function escape($text) + public static function escape(string $text) { - $text = preg_replace('/([^\\\\]?)])/', '$1\\\\$2', $text); + + return self::escapeTrailingBackslash($text); + } - if ('\\' === substr($text, -1)) { - $len = strlen($text); + /** + * Escapes trailing "\" in given text. + * + * @internal + */ + public static function escapeTrailingBackslash(string $text): string + { + if (str_ends_with($text, '\\')) { + $len = \strlen($text); $text = rtrim($text, '\\'); - $text .= str_repeat('<<', $len - strlen($text)); + $text = str_replace("\0", '', $text); + $text .= str_repeat("\0", $len - \strlen($text)); } return $text; @@ -47,12 +65,11 @@ public static function escape($text) /** * Initializes console output formatter. * - * @param bool $decorated Whether this formatter should actually decorate strings - * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances + * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances */ - public function __construct($decorated = false, array $styles = array()) + public function __construct(bool $decorated = false, array $styles = []) { - $this->decorated = (bool) $decorated; + $this->decorated = $decorated; $this->setStyle('error', new OutputFormatterStyle('white', 'red')); $this->setStyle('info', new OutputFormatterStyle('green')); @@ -67,19 +84,15 @@ public function __construct($decorated = false, array $styles = array()) } /** - * Sets the decorated flag. - * - * @param bool $decorated Whether to decorate the messages or not + * {@inheritdoc} */ - public function setDecorated($decorated) + public function setDecorated(bool $decorated) { - $this->decorated = (bool) $decorated; + $this->decorated = $decorated; } /** - * Gets the decorated flag. - * - * @return bool true if the output will decorate messages, false otherwise + * {@inheritdoc} */ public function isDecorated() { @@ -87,60 +100,56 @@ public function isDecorated() } /** - * Sets a new style. - * - * @param string $name The style name - * @param OutputFormatterStyleInterface $style The style instance + * {@inheritdoc} */ - public function setStyle($name, OutputFormatterStyleInterface $style) + public function setStyle(string $name, OutputFormatterStyleInterface $style) { $this->styles[strtolower($name)] = $style; } /** - * Checks if output formatter has style with specified name. - * - * @param string $name - * - * @return bool + * {@inheritdoc} */ - public function hasStyle($name) + public function hasStyle(string $name) { return isset($this->styles[strtolower($name)]); } /** - * Gets style options from style with specified name. - * - * @param string $name - * - * @return OutputFormatterStyleInterface - * - * @throws InvalidArgumentException When style isn't defined + * {@inheritdoc} */ - public function getStyle($name) + public function getStyle(string $name) { if (!$this->hasStyle($name)) { - throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name)); } return $this->styles[strtolower($name)]; } /** - * Formats a message according to the given styles. - * - * @param string $message The message to style - * - * @return string The styled message + * {@inheritdoc} */ - public function format($message) + public function format(?string $message) { - $message = (string) $message; + return $this->formatAndWrap($message, 0); + } + + /** + * {@inheritdoc} + */ + public function formatAndWrap(?string $message, int $width) + { + if (null === $message) { + return ''; + } + $offset = 0; $output = ''; - $tagRegex = '[a-z][a-z0-9,_=;-]*+'; - preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + $openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*'; + $closeTagRegex = '[a-z][^<>]*+'; + $currentLineLength = 0; + preg_match_all("#<(($openTagRegex) | /($closeTagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE); foreach ($matches[0] as $i => $match) { $pos = $match[1]; $text = $match[0]; @@ -150,21 +159,21 @@ public function format($message) } // add the text up to the next tag - $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); - $offset = $pos + strlen($text); + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength); + $offset = $pos + \strlen($text); // opening tag? if ($open = '/' != $text[1]) { $tag = $matches[1][$i][0]; } else { - $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + $tag = $matches[3][$i][0] ?? ''; } if (!$open && !$tag) { // $this->styleStack->pop(); - } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { - $output .= $this->applyCurrentStyle($text); + } elseif (null === $style = $this->createStyleFromString($tag)) { + $output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength); } elseif ($open) { $this->styleStack->push($style); } else { @@ -172,13 +181,9 @@ public function format($message) } } - $output .= $this->applyCurrentStyle(substr($message, $offset)); - - if (false !== strpos($output, '<<')) { - return strtr($output, array('\\<' => '<', '<<' => '\\')); - } + $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength); - return str_replace('\\<', '<', $output); + return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']); } /** @@ -191,43 +196,37 @@ public function getStyleStack() /** * Tries to create new style instance from string. - * - * @param string $string - * - * @return OutputFormatterStyle|bool false if string is not format string */ - private function createStyleFromString($string) + private function createStyleFromString(string $string): ?OutputFormatterStyleInterface { if (isset($this->styles[$string])) { return $this->styles[$string]; } - if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, PREG_SET_ORDER)) { - return false; + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) { + return null; } $style = new OutputFormatterStyle(); foreach ($matches as $match) { array_shift($match); + $match[0] = strtolower($match[0]); if ('fg' == $match[0]) { - $style->setForeground($match[1]); + $style->setForeground(strtolower($match[1])); } elseif ('bg' == $match[0]) { - $style->setBackground($match[1]); + $style->setBackground(strtolower($match[1])); + } elseif ('href' === $match[0]) { + $url = preg_replace('{\\\\([<>])}', '$1', $match[1]); + $style->setHref($url); } elseif ('options' === $match[0]) { - preg_match_all('([^,;]+)', $match[1], $options); + preg_match_all('([^,;]+)', strtolower($match[1]), $options); $options = array_shift($options); foreach ($options as $option) { - try { - $style->setOption($option); - } catch (\InvalidArgumentException $e) { - @trigger_error(sprintf('Unknown style options are deprecated since version 3.2 and will be removed in 4.0. Exception "%s".', $e->getMessage()), E_USER_DEPRECATED); - - return false; - } + $style->setOption($option); } } else { - return false; + return null; } } @@ -236,13 +235,51 @@ private function createStyleFromString($string) /** * Applies current style from stack to text, if must be applied. - * - * @param string $text Input text - * - * @return string Styled text */ - private function applyCurrentStyle($text) + private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string { - return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + if ('' === $text) { + return ''; + } + + if (!$width) { + return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text; + } + + if (!$currentLineLength && '' !== $current) { + $text = ltrim($text); + } + + if ($currentLineLength) { + $prefix = substr($text, 0, $i = $width - $currentLineLength)."\n"; + $text = substr($text, $i); + } else { + $prefix = ''; + } + + preg_match('~(\\n)$~', $text, $matches); + $text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text); + $text = rtrim($text, "\n").($matches[1] ?? ''); + + if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) { + $text = "\n".$text; + } + + $lines = explode("\n", $text); + + foreach ($lines as $line) { + $currentLineLength += \strlen($line); + if ($width <= $currentLineLength) { + $currentLineLength = 0; + } + } + + if ($this->isDecorated()) { + foreach ($lines as $i => $line) { + $lines[$i] = $this->styleStack->getCurrent()->apply($line); + } + } + + return implode("\n", $lines); } } diff --git a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterInterface.php b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterInterface.php index 5a52ba096..0b5f839a2 100644 --- a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterInterface.php +++ b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -20,50 +20,41 @@ interface OutputFormatterInterface { /** * Sets the decorated flag. - * - * @param bool $decorated Whether to decorate the messages or not */ - public function setDecorated($decorated); + public function setDecorated(bool $decorated); /** - * Gets the decorated flag. + * Whether the output will decorate messages. * - * @return bool true if the output will decorate messages, false otherwise + * @return bool */ public function isDecorated(); /** * Sets a new style. - * - * @param string $name The style name - * @param OutputFormatterStyleInterface $style The style instance */ - public function setStyle($name, OutputFormatterStyleInterface $style); + public function setStyle(string $name, OutputFormatterStyleInterface $style); /** * Checks if output formatter has style with specified name. * - * @param string $name - * * @return bool */ - public function hasStyle($name); + public function hasStyle(string $name); /** * Gets style options from style with specified name. * - * @param string $name - * * @return OutputFormatterStyleInterface + * + * @throws \InvalidArgumentException When style isn't defined */ - public function getStyle($name); + public function getStyle(string $name); /** * Formats a message according to the given styles. * - * @param string $message The message to style - * - * @return string The styled message + * @return string|null */ - public function format($message); + public function format(?string $message); } diff --git a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyle.php index c7c6b4a01..0fb36ac63 100644 --- a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyle.php +++ b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Console\Formatter; -use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Color; /** * Formatter style class for defining styles. @@ -20,202 +20,89 @@ */ class OutputFormatterStyle implements OutputFormatterStyleInterface { - private static $availableForegroundColors = array( - 'black' => array('set' => 30, 'unset' => 39), - 'red' => array('set' => 31, 'unset' => 39), - 'green' => array('set' => 32, 'unset' => 39), - 'yellow' => array('set' => 33, 'unset' => 39), - 'blue' => array('set' => 34, 'unset' => 39), - 'magenta' => array('set' => 35, 'unset' => 39), - 'cyan' => array('set' => 36, 'unset' => 39), - 'white' => array('set' => 37, 'unset' => 39), - 'default' => array('set' => 39, 'unset' => 39), - ); - private static $availableBackgroundColors = array( - 'black' => array('set' => 40, 'unset' => 49), - 'red' => array('set' => 41, 'unset' => 49), - 'green' => array('set' => 42, 'unset' => 49), - 'yellow' => array('set' => 43, 'unset' => 49), - 'blue' => array('set' => 44, 'unset' => 49), - 'magenta' => array('set' => 45, 'unset' => 49), - 'cyan' => array('set' => 46, 'unset' => 49), - 'white' => array('set' => 47, 'unset' => 49), - 'default' => array('set' => 49, 'unset' => 49), - ); - private static $availableOptions = array( - 'bold' => array('set' => 1, 'unset' => 22), - 'underscore' => array('set' => 4, 'unset' => 24), - 'blink' => array('set' => 5, 'unset' => 25), - 'reverse' => array('set' => 7, 'unset' => 27), - 'conceal' => array('set' => 8, 'unset' => 28), - ); - + private $color; private $foreground; private $background; - private $options = array(); + private $options; + private $href; + private $handlesHrefGracefully; /** * Initializes output formatter style. * * @param string|null $foreground The style foreground color name * @param string|null $background The style background color name - * @param array $options The style options */ - public function __construct($foreground = null, $background = null, array $options = array()) + public function __construct(string $foreground = null, string $background = null, array $options = []) { - if (null !== $foreground) { - $this->setForeground($foreground); - } - if (null !== $background) { - $this->setBackground($background); - } - if (count($options)) { - $this->setOptions($options); - } + $this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options); } /** - * Sets style foreground color. - * - * @param string|null $color The color name - * - * @throws InvalidArgumentException When the color name isn't defined + * {@inheritdoc} */ - public function setForeground($color = null) + public function setForeground(string $color = null) { - if (null === $color) { - $this->foreground = null; - - return; - } - - if (!isset(static::$availableForegroundColors[$color])) { - throw new InvalidArgumentException(sprintf( - 'Invalid foreground color specified: "%s". Expected one of (%s)', - $color, - implode(', ', array_keys(static::$availableForegroundColors)) - )); - } - - $this->foreground = static::$availableForegroundColors[$color]; + $this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options); } /** - * Sets style background color. - * - * @param string|null $color The color name - * - * @throws InvalidArgumentException When the color name isn't defined + * {@inheritdoc} */ - public function setBackground($color = null) + public function setBackground(string $color = null) { - if (null === $color) { - $this->background = null; - - return; - } - - if (!isset(static::$availableBackgroundColors[$color])) { - throw new InvalidArgumentException(sprintf( - 'Invalid background color specified: "%s". Expected one of (%s)', - $color, - implode(', ', array_keys(static::$availableBackgroundColors)) - )); - } + $this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options); + } - $this->background = static::$availableBackgroundColors[$color]; + public function setHref(string $url): void + { + $this->href = $url; } /** - * Sets some specific style option. - * - * @param string $option The option name - * - * @throws InvalidArgumentException When the option name isn't defined + * {@inheritdoc} */ - public function setOption($option) + public function setOption(string $option) { - if (!isset(static::$availableOptions[$option])) { - throw new InvalidArgumentException(sprintf( - 'Invalid option specified: "%s". Expected one of (%s)', - $option, - implode(', ', array_keys(static::$availableOptions)) - )); - } - - if (!in_array(static::$availableOptions[$option], $this->options)) { - $this->options[] = static::$availableOptions[$option]; - } + $this->options[] = $option; + $this->color = new Color($this->foreground, $this->background, $this->options); } /** - * Unsets some specific style option. - * - * @param string $option The option name - * - * @throws InvalidArgumentException When the option name isn't defined + * {@inheritdoc} */ - public function unsetOption($option) + public function unsetOption(string $option) { - if (!isset(static::$availableOptions[$option])) { - throw new InvalidArgumentException(sprintf( - 'Invalid option specified: "%s". Expected one of (%s)', - $option, - implode(', ', array_keys(static::$availableOptions)) - )); - } - - $pos = array_search(static::$availableOptions[$option], $this->options); + $pos = array_search($option, $this->options); if (false !== $pos) { unset($this->options[$pos]); } + + $this->color = new Color($this->foreground, $this->background, $this->options); } /** - * Sets multiple style options at once. - * - * @param array $options + * {@inheritdoc} */ public function setOptions(array $options) { - $this->options = array(); - - foreach ($options as $option) { - $this->setOption($option); - } + $this->color = new Color($this->foreground, $this->background, $this->options = $options); } /** - * Applies the style to a given text. - * - * @param string $text The text to style - * - * @return string + * {@inheritdoc} */ - public function apply($text) + public function apply(string $text) { - $setCodes = array(); - $unsetCodes = array(); - - if (null !== $this->foreground) { - $setCodes[] = $this->foreground['set']; - $unsetCodes[] = $this->foreground['unset']; - } - if (null !== $this->background) { - $setCodes[] = $this->background['set']; - $unsetCodes[] = $this->background['unset']; - } - if (count($this->options)) { - foreach ($this->options as $option) { - $setCodes[] = $option['set']; - $unsetCodes[] = $option['unset']; - } + if (null === $this->handlesHrefGracefully) { + $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); } - if (0 === count($setCodes)) { - return $text; + if (null !== $this->href && $this->handlesHrefGracefully) { + $text = "\033]8;;$this->href\033\\$text\033]8;;\033\\"; } - return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + return $this->color->apply($text); } } diff --git a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php index c36fda807..b30560d22 100644 --- a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php +++ b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -20,45 +20,33 @@ interface OutputFormatterStyleInterface { /** * Sets style foreground color. - * - * @param string $color The color name */ - public function setForeground($color = null); + public function setForeground(string $color = null); /** * Sets style background color. - * - * @param string $color The color name */ - public function setBackground($color = null); + public function setBackground(string $color = null); /** * Sets some specific style option. - * - * @param string $option The option name */ - public function setOption($option); + public function setOption(string $option); /** * Unsets some specific style option. - * - * @param string $option The option name */ - public function unsetOption($option); + public function unsetOption(string $option); /** * Sets multiple style options at once. - * - * @param array $options */ public function setOptions(array $options); /** * Applies the style to a given text. * - * @param string $text The text to style - * * @return string */ - public function apply($text); + public function apply(string $text); } diff --git a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php index e5d14ea3f..fc48dc0e1 100644 --- a/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php +++ b/tests/integration/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -12,30 +12,23 @@ namespace Symfony\Component\Console\Formatter; use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Contracts\Service\ResetInterface; /** * @author Jean-François Simon */ -class OutputFormatterStyleStack +class OutputFormatterStyleStack implements ResetInterface { /** * @var OutputFormatterStyleInterface[] */ private $styles; - /** - * @var OutputFormatterStyleInterface - */ private $emptyStyle; - /** - * Constructor. - * - * @param OutputFormatterStyleInterface|null $emptyStyle - */ public function __construct(OutputFormatterStyleInterface $emptyStyle = null) { - $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->emptyStyle = $emptyStyle ?? new OutputFormatterStyle(); $this->reset(); } @@ -44,13 +37,11 @@ public function __construct(OutputFormatterStyleInterface $emptyStyle = null) */ public function reset() { - $this->styles = array(); + $this->styles = []; } /** * Pushes a style in the stack. - * - * @param OutputFormatterStyleInterface $style */ public function push(OutputFormatterStyleInterface $style) { @@ -60,8 +51,6 @@ public function push(OutputFormatterStyleInterface $style) /** * Pops a style from the stack. * - * @param OutputFormatterStyleInterface|null $style - * * @return OutputFormatterStyleInterface * * @throws InvalidArgumentException When style tags incorrectly nested @@ -78,7 +67,7 @@ public function pop(OutputFormatterStyleInterface $style = null) foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { if ($style->apply('') === $stackedStyle->apply('')) { - $this->styles = array_slice($this->styles, 0, $index); + $this->styles = \array_slice($this->styles, 0, $index); return $stackedStyle; } @@ -98,13 +87,11 @@ public function getCurrent() return $this->emptyStyle; } - return $this->styles[count($this->styles) - 1]; + return $this->styles[\count($this->styles) - 1]; } /** - * @param OutputFormatterStyleInterface $emptyStyle - * - * @return OutputFormatterStyleStack + * @return $this */ public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) { diff --git a/tests/integration/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php b/tests/integration/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php new file mode 100644 index 000000000..42319ee55 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output that supports word wrapping. + * + * @author Roland Franssen + */ +interface WrappableOutputFormatterInterface extends OutputFormatterInterface +{ + /** + * Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping). + */ + public function formatAndWrap(?string $message, int $width); +} diff --git a/tests/integration/vendor/symfony/console/Helper/DebugFormatterHelper.php b/tests/integration/vendor/symfony/console/Helper/DebugFormatterHelper.php index 1119b795c..e258ba050 100644 --- a/tests/integration/vendor/symfony/console/Helper/DebugFormatterHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/DebugFormatterHelper.php @@ -20,22 +20,18 @@ */ class DebugFormatterHelper extends Helper { - private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'); - private $started = array(); + private const COLORS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default']; + private $started = []; private $count = -1; /** * Starts a debug formatting session. * - * @param string $id The id of the formatting session - * @param string $message The message to display - * @param string $prefix The prefix to use - * * @return string */ - public function start($id, $message, $prefix = 'RUN') + public function start(string $id, string $message, string $prefix = 'RUN') { - $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + $this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)]; return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); } @@ -43,15 +39,9 @@ public function start($id, $message, $prefix = 'RUN') /** * Adds progress to a formatting session. * - * @param string $id The id of the formatting session - * @param string $buffer The message to display - * @param bool $error Whether to consider the buffer as error - * @param string $prefix The prefix for output - * @param string $errorPrefix The prefix for error output - * * @return string */ - public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + public function progress(string $id, string $buffer, bool $error = false, string $prefix = 'OUT', string $errorPrefix = 'ERR') { $message = ''; @@ -85,14 +75,9 @@ public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPr /** * Stops a formatting session. * - * @param string $id The id of the formatting session - * @param string $message The message to display - * @param bool $successful Whether to consider the result as success - * @param string $prefix The prefix for the end output - * * @return string */ - public function stop($id, $message, $successful, $prefix = 'RES') + public function stop(string $id, string $message, bool $successful, string $prefix = 'RES') { $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; @@ -107,14 +92,9 @@ public function stop($id, $message, $successful, $prefix = 'RES') return $message; } - /** - * @param string $id The id of the formatting session - * - * @return string - */ - private function getBorder($id) + private function getBorder(string $id): string { - return sprintf(' ', $this->colors[$this->started[$id]['border']]); + return sprintf(' ', self::COLORS[$this->started[$id]['border']]); } /** diff --git a/tests/integration/vendor/symfony/console/Helper/DescriptorHelper.php b/tests/integration/vendor/symfony/console/Helper/DescriptorHelper.php index a53b476b1..af85e9c0a 100644 --- a/tests/integration/vendor/symfony/console/Helper/DescriptorHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/DescriptorHelper.php @@ -16,8 +16,8 @@ use Symfony\Component\Console\Descriptor\MarkdownDescriptor; use Symfony\Component\Console\Descriptor\TextDescriptor; use Symfony\Component\Console\Descriptor\XmlDescriptor; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Output\OutputInterface; /** * This class adds helper method to describe objects in various formats. @@ -29,11 +29,8 @@ class DescriptorHelper extends Helper /** * @var DescriptorInterface[] */ - private $descriptors = array(); + private $descriptors = []; - /** - * Constructor. - */ public function __construct() { $this @@ -51,18 +48,14 @@ public function __construct() * * format: string, the output format name * * raw_text: boolean, sets output type as raw * - * @param OutputInterface $output - * @param object $object - * @param array $options - * * @throws InvalidArgumentException when the given format is not supported */ - public function describe(OutputInterface $output, $object, array $options = array()) + public function describe(OutputInterface $output, ?object $object, array $options = []) { - $options = array_merge(array( + $options = array_merge([ 'raw_text' => false, 'format' => 'txt', - ), $options); + ], $options); if (!isset($this->descriptors[$options['format']])) { throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); @@ -75,12 +68,9 @@ public function describe(OutputInterface $output, $object, array $options = arra /** * Registers a descriptor. * - * @param string $format - * @param DescriptorInterface $descriptor - * - * @return DescriptorHelper + * @return $this */ - public function register($format, DescriptorInterface $descriptor) + public function register(string $format, DescriptorInterface $descriptor) { $this->descriptors[$format] = $descriptor; @@ -94,4 +84,9 @@ public function getName() { return 'descriptor'; } + + public function getFormats(): array + { + return array_keys($this->descriptors); + } } diff --git a/tests/integration/vendor/symfony/console/Helper/Dumper.php b/tests/integration/vendor/symfony/console/Helper/Dumper.php new file mode 100644 index 000000000..b013b6c52 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Helper/Dumper.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * @author Roland Franssen + */ +final class Dumper +{ + private $output; + private $dumper; + private $cloner; + private $handler; + + public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null) + { + $this->output = $output; + $this->dumper = $dumper; + $this->cloner = $cloner; + + if (class_exists(CliDumper::class)) { + $this->handler = function ($var): string { + $dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); + $dumper->setColors($this->output->isDecorated()); + + return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true)); + }; + } else { + $this->handler = function ($var): string { + switch (true) { + case null === $var: + return 'null'; + case true === $var: + return 'true'; + case false === $var: + return 'false'; + case \is_string($var): + return '"'.$var.'"'; + default: + return rtrim(print_r($var, true)); + } + }; + } + } + + public function __invoke($var): string + { + return ($this->handler)($var); + } +} diff --git a/tests/integration/vendor/symfony/console/Helper/FormatterHelper.php b/tests/integration/vendor/symfony/console/Helper/FormatterHelper.php index 6a48a77f2..92d8dc724 100644 --- a/tests/integration/vendor/symfony/console/Helper/FormatterHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/FormatterHelper.php @@ -23,13 +23,9 @@ class FormatterHelper extends Helper /** * Formats a message within a section. * - * @param string $section The section name - * @param string $message The message - * @param string $style The style to apply to the section - * - * @return string The format section + * @return string */ - public function formatSection($section, $message, $style = 'info') + public function formatSection(string $section, string $message, string $style = 'info') { return sprintf('<%s>[%s] %s', $style, $section, $style, $message); } @@ -38,28 +34,26 @@ public function formatSection($section, $message, $style = 'info') * Formats a message as a block of text. * * @param string|array $messages The message to write in the block - * @param string $style The style to apply to the whole block - * @param bool $large Whether to return a large block * - * @return string The formatter message + * @return string */ - public function formatBlock($messages, $style, $large = false) + public function formatBlock($messages, string $style, bool $large = false) { - if (!is_array($messages)) { - $messages = array($messages); + if (!\is_array($messages)) { + $messages = [$messages]; } $len = 0; - $lines = array(); + $lines = []; foreach ($messages as $message) { $message = OutputFormatter::escape($message); $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); - $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + $len = max(self::width($message) + ($large ? 4 : 2), $len); } - $messages = $large ? array(str_repeat(' ', $len)) : array(); + $messages = $large ? [str_repeat(' ', $len)] : []; for ($i = 0; isset($lines[$i]); ++$i) { - $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + $messages[] = $lines[$i].str_repeat(' ', $len - self::width($lines[$i])); } if ($large) { $messages[] = str_repeat(' ', $len); @@ -75,25 +69,17 @@ public function formatBlock($messages, $style, $large = false) /** * Truncates a message to the given length. * - * @param string $message - * @param int $length - * @param string $suffix - * * @return string */ - public function truncate($message, $length, $suffix = '...') + public function truncate(string $message, int $length, string $suffix = '...') { - $computedLength = $length - $this->strlen($suffix); + $computedLength = $length - self::width($suffix); - if ($computedLength > $this->strlen($message)) { + if ($computedLength > self::width($message)) { return $message; } - if (false === $encoding = mb_detect_encoding($message, null, true)) { - return substr($message, 0, $length).$suffix; - } - - return mb_substr($message, 0, $length, $encoding).$suffix; + return self::substr($message, 0, $length).$suffix; } /** diff --git a/tests/integration/vendor/symfony/console/Helper/Helper.php b/tests/integration/vendor/symfony/console/Helper/Helper.php index 43f9a1694..50f51c790 100644 --- a/tests/integration/vendor/symfony/console/Helper/Helper.php +++ b/tests/integration/vendor/symfony/console/Helper/Helper.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Console\Helper; use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\String\UnicodeString; /** * Helper is the base class for all helper classes. @@ -23,9 +24,7 @@ abstract class Helper implements HelperInterface protected $helperSet = null; /** - * Sets the helper set associated with this helper. - * - * @param HelperSet $helperSet A HelperSet instance + * {@inheritdoc} */ public function setHelperSet(HelperSet $helperSet = null) { @@ -33,9 +32,7 @@ public function setHelperSet(HelperSet $helperSet = null) } /** - * Gets the helper set associated with this helper. - * - * @return HelperSet A HelperSet instance + * {@inheritdoc} */ public function getHelperSet() { @@ -45,39 +42,91 @@ public function getHelperSet() /** * Returns the length of a string, using mb_strwidth if it is available. * - * @param string $string The string to check its length + * @deprecated since Symfony 5.3 * - * @return int The length of the string + * @return int + */ + public static function strlen(?string $string) + { + trigger_deprecation('symfony/console', '5.3', 'Method "%s()" is deprecated and will be removed in Symfony 6.0. Use Helper::width() or Helper::length() instead.', __METHOD__); + + return self::width($string); + } + + /** + * Returns the width of a string, using mb_strwidth if it is available. + * The width is how many characters positions the string will use. */ - public static function strlen($string) + public static function width(?string $string): int { + $string ?? $string = ''; + + if (preg_match('//u', $string)) { + return (new UnicodeString($string))->width(false); + } + if (false === $encoding = mb_detect_encoding($string, null, true)) { - return strlen($string); + return \strlen($string); } return mb_strwidth($string, $encoding); } + /** + * Returns the length of a string, using mb_strlen if it is available. + * The length is related to how many bytes the string will use. + */ + public static function length(?string $string): int + { + $string ?? $string = ''; + + if (preg_match('//u', $string)) { + return (new UnicodeString($string))->length(); + } + + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return \strlen($string); + } + + return mb_strlen($string, $encoding); + } + + /** + * Returns the subset of a string, using mb_substr if it is available. + * + * @return string + */ + public static function substr(?string $string, int $from, int $length = null) + { + $string ?? $string = ''; + + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return substr($string, $from, $length); + } + + return mb_substr($string, $from, $length, $encoding); + } + public static function formatTime($secs) { - static $timeFormats = array( - array(0, '< 1 sec'), - array(1, '1 sec'), - array(2, 'secs', 1), - array(60, '1 min'), - array(120, 'mins', 60), - array(3600, '1 hr'), - array(7200, 'hrs', 3600), - array(86400, '1 day'), - array(172800, 'days', 86400), - ); + static $timeFormats = [ + [0, '< 1 sec'], + [1, '1 sec'], + [2, 'secs', 1], + [60, '1 min'], + [120, 'mins', 60], + [3600, '1 hr'], + [7200, 'hrs', 3600], + [86400, '1 day'], + [172800, 'days', 86400], + ]; foreach ($timeFormats as $index => $format) { if ($secs >= $format[0]) { if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) - || $index == count($timeFormats) - 1 + || $index == \count($timeFormats) - 1 ) { - if (2 == count($format)) { + if (2 == \count($format)) { return $format[1]; } @@ -87,7 +136,7 @@ public static function formatTime($secs) } } - public static function formatMemory($memory) + public static function formatMemory(int $memory) { if ($memory >= 1024 * 1024 * 1024) { return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); @@ -104,16 +153,26 @@ public static function formatMemory($memory) return sprintf('%d B', $memory); } - public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + /** + * @deprecated since Symfony 5.3 + */ + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, ?string $string) + { + trigger_deprecation('symfony/console', '5.3', 'Method "%s()" is deprecated and will be removed in Symfony 6.0. Use Helper::removeDecoration() instead.', __METHOD__); + + return self::width(self::removeDecoration($formatter, $string)); + } + + public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string) { $isDecorated = $formatter->isDecorated(); $formatter->setDecorated(false); // remove <...> formatting - $string = $formatter->format($string); + $string = $formatter->format($string ?? ''); // remove already formatted characters - $string = preg_replace("/\033\[[^m]*m/", '', $string); + $string = preg_replace("/\033\[[^m]*m/", '', $string ?? ''); $formatter->setDecorated($isDecorated); - return self::strlen($string); + return $string; } } diff --git a/tests/integration/vendor/symfony/console/Helper/HelperInterface.php b/tests/integration/vendor/symfony/console/Helper/HelperInterface.php index 5a923e0a8..fc952b486 100644 --- a/tests/integration/vendor/symfony/console/Helper/HelperInterface.php +++ b/tests/integration/vendor/symfony/console/Helper/HelperInterface.php @@ -20,22 +20,20 @@ interface HelperInterface { /** * Sets the helper set associated with this helper. - * - * @param HelperSet $helperSet A HelperSet instance */ public function setHelperSet(HelperSet $helperSet = null); /** * Gets the helper set associated with this helper. * - * @return HelperSet A HelperSet instance + * @return HelperSet|null */ public function getHelperSet(); /** * Returns the canonical name of this helper. * - * @return string The canonical name + * @return string */ public function getName(); } diff --git a/tests/integration/vendor/symfony/console/Helper/HelperSet.php b/tests/integration/vendor/symfony/console/Helper/HelperSet.php index 6f12b39d9..719762d24 100644 --- a/tests/integration/vendor/symfony/console/Helper/HelperSet.php +++ b/tests/integration/vendor/symfony/console/Helper/HelperSet.php @@ -18,34 +18,26 @@ * HelperSet represents a set of helpers to be used with a command. * * @author Fabien Potencier + * + * @implements \IteratorAggregate */ class HelperSet implements \IteratorAggregate { - /** - * @var Helper[] - */ - private $helpers = array(); + /** @var array */ + private $helpers = []; private $command; /** - * Constructor. - * * @param Helper[] $helpers An array of helper */ - public function __construct(array $helpers = array()) + public function __construct(array $helpers = []) { foreach ($helpers as $alias => $helper) { - $this->set($helper, is_int($alias) ? null : $alias); + $this->set($helper, \is_int($alias) ? null : $alias); } } - /** - * Sets a helper. - * - * @param HelperInterface $helper The helper instance - * @param string $alias An alias - */ - public function set(HelperInterface $helper, $alias = null) + public function set(HelperInterface $helper, string $alias = null) { $this->helpers[$helper->getName()] = $helper; if (null !== $alias) { @@ -58,11 +50,9 @@ public function set(HelperInterface $helper, $alias = null) /** * Returns true if the helper if defined. * - * @param string $name The helper name - * - * @return bool true if the helper is defined, false otherwise + * @return bool */ - public function has($name) + public function has(string $name) { return isset($this->helpers[$name]); } @@ -70,13 +60,11 @@ public function has($name) /** * Gets a helper value. * - * @param string $name The helper name - * - * @return HelperInterface The helper instance + * @return HelperInterface * * @throws InvalidArgumentException if the helper is not defined */ - public function get($name) + public function get(string $name) { if (!$this->has($name)) { throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); @@ -86,28 +74,33 @@ public function get($name) } /** - * Sets the command associated with this helper set. - * - * @param Command $command A Command instance + * @deprecated since Symfony 5.4 */ public function setCommand(Command $command = null) { + trigger_deprecation('symfony/console', '5.4', 'Method "%s()" is deprecated.', __METHOD__); + $this->command = $command; } /** * Gets the command associated with this helper set. * - * @return Command A Command instance + * @return Command + * + * @deprecated since Symfony 5.4 */ public function getCommand() { + trigger_deprecation('symfony/console', '5.4', 'Method "%s()" is deprecated.', __METHOD__); + return $this->command; } /** - * @return Helper[] + * @return \Traversable */ + #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->helpers); diff --git a/tests/integration/vendor/symfony/console/Helper/InputAwareHelper.php b/tests/integration/vendor/symfony/console/Helper/InputAwareHelper.php index 426176742..0d0dba23e 100644 --- a/tests/integration/vendor/symfony/console/Helper/InputAwareHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/InputAwareHelper.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Console\Helper; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Input\InputInterface; /** * An implementation of InputAwareInterface for Helpers. diff --git a/tests/integration/vendor/symfony/console/Helper/ProcessHelper.php b/tests/integration/vendor/symfony/console/Helper/ProcessHelper.php index 2c46a2c39..4ea3d724d 100644 --- a/tests/integration/vendor/symfony/console/Helper/ProcessHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/ProcessHelper.php @@ -15,41 +15,51 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; -use Symfony\Component\Process\ProcessBuilder; /** * The ProcessHelper class provides helpers to run external processes. * * @author Fabien Potencier + * + * @final */ class ProcessHelper extends Helper { /** * Runs an external process. * - * @param OutputInterface $output An OutputInterface instance - * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run - * @param string|null $error An error message that must be displayed if something went wrong - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR - * @param int $verbosity The threshold for verbosity - * - * @return Process The process that ran + * @param array|Process $cmd An instance of Process or an array of the command and arguments + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR */ - public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process { + if (!class_exists(Process::class)) { + throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".'); + } + if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $formatter = $this->getHelperSet()->get('debug_formatter'); - if (is_array($cmd)) { - $process = ProcessBuilder::create($cmd)->getProcess(); - } elseif ($cmd instanceof Process) { - $process = $cmd; - } else { + if ($cmd instanceof Process) { + $cmd = [$cmd]; + } + + if (!\is_array($cmd)) { + throw new \TypeError(sprintf('The "command" argument of "%s()" must be an array or a "%s" instance, "%s" given.', __METHOD__, Process::class, get_debug_type($cmd))); + } + + if (\is_string($cmd[0] ?? null)) { $process = new Process($cmd); + $cmd = []; + } elseif (($cmd[0] ?? null) instanceof Process) { + $process = $cmd[0]; + unset($cmd[0]); + } else { + throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__)); } if ($verbosity <= $output->getVerbosity()) { @@ -60,7 +70,7 @@ public function run(OutputInterface $output, $cmd, $error = null, callable $call $callback = $this->wrapCallback($output, $process, $callback); } - $process->run($callback); + $process->run($callback, $cmd); if ($verbosity <= $output->getVerbosity()) { $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); @@ -80,19 +90,15 @@ public function run(OutputInterface $output, $cmd, $error = null, callable $call * This is identical to run() except that an exception is thrown if the process * exits with a non-zero exit code. * - * @param OutputInterface $output An OutputInterface instance - * @param string|Process $cmd An instance of Process or a command to run - * @param string|null $error An error message that must be displayed if something went wrong - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR - * - * @return Process The process that ran + * @param array|Process $cmd An instance of Process or a command to run + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR * * @throws ProcessFailedException * * @see run() */ - public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null) + public function mustRun(OutputInterface $output, $cmd, string $error = null, callable $callback = null): Process { $process = $this->run($output, $cmd, $error, $callback); @@ -105,14 +111,8 @@ public function mustRun(OutputInterface $output, $cmd, $error = null, callable $ /** * Wraps a Process callback to add debugging output. - * - * @param OutputInterface $output An OutputInterface interface - * @param Process $process The Process - * @param callable|null $callback A PHP callable - * - * @return callable */ - public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null) + public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null): callable { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); @@ -124,12 +124,12 @@ public function wrapCallback(OutputInterface $output, Process $process, callable $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); if (null !== $callback) { - call_user_func($callback, $type, $buffer); + $callback($type, $buffer); } }; } - private function escapeString($str) + private function escapeString(string $str): string { return str_replace('<', '\\<', $str); } @@ -137,7 +137,7 @@ private function escapeString($str) /** * {@inheritdoc} */ - public function getName() + public function getName(): string { return 'process'; } diff --git a/tests/integration/vendor/symfony/console/Helper/ProgressBar.php b/tests/integration/vendor/symfony/console/Helper/ProgressBar.php index 8212bedbf..b1fb213b6 100644 --- a/tests/integration/vendor/symfony/console/Helper/ProgressBar.php +++ b/tests/integration/vendor/symfony/console/Helper/ProgressBar.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Console\Helper; +use Symfony\Component\Console\Cursor; +use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Terminal; /** @@ -22,9 +24,18 @@ * @author Fabien Potencier * @author Chris Jones */ -class ProgressBar +final class ProgressBar { - // options + public const FORMAT_VERBOSE = 'verbose'; + public const FORMAT_VERY_VERBOSE = 'very_verbose'; + public const FORMAT_DEBUG = 'debug'; + public const FORMAT_NORMAL = 'normal'; + + private const FORMAT_VERBOSE_NOMAX = 'verbose_nomax'; + private const FORMAT_VERY_VERBOSE_NOMAX = 'very_verbose_nomax'; + private const FORMAT_DEBUG_NOMAX = 'debug_nomax'; + private const FORMAT_NORMAL_NOMAX = 'normal_nomax'; + private $barWidth = 28; private $barChar; private $emptyBarChar = '-'; @@ -32,10 +43,10 @@ class ProgressBar private $format; private $internalFormat; private $redrawFreq = 1; - - /** - * @var OutputInterface - */ + private $writeCount; + private $lastWriteTime; + private $minSecondsBetweenRedraws = 0; + private $maxSecondsBetweenRedraws = 1; private $output; private $step = 0; private $max; @@ -43,19 +54,19 @@ class ProgressBar private $stepWidth; private $percent = 0.0; private $formatLineCount; - private $messages = array(); + private $messages = []; private $overwrite = true; private $terminal; - private $firstRun = true; + private $previousMessage; + private $cursor; private static $formatters; private static $formats; /** - * @param OutputInterface $output An OutputInterface instance - * @param int $max Maximum steps (0 if unknown) + * @param int $max Maximum steps (0 if unknown) */ - public function __construct(OutputInterface $output, $max = 0) + public function __construct(OutputInterface $output, int $max = 0, float $minSecondsBetweenRedraws = 1 / 25) { if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); @@ -65,15 +76,21 @@ public function __construct(OutputInterface $output, $max = 0) $this->setMaxSteps($max); $this->terminal = new Terminal(); + if (0 < $minSecondsBetweenRedraws) { + $this->redrawFreq = null; + $this->minSecondsBetweenRedraws = $minSecondsBetweenRedraws; + } + if (!$this->output->isDecorated()) { // disable overwrite when output does not support ANSI codes. $this->overwrite = false; // set a reasonable redraw frequency so output isn't flooded - $this->setRedrawFrequency($max / 10); + $this->redrawFreq = null; } $this->startTime = time(); + $this->cursor = new Cursor($output); } /** @@ -84,7 +101,7 @@ public function __construct(OutputInterface $output, $max = 0) * @param string $name The placeholder name (including the delimiter char like %) * @param callable $callable A PHP callable */ - public static function setPlaceholderFormatterDefinition($name, callable $callable) + public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); @@ -97,16 +114,14 @@ public static function setPlaceholderFormatterDefinition($name, callable $callab * Gets the placeholder formatter for a given name. * * @param string $name The placeholder name (including the delimiter char like %) - * - * @return callable|null A PHP callable */ - public static function getPlaceholderFormatterDefinition($name) + public static function getPlaceholderFormatterDefinition(string $name): ?callable { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } - return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + return self::$formatters[$name] ?? null; } /** @@ -117,7 +132,7 @@ public static function getPlaceholderFormatterDefinition($name) * @param string $name The format name * @param string $format A format string */ - public static function setFormatDefinition($name, $format) + public static function setFormatDefinition(string $name, string $format): void { if (!self::$formats) { self::$formats = self::initFormats(); @@ -130,16 +145,14 @@ public static function setFormatDefinition($name, $format) * Gets the format for a given name. * * @param string $name The format name - * - * @return string|null A format string */ - public static function getFormatDefinition($name) + public static function getFormatDefinition(string $name): ?string { if (!self::$formats) { self::$formats = self::initFormats(); } - return isset(self::$formats[$name]) ? self::$formats[$name] : null; + return self::$formats[$name] ?? null; } /** @@ -152,156 +165,105 @@ public static function getFormatDefinition($name) * @param string $message The text to associate with the placeholder * @param string $name The name of the placeholder */ - public function setMessage($message, $name = 'message') + public function setMessage(string $message, string $name = 'message') { $this->messages[$name] = $message; } - public function getMessage($name = 'message') + public function getMessage(string $name = 'message') { return $this->messages[$name]; } - /** - * Gets the progress bar start time. - * - * @return int The progress bar start time - */ - public function getStartTime() + public function getStartTime(): int { return $this->startTime; } - /** - * Gets the progress bar maximal steps. - * - * @return int The progress bar max steps - */ - public function getMaxSteps() + public function getMaxSteps(): int { return $this->max; } - /** - * Gets the current step position. - * - * @return int The progress bar step - */ - public function getProgress() + public function getProgress(): int { return $this->step; } - /** - * Gets the progress bar step width. - * - * @return int The progress bar step width - */ - private function getStepWidth() + private function getStepWidth(): int { return $this->stepWidth; } - /** - * Gets the current progress bar percent. - * - * @return float The current progress bar percent - */ - public function getProgressPercent() + public function getProgressPercent(): float { return $this->percent; } - /** - * Sets the progress bar width. - * - * @param int $size The progress bar size - */ - public function setBarWidth($size) + public function getBarOffset(): float { - $this->barWidth = max(1, (int) $size); + return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); } - /** - * Gets the progress bar width. - * - * @return int The progress bar size - */ - public function getBarWidth() + public function getEstimated(): float + { + if (!$this->step) { + return 0; + } + + return round((time() - $this->startTime) / $this->step * $this->max); + } + + public function getRemaining(): float + { + if (!$this->step) { + return 0; + } + + return round((time() - $this->startTime) / $this->step * ($this->max - $this->step)); + } + + public function setBarWidth(int $size) + { + $this->barWidth = max(1, $size); + } + + public function getBarWidth(): int { return $this->barWidth; } - /** - * Sets the bar character. - * - * @param string $char A character - */ - public function setBarCharacter($char) + public function setBarCharacter(string $char) { $this->barChar = $char; } - /** - * Gets the bar character. - * - * @return string A character - */ - public function getBarCharacter() + public function getBarCharacter(): string { - if (null === $this->barChar) { - return $this->max ? '=' : $this->emptyBarChar; - } - - return $this->barChar; + return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); } - /** - * Sets the empty bar character. - * - * @param string $char A character - */ - public function setEmptyBarCharacter($char) + public function setEmptyBarCharacter(string $char) { $this->emptyBarChar = $char; } - /** - * Gets the empty bar character. - * - * @return string A character - */ - public function getEmptyBarCharacter() + public function getEmptyBarCharacter(): string { return $this->emptyBarChar; } - /** - * Sets the progress bar character. - * - * @param string $char A character - */ - public function setProgressCharacter($char) + public function setProgressCharacter(string $char) { $this->progressChar = $char; } - /** - * Gets the progress bar character. - * - * @return string A character - */ - public function getProgressCharacter() + public function getProgressCharacter(): string { return $this->progressChar; } - /** - * Sets the progress bar format. - * - * @param string $format The format - */ - public function setFormat($format) + public function setFormat(string $format) { $this->format = null; $this->internalFormat = $format; @@ -310,11 +272,39 @@ public function setFormat($format) /** * Sets the redraw frequency. * - * @param int|float $freq The frequency in steps + * @param int|null $freq The frequency in steps + */ + public function setRedrawFrequency(?int $freq) + { + $this->redrawFreq = null !== $freq ? max(1, $freq) : null; + } + + public function minSecondsBetweenRedraws(float $seconds): void + { + $this->minSecondsBetweenRedraws = $seconds; + } + + public function maxSecondsBetweenRedraws(float $seconds): void + { + $this->maxSecondsBetweenRedraws = $seconds; + } + + /** + * Returns an iterator that will automatically update the progress bar when iterated. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable */ - public function setRedrawFrequency($freq) + public function iterate(iterable $iterable, int $max = null): iterable { - $this->redrawFreq = max((int) $freq, 1); + $this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0)); + + foreach ($iterable as $key => $value) { + yield $key => $value; + + $this->advance(); + } + + $this->finish(); } /** @@ -322,7 +312,7 @@ public function setRedrawFrequency($freq) * * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged */ - public function start($max = null) + public function start(int $max = null) { $this->startTime = time(); $this->step = 0; @@ -340,49 +330,63 @@ public function start($max = null) * * @param int $step Number of steps to advance */ - public function advance($step = 1) + public function advance(int $step = 1) { $this->setProgress($this->step + $step); } /** * Sets whether to overwrite the progressbar, false for new line. - * - * @param bool $overwrite */ - public function setOverwrite($overwrite) + public function setOverwrite(bool $overwrite) { - $this->overwrite = (bool) $overwrite; + $this->overwrite = $overwrite; } - /** - * Sets the current progress. - * - * @param int $step The current progress - */ - public function setProgress($step) + public function setProgress(int $step) { - $step = (int) $step; - if ($this->max && $step > $this->max) { $this->max = $step; } elseif ($step < 0) { $step = 0; } - $prevPeriod = (int) ($this->step / $this->redrawFreq); - $currPeriod = (int) ($step / $this->redrawFreq); + $redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10); + $prevPeriod = (int) ($this->step / $redrawFreq); + $currPeriod = (int) ($step / $redrawFreq); $this->step = $step; $this->percent = $this->max ? (float) $this->step / $this->max : 0; - if ($prevPeriod !== $currPeriod || $this->max === $step) { + $timeInterval = microtime(true) - $this->lastWriteTime; + + // Draw regardless of other limits + if ($this->max === $step) { $this->display(); + + return; } + + // Throttling + if ($timeInterval < $this->minSecondsBetweenRedraws) { + return; + } + + // Draw each step period, but not too late + if ($prevPeriod !== $currPeriod || $timeInterval >= $this->maxSecondsBetweenRedraws) { + $this->display(); + } + } + + public function setMaxSteps(int $max) + { + $this->format = null; + $this->max = max(0, $max); + $this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4; } /** * Finishes the progress output. */ - public function finish() + public function finish(): void { if (!$this->max) { $this->max = $this->step; @@ -399,7 +403,7 @@ public function finish() /** * Outputs the current progress string. */ - public function display() + public function display(): void { if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { return; @@ -419,7 +423,7 @@ public function display() * while a progress bar is running. * Call display() to show the progress bar again. */ - public function clear() + public function clear(): void { if (!$this->overwrite) { return; @@ -432,12 +436,7 @@ public function clear() $this->overwrite(''); } - /** - * Sets the progress bar format. - * - * @param string $format The format - */ - private function setRealFormat($format) + private function setRealFormat(string $format) { // try to use the _nomax variant if available if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { @@ -451,144 +450,134 @@ private function setRealFormat($format) $this->formatLineCount = substr_count($this->format, "\n"); } - /** - * Sets the progress bar maximal steps. - * - * @param int $max The progress bar max steps - */ - private function setMaxSteps($max) - { - $this->max = max(0, (int) $max); - $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; - } - /** * Overwrites a previous message to the output. - * - * @param string $message The message */ - private function overwrite($message) + private function overwrite(string $message): void { - if ($this->overwrite) { - if (!$this->firstRun) { - // Move the cursor to the beginning of the line - $this->output->write("\x0D"); + if ($this->previousMessage === $message) { + return; + } - // Erase the line - $this->output->write("\x1B[2K"); + $originalMessage = $message; - // Erase previous lines - if ($this->formatLineCount > 0) { - $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount)); + if ($this->overwrite) { + if (null !== $this->previousMessage) { + if ($this->output instanceof ConsoleSectionOutput) { + $messageLines = explode("\n", $message); + $lineCount = \count($messageLines); + foreach ($messageLines as $messageLine) { + $messageLineLength = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $messageLine)); + if ($messageLineLength > $this->terminal->getWidth()) { + $lineCount += floor($messageLineLength / $this->terminal->getWidth()); + } + } + $this->output->clear($lineCount); + } else { + for ($i = 0; $i < $this->formatLineCount; ++$i) { + $this->cursor->moveToColumn(1); + $this->cursor->clearLine(); + $this->cursor->moveUp(); + } + + $this->cursor->moveToColumn(1); + $this->cursor->clearLine(); } } } elseif ($this->step > 0) { - $this->output->writeln(''); + $message = \PHP_EOL.$message; } - $this->firstRun = false; + $this->previousMessage = $originalMessage; + $this->lastWriteTime = microtime(true); $this->output->write($message); + ++$this->writeCount; } - private function determineBestFormat() + private function determineBestFormat(): string { switch ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway case OutputInterface::VERBOSITY_VERBOSE: - return $this->max ? 'verbose' : 'verbose_nomax'; + return $this->max ? self::FORMAT_VERBOSE : self::FORMAT_VERBOSE_NOMAX; case OutputInterface::VERBOSITY_VERY_VERBOSE: - return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + return $this->max ? self::FORMAT_VERY_VERBOSE : self::FORMAT_VERY_VERBOSE_NOMAX; case OutputInterface::VERBOSITY_DEBUG: - return $this->max ? 'debug' : 'debug_nomax'; + return $this->max ? self::FORMAT_DEBUG : self::FORMAT_DEBUG_NOMAX; default: - return $this->max ? 'normal' : 'normal_nomax'; + return $this->max ? self::FORMAT_NORMAL : self::FORMAT_NORMAL_NOMAX; } } - private static function initPlaceholderFormatters() + private static function initPlaceholderFormatters(): array { - return array( - 'bar' => function (ProgressBar $bar, OutputInterface $output) { - $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + return [ + 'bar' => function (self $bar, OutputInterface $output) { + $completeBars = $bar->getBarOffset(); $display = str_repeat($bar->getBarCharacter(), $completeBars); if ($completeBars < $bar->getBarWidth()) { - $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::length(Helper::removeDecoration($output->getFormatter(), $bar->getProgressCharacter())); $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); } return $display; }, - 'elapsed' => function (ProgressBar $bar) { + 'elapsed' => function (self $bar) { return Helper::formatTime(time() - $bar->getStartTime()); }, - 'remaining' => function (ProgressBar $bar) { + 'remaining' => function (self $bar) { if (!$bar->getMaxSteps()) { throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); } - if (!$bar->getProgress()) { - $remaining = 0; - } else { - $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); - } - - return Helper::formatTime($remaining); + return Helper::formatTime($bar->getRemaining()); }, - 'estimated' => function (ProgressBar $bar) { + 'estimated' => function (self $bar) { if (!$bar->getMaxSteps()) { throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); } - if (!$bar->getProgress()) { - $estimated = 0; - } else { - $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); - } - - return Helper::formatTime($estimated); + return Helper::formatTime($bar->getEstimated()); }, - 'memory' => function (ProgressBar $bar) { + 'memory' => function (self $bar) { return Helper::formatMemory(memory_get_usage(true)); }, - 'current' => function (ProgressBar $bar) { - return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + 'current' => function (self $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT); }, - 'max' => function (ProgressBar $bar) { + 'max' => function (self $bar) { return $bar->getMaxSteps(); }, - 'percent' => function (ProgressBar $bar) { + 'percent' => function (self $bar) { return floor($bar->getProgressPercent() * 100); }, - ); + ]; } - private static function initFormats() + private static function initFormats(): array { - return array( - 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', - 'normal_nomax' => ' %current% [%bar%]', + return [ + self::FORMAT_NORMAL => ' %current%/%max% [%bar%] %percent:3s%%', + self::FORMAT_NORMAL_NOMAX => ' %current% [%bar%]', - 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', - 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + self::FORMAT_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + self::FORMAT_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', - 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', - 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + self::FORMAT_VERY_VERBOSE => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + self::FORMAT_VERY_VERBOSE_NOMAX => ' %current% [%bar%] %elapsed:6s%', - 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', - 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', - ); + self::FORMAT_DEBUG => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + self::FORMAT_DEBUG_NOMAX => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ]; } - /** - * @return string - */ - private function buildLine() + private function buildLine(): string { $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; $callback = function ($matches) { if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { - $text = call_user_func($formatter, $this, $this->output); + $text = $formatter($this, $this->output); } elseif (isset($this->messages[$matches[1]])) { $text = $this->messages[$matches[1]]; } else { @@ -603,13 +592,19 @@ private function buildLine() }; $line = preg_replace_callback($regex, $callback, $this->format); - $lineLength = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line); + // gets string length for each sub line with multiline format + $linesLength = array_map(function ($subLine) { + return Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r"))); + }, explode("\n", $line)); + + $linesWidth = max($linesLength); + $terminalWidth = $this->terminal->getWidth(); - if ($lineLength <= $terminalWidth) { + if ($linesWidth <= $terminalWidth) { return $line; } - $this->setBarWidth($this->barWidth - $lineLength + $terminalWidth); + $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth); return preg_replace_callback($regex, $callback, $this->format); } diff --git a/tests/integration/vendor/symfony/console/Helper/ProgressIndicator.php b/tests/integration/vendor/symfony/console/Helper/ProgressIndicator.php index d441accd4..3482343fc 100644 --- a/tests/integration/vendor/symfony/console/Helper/ProgressIndicator.php +++ b/tests/integration/vendor/symfony/console/Helper/ProgressIndicator.php @@ -20,6 +20,17 @@ */ class ProgressIndicator { + private const FORMATS = [ + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ]; + private $output; private $startTime; private $format; @@ -30,16 +41,16 @@ class ProgressIndicator private $indicatorUpdateTime; private $started = false; + /** + * @var array + */ private static $formatters; - private static $formats; /** - * @param OutputInterface $output - * @param string|null $format Indicator format - * @param int $indicatorChangeInterval Change interval in milliseconds - * @param array|null $indicatorValues Animated indicator characters + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters */ - public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + public function __construct(OutputInterface $output, string $format = null, int $indicatorChangeInterval = 100, array $indicatorValues = null) { $this->output = $output; @@ -48,12 +59,12 @@ public function __construct(OutputInterface $output, $format = null, $indicatorC } if (null === $indicatorValues) { - $indicatorValues = array('-', '\\', '|', '/'); + $indicatorValues = ['-', '\\', '|', '/']; } $indicatorValues = array_values($indicatorValues); - if (2 > count($indicatorValues)) { + if (2 > \count($indicatorValues)) { throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); } @@ -65,10 +76,8 @@ public function __construct(OutputInterface $output, $format = null, $indicatorC /** * Sets the current indicator message. - * - * @param string|null $message */ - public function setMessage($message) + public function setMessage(?string $message) { $this->message = $message; @@ -77,10 +86,8 @@ public function setMessage($message) /** * Starts the indicator output. - * - * @param $message */ - public function start($message) + public function start(string $message) { if ($this->started) { throw new LogicException('Progress indicator already started.'); @@ -125,7 +132,7 @@ public function advance() * * @param $message */ - public function finish($message) + public function finish(string $message) { if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); @@ -140,28 +147,19 @@ public function finish($message) /** * Gets the format for a given name. * - * @param string $name The format name - * - * @return string|null A format string + * @return string|null */ - public static function getFormatDefinition($name) + public static function getFormatDefinition(string $name) { - if (!self::$formats) { - self::$formats = self::initFormats(); - } - - return isset(self::$formats[$name]) ? self::$formats[$name] : null; + return self::FORMATS[$name] ?? null; } /** * Sets a placeholder formatter for a given name. * * This method also allow you to override an existing placeholder. - * - * @param string $name The placeholder name (including the delimiter char like %) - * @param callable $callable A PHP callable */ - public static function setPlaceholderFormatterDefinition($name, $callable) + public static function setPlaceholderFormatterDefinition(string $name, callable $callable) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); @@ -171,19 +169,17 @@ public static function setPlaceholderFormatterDefinition($name, $callable) } /** - * Gets the placeholder formatter for a given name. - * - * @param string $name The placeholder name (including the delimiter char like %) + * Gets the placeholder formatter for a given name (including the delimiter char like %). * - * @return callable|null A PHP callable + * @return callable|null */ - public static function getPlaceholderFormatterDefinition($name) + public static function getPlaceholderFormatterDefinition(string $name) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } - return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + return self::$formatters[$name] ?? null; } private function display() @@ -192,18 +188,16 @@ private function display() return; } - $self = $this; - - $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { - if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { - return call_user_func($formatter, $self); + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { + if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) { + return $formatter($this); } return $matches[0]; - }, $this->format)); + }, $this->format ?? '')); } - private function determineBestFormat() + private function determineBestFormat(): string { switch ($this->output->getVerbosity()) { // OutputInterface::VERBOSITY_QUIET: display is disabled anyway @@ -219,10 +213,8 @@ private function determineBestFormat() /** * Overwrites a previous message to the output. - * - * @param string $message The message */ - private function overwrite($message) + private function overwrite(string $message) { if ($this->output->isDecorated()) { $this->output->write("\x0D\x1B[2K"); @@ -232,40 +224,26 @@ private function overwrite($message) } } - private function getCurrentTimeInMilliseconds() + private function getCurrentTimeInMilliseconds(): float { return round(microtime(true) * 1000); } - private static function initPlaceholderFormatters() + private static function initPlaceholderFormatters(): array { - return array( - 'indicator' => function (ProgressIndicator $indicator) { - return $indicator->indicatorValues[$indicator->indicatorCurrent % count($indicator->indicatorValues)]; + return [ + 'indicator' => function (self $indicator) { + return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)]; }, - 'message' => function (ProgressIndicator $indicator) { + 'message' => function (self $indicator) { return $indicator->message; }, - 'elapsed' => function (ProgressIndicator $indicator) { + 'elapsed' => function (self $indicator) { return Helper::formatTime(time() - $indicator->startTime); }, 'memory' => function () { return Helper::formatMemory(memory_get_usage(true)); }, - ); - } - - private static function initFormats() - { - return array( - 'normal' => ' %indicator% %message%', - 'normal_no_ansi' => ' %message%', - - 'verbose' => ' %indicator% %message% (%elapsed:6s%)', - 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', - - 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', - 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', - ); + ]; } } diff --git a/tests/integration/vendor/symfony/console/Helper/QuestionHelper.php b/tests/integration/vendor/symfony/console/Helper/QuestionHelper.php index d7f4cb775..842a618ef 100644 --- a/tests/integration/vendor/symfony/console/Helper/QuestionHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/QuestionHelper.php @@ -11,15 +11,20 @@ namespace Symfony\Component\Console\Helper; -use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Cursor; +use Symfony\Component\Console\Exception\MissingInputException; use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\StreamableInputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; -use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Terminal; +use function Symfony\Component\String\s; /** * The QuestionHelper class provides helpers to interact with the user. @@ -28,18 +33,18 @@ */ class QuestionHelper extends Helper { + /** + * @var resource|null + */ private $inputStream; - private static $shell; - private static $stty; + + private static $stty = true; + private static $stdinIsInteractive; /** * Asks a question to the user. * - * @param InputInterface $input An InputInterface instance - * @param OutputInterface $output An OutputInterface instance - * @param Question $question The question to ask - * - * @return string The user answer + * @return mixed The user answer * * @throws RuntimeException If there is no data to read in the input stream */ @@ -50,96 +55,71 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu } if (!$input->isInteractive()) { - return $question->getDefault(); + return $this->getDefaultAnswer($question); } if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { $this->inputStream = $stream; } - if (!$question->getValidator()) { - return $this->doAsk($output, $question); - } + try { + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } - $interviewer = function () use ($output, $question) { - return $this->doAsk($output, $question); - }; + $interviewer = function () use ($output, $question) { + return $this->doAsk($output, $question); + }; - return $this->validateAttempts($interviewer, $output, $question); - } + return $this->validateAttempts($interviewer, $output, $question); + } catch (MissingInputException $exception) { + $input->setInteractive(false); - /** - * Sets the input stream to read from when interacting with the user. - * - * This is mainly useful for testing purpose. - * - * @deprecated since version 3.2, to be removed in 4.0. Use - * StreamableInputInterface::setStream() instead. - * - * @param resource $stream The input stream - * - * @throws InvalidArgumentException In case the stream is not a resource - */ - public function setInputStream($stream) - { - @trigger_error(sprintf('The %s() method is deprecated since version 3.2 and will be removed in 4.0. Use %s::setStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); + if (null === $fallbackOutput = $this->getDefaultAnswer($question)) { + throw $exception; + } - if (!is_resource($stream)) { - throw new InvalidArgumentException('Input stream must be a valid resource.'); + return $fallbackOutput; } - - $this->inputStream = $stream; } /** - * Returns the helper's input stream. - * - * @deprecated since version 3.2, to be removed in 4.0. Use - * StreamableInputInterface::getStream() instead. - * - * @return resource + * {@inheritdoc} */ - public function getInputStream() + public function getName() { - if (0 === func_num_args() || func_get_arg(0)) { - @trigger_error(sprintf('The %s() method is deprecated since version 3.2 and will be removed in 4.0. Use %s::getStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); - } - - return $this->inputStream; + return 'question'; } /** - * {@inheritdoc} + * Prevents usage of stty. */ - public function getName() + public static function disableStty() { - return 'question'; + self::$stty = false; } /** * Asks the question to the user. * - * @param OutputInterface $output - * @param Question $question - * - * @return bool|mixed|null|string + * @return mixed * - * @throws \Exception - * @throws \RuntimeException + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ private function doAsk(OutputInterface $output, Question $question) { $this->writePrompt($output, $question); - $inputStream = $this->inputStream ?: STDIN; - $autocomplete = $question->getAutocompleterValues(); + $inputStream = $this->inputStream ?: \STDIN; + $autocomplete = $question->getAutocompleterCallback(); - if (null === $autocomplete || !$this->hasSttyAvailable()) { + if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) { $ret = false; if ($question->isHidden()) { try { - $ret = trim($this->getHiddenResponse($output, $inputStream)); - } catch (\RuntimeException $e) { + $hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable()); + $ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse; + } catch (RuntimeException $e) { if (!$question->isHiddenFallback()) { throw $e; } @@ -147,17 +127,24 @@ private function doAsk(OutputInterface $output, Question $question) } if (false === $ret) { - $ret = fgets($inputStream, 4096); + $ret = $this->readInput($inputStream, $question); if (false === $ret) { - throw new RuntimeException('Aborted'); + throw new MissingInputException('Aborted.'); + } + if ($question->isTrimmable()) { + $ret = trim($ret); } - $ret = trim($ret); } } else { - $ret = trim($this->autocomplete($output, $question, $inputStream)); + $autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete); + $ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete; + } + + if ($output instanceof ConsoleSectionOutput) { + $output->addContent($ret); } - $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); + $ret = \strlen($ret) > 0 ? $ret : $question->getDefault(); if ($normalizer = $question->getNormalizer()) { return $normalizer($ret); @@ -166,26 +153,47 @@ private function doAsk(OutputInterface $output, Question $question) return $ret; } + /** + * @return mixed + */ + private function getDefaultAnswer(Question $question) + { + $default = $question->getDefault(); + + if (null === $default) { + return $default; + } + + if ($validator = $question->getValidator()) { + return \call_user_func($question->getValidator(), $default); + } elseif ($question instanceof ChoiceQuestion) { + $choices = $question->getChoices(); + + if (!$question->isMultiselect()) { + return $choices[$default] ?? $default; + } + + $default = explode(',', $default); + foreach ($default as $k => $v) { + $v = $question->isTrimmable() ? trim($v) : $v; + $default[$k] = $choices[$v] ?? $v; + } + } + + return $default; + } + /** * Outputs the question prompt. - * - * @param OutputInterface $output - * @param Question $question */ protected function writePrompt(OutputInterface $output, Question $question) { $message = $question->getQuestion(); if ($question instanceof ChoiceQuestion) { - $maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices()))); - - $messages = (array) $question->getQuestion(); - foreach ($question->getChoices() as $key => $value) { - $width = $maxWidth - $this->strlen($key); - $messages[] = ' ['.$key.str_repeat(' ', $width).'] '.$value; - } - - $output->writeln($messages); + $output->writeln(array_merge([ + $question->getQuestion(), + ], $this->formatChoiceQuestionChoices($question, 'info'))); $message = $question->getPrompt(); } @@ -193,11 +201,26 @@ protected function writePrompt(OutputInterface $output, Question $question) $output->write($message); } + /** + * @return string[] + */ + protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string $tag) + { + $messages = []; + + $maxWidth = max(array_map([__CLASS__, 'width'], array_keys($choices = $question->getChoices()))); + + foreach ($choices as $key => $value) { + $padding = str_repeat(' ', $maxWidth - self::width($key)); + + $messages[] = sprintf(" [<$tag>%s$padding] %s", $key, $value); + } + + return $messages; + } + /** * Outputs an error message. - * - * @param OutputInterface $output - * @param \Exception $error */ protected function writeError(OutputInterface $output, \Exception $error) { @@ -213,23 +236,24 @@ protected function writeError(OutputInterface $output, \Exception $error) /** * Autocompletes a question. * - * @param OutputInterface $output - * @param Question $question - * @param resource $inputStream - * - * @return string + * @param resource $inputStream */ - private function autocomplete(OutputInterface $output, Question $question, $inputStream) + private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string { - $autocomplete = $question->getAutocompleterValues(); + $cursor = new Cursor($output, $inputStream); + + $fullChoice = ''; $ret = ''; $i = 0; $ofs = -1; - $matches = $autocomplete; - $numMatches = count($matches); + $matches = $autocomplete($ret); + $numMatches = \count($matches); $sttyMode = shell_exec('stty -g'); + $isStdin = 'php://stdin' === (stream_get_meta_data($inputStream)['uri'] ?? null); + $r = [$inputStream]; + $w = []; // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) shell_exec('stty -icanon -echo'); @@ -239,26 +263,34 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu // Read a keypress while (!feof($inputStream)) { + while ($isStdin && 0 === @stream_select($r, $w, $w, 0, 100)) { + // Give signal handlers a chance to run + $r = [$inputStream]; + } $c = fread($inputStream, 1); - // Backspace Character - if ("\177" === $c) { + // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. + if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { + shell_exec('stty '.$sttyMode); + throw new MissingInputException('Aborted.'); + } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; - // Move cursor backwards - $output->write("\033[1D"); + $cursor->moveLeft(s($fullChoice)->slice(-1)->width(false)); + + $fullChoice = self::substr($fullChoice, 0, $i); } - if ($i === 0) { + if (0 === $i) { $ofs = -1; - $matches = $autocomplete; - $numMatches = count($matches); + $matches = $autocomplete($ret); + $numMatches = \count($matches); } else { $numMatches = 0; } // Pop the last character off the end of our string - $ret = substr($ret, 0, $i); + $ret = self::substr($ret, 0, $i); } elseif ("\033" === $c) { // Did we read an escape sequence? $c .= fread($inputStream, 2); @@ -276,13 +308,24 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu $ofs += ('A' === $c[2]) ? -1 : 1; $ofs = ($numMatches + $ofs) % $numMatches; } - } elseif (ord($c) < 32) { + } elseif (\ord($c) < 32) { if ("\t" === $c || "\n" === $c) { if ($numMatches > 0 && -1 !== $ofs) { - $ret = $matches[$ofs]; + $ret = (string) $matches[$ofs]; // Echo out remaining chars for current match - $output->write(substr($ret, $i)); - $i = strlen($ret); + $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); + $output->write($remainingCharacters); + $fullChoice .= $remainingCharacters; + $i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding); + + $matches = array_filter( + $autocomplete($ret), + function ($match) use ($ret) { + return '' === $ret || str_starts_with($match, $ret); + } + ); + $numMatches = \count($matches); + $ofs = -1; } if ("\n" === $c) { @@ -295,53 +338,75 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu continue; } else { + if ("\x80" <= $c) { + $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]); + } + $output->write($c); $ret .= $c; + $fullChoice .= $c; ++$i; + $tempRet = $ret; + + if ($question instanceof ChoiceQuestion && $question->isMultiselect()) { + $tempRet = $this->mostRecentlyEnteredValue($fullChoice); + } + $numMatches = 0; $ofs = 0; - foreach ($autocomplete as $value) { + foreach ($autocomplete($ret) as $value) { // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) - if (0 === strpos($value, $ret) && $i !== strlen($value)) { + if (str_starts_with($value, $tempRet)) { $matches[$numMatches++] = $value; } } } - // Erase characters from cursor to end of line - $output->write("\033[K"); + $cursor->clearLineAfter(); if ($numMatches > 0 && -1 !== $ofs) { - // Save cursor position - $output->write("\0337"); - // Write highlighted text - $output->write(''.substr($matches[$ofs], $i).''); - // Restore cursor position - $output->write("\0338"); + $cursor->savePosition(); + // Write highlighted text, complete the partially entered response + $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); + $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).''); + $cursor->restorePosition(); } } // Reset stty so it behaves normally again - shell_exec(sprintf('stty %s', $sttyMode)); + shell_exec('stty '.$sttyMode); - return $ret; + return $fullChoice; + } + + private function mostRecentlyEnteredValue(string $entered): string + { + // Determine the most recent value that the user entered + if (!str_contains($entered, ',')) { + return $entered; + } + + $choices = explode(',', $entered); + if ('' !== $lastChoice = trim($choices[\count($choices) - 1])) { + return $lastChoice; + } + + return $entered; } /** * Gets a hidden response from user. * - * @param OutputInterface $output An Output instance - * @param resource $inputStream The handler resource - * - * @return string The answer + * @param resource $inputStream The handler resource + * @param bool $trimmable Is the answer trimmable * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ - private function getHiddenResponse(OutputInterface $output, $inputStream) + private function getHiddenResponse(OutputInterface $output, $inputStream, bool $trimmable = true): string { - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' === \DIRECTORY_SEPARATOR) { $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; // handle code running from a phar @@ -351,7 +416,8 @@ private function getHiddenResponse(OutputInterface $output, $inputStream) $exe = $tmpExe; } - $value = rtrim(shell_exec($exe)); + $sExec = shell_exec('"'.$exe.'"'); + $value = $trimmable ? rtrim($sExec) : $sExec; $output->writeln(''); if (isset($tmpExe)) { @@ -361,43 +427,36 @@ private function getHiddenResponse(OutputInterface $output, $inputStream) return $value; } - if ($this->hasSttyAvailable()) { + if (self::$stty && Terminal::hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); - shell_exec('stty -echo'); - $value = fgets($inputStream, 4096); - shell_exec(sprintf('stty %s', $sttyMode)); + } elseif ($this->isInteractiveInput($inputStream)) { + throw new RuntimeException('Unable to hide the response.'); + } - if (false === $value) { - throw new RuntimeException('Aborted'); - } + $value = fgets($inputStream, 4096); - $value = trim($value); - $output->writeln(''); - - return $value; + if (self::$stty && Terminal::hasSttyAvailable()) { + shell_exec('stty '.$sttyMode); } - if (false !== $shell = $this->getShell()) { - $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; - $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); - $value = rtrim(shell_exec($command)); - $output->writeln(''); - - return $value; + if (false === $value) { + throw new MissingInputException('Aborted.'); } + if ($trimmable) { + $value = trim($value); + } + $output->writeln(''); - throw new RuntimeException('Unable to hide the response.'); + return $value; } /** * Validates an attempt. * - * @param callable $interviewer A callable that will ask for a question and return the result - * @param OutputInterface $output An Output instance - * @param Question $question A Question instance + * @param callable $interviewer A callable that will ask for a question and return the result * - * @return string The validated response + * @return mixed The validated response * * @throws \Exception In case the max number of attempts has been reached and no valid response has been given */ @@ -405,13 +464,14 @@ private function validateAttempts(callable $interviewer, OutputInterface $output { $error = null; $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { if (null !== $error) { $this->writeError($output, $error); } try { - return call_user_func($question->getValidator(), $interviewer()); + return $question->getValidator()($interviewer()); } catch (RuntimeException $e) { throw $e; } catch (\Exception $error) { @@ -421,46 +481,135 @@ private function validateAttempts(callable $interviewer, OutputInterface $output throw $error; } + private function isInteractiveInput($inputStream): bool + { + if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) { + return false; + } + + if (null !== self::$stdinIsInteractive) { + return self::$stdinIsInteractive; + } + + if (\function_exists('stream_isatty')) { + return self::$stdinIsInteractive = @stream_isatty(fopen('php://stdin', 'r')); + } + + if (\function_exists('posix_isatty')) { + return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); + } + + if (!\function_exists('exec')) { + return self::$stdinIsInteractive = true; + } + + exec('stty 2> /dev/null', $output, $status); + + return self::$stdinIsInteractive = 1 !== $status; + } + /** - * Returns a valid unix shell. + * Reads one or more lines of input and returns what is read. * - * @return string|bool The valid shell name, false in case no valid shell is found + * @param resource $inputStream The handler resource + * @param Question $question The question being asked + * + * @return string|false The input received, false in case input could not be read */ - private function getShell() + private function readInput($inputStream, Question $question) { - if (null !== self::$shell) { - return self::$shell; + if (!$question->isMultiline()) { + $cp = $this->setIOCodepage(); + $ret = fgets($inputStream, 4096); + + return $this->resetIOCodepage($cp, $ret); } - self::$shell = false; + $multiLineStreamReader = $this->cloneInputStream($inputStream); + if (null === $multiLineStreamReader) { + return false; + } - if (file_exists('/usr/bin/env')) { - // handle other OSs with bash/zsh/ksh/csh if available to hide the answer - $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; - foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { - if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { - self::$shell = $sh; - break; - } + $ret = ''; + $cp = $this->setIOCodepage(); + while (false !== ($char = fgetc($multiLineStreamReader))) { + if (\PHP_EOL === "{$ret}{$char}") { + break; + } + $ret .= $char; + } + + return $this->resetIOCodepage($cp, $ret); + } + + /** + * Sets console I/O to the host code page. + * + * @return int Previous code page in IBM/EBCDIC format + */ + private function setIOCodepage(): int + { + if (\function_exists('sapi_windows_cp_set')) { + $cp = sapi_windows_cp_get(); + sapi_windows_cp_set(sapi_windows_cp_get('oem')); + + return $cp; + } + + return 0; + } + + /** + * Sets console I/O to the specified code page and converts the user input. + * + * @param string|false $input + * + * @return string|false + */ + private function resetIOCodepage(int $cp, $input) + { + if (0 !== $cp) { + sapi_windows_cp_set($cp); + + if (false !== $input && '' !== $input) { + $input = sapi_windows_cp_conv(sapi_windows_cp_get('oem'), $cp, $input); } } - return self::$shell; + return $input; } /** - * Returns whether Stty is available or not. + * Clones an input stream in order to act on one instance of the same + * stream without affecting the other instance. * - * @return bool + * @param resource $inputStream The handler resource + * + * @return resource|null The cloned resource, null in case it could not be cloned */ - private function hasSttyAvailable() + private function cloneInputStream($inputStream) { - if (null !== self::$stty) { - return self::$stty; + $streamMetaData = stream_get_meta_data($inputStream); + $seekable = $streamMetaData['seekable'] ?? false; + $mode = $streamMetaData['mode'] ?? 'rb'; + $uri = $streamMetaData['uri'] ?? null; + + if (null === $uri) { + return null; } - exec('stty 2>&1', $output, $exitcode); + $cloneStream = fopen($uri, $mode); + + // For seekable and writable streams, add all the same data to the + // cloned stream and then seek to the same offset. + if (true === $seekable && !\in_array($mode, ['r', 'rb', 'rt'])) { + $offset = ftell($inputStream); + rewind($inputStream); + stream_copy_to_stream($inputStream, $cloneStream); + fseek($inputStream, $offset); + fseek($cloneStream, $offset); + } - return self::$stty = $exitcode === 0; + return $cloneStream; } } diff --git a/tests/integration/vendor/symfony/console/Helper/SymfonyQuestionHelper.php b/tests/integration/vendor/symfony/console/Helper/SymfonyQuestionHelper.php index 88351d164..01f94aba4 100644 --- a/tests/integration/vendor/symfony/console/Helper/SymfonyQuestionHelper.php +++ b/tests/integration/vendor/symfony/console/Helper/SymfonyQuestionHelper.php @@ -11,14 +11,12 @@ namespace Symfony\Component\Console\Helper; -use Symfony\Component\Console\Exception\LogicException; -use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\Console\Formatter\OutputFormatter; /** * Symfony Style Guide compliant question helper. @@ -27,36 +25,18 @@ */ class SymfonyQuestionHelper extends QuestionHelper { - /** - * {@inheritdoc} - */ - public function ask(InputInterface $input, OutputInterface $output, Question $question) - { - $validator = $question->getValidator(); - $question->setValidator(function ($value) use ($validator) { - if (null !== $validator) { - $value = $validator($value); - } else { - // make required - if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { - throw new LogicException('A value is required.'); - } - } - - return $value; - }); - - return parent::ask($input, $output, $question); - } - /** * {@inheritdoc} */ protected function writePrompt(OutputInterface $output, Question $question) { - $text = OutputFormatter::escape($question->getQuestion()); + $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); $default = $question->getDefault(); + if ($question->isMultiline()) { + $text .= sprintf(' (press %s to continue)', $this->getEofShortcut()); + } + switch (true) { case null === $default: $text = sprintf(' %s:', $text); @@ -82,7 +62,7 @@ protected function writePrompt(OutputInterface $output, Question $question) case $question instanceof ChoiceQuestion: $choices = $question->getChoices(); - $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($choices[$default])); + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($choices[$default] ?? $default)); break; @@ -92,15 +72,15 @@ protected function writePrompt(OutputInterface $output, Question $question) $output->writeln($text); + $prompt = ' > '; + if ($question instanceof ChoiceQuestion) { - $width = max(array_map('strlen', array_keys($question->getChoices()))); + $output->writeln($this->formatChoiceQuestionChoices($question, 'comment')); - foreach ($question->getChoices() as $key => $value) { - $output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); - } + $prompt = $question->getPrompt(); } - $output->write(' > '); + $output->write($prompt); } /** @@ -117,4 +97,13 @@ protected function writeError(OutputInterface $output, \Exception $error) parent::writeError($output, $error); } + + private function getEofShortcut(): string + { + if ('Windows' === \PHP_OS_FAMILY) { + return 'Ctrl+Z then Enter'; + } + + return 'Ctrl+D'; + } } diff --git a/tests/integration/vendor/symfony/console/Helper/Table.php b/tests/integration/vendor/symfony/console/Helper/Table.php index 1434562df..6ade1360d 100644 --- a/tests/integration/vendor/symfony/console/Helper/Table.php +++ b/tests/integration/vendor/symfony/console/Helper/Table.php @@ -11,8 +11,12 @@ namespace Symfony\Component\Console\Helper; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface; +use Symfony\Component\Console\Output\ConsoleSectionOutput; +use Symfony\Component\Console\Output\OutputInterface; /** * Provides helpers to display a table. @@ -21,34 +25,40 @@ * @author Саша Стаменковић * @author Abdellatif Ait boudad * @author Max Grigorian + * @author Dany Maillard */ class Table { + private const SEPARATOR_TOP = 0; + private const SEPARATOR_TOP_BOTTOM = 1; + private const SEPARATOR_MID = 2; + private const SEPARATOR_BOTTOM = 3; + private const BORDER_OUTSIDE = 0; + private const BORDER_INSIDE = 1; + + private $headerTitle; + private $footerTitle; + /** * Table headers. - * - * @var array */ - private $headers = array(); + private $headers = []; /** * Table rows. - * - * @var array */ - private $rows = array(); + private $rows = []; + private $horizontal = false; /** * Column widths cache. - * - * @var array */ - private $effectiveColumnWidths = array(); + private $effectiveColumnWidths = []; /** * Number of columns cache. * - * @var array + * @var int */ private $numberOfColumns; @@ -65,17 +75,23 @@ class Table /** * @var array */ - private $columnStyles = array(); + private $columnStyles = []; /** * User set column widths. * * @var array */ - private $columnWidths = array(); + private $columnWidths = []; + private $columnMaxWidths = []; + /** + * @var array|null + */ private static $styles; + private $rendered = false; + public function __construct(OutputInterface $output) { $this->output = $output; @@ -89,11 +105,8 @@ public function __construct(OutputInterface $output) /** * Sets a style definition. - * - * @param string $name The style name - * @param TableStyle $style A TableStyle instance */ - public static function setStyleDefinition($name, TableStyle $style) + public static function setStyleDefinition(string $name, TableStyle $style) { if (!self::$styles) { self::$styles = self::initStyles(); @@ -105,11 +118,9 @@ public static function setStyleDefinition($name, TableStyle $style) /** * Gets a style definition by name. * - * @param string $name The style name - * - * @return TableStyle A TableStyle instance + * @return TableStyle */ - public static function getStyleDefinition($name) + public static function getStyleDefinition(string $name) { if (!self::$styles) { self::$styles = self::initStyles(); @@ -127,7 +138,7 @@ public static function getStyleDefinition($name) * * @param TableStyle|string $name The style name or a TableStyle instance * - * @return Table + * @return $this */ public function setStyle($name) { @@ -149,15 +160,12 @@ public function getStyle() /** * Sets table column style. * - * @param int $columnIndex Column index - * @param TableStyle|string $name The style name or a TableStyle instance + * @param TableStyle|string $name The style name or a TableStyle instance * - * @return Table + * @return $this */ - public function setColumnStyle($columnIndex, $name) + public function setColumnStyle(int $columnIndex, $name) { - $columnIndex = intval($columnIndex); - $this->columnStyles[$columnIndex] = $this->resolveStyle($name); return $this; @@ -168,30 +176,21 @@ public function setColumnStyle($columnIndex, $name) * * If style was not set, it returns the global table style. * - * @param int $columnIndex Column index - * * @return TableStyle */ - public function getColumnStyle($columnIndex) + public function getColumnStyle(int $columnIndex) { - if (isset($this->columnStyles[$columnIndex])) { - return $this->columnStyles[$columnIndex]; - } - - return $this->getStyle(); + return $this->columnStyles[$columnIndex] ?? $this->getStyle(); } /** * Sets the minimum width of a column. * - * @param int $columnIndex Column index - * @param int $width Minimum column width in characters - * - * @return Table + * @return $this */ - public function setColumnWidth($columnIndex, $width) + public function setColumnWidth(int $columnIndex, int $width) { - $this->columnWidths[intval($columnIndex)] = intval($width); + $this->columnWidths[$columnIndex] = $width; return $this; } @@ -199,13 +198,11 @@ public function setColumnWidth($columnIndex, $width) /** * Sets the minimum width of all columns. * - * @param array $widths - * - * @return Table + * @return $this */ public function setColumnWidths(array $widths) { - $this->columnWidths = array(); + $this->columnWidths = []; foreach ($widths as $index => $width) { $this->setColumnWidth($index, $width); } @@ -213,11 +210,33 @@ public function setColumnWidths(array $widths) return $this; } + /** + * Sets the maximum width of a column. + * + * Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while + * formatted strings are preserved. + * + * @return $this + */ + public function setColumnMaxWidth(int $columnIndex, int $width): self + { + if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) { + throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter()))); + } + + $this->columnMaxWidths[$columnIndex] = $width; + + return $this; + } + + /** + * @return $this + */ public function setHeaders(array $headers) { $headers = array_values($headers); - if (!empty($headers) && !is_array($headers[0])) { - $headers = array($headers); + if (!empty($headers) && !\is_array($headers[0])) { + $headers = [$headers]; } $this->headers = $headers; @@ -227,11 +246,14 @@ public function setHeaders(array $headers) public function setRows(array $rows) { - $this->rows = array(); + $this->rows = []; return $this->addRows($rows); } + /** + * @return $this + */ public function addRows(array $rows) { foreach ($rows as $row) { @@ -241,6 +263,9 @@ public function addRows(array $rows) return $this; } + /** + * @return $this + */ public function addRow($row) { if ($row instanceof TableSeparator) { @@ -249,7 +274,7 @@ public function addRow($row) return $this; } - if (!is_array($row)) { + if (!\is_array($row)) { throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); } @@ -258,6 +283,30 @@ public function addRow($row) return $this; } + /** + * Adds a row to the table, and re-renders the table. + * + * @return $this + */ + public function appendRow($row): self + { + if (!$this->output instanceof ConsoleSectionOutput) { + throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__)); + } + + if ($this->rendered) { + $this->output->clear($this->calculateRowCount()); + } + + $this->addRow($row); + $this->render(); + + return $this; + } + + /** + * @return $this + */ public function setRow($column, array $row) { $this->rows[$column] = $row; @@ -265,65 +314,168 @@ public function setRow($column, array $row) return $this; } + /** + * @return $this + */ + public function setHeaderTitle(?string $title): self + { + $this->headerTitle = $title; + + return $this; + } + + /** + * @return $this + */ + public function setFooterTitle(?string $title): self + { + $this->footerTitle = $title; + + return $this; + } + + /** + * @return $this + */ + public function setHorizontal(bool $horizontal = true): self + { + $this->horizontal = $horizontal; + + return $this; + } + /** * Renders table to output. * * Example: - * +---------------+-----------------------+------------------+ - * | ISBN | Title | Author | - * +---------------+-----------------------+------------------+ - * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | - * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | - * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | - * +---------------+-----------------------+------------------+ + * + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ */ public function render() { - $this->calculateNumberOfColumns(); - $rows = $this->buildTableRows($this->rows); - $headers = $this->buildTableRows($this->headers); - - $this->calculateColumnsWidth(array_merge($headers, $rows)); - - $this->renderRowSeparator(); - if (!empty($headers)) { - foreach ($headers as $header) { - $this->renderRow($header, $this->style->getCellHeaderFormat()); - $this->renderRowSeparator(); + $divider = new TableSeparator(); + if ($this->horizontal) { + $rows = []; + foreach ($this->headers[0] ?? [] as $i => $header) { + $rows[$i] = [$header]; + foreach ($this->rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + if (isset($row[$i])) { + $rows[$i][] = $row[$i]; + } elseif ($rows[$i][0] instanceof TableCell && $rows[$i][0]->getColspan() >= 2) { + // Noop, there is a "title" + } else { + $rows[$i][] = null; + } + } } + } else { + $rows = array_merge($this->headers, [$divider], $this->rows); } + + $this->calculateNumberOfColumns($rows); + + $rows = $this->buildTableRows($rows); + $this->calculateColumnsWidth($rows); + + $isHeader = !$this->horizontal; + $isFirstRow = $this->horizontal; + $hasTitle = (bool) $this->headerTitle; foreach ($rows as $row) { + if ($divider === $row) { + $isHeader = false; + $isFirstRow = true; + + continue; + } if ($row instanceof TableSeparator) { $this->renderRowSeparator(); + + continue; + } + if (!$row) { + continue; + } + + if ($isHeader || $isFirstRow) { + $this->renderRowSeparator( + $isHeader ? self::SEPARATOR_TOP : self::SEPARATOR_TOP_BOTTOM, + $hasTitle ? $this->headerTitle : null, + $hasTitle ? $this->style->getHeaderTitleFormat() : null + ); + $isFirstRow = false; + $hasTitle = false; + } + if ($this->horizontal) { + $this->renderRow($row, $this->style->getCellRowFormat(), $this->style->getCellHeaderFormat()); } else { - $this->renderRow($row, $this->style->getCellRowFormat()); + $this->renderRow($row, $isHeader ? $this->style->getCellHeaderFormat() : $this->style->getCellRowFormat()); } } - if (!empty($rows)) { - $this->renderRowSeparator(); - } + $this->renderRowSeparator(self::SEPARATOR_BOTTOM, $this->footerTitle, $this->style->getFooterTitleFormat()); $this->cleanup(); + $this->rendered = true; } /** * Renders horizontal header separator. * - * Example: +-----+-----------+-------+ + * Example: + * + * +-----+-----------+-------+ */ - private function renderRowSeparator() + private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null) { if (0 === $count = $this->numberOfColumns) { return; } - if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + $borders = $this->style->getBorderChars(); + if (!$borders[0] && !$borders[2] && !$this->style->getCrossingChar()) { return; } - $markup = $this->style->getCrossingChar(); + $crossings = $this->style->getCrossingChars(); + if (self::SEPARATOR_MID === $type) { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[2], $crossings[8], $crossings[0], $crossings[4]]; + } elseif (self::SEPARATOR_TOP === $type) { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[1], $crossings[2], $crossings[3]]; + } elseif (self::SEPARATOR_TOP_BOTTOM === $type) { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[9], $crossings[10], $crossings[11]]; + } else { + [$horizontal, $leftChar, $midChar, $rightChar] = [$borders[0], $crossings[7], $crossings[6], $crossings[5]]; + } + + $markup = $leftChar; for ($column = 0; $column < $count; ++$column) { - $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->effectiveColumnWidths[$column]).$this->style->getCrossingChar(); + $markup .= str_repeat($horizontal, $this->effectiveColumnWidths[$column]); + $markup .= $column === $count - 1 ? $rightChar : $midChar; + } + + if (null !== $title) { + $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title))); + $markupLength = Helper::width($markup); + if ($titleLength > $limit = $markupLength - 4) { + $titleLength = $limit; + $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, ''))); + $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); + } + + $titleStart = intdiv($markupLength - $titleLength, 2); + if (false === mb_detect_encoding($markup, null, true)) { + $markup = substr_replace($markup, $formattedTitle, $titleStart, $titleLength); + } else { + $markup = mb_substr($markup, 0, $titleStart).$formattedTitle.mb_substr($markup, $titleStart + $titleLength); + } } $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); @@ -332,43 +484,42 @@ private function renderRowSeparator() /** * Renders vertical column separator. */ - private function renderColumnSeparator() + private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string { - return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); + $borders = $this->style->getBorderChars(); + + return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]); } /** * Renders table row. * - * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * Example: * - * @param array $row - * @param string $cellFormat + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | */ - private function renderRow(array $row, $cellFormat) - { - if (empty($row)) { - return; - } - - $rowContent = $this->renderColumnSeparator(); - foreach ($this->getRowColumns($row) as $column) { - $rowContent .= $this->renderCell($row, $column, $cellFormat); - $rowContent .= $this->renderColumnSeparator(); + private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null) + { + $rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE); + $columns = $this->getRowColumns($row); + $last = \count($columns) - 1; + foreach ($columns as $i => $column) { + if ($firstCellFormat && 0 === $i) { + $rowContent .= $this->renderCell($row, $column, $firstCellFormat); + } else { + $rowContent .= $this->renderCell($row, $column, $cellFormat); + } + $rowContent .= $this->renderColumnSeparator($last === $i ? self::BORDER_OUTSIDE : self::BORDER_INSIDE); } $this->output->writeln($rowContent); } /** * Renders table cell with padding. - * - * @param array $row - * @param int $column - * @param string $cellFormat */ - private function renderCell(array $row, $column, $cellFormat) + private function renderCell(array $row, int $column, string $cellFormat): string { - $cell = isset($row[$column]) ? $row[$column] : ''; + $cell = $row[$column] ?? ''; $width = $this->effectiveColumnWidths[$column]; if ($cell instanceof TableCell && $cell->getColspan() > 1) { // add the width of the following columns(numbers of colspan). @@ -379,32 +530,51 @@ private function renderCell(array $row, $column, $cellFormat) // str_pad won't work properly with multi-byte strings, we need to fix the padding if (false !== $encoding = mb_detect_encoding($cell, null, true)) { - $width += strlen($cell) - mb_strwidth($cell, $encoding); + $width += \strlen($cell) - mb_strwidth($cell, $encoding); } $style = $this->getColumnStyle($column); if ($cell instanceof TableSeparator) { - return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); + return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width)); } - $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell)); $content = sprintf($style->getCellRowContentFormat(), $cell); - return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); + $padType = $style->getPadType(); + if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) { + $isNotStyledByTag = !preg_match('/^<(\w+|(\w+=[\w,]+;?)*)>.+<\/(\w+|(\w+=\w+;?)*)?>$/', $cell); + if ($isNotStyledByTag) { + $cellFormat = $cell->getStyle()->getCellFormat(); + if (!\is_string($cellFormat)) { + $tag = http_build_query($cell->getStyle()->getTagOptions(), '', ';'); + $cellFormat = '<'.$tag.'>%s'; + } + + if (strstr($content, '')) { + $content = str_replace('', '', $content); + $width -= 3; + } + if (strstr($content, '')) { + $content = str_replace('', '', $content); + $width -= \strlen(''); + } + } + + $padType = $cell->getStyle()->getPadByAlign(); + } + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType)); } /** * Calculate number of columns for this table. */ - private function calculateNumberOfColumns() + private function calculateNumberOfColumns(array $rows) { - if (null !== $this->numberOfColumns) { - return; - } - - $columns = array(0); - foreach (array_merge($this->headers, $this->rows) as $row) { + $columns = [0]; + foreach ($rows as $row) { if ($row instanceof TableSeparator) { continue; } @@ -415,80 +585,112 @@ private function calculateNumberOfColumns() $this->numberOfColumns = max($columns); } - private function buildTableRows($rows) + private function buildTableRows(array $rows): TableRows { - $unmergedRows = array(); - for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { + /** @var WrappableOutputFormatterInterface $formatter */ + $formatter = $this->output->getFormatter(); + $unmergedRows = []; + for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) { $rows = $this->fillNextRows($rows, $rowKey); // Remove any new line breaks and replace it with a new line foreach ($rows[$rowKey] as $column => $cell) { - if (!strstr($cell, "\n")) { + $colspan = $cell instanceof TableCell ? $cell->getColspan() : 1; + + if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) { + $cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan); + } + if (!strstr($cell ?? '', "\n")) { continue; } - $lines = explode("\n", $cell); + $escaped = implode("\n", array_map([OutputFormatter::class, 'escapeTrailingBackslash'], explode("\n", $cell))); + $cell = $cell instanceof TableCell ? new TableCell($escaped, ['colspan' => $cell->getColspan()]) : $escaped; + $lines = explode("\n", str_replace("\n", "\n", $cell)); foreach ($lines as $lineKey => $line) { - if ($cell instanceof TableCell) { - $line = new TableCell($line, array('colspan' => $cell->getColspan())); + if ($colspan > 1) { + $line = new TableCell($line, ['colspan' => $colspan]); } if (0 === $lineKey) { $rows[$rowKey][$column] = $line; } else { + if (!\array_key_exists($rowKey, $unmergedRows) || !\array_key_exists($lineKey, $unmergedRows[$rowKey])) { + $unmergedRows[$rowKey][$lineKey] = $this->copyRow($rows, $rowKey); + } $unmergedRows[$rowKey][$lineKey][$column] = $line; } } } } - $tableRows = array(); - foreach ($rows as $rowKey => $row) { - $tableRows[] = $this->fillCells($row); - if (isset($unmergedRows[$rowKey])) { - $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + return new TableRows(function () use ($rows, $unmergedRows): \Traversable { + foreach ($rows as $rowKey => $row) { + yield $row instanceof TableSeparator ? $row : $this->fillCells($row); + + if (isset($unmergedRows[$rowKey])) { + foreach ($unmergedRows[$rowKey] as $row) { + yield $row instanceof TableSeparator ? $row : $this->fillCells($row); + } + } } + }); + } + + private function calculateRowCount(): int + { + $numberOfRows = \count(iterator_to_array($this->buildTableRows(array_merge($this->headers, [new TableSeparator()], $this->rows)))); + + if ($this->headers) { + ++$numberOfRows; // Add row for header separator } - return $tableRows; + if (\count($this->rows) > 0) { + ++$numberOfRows; // Add row for footer separator + } + + return $numberOfRows; } /** * fill rows that contains rowspan > 1. * - * @param array $rows - * @param int $line - * - * @return array + * @throws InvalidArgumentException */ - private function fillNextRows($rows, $line) + private function fillNextRows(array $rows, int $line): array { - $unmergedRows = array(); + $unmergedRows = []; foreach ($rows[$line] as $column => $cell) { + if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) { + throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell))); + } if ($cell instanceof TableCell && $cell->getRowspan() > 1) { $nbLines = $cell->getRowspan() - 1; - $lines = array($cell); + $lines = [$cell]; if (strstr($cell, "\n")) { - $lines = explode("\n", $cell); - $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + $lines = explode("\n", str_replace("\n", "\n", $cell)); + $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; - $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); + $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]); unset($lines[0]); } // create a two dimensional array (rowspan x colspan) - $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows); + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows); foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { - $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; - $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); + $value = $lines[$unmergedRowKey - $line] ?? ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan(), 'style' => $cell->getStyle()]); + if ($nbLines === $unmergedRowKey - $line) { + break; + } } } } foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { // we need to know if $unmergedRow will be merged or inserted into $rows - if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { foreach ($unmergedRow as $cellKey => $cell) { // insert cell into row at cellKey position - array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); + array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]); } } else { $row = $this->copyRow($rows, $unmergedRowKey - 1); @@ -497,7 +699,7 @@ private function fillNextRows($rows, $line) $row[$column] = $unmergedRow[$column]; } } - array_splice($rows, $unmergedRowKey, 0, array($row)); + array_splice($rows, $unmergedRowKey, 0, [$row]); } } @@ -506,14 +708,11 @@ private function fillNextRows($rows, $line) /** * fill cells for a row that contains colspan > 1. - * - * @param array $row - * - * @return array */ - private function fillCells($row) + private function fillCells(iterable $row) { - $newRow = array(); + $newRow = []; + foreach ($row as $column => $cell) { $newRow[] = $cell; if ($cell instanceof TableCell && $cell->getColspan() > 1) { @@ -527,19 +726,13 @@ private function fillCells($row) return $newRow ?: $row; } - /** - * @param array $rows - * @param int $line - * - * @return array - */ - private function copyRow($rows, $line) + private function copyRow(array $rows, int $line): array { $row = $rows[$line]; foreach ($row as $cellKey => $cellValue) { $row[$cellKey] = ''; if ($cellValue instanceof TableCell) { - $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); + $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]); } } @@ -548,14 +741,10 @@ private function copyRow($rows, $line) /** * Gets number of columns by row. - * - * @param array $row - * - * @return int */ - private function getNumberOfColumns(array $row) + private function getNumberOfColumns(array $row): int { - $columns = count($row); + $columns = \count($row); foreach ($row as $column) { $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; } @@ -565,12 +754,8 @@ private function getNumberOfColumns(array $row) /** * Gets list of columns for the given row. - * - * @param array $row - * - * @return array */ - private function getRowColumns($row) + private function getRowColumns(array $row): array { $columns = range(0, $this->numberOfColumns - 1); foreach ($row as $cellKey => $cell) { @@ -585,13 +770,11 @@ private function getRowColumns($row) /** * Calculates columns widths. - * - * @param array $rows */ - private function calculateColumnsWidth($rows) + private function calculateColumnsWidth(iterable $rows) { for ($column = 0; $column < $this->numberOfColumns; ++$column) { - $lengths = array(); + $lengths = []; foreach ($rows as $row) { if ($row instanceof TableSeparator) { continue; @@ -599,9 +782,10 @@ private function calculateColumnsWidth($rows) foreach ($row as $i => $cell) { if ($cell instanceof TableCell) { - $textLength = strlen($cell); + $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); + $textLength = Helper::width($textContent); if ($textLength > 0) { - $contentColumns = str_split($cell, ceil($textLength / $cell->getColspan())); + $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); foreach ($contentColumns as $position => $content) { $row[$i + $position] = $content; } @@ -612,40 +796,28 @@ private function calculateColumnsWidth($rows) $lengths[] = $this->getCellWidth($row, $column); } - $this->effectiveColumnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; + $this->effectiveColumnWidths[$column] = max($lengths) + Helper::width($this->style->getCellRowContentFormat()) - 2; } } - /** - * Gets column width. - * - * @return int - */ - private function getColumnSeparatorWidth() + private function getColumnSeparatorWidth(): int { - return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3])); } - /** - * Gets cell width. - * - * @param array $row - * @param int $column - * - * @return int - */ - private function getCellWidth(array $row, $column) + private function getCellWidth(array $row, int $column): int { $cellWidth = 0; if (isset($row[$column])) { $cell = $row[$column]; - $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $cellWidth = Helper::width(Helper::removeDecoration($this->output->getFormatter(), $cell)); } - $columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0; + $columnWidth = $this->columnWidths[$column] ?? 0; + $cellWidth = max($cellWidth, $columnWidth); - return max($cellWidth, $columnWidth); + return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth; } /** @@ -653,44 +825,61 @@ private function getCellWidth(array $row, $column) */ private function cleanup() { - $this->effectiveColumnWidths = array(); + $this->effectiveColumnWidths = []; $this->numberOfColumns = null; } - private static function initStyles() + /** + * @return array + */ + private static function initStyles(): array { $borderless = new TableStyle(); $borderless - ->setHorizontalBorderChar('=') - ->setVerticalBorderChar(' ') - ->setCrossingChar(' ') + ->setHorizontalBorderChars('=') + ->setVerticalBorderChars(' ') + ->setDefaultCrossingChar(' ') ; $compact = new TableStyle(); $compact - ->setHorizontalBorderChar('') - ->setVerticalBorderChar(' ') - ->setCrossingChar('') + ->setHorizontalBorderChars('') + ->setVerticalBorderChars(' ') + ->setDefaultCrossingChar('') ->setCellRowContentFormat('%s') ; $styleGuide = new TableStyle(); $styleGuide - ->setHorizontalBorderChar('-') - ->setVerticalBorderChar(' ') - ->setCrossingChar(' ') + ->setHorizontalBorderChars('-') + ->setVerticalBorderChars(' ') + ->setDefaultCrossingChar(' ') ->setCellHeaderFormat('%s') ; - return array( + $box = (new TableStyle()) + ->setHorizontalBorderChars('─') + ->setVerticalBorderChars('│') + ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├') + ; + + $boxDouble = (new TableStyle()) + ->setHorizontalBorderChars('═', '─') + ->setVerticalBorderChars('║', '│') + ->setCrossingChars('┼', '╔', '╤', '╗', '╢', '╝', '╧', '╚', '╟', '╠', '╪', '╣') + ; + + return [ 'default' => new TableStyle(), 'borderless' => $borderless, 'compact' => $compact, 'symfony-style-guide' => $styleGuide, - ); + 'box' => $box, + 'box-double' => $boxDouble, + ]; } - private function resolveStyle($name) + private function resolveStyle($name): TableStyle { if ($name instanceof TableStyle) { return $name; diff --git a/tests/integration/vendor/symfony/console/Helper/TableCell.php b/tests/integration/vendor/symfony/console/Helper/TableCell.php index 69442d424..1a7bc6ede 100644 --- a/tests/integration/vendor/symfony/console/Helper/TableCell.php +++ b/tests/integration/vendor/symfony/console/Helper/TableCell.php @@ -18,24 +18,14 @@ */ class TableCell { - /** - * @var string - */ private $value; - - /** - * @var array - */ - private $options = array( + private $options = [ 'rowspan' => 1, 'colspan' => 1, - ); + 'style' => null, + ]; - /** - * @param string $value - * @param array $options - */ - public function __construct($value = '', array $options = array()) + public function __construct(string $value = '', array $options = []) { $this->value = $value; @@ -44,6 +34,10 @@ public function __construct($value = '', array $options = array()) throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); } + if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) { + throw new InvalidArgumentException('The style option must be an instance of "TableCellStyle".'); + } + $this->options = array_merge($this->options, $options); } @@ -76,4 +70,9 @@ public function getRowspan() { return (int) $this->options['rowspan']; } + + public function getStyle(): ?TableCellStyle + { + return $this->options['style']; + } } diff --git a/tests/integration/vendor/symfony/console/Helper/TableCellStyle.php b/tests/integration/vendor/symfony/console/Helper/TableCellStyle.php new file mode 100644 index 000000000..19cd0ffc6 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Helper/TableCellStyle.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Yewhen Khoptynskyi + */ +class TableCellStyle +{ + public const DEFAULT_ALIGN = 'left'; + + private const TAG_OPTIONS = [ + 'fg', + 'bg', + 'options', + ]; + + private const ALIGN_MAP = [ + 'left' => \STR_PAD_RIGHT, + 'center' => \STR_PAD_BOTH, + 'right' => \STR_PAD_LEFT, + ]; + + private $options = [ + 'fg' => 'default', + 'bg' => 'default', + 'options' => null, + 'align' => self::DEFAULT_ALIGN, + 'cellFormat' => null, + ]; + + public function __construct(array $options = []) + { + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) { + throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP)))); + } + + $this->options = array_merge($this->options, $options); + } + + public function getOptions(): array + { + return $this->options; + } + + /** + * Gets options we need for tag for example fg, bg. + * + * @return string[] + */ + public function getTagOptions() + { + return array_filter( + $this->getOptions(), + function ($key) { + return \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]); + }, + \ARRAY_FILTER_USE_KEY + ); + } + + /** + * @return int + */ + public function getPadByAlign() + { + return self::ALIGN_MAP[$this->getOptions()['align']]; + } + + public function getCellFormat(): ?string + { + return $this->getOptions()['cellFormat']; + } +} diff --git a/tests/integration/vendor/symfony/console/Helper/TableRows.php b/tests/integration/vendor/symfony/console/Helper/TableRows.php new file mode 100644 index 000000000..cbc07d294 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Helper/TableRows.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * @internal + */ +class TableRows implements \IteratorAggregate +{ + private $generator; + + public function __construct(\Closure $generator) + { + $this->generator = $generator; + } + + public function getIterator(): \Traversable + { + return ($this->generator)(); + } +} diff --git a/tests/integration/vendor/symfony/console/Helper/TableSeparator.php b/tests/integration/vendor/symfony/console/Helper/TableSeparator.php index 8cc73e69a..e541c5315 100644 --- a/tests/integration/vendor/symfony/console/Helper/TableSeparator.php +++ b/tests/integration/vendor/symfony/console/Helper/TableSeparator.php @@ -18,10 +18,7 @@ */ class TableSeparator extends TableCell { - /** - * @param array $options - */ - public function __construct(array $options = array()) + public function __construct(array $options = []) { parent::__construct('', $options); } diff --git a/tests/integration/vendor/symfony/console/Helper/TableStyle.php b/tests/integration/vendor/symfony/console/Helper/TableStyle.php index d7e28ff2b..dfc41e6a4 100644 --- a/tests/integration/vendor/symfony/console/Helper/TableStyle.php +++ b/tests/integration/vendor/symfony/console/Helper/TableStyle.php @@ -19,30 +19,44 @@ * * @author Fabien Potencier * @author Саша Стаменковић + * @author Dany Maillard */ class TableStyle { private $paddingChar = ' '; - private $horizontalBorderChar = '-'; - private $verticalBorderChar = '|'; + private $horizontalOutsideBorderChar = '-'; + private $horizontalInsideBorderChar = '-'; + private $verticalOutsideBorderChar = '|'; + private $verticalInsideBorderChar = '|'; private $crossingChar = '+'; + private $crossingTopRightChar = '+'; + private $crossingTopMidChar = '+'; + private $crossingTopLeftChar = '+'; + private $crossingMidRightChar = '+'; + private $crossingBottomRightChar = '+'; + private $crossingBottomMidChar = '+'; + private $crossingBottomLeftChar = '+'; + private $crossingMidLeftChar = '+'; + private $crossingTopLeftBottomChar = '+'; + private $crossingTopMidBottomChar = '+'; + private $crossingTopRightBottomChar = '+'; + private $headerTitleFormat = ' %s '; + private $footerTitleFormat = ' %s '; private $cellHeaderFormat = '%s'; private $cellRowFormat = '%s'; private $cellRowContentFormat = ' %s '; private $borderFormat = '%s'; - private $padType = STR_PAD_RIGHT; + private $padType = \STR_PAD_RIGHT; /** * Sets padding character, used for cell padding. * - * @param string $paddingChar - * - * @return TableStyle + * @return $this */ - public function setPaddingChar($paddingChar) + public function setPaddingChar(string $paddingChar) { if (!$paddingChar) { - throw new LogicException('The padding char must not be empty'); + throw new LogicException('The padding char must not be empty.'); } $this->paddingChar = $paddingChar; @@ -61,71 +75,132 @@ public function getPaddingChar() } /** - * Sets horizontal border character. + * Sets horizontal border characters. * - * @param string $horizontalBorderChar + * + * ╔═══════════════╤══════════════════════════╤══════════════════╗ + * 1 ISBN 2 Title │ Author ║ + * ╠═══════════════╪══════════════════════════╪══════════════════╣ + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * ╚═══════════════╧══════════════════════════╧══════════════════╝ + * * - * @return TableStyle + * @return $this */ - public function setHorizontalBorderChar($horizontalBorderChar) + public function setHorizontalBorderChars(string $outside, string $inside = null): self { - $this->horizontalBorderChar = $horizontalBorderChar; + $this->horizontalOutsideBorderChar = $outside; + $this->horizontalInsideBorderChar = $inside ?? $outside; return $this; } /** - * Gets horizontal border character. - * - * @return string - */ - public function getHorizontalBorderChar() - { - return $this->horizontalBorderChar; - } - - /** - * Sets vertical border character. + * Sets vertical border characters. * - * @param string $verticalBorderChar + * + * ╔═══════════════╤══════════════════════════╤══════════════════╗ + * ║ ISBN │ Title │ Author ║ + * ╠═══════1═══════╪══════════════════════════╪══════════════════╣ + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * ╟───────2───────┼──────────────────────────┼──────────────────╢ + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * ╚═══════════════╧══════════════════════════╧══════════════════╝ + * * - * @return TableStyle + * @return $this */ - public function setVerticalBorderChar($verticalBorderChar) + public function setVerticalBorderChars(string $outside, string $inside = null): self { - $this->verticalBorderChar = $verticalBorderChar; + $this->verticalOutsideBorderChar = $outside; + $this->verticalInsideBorderChar = $inside ?? $outside; return $this; } /** - * Gets vertical border character. + * Gets border characters. * - * @return string + * @internal */ - public function getVerticalBorderChar() + public function getBorderChars(): array { - return $this->verticalBorderChar; + return [ + $this->horizontalOutsideBorderChar, + $this->verticalOutsideBorderChar, + $this->horizontalInsideBorderChar, + $this->verticalInsideBorderChar, + ]; } /** - * Sets crossing character. + * Sets crossing characters. * - * @param string $crossingChar + * Example: + * + * 1═══════════════2══════════════════════════2══════════════════3 + * ║ ISBN │ Title │ Author ║ + * 8'══════════════0'═════════════════════════0'═════════════════4' + * ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + * ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + * 8───────────────0──────────────────────────0──────────────────4 + * ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + * ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + * 7═══════════════6══════════════════════════6══════════════════5 + * * - * @return TableStyle + * @param string $cross Crossing char (see #0 of example) + * @param string $topLeft Top left char (see #1 of example) + * @param string $topMid Top mid char (see #2 of example) + * @param string $topRight Top right char (see #3 of example) + * @param string $midRight Mid right char (see #4 of example) + * @param string $bottomRight Bottom right char (see #5 of example) + * @param string $bottomMid Bottom mid char (see #6 of example) + * @param string $bottomLeft Bottom left char (see #7 of example) + * @param string $midLeft Mid left char (see #8 of example) + * @param string|null $topLeftBottom Top left bottom char (see #8' of example), equals to $midLeft if null + * @param string|null $topMidBottom Top mid bottom char (see #0' of example), equals to $cross if null + * @param string|null $topRightBottom Top right bottom char (see #4' of example), equals to $midRight if null + * + * @return $this */ - public function setCrossingChar($crossingChar) + public function setCrossingChars(string $cross, string $topLeft, string $topMid, string $topRight, string $midRight, string $bottomRight, string $bottomMid, string $bottomLeft, string $midLeft, string $topLeftBottom = null, string $topMidBottom = null, string $topRightBottom = null): self { - $this->crossingChar = $crossingChar; + $this->crossingChar = $cross; + $this->crossingTopLeftChar = $topLeft; + $this->crossingTopMidChar = $topMid; + $this->crossingTopRightChar = $topRight; + $this->crossingMidRightChar = $midRight; + $this->crossingBottomRightChar = $bottomRight; + $this->crossingBottomMidChar = $bottomMid; + $this->crossingBottomLeftChar = $bottomLeft; + $this->crossingMidLeftChar = $midLeft; + $this->crossingTopLeftBottomChar = $topLeftBottom ?? $midLeft; + $this->crossingTopMidBottomChar = $topMidBottom ?? $cross; + $this->crossingTopRightBottomChar = $topRightBottom ?? $midRight; return $this; } + /** + * Sets default crossing character used for each cross. + * + * @see {@link setCrossingChars()} for setting each crossing individually. + */ + public function setDefaultCrossingChar(string $char): self + { + return $this->setCrossingChars($char, $char, $char, $char, $char, $char, $char, $char, $char); + } + /** * Gets crossing character. * - * @return string $crossingChar + * @return string */ public function getCrossingChar() { @@ -133,13 +208,34 @@ public function getCrossingChar() } /** - * Sets header cell format. + * Gets crossing characters. * - * @param string $cellHeaderFormat + * @internal + */ + public function getCrossingChars(): array + { + return [ + $this->crossingChar, + $this->crossingTopLeftChar, + $this->crossingTopMidChar, + $this->crossingTopRightChar, + $this->crossingMidRightChar, + $this->crossingBottomRightChar, + $this->crossingBottomMidChar, + $this->crossingBottomLeftChar, + $this->crossingMidLeftChar, + $this->crossingTopLeftBottomChar, + $this->crossingTopMidBottomChar, + $this->crossingTopRightBottomChar, + ]; + } + + /** + * Sets header cell format. * - * @return TableStyle + * @return $this */ - public function setCellHeaderFormat($cellHeaderFormat) + public function setCellHeaderFormat(string $cellHeaderFormat) { $this->cellHeaderFormat = $cellHeaderFormat; @@ -159,11 +255,9 @@ public function getCellHeaderFormat() /** * Sets row cell format. * - * @param string $cellRowFormat - * - * @return TableStyle + * @return $this */ - public function setCellRowFormat($cellRowFormat) + public function setCellRowFormat(string $cellRowFormat) { $this->cellRowFormat = $cellRowFormat; @@ -183,11 +277,9 @@ public function getCellRowFormat() /** * Sets row cell content format. * - * @param string $cellRowContentFormat - * - * @return TableStyle + * @return $this */ - public function setCellRowContentFormat($cellRowContentFormat) + public function setCellRowContentFormat(string $cellRowContentFormat) { $this->cellRowContentFormat = $cellRowContentFormat; @@ -207,11 +299,9 @@ public function getCellRowContentFormat() /** * Sets table border format. * - * @param string $borderFormat - * - * @return TableStyle + * @return $this */ - public function setBorderFormat($borderFormat) + public function setBorderFormat(string $borderFormat) { $this->borderFormat = $borderFormat; @@ -231,13 +321,11 @@ public function getBorderFormat() /** * Sets cell padding type. * - * @param int $padType STR_PAD_* - * - * @return TableStyle + * @return $this */ - public function setPadType($padType) + public function setPadType(int $padType) { - if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) { + if (!\in_array($padType, [\STR_PAD_LEFT, \STR_PAD_RIGHT, \STR_PAD_BOTH], true)) { throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); } @@ -255,4 +343,34 @@ public function getPadType() { return $this->padType; } + + public function getHeaderTitleFormat(): string + { + return $this->headerTitleFormat; + } + + /** + * @return $this + */ + public function setHeaderTitleFormat(string $format): self + { + $this->headerTitleFormat = $format; + + return $this; + } + + public function getFooterTitleFormat(): string + { + return $this->footerTitleFormat; + } + + /** + * @return $this + */ + public function setFooterTitleFormat(string $format): self + { + $this->footerTitleFormat = $format; + + return $this; + } } diff --git a/tests/integration/vendor/symfony/console/Input/ArgvInput.php b/tests/integration/vendor/symfony/console/Input/ArgvInput.php index f626c33e5..675b9ef58 100644 --- a/tests/integration/vendor/symfony/console/Input/ArgvInput.php +++ b/tests/integration/vendor/symfony/console/Input/ArgvInput.php @@ -43,17 +43,9 @@ class ArgvInput extends Input private $tokens; private $parsed; - /** - * Constructor. - * - * @param array|null $argv An array of parameters from the CLI (in the argv format) - * @param InputDefinition|null $definition A InputDefinition instance - */ public function __construct(array $argv = null, InputDefinition $definition = null) { - if (null === $argv) { - $argv = $_SERVER['argv']; - } + $argv = $argv ?? $_SERVER['argv'] ?? []; // strip the application name array_shift($argv); @@ -76,30 +68,35 @@ protected function parse() $parseOptions = true; $this->parsed = $this->tokens; while (null !== $token = array_shift($this->parsed)) { - if ($parseOptions && '' == $token) { - $this->parseArgument($token); - } elseif ($parseOptions && '--' == $token) { - $parseOptions = false; - } elseif ($parseOptions && 0 === strpos($token, '--')) { - $this->parseLongOption($token); - } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { - $this->parseShortOption($token); - } else { - $this->parseArgument($token); - } + $parseOptions = $this->parseToken($token, $parseOptions); + } + } + + protected function parseToken(string $token, bool $parseOptions): bool + { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + return false; + } elseif ($parseOptions && str_starts_with($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); } + + return $parseOptions; } /** * Parses a short option. - * - * @param string $token The current token */ - private function parseShortOption($token) + private function parseShortOption(string $token) { $name = substr($token, 1); - if (strlen($name) > 1) { + if (\strlen($name) > 1) { if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { // an option with a value (with no space) $this->addShortOption($name[0], substr($name, 1)); @@ -114,16 +111,15 @@ private function parseShortOption($token) /** * Parses a short option set. * - * @param string $name The current token - * * @throws RuntimeException When option given doesn't exist */ - private function parseShortOptionSet($name) + private function parseShortOptionSet(string $name) { - $len = strlen($name); + $len = \strlen($name); for ($i = 0; $i < $len; ++$i) { if (!$this->definition->hasShortcut($name[$i])) { - throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + $encoding = mb_detect_encoding($name, null, true); + throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding))); } $option = $this->definition->getOptionForShortcut($name[$i]); @@ -139,16 +135,14 @@ private function parseShortOptionSet($name) /** * Parses a long option. - * - * @param string $token The current token */ - private function parseLongOption($token) + private function parseLongOption(string $token) { $name = substr($token, 2); if (false !== $pos = strpos($name, '=')) { - if (0 === strlen($value = substr($name, $pos + 1))) { - array_unshift($this->parsed, null); + if ('' === $value = substr($name, $pos + 1)) { + array_unshift($this->parsed, $value); } $this->addLongOption(substr($name, 0, $pos), $value); } else { @@ -159,18 +153,16 @@ private function parseLongOption($token) /** * Parses an argument. * - * @param string $token The current token - * * @throws RuntimeException When too many arguments are given */ - private function parseArgument($token) + private function parseArgument(string $token) { - $c = count($this->arguments); + $c = \count($this->arguments); // if input is expecting another argument, add it if ($this->definition->hasArgument($c)) { $arg = $this->definition->getArgument($c); - $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token; + $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token; // if last argument isArray(), append token to last argument } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { @@ -180,23 +172,34 @@ private function parseArgument($token) // unexpected argument } else { $all = $this->definition->getArguments(); - if (count($all)) { - throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)))); + $symfonyCommandName = null; + if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) { + $symfonyCommandName = $this->arguments['command'] ?? null; + unset($all[$key]); + } + + if (\count($all)) { + if ($symfonyCommandName) { + $message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all))); + } else { + $message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))); + } + } elseif ($symfonyCommandName) { + $message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token); + } else { + $message = sprintf('No arguments expected, got "%s".', $token); } - throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token)); + throw new RuntimeException($message); } } /** * Adds a short option value. * - * @param string $shortcut The short option key - * @param mixed $value The value for the option - * * @throws RuntimeException When option given doesn't exist */ - private function addShortOption($shortcut, $value) + private function addShortOption(string $shortcut, $value) { if (!$this->definition->hasShortcut($shortcut)) { throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); @@ -208,36 +211,36 @@ private function addShortOption($shortcut, $value) /** * Adds a long option value. * - * @param string $name The long option key - * @param mixed $value The value for the option - * * @throws RuntimeException When option given doesn't exist */ - private function addLongOption($name, $value) + private function addLongOption(string $name, $value) { if (!$this->definition->hasOption($name)) { - throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); - } + if (!$this->definition->hasNegation($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } - $option = $this->definition->getOption($name); + $optionName = $this->definition->negationToName($name); + if (null !== $value) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + $this->options[$optionName] = false; - // Convert empty values to null - if (!isset($value[0])) { - $value = null; + return; } + $option = $this->definition->getOption($name); + if (null !== $value && !$option->acceptValue()) { throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); } - if (null === $value && $option->acceptValue() && count($this->parsed)) { + if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) { // if option accepts an optional or mandatory argument // let's see if there is one provided $next = array_shift($this->parsed); - if (isset($next[0]) && '-' !== $next[0]) { + if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) { $value = $next; - } elseif (empty($next)) { - $value = null; } else { array_unshift($this->parsed, $next); } @@ -248,8 +251,8 @@ private function addLongOption($name, $value) throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); } - if (!$option->isArray()) { - $value = $option->isValueOptional() ? $option->getDefault() : true; + if (!$option->isArray() && !$option->isValueOptional()) { + $value = true; } } @@ -265,28 +268,53 @@ private function addLongOption($name, $value) */ public function getFirstArgument() { - foreach ($this->tokens as $token) { + $isOption = false; + foreach ($this->tokens as $i => $token) { if ($token && '-' === $token[0]) { + if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) { + continue; + } + + // If it's a long option, consider that everything after "--" is the option name. + // Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator) + $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1); + if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) { + // noop + } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) { + $isOption = true; + } + + continue; + } + + if ($isOption) { + $isOption = false; continue; } return $token; } + + return null; } /** * {@inheritdoc} */ - public function hasParameterOption($values, $onlyParams = false) + public function hasParameterOption($values, bool $onlyParams = false) { $values = (array) $values; foreach ($this->tokens as $token) { - if ($onlyParams && $token === '--') { + if ($onlyParams && '--' === $token) { return false; } foreach ($values as $value) { - if ($token === $value || 0 === strpos($token, $value.'=')) { + // Options with values: + // For long options, test for '--option=' at beginning + // For short options, test for '-o' at beginning + $leading = str_starts_with($value, '--') ? $value.'=' : $value; + if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) { return true; } } @@ -298,25 +326,28 @@ public function hasParameterOption($values, $onlyParams = false) /** * {@inheritdoc} */ - public function getParameterOption($values, $default = false, $onlyParams = false) + public function getParameterOption($values, $default = false, bool $onlyParams = false) { $values = (array) $values; $tokens = $this->tokens; - while (0 < count($tokens)) { + while (0 < \count($tokens)) { $token = array_shift($tokens); - if ($onlyParams && $token === '--') { - return false; + if ($onlyParams && '--' === $token) { + return $default; } foreach ($values as $value) { - if ($token === $value || 0 === strpos($token, $value.'=')) { - if (false !== $pos = strpos($token, '=')) { - return substr($token, $pos + 1); - } - + if ($token === $value) { return array_shift($tokens); } + // Options with values: + // For long options, test for '--option=' at beginning + // For short options, test for '-o' at beginning + $leading = str_starts_with($value, '--') ? $value.'=' : $value; + if ('' !== $leading && str_starts_with($token, $leading)) { + return substr($token, \strlen($leading)); + } } } @@ -335,7 +366,7 @@ public function __toString() return $match[1].$this->escapeToken($match[2]); } - if ($token && $token[0] !== '-') { + if ($token && '-' !== $token[0]) { return $this->escapeToken($token); } diff --git a/tests/integration/vendor/symfony/console/Input/ArrayInput.php b/tests/integration/vendor/symfony/console/Input/ArrayInput.php index a44b6b281..c65161484 100644 --- a/tests/integration/vendor/symfony/console/Input/ArrayInput.php +++ b/tests/integration/vendor/symfony/console/Input/ArrayInput.php @@ -19,7 +19,7 @@ * * Usage: * - * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']); * * @author Fabien Potencier */ @@ -27,12 +27,6 @@ class ArrayInput extends Input { private $parameters; - /** - * Constructor. - * - * @param array $parameters An array of parameters - * @param InputDefinition|null $definition A InputDefinition instance - */ public function __construct(array $parameters, InputDefinition $definition = null) { $this->parameters = $parameters; @@ -45,32 +39,34 @@ public function __construct(array $parameters, InputDefinition $definition = nul */ public function getFirstArgument() { - foreach ($this->parameters as $key => $value) { - if ($key && '-' === $key[0]) { + foreach ($this->parameters as $param => $value) { + if ($param && \is_string($param) && '-' === $param[0]) { continue; } return $value; } + + return null; } /** * {@inheritdoc} */ - public function hasParameterOption($values, $onlyParams = false) + public function hasParameterOption($values, bool $onlyParams = false) { $values = (array) $values; foreach ($this->parameters as $k => $v) { - if (!is_int($k)) { + if (!\is_int($k)) { $v = $k; } - if ($onlyParams && $v === '--') { + if ($onlyParams && '--' === $v) { return false; } - if (in_array($v, $values)) { + if (\in_array($v, $values)) { return true; } } @@ -81,20 +77,20 @@ public function hasParameterOption($values, $onlyParams = false) /** * {@inheritdoc} */ - public function getParameterOption($values, $default = false, $onlyParams = false) + public function getParameterOption($values, $default = false, bool $onlyParams = false) { $values = (array) $values; foreach ($this->parameters as $k => $v) { - if ($onlyParams && ($k === '--' || (is_int($k) && $v === '--'))) { - return false; + if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) { + return $default; } - if (is_int($k)) { - if (in_array($v, $values)) { + if (\is_int($k)) { + if (\in_array($v, $values)) { return true; } - } elseif (in_array($k, $values)) { + } elseif (\in_array($k, $values)) { return $v; } } @@ -109,12 +105,19 @@ public function getParameterOption($values, $default = false, $onlyParams = fals */ public function __toString() { - $params = array(); + $params = []; foreach ($this->parameters as $param => $val) { - if ($param && '-' === $param[0]) { - $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + if ($param && \is_string($param) && '-' === $param[0]) { + $glue = ('-' === $param[1]) ? '=' : ' '; + if (\is_array($val)) { + foreach ($val as $v) { + $params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : ''); + } + } else { + $params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : ''); + } } else { - $params[] = $this->escapeToken($val); + $params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); } } @@ -127,12 +130,12 @@ public function __toString() protected function parse() { foreach ($this->parameters as $key => $value) { - if ($key === '--') { + if ('--' === $key) { return; } - if (0 === strpos($key, '--')) { + if (str_starts_with($key, '--')) { $this->addLongOption(substr($key, 2), $value); - } elseif ('-' === $key[0]) { + } elseif (str_starts_with($key, '-')) { $this->addShortOption(substr($key, 1), $value); } else { $this->addArgument($key, $value); @@ -143,12 +146,9 @@ protected function parse() /** * Adds a short option value. * - * @param string $shortcut The short option key - * @param mixed $value The value for the option - * * @throws InvalidOptionException When option given doesn't exist */ - private function addShortOption($shortcut, $value) + private function addShortOption(string $shortcut, $value) { if (!$this->definition->hasShortcut($shortcut)) { throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); @@ -160,16 +160,20 @@ private function addShortOption($shortcut, $value) /** * Adds a long option value. * - * @param string $name The long option key - * @param mixed $value The value for the option - * * @throws InvalidOptionException When option given doesn't exist * @throws InvalidOptionException When a required value is missing */ - private function addLongOption($name, $value) + private function addLongOption(string $name, $value) { if (!$this->definition->hasOption($name)) { - throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + if (!$this->definition->hasNegation($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $optionName = $this->definition->negationToName($name); + $this->options[$optionName] = false; + + return; } $option = $this->definition->getOption($name); @@ -179,7 +183,9 @@ private function addLongOption($name, $value) throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); } - $value = $option->isValueOptional() ? $option->getDefault() : true; + if (!$option->isValueOptional()) { + $value = true; + } } $this->options[$name] = $value; @@ -188,8 +194,8 @@ private function addLongOption($name, $value) /** * Adds an argument value. * - * @param string $name The argument name - * @param mixed $value The value for the argument + * @param string|int $name The argument name + * @param mixed $value The value for the argument * * @throws InvalidArgumentException When argument given doesn't exist */ diff --git a/tests/integration/vendor/symfony/console/Input/Input.php b/tests/integration/vendor/symfony/console/Input/Input.php index 474a02031..d37460ed3 100644 --- a/tests/integration/vendor/symfony/console/Input/Input.php +++ b/tests/integration/vendor/symfony/console/Input/Input.php @@ -27,20 +27,12 @@ */ abstract class Input implements InputInterface, StreamableInputInterface { - /** - * @var InputDefinition - */ protected $definition; protected $stream; - protected $options = array(); - protected $arguments = array(); + protected $options = []; + protected $arguments = []; protected $interactive = true; - /** - * Constructor. - * - * @param InputDefinition|null $definition A InputDefinition instance - */ public function __construct(InputDefinition $definition = null) { if (null === $definition) { @@ -56,8 +48,8 @@ public function __construct(InputDefinition $definition = null) */ public function bind(InputDefinition $definition) { - $this->arguments = array(); - $this->options = array(); + $this->arguments = []; + $this->options = []; $this->definition = $definition; $this->parse(); @@ -77,10 +69,10 @@ public function validate() $givenArguments = $this->arguments; $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { - return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); }); - if (count($missingArguments) > 0) { + if (\count($missingArguments) > 0) { throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); } } @@ -96,9 +88,9 @@ public function isInteractive() /** * {@inheritdoc} */ - public function setInteractive($interactive) + public function setInteractive(bool $interactive) { - $this->interactive = (bool) $interactive; + $this->interactive = $interactive; } /** @@ -112,19 +104,19 @@ public function getArguments() /** * {@inheritdoc} */ - public function getArgument($name) + public function getArgument(string $name) { if (!$this->definition->hasArgument($name)) { throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); } - return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault(); } /** * {@inheritdoc} */ - public function setArgument($name, $value) + public function setArgument(string $name, $value) { if (!$this->definition->hasArgument($name)) { throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); @@ -136,7 +128,7 @@ public function setArgument($name, $value) /** * {@inheritdoc} */ - public function hasArgument($name) + public function hasArgument(string $name) { return $this->definition->hasArgument($name); } @@ -152,21 +144,33 @@ public function getOptions() /** * {@inheritdoc} */ - public function getOption($name) + public function getOption(string $name) { + if ($this->definition->hasNegation($name)) { + if (null === $value = $this->getOption($this->definition->negationToName($name))) { + return $value; + } + + return !$value; + } + if (!$this->definition->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); } - return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); } /** * {@inheritdoc} */ - public function setOption($name, $value) + public function setOption(string $name, $value) { - if (!$this->definition->hasOption($name)) { + if ($this->definition->hasNegation($name)) { + $this->options[$this->definition->negationToName($name)] = !$value; + + return; + } elseif (!$this->definition->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); } @@ -176,19 +180,17 @@ public function setOption($name, $value) /** * {@inheritdoc} */ - public function hasOption($name) + public function hasOption(string $name) { - return $this->definition->hasOption($name); + return $this->definition->hasOption($name) || $this->definition->hasNegation($name); } /** * Escapes a token through escapeshellarg if it contains unsafe chars. * - * @param string $token - * * @return string */ - public function escapeToken($token) + public function escapeToken(string $token) { return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); } diff --git a/tests/integration/vendor/symfony/console/Input/InputArgument.php b/tests/integration/vendor/symfony/console/Input/InputArgument.php index 048ee4ff6..e891b9443 100644 --- a/tests/integration/vendor/symfony/console/Input/InputArgument.php +++ b/tests/integration/vendor/symfony/console/Input/InputArgument.php @@ -21,9 +21,9 @@ */ class InputArgument { - const REQUIRED = 1; - const OPTIONAL = 2; - const IS_ARRAY = 4; + public const REQUIRED = 1; + public const OPTIONAL = 2; + public const IS_ARRAY = 4; private $name; private $mode; @@ -31,20 +31,18 @@ class InputArgument private $description; /** - * Constructor. - * - * @param string $name The argument name - * @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (for self::OPTIONAL mode only) + * @param string $name The argument name + * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) * * @throws InvalidArgumentException When argument mode is not valid */ - public function __construct($name, $mode = null, $description = '', $default = null) + public function __construct(string $name, int $mode = null, string $description = '', $default = null) { if (null === $mode) { $mode = self::OPTIONAL; - } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + } elseif ($mode > 7 || $mode < 1) { throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); } @@ -58,7 +56,7 @@ public function __construct($name, $mode = null, $description = '', $default = n /** * Returns the argument name. * - * @return string The argument name + * @return string */ public function getName() { @@ -88,7 +86,7 @@ public function isArray() /** * Sets the default value. * - * @param mixed $default The default value + * @param string|bool|int|float|array|null $default * * @throws LogicException When incorrect default value is given */ @@ -100,8 +98,8 @@ public function setDefault($default = null) if ($this->isArray()) { if (null === $default) { - $default = array(); - } elseif (!is_array($default)) { + $default = []; + } elseif (!\is_array($default)) { throw new LogicException('A default value for an array argument must be an array.'); } } @@ -112,7 +110,7 @@ public function setDefault($default = null) /** * Returns the default value. * - * @return mixed The default value + * @return string|bool|int|float|array|null */ public function getDefault() { @@ -122,7 +120,7 @@ public function getDefault() /** * Returns the description text. * - * @return string The description text + * @return string */ public function getDescription() { diff --git a/tests/integration/vendor/symfony/console/Input/InputAwareInterface.php b/tests/integration/vendor/symfony/console/Input/InputAwareInterface.php index d0f11e986..5a288de5d 100644 --- a/tests/integration/vendor/symfony/console/Input/InputAwareInterface.php +++ b/tests/integration/vendor/symfony/console/Input/InputAwareInterface.php @@ -21,8 +21,6 @@ interface InputAwareInterface { /** * Sets the Console Input. - * - * @param InputInterface */ public function setInput(InputInterface $input); } diff --git a/tests/integration/vendor/symfony/console/Input/InputDefinition.php b/tests/integration/vendor/symfony/console/Input/InputDefinition.php index 85b778b22..11f704f0e 100644 --- a/tests/integration/vendor/symfony/console/Input/InputDefinition.php +++ b/tests/integration/vendor/symfony/console/Input/InputDefinition.php @@ -19,10 +19,10 @@ * * Usage: * - * $definition = new InputDefinition(array( - * new InputArgument('name', InputArgument::REQUIRED), - * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), - * )); + * $definition = new InputDefinition([ + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * ]); * * @author Fabien Potencier */ @@ -30,30 +30,27 @@ class InputDefinition { private $arguments; private $requiredCount; - private $hasAnArrayArgument = false; - private $hasOptional; + private $lastArrayArgument; + private $lastOptionalArgument; private $options; + private $negations; private $shortcuts; /** - * Constructor. - * * @param array $definition An array of InputArgument and InputOption instance */ - public function __construct(array $definition = array()) + public function __construct(array $definition = []) { $this->setDefinition($definition); } /** * Sets the definition of the input. - * - * @param array $definition The definition array */ public function setDefinition(array $definition) { - $arguments = array(); - $options = array(); + $arguments = []; + $options = []; foreach ($definition as $item) { if ($item instanceof InputOption) { $options[] = $item; @@ -71,12 +68,12 @@ public function setDefinition(array $definition) * * @param InputArgument[] $arguments An array of InputArgument objects */ - public function setArguments($arguments = array()) + public function setArguments(array $arguments = []) { - $this->arguments = array(); + $this->arguments = []; $this->requiredCount = 0; - $this->hasOptional = false; - $this->hasAnArrayArgument = false; + $this->lastOptionalArgument = null; + $this->lastArrayArgument = null; $this->addArguments($arguments); } @@ -85,7 +82,7 @@ public function setArguments($arguments = array()) * * @param InputArgument[] $arguments An array of InputArgument objects */ - public function addArguments($arguments = array()) + public function addArguments(?array $arguments = []) { if (null !== $arguments) { foreach ($arguments as $argument) { @@ -95,10 +92,6 @@ public function addArguments($arguments = array()) } /** - * Adds an InputArgument object. - * - * @param InputArgument $argument An InputArgument object - * * @throws LogicException When incorrect argument is given */ public function addArgument(InputArgument $argument) @@ -107,22 +100,22 @@ public function addArgument(InputArgument $argument) throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); } - if ($this->hasAnArrayArgument) { - throw new LogicException('Cannot add an argument after an array argument.'); + if (null !== $this->lastArrayArgument) { + throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName())); } - if ($argument->isRequired() && $this->hasOptional) { - throw new LogicException('Cannot add a required argument after an optional one.'); + if ($argument->isRequired() && null !== $this->lastOptionalArgument) { + throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName())); } if ($argument->isArray()) { - $this->hasAnArrayArgument = true; + $this->lastArrayArgument = $argument; } if ($argument->isRequired()) { ++$this->requiredCount; } else { - $this->hasOptional = true; + $this->lastOptionalArgument = $argument; } $this->arguments[$argument->getName()] = $argument; @@ -133,7 +126,7 @@ public function addArgument(InputArgument $argument) * * @param string|int $name The InputArgument name or position * - * @return InputArgument An InputArgument object + * @return InputArgument * * @throws InvalidArgumentException When argument given doesn't exist */ @@ -143,7 +136,7 @@ public function getArgument($name) throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); } - $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; return $arguments[$name]; } @@ -153,11 +146,11 @@ public function getArgument($name) * * @param string|int $name The InputArgument name or position * - * @return bool true if the InputArgument object exists, false otherwise + * @return bool */ public function hasArgument($name) { - $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; return isset($arguments[$name]); } @@ -165,7 +158,7 @@ public function hasArgument($name) /** * Gets the array of InputArgument objects. * - * @return InputArgument[] An array of InputArgument objects + * @return InputArgument[] */ public function getArguments() { @@ -175,17 +168,17 @@ public function getArguments() /** * Returns the number of InputArguments. * - * @return int The number of InputArguments + * @return int */ public function getArgumentCount() { - return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + return null !== $this->lastArrayArgument ? \PHP_INT_MAX : \count($this->arguments); } /** * Returns the number of required InputArguments. * - * @return int The number of required InputArguments + * @return int */ public function getArgumentRequiredCount() { @@ -193,13 +186,11 @@ public function getArgumentRequiredCount() } /** - * Gets the default values. - * - * @return array An array of default values + * @return array */ public function getArgumentDefaults() { - $values = array(); + $values = []; foreach ($this->arguments as $argument) { $values[$argument->getName()] = $argument->getDefault(); } @@ -212,10 +203,11 @@ public function getArgumentDefaults() * * @param InputOption[] $options An array of InputOption objects */ - public function setOptions($options = array()) + public function setOptions(array $options = []) { - $this->options = array(); - $this->shortcuts = array(); + $this->options = []; + $this->shortcuts = []; + $this->negations = []; $this->addOptions($options); } @@ -224,7 +216,7 @@ public function setOptions($options = array()) * * @param InputOption[] $options An array of InputOption objects */ - public function addOptions($options = array()) + public function addOptions(array $options = []) { foreach ($options as $option) { $this->addOption($option); @@ -232,10 +224,6 @@ public function addOptions($options = array()) } /** - * Adds an InputOption object. - * - * @param InputOption $option An InputOption object - * * @throws LogicException When option given already exist */ public function addOption(InputOption $option) @@ -243,6 +231,9 @@ public function addOption(InputOption $option) if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); } + if (isset($this->negations[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } if ($option->getShortcut()) { foreach (explode('|', $option->getShortcut()) as $shortcut) { @@ -258,18 +249,24 @@ public function addOption(InputOption $option) $this->shortcuts[$shortcut] = $option->getName(); } } + + if ($option->isNegatable()) { + $negatedName = 'no-'.$option->getName(); + if (isset($this->options[$negatedName])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName)); + } + $this->negations[$negatedName] = $option->getName(); + } } /** * Returns an InputOption by name. * - * @param string $name The InputOption name - * - * @return InputOption A InputOption object + * @return InputOption * * @throws InvalidArgumentException When option given doesn't exist */ - public function getOption($name) + public function getOption(string $name) { if (!$this->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); @@ -284,11 +281,9 @@ public function getOption($name) * This method can't be used to check if the user included the option when * executing the command (use getOption() instead). * - * @param string $name The InputOption name - * - * @return bool true if the InputOption object exists, false otherwise + * @return bool */ - public function hasOption($name) + public function hasOption(string $name) { return isset($this->options[$name]); } @@ -296,7 +291,7 @@ public function hasOption($name) /** * Gets the array of InputOption objects. * - * @return InputOption[] An array of InputOption objects + * @return InputOption[] */ public function getOptions() { @@ -306,35 +301,37 @@ public function getOptions() /** * Returns true if an InputOption object exists by shortcut. * - * @param string $name The InputOption shortcut - * - * @return bool true if the InputOption object exists, false otherwise + * @return bool */ - public function hasShortcut($name) + public function hasShortcut(string $name) { return isset($this->shortcuts[$name]); } + /** + * Returns true if an InputOption object exists by negated name. + */ + public function hasNegation(string $name): bool + { + return isset($this->negations[$name]); + } + /** * Gets an InputOption by shortcut. * - * @param string $shortcut the Shortcut name - * - * @return InputOption An InputOption object + * @return InputOption */ - public function getOptionForShortcut($shortcut) + public function getOptionForShortcut(string $shortcut) { return $this->getOption($this->shortcutToName($shortcut)); } /** - * Gets an array of default values. - * - * @return array An array of all default values + * @return array */ public function getOptionDefaults() { - $values = array(); + $values = []; foreach ($this->options as $option) { $values[$option->getName()] = $option->getDefault(); } @@ -345,13 +342,11 @@ public function getOptionDefaults() /** * Returns the InputOption name given a shortcut. * - * @param string $shortcut The shortcut - * - * @return string The InputOption name - * * @throws InvalidArgumentException When option given does not exist + * + * @internal */ - private function shortcutToName($shortcut) + public function shortcutToName(string $shortcut): string { if (!isset($this->shortcuts[$shortcut])) { throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); @@ -361,15 +356,29 @@ private function shortcutToName($shortcut) } /** - * Gets the synopsis. + * Returns the InputOption name given a negation. * - * @param bool $short Whether to return the short version (with options folded) or not + * @throws InvalidArgumentException When option given does not exist * - * @return string The synopsis + * @internal */ - public function getSynopsis($short = false) + public function negationToName(string $negation): string { - $elements = array(); + if (!isset($this->negations[$negation])) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation)); + } + + return $this->negations[$negation]; + } + + /** + * Gets the synopsis. + * + * @return string + */ + public function getSynopsis(bool $short = false) + { + $elements = []; if ($short && $this->getOptions()) { $elements[] = '[options]'; @@ -386,29 +395,30 @@ public function getSynopsis($short = false) } $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; - $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + $negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : ''; + $elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation); } } - if (count($elements) && $this->getArguments()) { + if (\count($elements) && $this->getArguments()) { $elements[] = '[--]'; } + $tail = ''; foreach ($this->getArguments() as $argument) { $element = '<'.$argument->getName().'>'; - if (!$argument->isRequired()) { - $element = '['.$element.']'; - } elseif ($argument->isArray()) { - $element = $element.' ('.$element.')'; - } - if ($argument->isArray()) { $element .= '...'; } + if (!$argument->isRequired()) { + $element = '['.$element; + $tail .= ']'; + } + $elements[] = $element; } - return implode(' ', $elements); + return implode(' ', $elements).$tail; } } diff --git a/tests/integration/vendor/symfony/console/Input/InputInterface.php b/tests/integration/vendor/symfony/console/Input/InputInterface.php index bc6646643..628b6037a 100644 --- a/tests/integration/vendor/symfony/console/Input/InputInterface.php +++ b/tests/integration/vendor/symfony/console/Input/InputInterface.php @@ -24,7 +24,7 @@ interface InputInterface /** * Returns the first argument from the raw parameters (not parsed). * - * @return string The value of the first argument or null otherwise + * @return string|null */ public function getFirstArgument(); @@ -33,32 +33,36 @@ public function getFirstArgument(); * * This method is to be used to introspect the input parameters * before they have been validated. It must be used carefully. + * Does not necessarily return the correct result for short options + * when multiple flags are combined in the same option. * * @param string|array $values The values to look for in the raw parameters (can be an array) * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal * - * @return bool true if the value is contained in the raw parameters + * @return bool */ - public function hasParameterOption($values, $onlyParams = false); + public function hasParameterOption($values, bool $onlyParams = false); /** * Returns the value of a raw option (not parsed). * * This method is to be used to introspect the input parameters * before they have been validated. It must be used carefully. + * Does not necessarily return the correct result for short options + * when multiple flags are combined in the same option. * - * @param string|array $values The value(s) to look for in the raw parameters (can be an array) - * @param mixed $default The default value to return if no result is found - * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param string|bool|int|float|array|null $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal * - * @return mixed The option value + * @return mixed */ - public function getParameterOption($values, $default = false, $onlyParams = false); + public function getParameterOption($values, $default = false, bool $onlyParams = false); /** * Binds the current Input instance with the given arguments and options. * - * @param InputDefinition $definition A InputDefinition instance + * @throws RuntimeException */ public function bind(InputDefinition $definition); @@ -72,76 +76,66 @@ public function validate(); /** * Returns all the given arguments merged with the default values. * - * @return array + * @return array */ public function getArguments(); /** * Returns the argument value for a given argument name. * - * @param string $name The argument name - * - * @return mixed The argument value + * @return mixed * * @throws InvalidArgumentException When argument given doesn't exist */ - public function getArgument($name); + public function getArgument(string $name); /** * Sets an argument value by name. * - * @param string $name The argument name - * @param string $value The argument value + * @param mixed $value The argument value * * @throws InvalidArgumentException When argument given doesn't exist */ - public function setArgument($name, $value); + public function setArgument(string $name, $value); /** * Returns true if an InputArgument object exists by name or position. * - * @param string|int $name The InputArgument name or position - * - * @return bool true if the InputArgument object exists, false otherwise + * @return bool */ - public function hasArgument($name); + public function hasArgument(string $name); /** * Returns all the given options merged with the default values. * - * @return array + * @return array */ public function getOptions(); /** * Returns the option value for a given option name. * - * @param string $name The option name - * - * @return mixed The option value + * @return mixed * * @throws InvalidArgumentException When option given doesn't exist */ - public function getOption($name); + public function getOption(string $name); /** * Sets an option value by name. * - * @param string $name The option name - * @param string|bool $value The option value + * @param mixed $value The option value * * @throws InvalidArgumentException When option given doesn't exist */ - public function setOption($name, $value); + public function setOption(string $name, $value); /** * Returns true if an InputOption object exists by name. * - * @param string $name The InputOption name - * - * @return bool true if the InputOption object exists, false otherwise + * @return bool */ - public function hasOption($name); + public function hasOption(string $name); /** * Is this input means interactive? @@ -152,8 +146,6 @@ public function isInteractive(); /** * Sets the input interactivity. - * - * @param bool $interactive If the input should be interactive */ - public function setInteractive($interactive); + public function setInteractive(bool $interactive); } diff --git a/tests/integration/vendor/symfony/console/Input/InputOption.php b/tests/integration/vendor/symfony/console/Input/InputOption.php index f08c5f26c..2bec34fe1 100644 --- a/tests/integration/vendor/symfony/console/Input/InputOption.php +++ b/tests/integration/vendor/symfony/console/Input/InputOption.php @@ -21,10 +21,30 @@ */ class InputOption { - const VALUE_NONE = 1; - const VALUE_REQUIRED = 2; - const VALUE_OPTIONAL = 4; - const VALUE_IS_ARRAY = 8; + /** + * Do not accept input for the option (e.g. --yell). This is the default behavior of options. + */ + public const VALUE_NONE = 1; + + /** + * A value must be passed when the option is used (e.g. --iterations=5 or -i5). + */ + public const VALUE_REQUIRED = 2; + + /** + * The option may or may not have a value (e.g. --yell or --yell=loud). + */ + public const VALUE_OPTIONAL = 4; + + /** + * The option accepts multiple values (e.g. --dir=/foo --dir=/bar). + */ + public const VALUE_IS_ARRAY = 8; + + /** + * The option may have either positive or negative value (e.g. --ansi or --no-ansi). + */ + public const VALUE_NEGATABLE = 16; private $name; private $shortcut; @@ -33,19 +53,15 @@ class InputOption private $description; /** - * Constructor. - * - * @param string $name The option name - * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int $mode The option mode: One of the VALUE_* constants - * @param string $description A description text - * @param mixed $default The default value (must be null for self::VALUE_NONE) + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE) * * @throws InvalidArgumentException If option mode is invalid or incompatible */ - public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null) { - if (0 === strpos($name, '--')) { + if (str_starts_with($name, '--')) { $name = substr($name, 2); } @@ -58,7 +74,7 @@ public function __construct($name, $shortcut = null, $mode = null, $description } if (null !== $shortcut) { - if (is_array($shortcut)) { + if (\is_array($shortcut)) { $shortcut = implode('|', $shortcut); } $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); @@ -72,7 +88,7 @@ public function __construct($name, $shortcut = null, $mode = null, $description if (null === $mode) { $mode = self::VALUE_NONE; - } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + } elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) { throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); } @@ -84,6 +100,9 @@ public function __construct($name, $shortcut = null, $mode = null, $description if ($this->isArray() && !$this->acceptValue()) { throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); } + if ($this->isNegatable() && $this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_NEGATABLE if the option also accepts a value.'); + } $this->setDefault($default); } @@ -91,7 +110,7 @@ public function __construct($name, $shortcut = null, $mode = null, $description /** * Returns the option shortcut. * - * @return string The shortcut + * @return string|null */ public function getShortcut() { @@ -101,7 +120,7 @@ public function getShortcut() /** * Returns the option name. * - * @return string The name + * @return string */ public function getName() { @@ -148,12 +167,13 @@ public function isArray() return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); } + public function isNegatable(): bool + { + return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode); + } + /** - * Sets the default value. - * - * @param mixed $default The default value - * - * @throws LogicException When incorrect default value is given + * @param string|bool|int|float|array|null $default */ public function setDefault($default = null) { @@ -163,19 +183,19 @@ public function setDefault($default = null) if ($this->isArray()) { if (null === $default) { - $default = array(); - } elseif (!is_array($default)) { + $default = []; + } elseif (!\is_array($default)) { throw new LogicException('A default value for an array option must be an array.'); } } - $this->default = $this->acceptValue() ? $default : false; + $this->default = $this->acceptValue() || $this->isNegatable() ? $default : false; } /** * Returns the default value. * - * @return mixed The default value + * @return string|bool|int|float|array|null */ public function getDefault() { @@ -185,7 +205,7 @@ public function getDefault() /** * Returns the description text. * - * @return string The description text + * @return string */ public function getDescription() { @@ -195,15 +215,14 @@ public function getDescription() /** * Checks whether the given option equals this one. * - * @param InputOption $option option to compare - * * @return bool */ - public function equals(InputOption $option) + public function equals(self $option) { return $option->getName() === $this->getName() && $option->getShortcut() === $this->getShortcut() && $option->getDefault() === $this->getDefault() + && $option->isNegatable() === $this->isNegatable() && $option->isArray() === $this->isArray() && $option->isValueRequired() === $this->isValueRequired() && $option->isValueOptional() === $this->isValueOptional() diff --git a/tests/integration/vendor/symfony/console/Input/StringInput.php b/tests/integration/vendor/symfony/console/Input/StringInput.php index 9ce021745..56bb66cbf 100644 --- a/tests/integration/vendor/symfony/console/Input/StringInput.php +++ b/tests/integration/vendor/symfony/console/Input/StringInput.php @@ -24,17 +24,16 @@ */ class StringInput extends ArgvInput { - const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); } @@ -42,31 +41,42 @@ public function __construct($input) /** * Tokenizes a string. * - * @param string $input The input to tokenize - * - * @return array An array of tokens - * * @throws InvalidArgumentException When unable to parse input (should never happen) */ - private function tokenize($input) + private function tokenize(string $input): array { - $tokens = array(); - $length = strlen($input); + $tokens = []; + $length = \strlen($input); $cursor = 0; + $token = null; while ($cursor < $length) { - if (preg_match('/\s+/A', $input, $match, null, $cursor)) { - } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { - $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); - } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { - $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); - } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { - $tokens[] = stripcslashes($match[1]); + if ('\\' === $input[$cursor]) { + $token .= $input[++$cursor] ?? ''; + ++$cursor; + continue; + } + + if (preg_match('/\s+/A', $input, $match, 0, $cursor)) { + if (null !== $token) { + $tokens[] = $token; + $token = null; + } + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) { + $token .= $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) { + $token .= stripcslashes(substr($match[0], 1, -1)); + } elseif (preg_match('/'.self::REGEX_UNQUOTED_STRING.'/A', $input, $match, 0, $cursor)) { + $token .= $match[1]; } else { // should never happen - throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10))); } - $cursor += strlen($match[0]); + $cursor += \strlen($match[0]); + } + + if (null !== $token) { + $tokens[] = $token; } return $tokens; diff --git a/tests/integration/vendor/symfony/console/LICENSE b/tests/integration/vendor/symfony/console/LICENSE index 12a74531e..88bf75bb4 100644 --- a/tests/integration/vendor/symfony/console/LICENSE +++ b/tests/integration/vendor/symfony/console/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/symfony/console/Logger/ConsoleLogger.php b/tests/integration/vendor/symfony/console/Logger/ConsoleLogger.php index 208575ceb..c9ee03561 100644 --- a/tests/integration/vendor/symfony/console/Logger/ConsoleLogger.php +++ b/tests/integration/vendor/symfony/console/Logger/ConsoleLogger.php @@ -14,29 +14,23 @@ use Psr\Log\AbstractLogger; use Psr\Log\InvalidArgumentException; use Psr\Log\LogLevel; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; /** * PSR-3 compliant console logger. * * @author Kévin Dunglas * - * @see http://www.php-fig.org/psr/psr-3/ + * @see https://www.php-fig.org/psr/psr-3/ */ class ConsoleLogger extends AbstractLogger { - const INFO = 'info'; - const ERROR = 'error'; + public const INFO = 'info'; + public const ERROR = 'error'; - /** - * @var OutputInterface - */ private $output; - /** - * @var array - */ - private $verbosityLevelMap = array( + private $verbosityLevelMap = [ LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, @@ -45,11 +39,8 @@ class ConsoleLogger extends AbstractLogger LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, - ); - /** - * @var array - */ - private $formatLevelMap = array( + ]; + private $formatLevelMap = [ LogLevel::EMERGENCY => self::ERROR, LogLevel::ALERT => self::ERROR, LogLevel::CRITICAL => self::ERROR, @@ -58,15 +49,10 @@ class ConsoleLogger extends AbstractLogger LogLevel::NOTICE => self::INFO, LogLevel::INFO => self::INFO, LogLevel::DEBUG => self::INFO, - ); + ]; private $errored = false; - /** - * @param OutputInterface $output - * @param array $verbosityLevelMap - * @param array $formatLevelMap - */ - public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array()) + public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = []) { $this->output = $output; $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; @@ -75,8 +61,10 @@ public function __construct(OutputInterface $output, array $verbosityLevelMap = /** * {@inheritdoc} + * + * @return void */ - public function log($level, $message, array $context = array()) + public function log($level, $message, array $context = []) { if (!isset($this->verbosityLevelMap[$level])) { throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); @@ -85,7 +73,7 @@ public function log($level, $message, array $context = array()) $output = $this->output; // Write to the error output if necessary and available - if ($this->formatLevelMap[$level] === self::ERROR) { + if (self::ERROR === $this->formatLevelMap[$level]) { if ($this->output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } @@ -101,6 +89,8 @@ public function log($level, $message, array $context = array()) /** * Returns true when any messages have been logged at error levels. + * + * @return bool */ public function hasErrored() { @@ -111,23 +101,26 @@ public function hasErrored() * Interpolates context values into the message placeholders. * * @author PHP Framework Interoperability Group - * - * @param string $message - * @param array $context - * - * @return string */ - private function interpolate($message, array $context) + private function interpolate(string $message, array $context): string { - // build a replacement array with braces around the context keys - $replace = array(); + if (!str_contains($message, '{')) { + return $message; + } + + $replacements = []; foreach ($context as $key => $val) { - if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { - $replace[sprintf('{%s}', $key)] = $val; + if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) { + $replacements["{{$key}}"] = $val; + } elseif ($val instanceof \DateTimeInterface) { + $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339); + } elseif (\is_object($val)) { + $replacements["{{$key}}"] = '[object '.\get_class($val).']'; + } else { + $replacements["{{$key}}"] = '['.\gettype($val).']'; } } - // interpolate replacement values into the message and return - return strtr($message, $replace); + return strtr($message, $replacements); } } diff --git a/tests/integration/vendor/symfony/console/Output/BufferedOutput.php b/tests/integration/vendor/symfony/console/Output/BufferedOutput.php index 5682fc240..d37c6e323 100644 --- a/tests/integration/vendor/symfony/console/Output/BufferedOutput.php +++ b/tests/integration/vendor/symfony/console/Output/BufferedOutput.php @@ -16,9 +16,6 @@ */ class BufferedOutput extends Output { - /** - * @var string - */ private $buffer = ''; /** @@ -37,12 +34,12 @@ public function fetch() /** * {@inheritdoc} */ - protected function doWrite($message, $newline) + protected function doWrite(string $message, bool $newline) { $this->buffer .= $message; if ($newline) { - $this->buffer .= "\n"; + $this->buffer .= \PHP_EOL; } } } diff --git a/tests/integration/vendor/symfony/console/Output/ConsoleOutput.php b/tests/integration/vendor/symfony/console/Output/ConsoleOutput.php index 007f3f01b..f19f9ebf4 100644 --- a/tests/integration/vendor/symfony/console/Output/ConsoleOutput.php +++ b/tests/integration/vendor/symfony/console/Output/ConsoleOutput.php @@ -29,22 +29,25 @@ */ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface { - /** - * @var StreamOutput - */ private $stderr; + private $consoleSectionOutputs = []; /** - * Constructor. - * * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) */ - public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + public function __construct(int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) { parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + if (null === $formatter) { + // for BC reasons, stdErr has it own Formatter only when user don't inject a specific formatter. + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated); + + return; + } + $actualDecorated = $this->isDecorated(); $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); @@ -53,10 +56,18 @@ public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = nu } } + /** + * Creates a new output section. + */ + public function section(): ConsoleSectionOutput + { + return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter()); + } + /** * {@inheritdoc} */ - public function setDecorated($decorated) + public function setDecorated(bool $decorated) { parent::setDecorated($decorated); $this->stderr->setDecorated($decorated); @@ -74,7 +85,7 @@ public function setFormatter(OutputFormatterInterface $formatter) /** * {@inheritdoc} */ - public function setVerbosity($level) + public function setVerbosity(int $level) { parent::setVerbosity($level); $this->stderr->setVerbosity($level); @@ -121,16 +132,14 @@ protected function hasStderrSupport() /** * Checks if current executing environment is IBM iSeries (OS400), which * doesn't properly convert character-encodings between ASCII to EBCDIC. - * - * @return bool */ - private function isRunningOS400() + private function isRunningOS400(): bool { - $checks = array( - function_exists('php_uname') ? php_uname('s') : '', + $checks = [ + \function_exists('php_uname') ? php_uname('s') : '', getenv('OSTYPE'), - PHP_OS, - ); + \PHP_OS, + ]; return false !== stripos(implode(';', $checks), 'OS400'); } @@ -144,7 +153,8 @@ private function openOutputStream() return fopen('php://output', 'w'); } - return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w'); + // Use STDOUT when possible to prevent from opening too many file descriptors + return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w')); } /** @@ -152,6 +162,11 @@ private function openOutputStream() */ private function openErrorStream() { - return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); + if (!$this->hasStderrSupport()) { + return fopen('php://output', 'w'); + } + + // Use STDERR when possible to prevent from opening too many file descriptors + return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w')); } } diff --git a/tests/integration/vendor/symfony/console/Output/ConsoleOutputInterface.php b/tests/integration/vendor/symfony/console/Output/ConsoleOutputInterface.php index 5eb4fc7ac..6b6635f58 100644 --- a/tests/integration/vendor/symfony/console/Output/ConsoleOutputInterface.php +++ b/tests/integration/vendor/symfony/console/Output/ConsoleOutputInterface.php @@ -13,7 +13,7 @@ /** * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. - * This adds information about stderr output stream. + * This adds information about stderr and section output stream. * * @author Dariusz Górecki */ @@ -26,10 +26,7 @@ interface ConsoleOutputInterface extends OutputInterface */ public function getErrorOutput(); - /** - * Sets the OutputInterface used for errors. - * - * @param OutputInterface $error - */ public function setErrorOutput(OutputInterface $error); + + public function section(): ConsoleSectionOutput; } diff --git a/tests/integration/vendor/symfony/console/Output/ConsoleSectionOutput.php b/tests/integration/vendor/symfony/console/Output/ConsoleSectionOutput.php new file mode 100644 index 000000000..8f1649758 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Output/ConsoleSectionOutput.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Terminal; + +/** + * @author Pierre du Plessis + * @author Gabriel Ostrolucký + */ +class ConsoleSectionOutput extends StreamOutput +{ + private $content = []; + private $lines = 0; + private $sections; + private $terminal; + + /** + * @param resource $stream + * @param ConsoleSectionOutput[] $sections + */ + public function __construct($stream, array &$sections, int $verbosity, bool $decorated, OutputFormatterInterface $formatter) + { + parent::__construct($stream, $verbosity, $decorated, $formatter); + array_unshift($sections, $this); + $this->sections = &$sections; + $this->terminal = new Terminal(); + } + + /** + * Clears previous output for this section. + * + * @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared + */ + public function clear(int $lines = null) + { + if (empty($this->content) || !$this->isDecorated()) { + return; + } + + if ($lines) { + array_splice($this->content, -($lines * 2)); // Multiply lines by 2 to cater for each new line added between content + } else { + $lines = $this->lines; + $this->content = []; + } + + $this->lines -= $lines; + + parent::doWrite($this->popStreamContentUntilCurrentSection($lines), false); + } + + /** + * Overwrites the previous output with a new message. + * + * @param array|string $message + */ + public function overwrite($message) + { + $this->clear(); + $this->writeln($message); + } + + public function getContent(): string + { + return implode('', $this->content); + } + + /** + * @internal + */ + public function addContent(string $input) + { + foreach (explode(\PHP_EOL, $input) as $lineContent) { + $this->lines += ceil($this->getDisplayLength($lineContent) / $this->terminal->getWidth()) ?: 1; + $this->content[] = $lineContent; + $this->content[] = \PHP_EOL; + } + } + + /** + * {@inheritdoc} + */ + protected function doWrite(string $message, bool $newline) + { + if (!$this->isDecorated()) { + parent::doWrite($message, $newline); + + return; + } + + $erasedContent = $this->popStreamContentUntilCurrentSection(); + + $this->addContent($message); + + parent::doWrite($message, true); + parent::doWrite($erasedContent, false); + } + + /** + * At initial stage, cursor is at the end of stream output. This method makes cursor crawl upwards until it hits + * current section. Then it erases content it crawled through. Optionally, it erases part of current section too. + */ + private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFromCurrentSection = 0): string + { + $numberOfLinesToClear = $numberOfLinesToClearFromCurrentSection; + $erasedContent = []; + + foreach ($this->sections as $section) { + if ($section === $this) { + break; + } + + $numberOfLinesToClear += $section->lines; + $erasedContent[] = $section->getContent(); + } + + if ($numberOfLinesToClear > 0) { + // move cursor up n lines + parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false); + // erase to end of screen + parent::doWrite("\x1b[0J", false); + } + + return implode('', array_reverse($erasedContent)); + } + + private function getDisplayLength(string $text): int + { + return Helper::width(Helper::removeDecoration($this->getFormatter(), str_replace("\t", ' ', $text))); + } +} diff --git a/tests/integration/vendor/symfony/console/Output/NullOutput.php b/tests/integration/vendor/symfony/console/Output/NullOutput.php index 218f285bf..3bbe63ea0 100644 --- a/tests/integration/vendor/symfony/console/Output/NullOutput.php +++ b/tests/integration/vendor/symfony/console/Output/NullOutput.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Console\Output; -use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\NullOutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterInterface; /** @@ -24,6 +24,8 @@ */ class NullOutput implements OutputInterface { + private $formatter; + /** * {@inheritdoc} */ @@ -37,14 +39,17 @@ public function setFormatter(OutputFormatterInterface $formatter) */ public function getFormatter() { + if ($this->formatter) { + return $this->formatter; + } // to comply with the interface we must return a OutputFormatterInterface - return new OutputFormatter(); + return $this->formatter = new NullOutputFormatter(); } /** * {@inheritdoc} */ - public function setDecorated($decorated) + public function setDecorated(bool $decorated) { // do nothing } @@ -60,7 +65,7 @@ public function isDecorated() /** * {@inheritdoc} */ - public function setVerbosity($level) + public function setVerbosity(int $level) { // do nothing } @@ -108,7 +113,7 @@ public function isDebug() /** * {@inheritdoc} */ - public function writeln($messages, $options = self::OUTPUT_NORMAL) + public function writeln($messages, int $options = self::OUTPUT_NORMAL) { // do nothing } @@ -116,7 +121,7 @@ public function writeln($messages, $options = self::OUTPUT_NORMAL) /** * {@inheritdoc} */ - public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + public function write($messages, bool $newline = false, int $options = self::OUTPUT_NORMAL) { // do nothing } diff --git a/tests/integration/vendor/symfony/console/Output/Output.php b/tests/integration/vendor/symfony/console/Output/Output.php index c12015cc8..d7c5fb2d1 100644 --- a/tests/integration/vendor/symfony/console/Output/Output.php +++ b/tests/integration/vendor/symfony/console/Output/Output.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Console\Output; -use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; /** * Base class for output classes. @@ -33,16 +33,14 @@ abstract class Output implements OutputInterface private $formatter; /** - * Constructor. - * - * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param int|null $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) * @param bool $decorated Whether to decorate messages * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) */ - public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null) { - $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; - $this->formatter = $formatter ?: new OutputFormatter(); + $this->verbosity = $verbosity ?? self::VERBOSITY_NORMAL; + $this->formatter = $formatter ?? new OutputFormatter(); $this->formatter->setDecorated($decorated); } @@ -65,7 +63,7 @@ public function getFormatter() /** * {@inheritdoc} */ - public function setDecorated($decorated) + public function setDecorated(bool $decorated) { $this->formatter->setDecorated($decorated); } @@ -81,9 +79,9 @@ public function isDecorated() /** * {@inheritdoc} */ - public function setVerbosity($level) + public function setVerbosity(int $level) { - $this->verbosity = (int) $level; + $this->verbosity = $level; } /** @@ -129,7 +127,7 @@ public function isDebug() /** * {@inheritdoc} */ - public function writeln($messages, $options = self::OUTPUT_NORMAL) + public function writeln($messages, int $options = self::OUTPUT_NORMAL) { $this->write($messages, true, $options); } @@ -137,9 +135,11 @@ public function writeln($messages, $options = self::OUTPUT_NORMAL) /** * {@inheritdoc} */ - public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + public function write($messages, bool $newline = false, int $options = self::OUTPUT_NORMAL) { - $messages = (array) $messages; + if (!is_iterable($messages)) { + $messages = [$messages]; + } $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; $type = $types & $options ?: self::OUTPUT_NORMAL; @@ -163,15 +163,12 @@ public function write($messages, $newline = false, $options = self::OUTPUT_NORMA break; } - $this->doWrite($message, $newline); + $this->doWrite($message ?? '', $newline); } } /** * Writes a message to the output. - * - * @param string $message A message to write to the output - * @param bool $newline Whether to add a newline or not */ - abstract protected function doWrite($message, $newline); + abstract protected function doWrite(string $message, bool $newline); } diff --git a/tests/integration/vendor/symfony/console/Output/OutputInterface.php b/tests/integration/vendor/symfony/console/Output/OutputInterface.php index a291ca7d7..55caab80b 100644 --- a/tests/integration/vendor/symfony/console/Output/OutputInterface.php +++ b/tests/integration/vendor/symfony/console/Output/OutputInterface.php @@ -20,94 +20,85 @@ */ interface OutputInterface { - const VERBOSITY_QUIET = 16; - const VERBOSITY_NORMAL = 32; - const VERBOSITY_VERBOSE = 64; - const VERBOSITY_VERY_VERBOSE = 128; - const VERBOSITY_DEBUG = 256; + public const VERBOSITY_QUIET = 16; + public const VERBOSITY_NORMAL = 32; + public const VERBOSITY_VERBOSE = 64; + public const VERBOSITY_VERY_VERBOSE = 128; + public const VERBOSITY_DEBUG = 256; - const OUTPUT_NORMAL = 1; - const OUTPUT_RAW = 2; - const OUTPUT_PLAIN = 4; + public const OUTPUT_NORMAL = 1; + public const OUTPUT_RAW = 2; + public const OUTPUT_PLAIN = 4; /** * Writes a message to the output. * - * @param string|array $messages The message as an array of lines or a single string - * @param bool $newline Whether to add a newline - * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + * @param string|iterable $messages The message as an iterable of strings or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL */ - public function write($messages, $newline = false, $options = 0); + public function write($messages, bool $newline = false, int $options = 0); /** * Writes a message to the output and adds a newline at the end. * - * @param string|array $messages The message as an array of lines of a single string - * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + * @param string|iterable $messages The message as an iterable of strings or a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL */ - public function writeln($messages, $options = 0); + public function writeln($messages, int $options = 0); /** * Sets the verbosity of the output. - * - * @param int $level The level of verbosity (one of the VERBOSITY constants) */ - public function setVerbosity($level); + public function setVerbosity(int $level); /** * Gets the current verbosity of the output. * - * @return int The current level of verbosity (one of the VERBOSITY constants) + * @return int */ public function getVerbosity(); /** * Returns whether verbosity is quiet (-q). * - * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise + * @return bool */ public function isQuiet(); /** * Returns whether verbosity is verbose (-v). * - * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise + * @return bool */ public function isVerbose(); /** * Returns whether verbosity is very verbose (-vv). * - * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise + * @return bool */ public function isVeryVerbose(); /** * Returns whether verbosity is debug (-vvv). * - * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise + * @return bool */ public function isDebug(); /** * Sets the decorated flag. - * - * @param bool $decorated Whether to decorate the messages */ - public function setDecorated($decorated); + public function setDecorated(bool $decorated); /** * Gets the decorated flag. * - * @return bool true if the output will decorate messages, false otherwise + * @return bool */ public function isDecorated(); - /** - * Sets output formatter. - * - * @param OutputFormatterInterface $formatter - */ public function setFormatter(OutputFormatterInterface $formatter); /** diff --git a/tests/integration/vendor/symfony/console/Output/StreamOutput.php b/tests/integration/vendor/symfony/console/Output/StreamOutput.php index 22b29aa17..7f5551827 100644 --- a/tests/integration/vendor/symfony/console/Output/StreamOutput.php +++ b/tests/integration/vendor/symfony/console/Output/StreamOutput.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Console\Output; use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatterInterface; /** @@ -20,11 +19,11 @@ * * Usage: * - * $output = new StreamOutput(fopen('php://stdout', 'w')); + * $output = new StreamOutput(fopen('php://stdout', 'w')); * * As `StreamOutput` can use any stream, you can also use a file: * - * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); * * @author Fabien Potencier */ @@ -33,8 +32,6 @@ class StreamOutput extends Output private $stream; /** - * Constructor. - * * @param resource $stream A stream resource * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) @@ -42,9 +39,9 @@ class StreamOutput extends Output * * @throws InvalidArgumentException When first argument is not a real stream */ - public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + public function __construct($stream, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) { - if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); } @@ -60,7 +57,7 @@ public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decor /** * Gets the stream attached to this StreamOutput instance. * - * @return resource A stream resource + * @return resource */ public function getStream() { @@ -70,13 +67,14 @@ public function getStream() /** * {@inheritdoc} */ - protected function doWrite($message, $newline) + protected function doWrite(string $message, bool $newline) { - if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) { - // should never happen - throw new RuntimeException('Unable to write output.'); + if ($newline) { + $message .= \PHP_EOL; } + @fwrite($this->stream, $message); + fflush($this->stream); } @@ -85,21 +83,33 @@ protected function doWrite($message, $newline) * * Colorization is disabled if not supported by the stream: * - * - Windows != 10.0.10586 without Ansicon, ConEmu or Mintty - * - non tty consoles + * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo + * terminals via named pipes, so we can only check the environment. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler * * @return bool true if the stream supports colorization, false otherwise */ protected function hasColorSupport() { - if (DIRECTORY_SEPARATOR === '\\') { - return - '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD + // Follow https://no-color.org/ + if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { + return false; + } + + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + + if (\DIRECTORY_SEPARATOR === '\\') { + return (\function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($this->stream)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } - return function_exists('posix_isatty') && @posix_isatty($this->stream); + return stream_isatty($this->stream); } } diff --git a/tests/integration/vendor/symfony/console/Output/TrimmedBufferOutput.php b/tests/integration/vendor/symfony/console/Output/TrimmedBufferOutput.php new file mode 100644 index 000000000..3f4d375f4 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Output/TrimmedBufferOutput.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * A BufferedOutput that keeps only the last N chars. + * + * @author Jérémy Derussé + */ +class TrimmedBufferOutput extends Output +{ + private $maxLength; + private $buffer = ''; + + public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, OutputFormatterInterface $formatter = null) + { + if ($maxLength <= 0) { + throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength)); + } + + parent::__construct($verbosity, $decorated, $formatter); + $this->maxLength = $maxLength; + } + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite(string $message, bool $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= \PHP_EOL; + } + + $this->buffer = substr($this->buffer, 0 - $this->maxLength); + } +} diff --git a/tests/integration/vendor/symfony/console/Question/ChoiceQuestion.php b/tests/integration/vendor/symfony/console/Question/ChoiceQuestion.php index 39c4a852d..bf1f90487 100644 --- a/tests/integration/vendor/symfony/console/Question/ChoiceQuestion.php +++ b/tests/integration/vendor/symfony/console/Question/ChoiceQuestion.php @@ -26,14 +26,16 @@ class ChoiceQuestion extends Question private $errorMessage = 'Value "%s" is invalid'; /** - * Constructor. - * * @param string $question The question to ask to the user * @param array $choices The list of available choices * @param mixed $default The default answer to return */ - public function __construct($question, array $choices, $default = null) + public function __construct(string $question, array $choices, $default = null) { + if (!$choices) { + throw new \LogicException('Choice question must have at least 1 choice available.'); + } + parent::__construct($question, $default); $this->choices = $choices; @@ -56,11 +58,9 @@ public function getChoices() * * When multiselect is set to true, multiple choices can be answered. * - * @param bool $multiselect - * - * @return ChoiceQuestion The current instance + * @return $this */ - public function setMultiselect($multiselect) + public function setMultiselect(bool $multiselect) { $this->multiselect = $multiselect; $this->setValidator($this->getDefaultValidator()); @@ -91,11 +91,9 @@ public function getPrompt() /** * Sets the prompt for choices. * - * @param string $prompt - * - * @return ChoiceQuestion The current instance + * @return $this */ - public function setPrompt($prompt) + public function setPrompt(string $prompt) { $this->prompt = $prompt; @@ -107,11 +105,9 @@ public function setPrompt($prompt) * * The error message has a string placeholder (%s) for the invalid value. * - * @param string $errorMessage - * - * @return ChoiceQuestion The current instance + * @return $this */ - public function setErrorMessage($errorMessage) + public function setErrorMessage(string $errorMessage) { $this->errorMessage = $errorMessage; $this->setValidator($this->getDefaultValidator()); @@ -119,12 +115,7 @@ public function setErrorMessage($errorMessage) return $this; } - /** - * Returns the default answer validator. - * - * @return callable - */ - private function getDefaultValidator() + private function getDefaultValidator(): callable { $choices = $this->choices; $errorMessage = $this->errorMessage; @@ -132,30 +123,34 @@ private function getDefaultValidator() $isAssoc = $this->isAssoc($choices); return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { - // Collapse all spaces. - $selectedChoices = str_replace(' ', '', $selected); - if ($multiselect) { // Check for a separated comma values - if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) { throw new InvalidArgumentException(sprintf($errorMessage, $selected)); } - $selectedChoices = explode(',', $selectedChoices); + + $selectedChoices = explode(',', (string) $selected); } else { - $selectedChoices = array($selected); + $selectedChoices = [$selected]; + } + + if ($this->isTrimmable()) { + foreach ($selectedChoices as $k => $v) { + $selectedChoices[$k] = trim((string) $v); + } } - $multiselectChoices = array(); + $multiselectChoices = []; foreach ($selectedChoices as $value) { - $results = array(); + $results = []; foreach ($choices as $key => $choice) { if ($choice === $value) { $results[] = $key; } } - if (count($results) > 1) { - throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + if (\count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of "%s".', implode('" or "', $results))); } $result = array_search($value, $choices); @@ -174,7 +169,8 @@ private function getDefaultValidator() throw new InvalidArgumentException(sprintf($errorMessage, $value)); } - $multiselectChoices[] = (string) $result; + // For associative choices, consistently return the key as string: + $multiselectChoices[] = $isAssoc ? (string) $result : $result; } if ($multiselect) { diff --git a/tests/integration/vendor/symfony/console/Question/ConfirmationQuestion.php b/tests/integration/vendor/symfony/console/Question/ConfirmationQuestion.php index 29d98879f..4228521b9 100644 --- a/tests/integration/vendor/symfony/console/Question/ConfirmationQuestion.php +++ b/tests/integration/vendor/symfony/console/Question/ConfirmationQuestion.php @@ -21,15 +21,13 @@ class ConfirmationQuestion extends Question private $trueAnswerRegex; /** - * Constructor. - * * @param string $question The question to ask to the user * @param bool $default The default answer to return, true or false * @param string $trueAnswerRegex A regex to match the "yes" answer */ - public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i') { - parent::__construct($question, (bool) $default); + parent::__construct($question, $default); $this->trueAnswerRegex = $trueAnswerRegex; $this->setNormalizer($this->getDefaultNormalizer()); @@ -37,16 +35,14 @@ public function __construct($question, $default = true, $trueAnswerRegex = '/^y/ /** * Returns the default answer normalizer. - * - * @return callable */ - private function getDefaultNormalizer() + private function getDefaultNormalizer(): callable { $default = $this->getDefault(); $regex = $this->trueAnswerRegex; return function ($answer) use ($default, $regex) { - if (is_bool($answer)) { + if (\is_bool($answer)) { return $answer; } @@ -55,7 +51,7 @@ private function getDefaultNormalizer() return $answer && $answerIsTrue; } - return !$answer || $answerIsTrue; + return '' === $answer || $answerIsTrue; }; } } diff --git a/tests/integration/vendor/symfony/console/Question/Question.php b/tests/integration/vendor/symfony/console/Question/Question.php index 7a69279f4..3a73f04b2 100644 --- a/tests/integration/vendor/symfony/console/Question/Question.php +++ b/tests/integration/vendor/symfony/console/Question/Question.php @@ -25,18 +25,18 @@ class Question private $attempts; private $hidden = false; private $hiddenFallback = true; - private $autocompleterValues; + private $autocompleterCallback; private $validator; private $default; private $normalizer; + private $trimmable = true; + private $multiline = false; /** - * Constructor. - * - * @param string $question The question to ask to the user - * @param mixed $default The default answer to return if the user enters nothing + * @param string $question The question to ask to the user + * @param string|bool|int|float|null $default The default answer to return if the user enters nothing */ - public function __construct($question, $default = null) + public function __construct(string $question, $default = null) { $this->question = $question; $this->default = $default; @@ -55,13 +55,33 @@ public function getQuestion() /** * Returns the default answer. * - * @return mixed + * @return string|bool|int|float|null */ public function getDefault() { return $this->default; } + /** + * Returns whether the user response accepts newline characters. + */ + public function isMultiline(): bool + { + return $this->multiline; + } + + /** + * Sets whether the user response should accept newline characters. + * + * @return $this + */ + public function setMultiline(bool $multiline): self + { + $this->multiline = $multiline; + + return $this; + } + /** * Returns whether the user response must be hidden. * @@ -75,25 +95,23 @@ public function isHidden() /** * Sets whether the user response must be hidden or not. * - * @param bool $hidden - * - * @return Question The current instance + * @return $this * * @throws LogicException In case the autocompleter is also used */ - public function setHidden($hidden) + public function setHidden(bool $hidden) { - if ($this->autocompleterValues) { + if ($this->autocompleterCallback) { throw new LogicException('A hidden question cannot use the autocompleter.'); } - $this->hidden = (bool) $hidden; + $this->hidden = $hidden; return $this; } /** - * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * In case the response cannot be hidden, whether to fallback on non-hidden question or not. * * @return bool */ @@ -103,15 +121,13 @@ public function isHiddenFallback() } /** - * Sets whether to fallback on non-hidden question if the response can not be hidden. - * - * @param bool $fallback + * Sets whether to fallback on non-hidden question if the response cannot be hidden. * - * @return Question The current instance + * @return $this */ - public function setHiddenFallback($fallback) + public function setHiddenFallback(bool $fallback) { - $this->hiddenFallback = (bool) $fallback; + $this->hiddenFallback = $fallback; return $this; } @@ -119,40 +135,64 @@ public function setHiddenFallback($fallback) /** * Gets values for the autocompleter. * - * @return null|array|\Traversable + * @return iterable|null */ public function getAutocompleterValues() { - return $this->autocompleterValues; + $callback = $this->getAutocompleterCallback(); + + return $callback ? $callback('') : null; } /** * Sets values for the autocompleter. * - * @param null|array|\Traversable $values + * @return $this * - * @return Question The current instance - * - * @throws InvalidArgumentException * @throws LogicException */ - public function setAutocompleterValues($values) + public function setAutocompleterValues(?iterable $values) { - if (is_array($values)) { + if (\is_array($values)) { $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); - } - if (null !== $values && !is_array($values)) { - if (!$values instanceof \Traversable || !$values instanceof \Countable) { - throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); - } + $callback = static function () use ($values) { + return $values; + }; + } elseif ($values instanceof \Traversable) { + $valueCache = null; + $callback = static function () use ($values, &$valueCache) { + return $valueCache ?? $valueCache = iterator_to_array($values, false); + }; + } else { + $callback = null; } - if ($this->hidden) { + return $this->setAutocompleterCallback($callback); + } + + /** + * Gets the callback function used for the autocompleter. + */ + public function getAutocompleterCallback(): ?callable + { + return $this->autocompleterCallback; + } + + /** + * Sets the callback function used for the autocompleter. + * + * The callback is passed the user input as argument and should return an iterable of corresponding suggestions. + * + * @return $this + */ + public function setAutocompleterCallback(callable $callback = null): self + { + if ($this->hidden && null !== $callback) { throw new LogicException('A hidden question cannot use the autocompleter.'); } - $this->autocompleterValues = $values; + $this->autocompleterCallback = $callback; return $this; } @@ -160,9 +200,7 @@ public function setAutocompleterValues($values) /** * Sets a validator for the question. * - * @param null|callable $validator - * - * @return Question The current instance + * @return $this */ public function setValidator(callable $validator = null) { @@ -174,7 +212,7 @@ public function setValidator(callable $validator = null) /** * Gets the validator for the question. * - * @return null|callable + * @return callable|null */ public function getValidator() { @@ -186,13 +224,11 @@ public function getValidator() * * Null means an unlimited number of attempts. * - * @param null|int $attempts - * - * @return Question The current instance + * @return $this * - * @throws InvalidArgumentException In case the number of attempts is invalid. + * @throws InvalidArgumentException in case the number of attempts is invalid */ - public function setMaxAttempts($attempts) + public function setMaxAttempts(?int $attempts) { if (null !== $attempts && $attempts < 1) { throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); @@ -208,7 +244,7 @@ public function setMaxAttempts($attempts) * * Null means an unlimited number of attempts. * - * @return null|int + * @return int|null */ public function getMaxAttempts() { @@ -220,9 +256,7 @@ public function getMaxAttempts() * * The normalizer can be a callable (a string), a closure or a class implementing __invoke. * - * @param callable $normalizer - * - * @return Question The current instance + * @return $this */ public function setNormalizer(callable $normalizer) { @@ -236,15 +270,30 @@ public function setNormalizer(callable $normalizer) * * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. * - * @return callable + * @return callable|null */ public function getNormalizer() { return $this->normalizer; } - protected function isAssoc($array) + protected function isAssoc(array $array) + { + return (bool) \count(array_filter(array_keys($array), 'is_string')); + } + + public function isTrimmable(): bool { - return (bool) count(array_filter(array_keys($array), 'is_string')); + return $this->trimmable; + } + + /** + * @return $this + */ + public function setTrimmable(bool $trimmable): self + { + $this->trimmable = $trimmable; + + return $this; } } diff --git a/tests/integration/vendor/symfony/console/README.md b/tests/integration/vendor/symfony/console/README.md index 664a37c0e..c4c129989 100644 --- a/tests/integration/vendor/symfony/console/README.md +++ b/tests/integration/vendor/symfony/console/README.md @@ -4,17 +4,33 @@ Console Component The Console component eases the creation of beautiful and testable command line interfaces. +Sponsor +------- + +The Console component for Symfony 5.4/6.0 is [backed][1] by [Les-Tilleuls.coop][2]. + +Les-Tilleuls.coop is a team of 50+ Symfony experts who can help you design, develop and +fix your projects. We provide a wide range of professional services including development, +consulting, coaching, training and audits. We also are highly skilled in JS, Go and DevOps. +We are a worker cooperative! + +Help Symfony by [sponsoring][3] its development! + Resources --------- - * [Documentation](https://symfony.com/doc/current/components/console/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/components/console.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) Credits ------- `Resources/bin/hiddeninput.exe` is a third party binary provided within this component. Find sources and license at https://github.com/Seldaek/hidden-input. + +[1]: https://symfony.com/backers +[2]: https://les-tilleuls.coop +[3]: https://symfony.com/sponsor diff --git a/tests/integration/vendor/symfony/console/Resources/completion.bash b/tests/integration/vendor/symfony/console/Resources/completion.bash new file mode 100644 index 000000000..c5e89c3c2 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Resources/completion.bash @@ -0,0 +1,81 @@ +# This file is part of the Symfony package. +# +# (c) Fabien Potencier +# +# For the full copyright and license information, please view +# https://symfony.com/doc/current/contributing/code/license.html + +_sf_{{ COMMAND_NAME }}() { + # Use newline as only separator to allow space in completion values + IFS=$'\n' + local sf_cmd="${COMP_WORDS[0]}" + + # for an alias, get the real script behind it + if [[ $(type -t $sf_cmd) == "alias" ]]; then + sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/") + fi + + if [ ! -f "$sf_cmd" ]; then + return 1 + fi + + local cur prev words cword + _get_comp_words_by_ref -n := cur prev words cword + + local completecmd=("$sf_cmd" "_complete" "-sbash" "-c$cword" "-S{{ VERSION }}") + for w in ${words[@]}; do + w=$(printf -- '%b' "$w") + # remove quotes from typed values + quote="${w:0:1}" + if [ "$quote" == \' ]; then + w="${w%\'}" + w="${w#\'}" + elif [ "$quote" == \" ]; then + w="${w%\"}" + w="${w#\"}" + fi + # empty values are ignored + if [ ! -z "$w" ]; then + completecmd+=("-i$w") + fi + done + + local sfcomplete + if sfcomplete=$(${completecmd[@]} 2>&1); then + local quote suggestions + quote=${cur:0:1} + + # Use single quotes by default if suggestions contains backslash (FQCN) + if [ "$quote" == '' ] && [[ "$sfcomplete" =~ \\ ]]; then + quote=\' + fi + + if [ "$quote" == \' ]; then + # single quotes: no additional escaping (does not accept ' in values) + suggestions=$(for s in $sfcomplete; do printf $'%q%q%q\n' "$quote" "$s" "$quote"; done) + elif [ "$quote" == \" ]; then + # double quotes: double escaping for \ $ ` " + suggestions=$(for s in $sfcomplete; do + s=${s//\\/\\\\} + s=${s//\$/\\\$} + s=${s//\`/\\\`} + s=${s//\"/\\\"} + printf $'%q%q%q\n' "$quote" "$s" "$quote"; + done) + else + # no quotes: double escaping + suggestions=$(for s in $sfcomplete; do printf $'%q\n' $(printf '%q' "$s"); done) + fi + COMPREPLY=($(IFS=$'\n' compgen -W "$suggestions" -- $(printf -- "%q" "$cur"))) + __ltrim_colon_completions "$cur" + else + if [[ "$sfcomplete" != *"Command \"_complete\" is not defined."* ]]; then + >&2 echo + >&2 echo $sfcomplete + fi + + return 1 + fi +} + +complete -F _sf_{{ COMMAND_NAME }} {{ COMMAND_NAME }} diff --git a/tests/integration/vendor/symfony/console/SignalRegistry/SignalRegistry.php b/tests/integration/vendor/symfony/console/SignalRegistry/SignalRegistry.php new file mode 100644 index 000000000..ed93dd062 --- /dev/null +++ b/tests/integration/vendor/symfony/console/SignalRegistry/SignalRegistry.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\SignalRegistry; + +final class SignalRegistry +{ + private $signalHandlers = []; + + public function __construct() + { + if (\function_exists('pcntl_async_signals')) { + pcntl_async_signals(true); + } + } + + public function register(int $signal, callable $signalHandler): void + { + if (!isset($this->signalHandlers[$signal])) { + $previousCallback = pcntl_signal_get_handler($signal); + + if (\is_callable($previousCallback)) { + $this->signalHandlers[$signal][] = $previousCallback; + } + } + + $this->signalHandlers[$signal][] = $signalHandler; + + pcntl_signal($signal, [$this, 'handle']); + } + + public static function isSupported(): bool + { + if (!\function_exists('pcntl_signal')) { + return false; + } + + if (\in_array('pcntl_signal', explode(',', ini_get('disable_functions')))) { + return false; + } + + return true; + } + + /** + * @internal + */ + public function handle(int $signal): void + { + $count = \count($this->signalHandlers[$signal]); + + foreach ($this->signalHandlers[$signal] as $i => $signalHandler) { + $hasNext = $i !== $count - 1; + $signalHandler($signal, $hasNext); + } + } +} diff --git a/tests/integration/vendor/symfony/console/SingleCommandApplication.php b/tests/integration/vendor/symfony/console/SingleCommandApplication.php new file mode 100644 index 000000000..e93c1821b --- /dev/null +++ b/tests/integration/vendor/symfony/console/SingleCommandApplication.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Grégoire Pineau + */ +class SingleCommandApplication extends Command +{ + private $version = 'UNKNOWN'; + private $autoExit = true; + private $running = false; + + /** + * @return $this + */ + public function setVersion(string $version): self + { + $this->version = $version; + + return $this; + } + + /** + * @final + * + * @return $this + */ + public function setAutoExit(bool $autoExit): self + { + $this->autoExit = $autoExit; + + return $this; + } + + public function run(InputInterface $input = null, OutputInterface $output = null): int + { + if ($this->running) { + return parent::run($input, $output); + } + + // We use the command name as the application name + $application = new Application($this->getName() ?: 'UNKNOWN', $this->version); + $application->setAutoExit($this->autoExit); + // Fix the usage of the command displayed with "--help" + $this->setName($_SERVER['argv'][0]); + $application->add($this); + $application->setDefaultCommand($this->getName(), true); + + $this->running = true; + try { + $ret = $application->run($input, $output); + } finally { + $this->running = false; + } + + return $ret ?? 1; + } +} diff --git a/tests/integration/vendor/symfony/console/Style/OutputStyle.php b/tests/integration/vendor/symfony/console/Style/OutputStyle.php index de7be1e08..67a98ff07 100644 --- a/tests/integration/vendor/symfony/console/Style/OutputStyle.php +++ b/tests/integration/vendor/symfony/console/Style/OutputStyle.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; /** @@ -24,9 +25,6 @@ abstract class OutputStyle implements OutputInterface, StyleInterface { private $output; - /** - * @param OutputInterface $output - */ public function __construct(OutputInterface $output) { $this->output = $output; @@ -35,17 +33,15 @@ public function __construct(OutputInterface $output) /** * {@inheritdoc} */ - public function newLine($count = 1) + public function newLine(int $count = 1) { - $this->output->write(str_repeat(PHP_EOL, $count)); + $this->output->write(str_repeat(\PHP_EOL, $count)); } /** - * @param int $max - * * @return ProgressBar */ - public function createProgressBar($max = 0) + public function createProgressBar(int $max = 0) { return new ProgressBar($this->output, $max); } @@ -53,7 +49,7 @@ public function createProgressBar($max = 0) /** * {@inheritdoc} */ - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + public function write($messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) { $this->output->write($messages, $newline, $type); } @@ -61,7 +57,7 @@ public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) /** * {@inheritdoc} */ - public function writeln($messages, $type = self::OUTPUT_NORMAL) + public function writeln($messages, int $type = self::OUTPUT_NORMAL) { $this->output->writeln($messages, $type); } @@ -69,7 +65,7 @@ public function writeln($messages, $type = self::OUTPUT_NORMAL) /** * {@inheritdoc} */ - public function setVerbosity($level) + public function setVerbosity(int $level) { $this->output->setVerbosity($level); } @@ -85,7 +81,7 @@ public function getVerbosity() /** * {@inheritdoc} */ - public function setDecorated($decorated) + public function setDecorated(bool $decorated) { $this->output->setDecorated($decorated); } @@ -145,4 +141,13 @@ public function isDebug() { return $this->output->isDebug(); } + + protected function getErrorOutput() + { + if (!$this->output instanceof ConsoleOutputInterface) { + return $this->output; + } + + return $this->output->getErrorOutput(); + } } diff --git a/tests/integration/vendor/symfony/console/Style/StyleInterface.php b/tests/integration/vendor/symfony/console/Style/StyleInterface.php index 2448547f8..38d23b77e 100644 --- a/tests/integration/vendor/symfony/console/Style/StyleInterface.php +++ b/tests/integration/vendor/symfony/console/Style/StyleInterface.php @@ -20,22 +20,16 @@ interface StyleInterface { /** * Formats a command title. - * - * @param string $message */ - public function title($message); + public function title(string $message); /** * Formats a section title. - * - * @param string $message */ - public function section($message); + public function section(string $message); /** * Formats a list. - * - * @param array $elements */ public function listing(array $elements); @@ -83,74 +77,53 @@ public function caution($message); /** * Formats a table. - * - * @param array $headers - * @param array $rows */ public function table(array $headers, array $rows); /** * Asks a question. * - * @param string $question - * @param string|null $default - * @param callable|null $validator - * - * @return string + * @return mixed */ - public function ask($question, $default = null, $validator = null); + public function ask(string $question, string $default = null, callable $validator = null); /** * Asks a question with the user input hidden. * - * @param string $question - * @param callable|null $validator - * - * @return string + * @return mixed */ - public function askHidden($question, $validator = null); + public function askHidden(string $question, callable $validator = null); /** * Asks for confirmation. * - * @param string $question - * @param bool $default - * * @return bool */ - public function confirm($question, $default = true); + public function confirm(string $question, bool $default = true); /** * Asks a choice question. * - * @param string $question - * @param array $choices * @param string|int|null $default * - * @return string + * @return mixed */ - public function choice($question, array $choices, $default = null); + public function choice(string $question, array $choices, $default = null); /** * Add newline(s). - * - * @param int $count The number of newlines */ - public function newLine($count = 1); + public function newLine(int $count = 1); /** * Starts the progress output. - * - * @param int $max Maximum steps (0 if unknown) */ - public function progressStart($max = 0); + public function progressStart(int $max = 0); /** * Advances the progress output X steps. - * - * @param int $step Number of steps to advance */ - public function progressAdvance($step = 1); + public function progressAdvance(int $step = 1); /** * Finishes the progress output. diff --git a/tests/integration/vendor/symfony/console/Style/SymfonyStyle.php b/tests/integration/vendor/symfony/console/Style/SymfonyStyle.php index b18d7c9a6..bcf30d80c 100644 --- a/tests/integration/vendor/symfony/console/Style/SymfonyStyle.php +++ b/tests/integration/vendor/symfony/console/Style/SymfonyStyle.php @@ -11,15 +11,19 @@ namespace Symfony\Component\Console\Style; +use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\SymfonyQuestionHelper; use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\TrimmedBufferOutput; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; @@ -32,70 +36,63 @@ */ class SymfonyStyle extends OutputStyle { - const MAX_LINE_LENGTH = 120; + public const MAX_LINE_LENGTH = 120; private $input; + private $output; private $questionHelper; private $progressBar; private $lineLength; private $bufferedOutput; - /** - * @param InputInterface $input - * @param OutputInterface $output - */ public function __construct(InputInterface $input, OutputInterface $output) { $this->input = $input; - $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + $this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter()); // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH; - $this->lineLength = min($width - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); - parent::__construct($output); + parent::__construct($this->output = $output); } /** * Formats a message as a block of text. * * @param string|array $messages The message to write in the block - * @param string|null $type The block type (added in [] on first line) - * @param string|null $style The style to apply to the whole block - * @param string $prefix The prefix for the block - * @param bool $padding Whether to add vertical padding */ - public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) + public function block($messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true) { - $messages = is_array($messages) ? array_values($messages) : array($messages); + $messages = \is_array($messages) ? array_values($messages) : [$messages]; $this->autoPrependBlock(); - $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, true)); + $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape)); $this->newLine(); } /** * {@inheritdoc} */ - public function title($message) + public function title(string $message) { $this->autoPrependBlock(); - $this->writeln(array( - sprintf('%s', $message), - sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), - )); + $this->writeln([ + sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + sprintf('%s', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), + ]); $this->newLine(); } /** * {@inheritdoc} */ - public function section($message) + public function section(string $message) { $this->autoPrependBlock(); - $this->writeln(array( - sprintf('%s', $message), - sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), - )); + $this->writeln([ + sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + sprintf('%s', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), + ]); $this->newLine(); } @@ -120,7 +117,7 @@ public function text($message) { $this->autoPrependText(); - $messages = is_array($message) ? array_values($message) : array($message); + $messages = \is_array($message) ? array_values($message) : [$message]; foreach ($messages as $message) { $this->writeln(sprintf(' %s', $message)); } @@ -133,11 +130,7 @@ public function text($message) */ public function comment($message) { - $messages = is_array($message) ? array_values($message) : array($message); - - $this->autoPrependBlock(); - $this->writeln($this->createBlock($messages, null, null, ' // ')); - $this->newLine(); + $this->block($message, null, null, ' // ', false, false); } /** @@ -161,7 +154,7 @@ public function error($message) */ public function warning($message) { - $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true); } /** @@ -172,6 +165,16 @@ public function note($message) $this->block($message, 'NOTE', 'fg=yellow', ' ! '); } + /** + * Formats an info message. + * + * @param string|array $message + */ + public function info($message) + { + $this->block($message, 'INFO', 'fg=green', ' ', true); + } + /** * {@inheritdoc} */ @@ -185,22 +188,69 @@ public function caution($message) */ public function table(array $headers, array $rows) { - $style = clone Table::getStyleDefinition('symfony-style-guide'); - $style->setCellHeaderFormat('%s'); + $this->createTable() + ->setHeaders($headers) + ->setRows($rows) + ->render() + ; + + $this->newLine(); + } - $table = new Table($this); - $table->setHeaders($headers); - $table->setRows($rows); - $table->setStyle($style); + /** + * Formats a horizontal table. + */ + public function horizontalTable(array $headers, array $rows) + { + $this->createTable() + ->setHorizontal(true) + ->setHeaders($headers) + ->setRows($rows) + ->render() + ; - $table->render(); $this->newLine(); } + /** + * Formats a list of key/value horizontally. + * + * Each row can be one of: + * * 'A title' + * * ['key' => 'value'] + * * new TableSeparator() + * + * @param string|array|TableSeparator ...$list + */ + public function definitionList(...$list) + { + $headers = []; + $row = []; + foreach ($list as $value) { + if ($value instanceof TableSeparator) { + $headers[] = $value; + $row[] = $value; + continue; + } + if (\is_string($value)) { + $headers[] = new TableCell($value, ['colspan' => 2]); + $row[] = null; + continue; + } + if (!\is_array($value)) { + throw new InvalidArgumentException('Value should be an array, string, or an instance of TableSeparator.'); + } + $headers[] = key($value); + $row[] = current($value); + } + + $this->horizontalTable($headers, [$row]); + } + /** * {@inheritdoc} */ - public function ask($question, $default = null, $validator = null) + public function ask(string $question, string $default = null, callable $validator = null) { $question = new Question($question, $default); $question->setValidator($validator); @@ -211,7 +261,7 @@ public function ask($question, $default = null, $validator = null) /** * {@inheritdoc} */ - public function askHidden($question, $validator = null) + public function askHidden(string $question, callable $validator = null) { $question = new Question($question); @@ -224,7 +274,7 @@ public function askHidden($question, $validator = null) /** * {@inheritdoc} */ - public function confirm($question, $default = true) + public function confirm(string $question, bool $default = true) { return $this->askQuestion(new ConfirmationQuestion($question, $default)); } @@ -232,11 +282,11 @@ public function confirm($question, $default = true) /** * {@inheritdoc} */ - public function choice($question, array $choices, $default = null) + public function choice(string $question, array $choices, $default = null) { if (null !== $default) { $values = array_flip($choices); - $default = $values[$default]; + $default = $values[$default] ?? $default; } return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); @@ -245,7 +295,7 @@ public function choice($question, array $choices, $default = null) /** * {@inheritdoc} */ - public function progressStart($max = 0) + public function progressStart(int $max = 0) { $this->progressBar = $this->createProgressBar($max); $this->progressBar->start(); @@ -254,7 +304,7 @@ public function progressStart($max = 0) /** * {@inheritdoc} */ - public function progressAdvance($step = 1) + public function progressAdvance(int $step = 1) { $this->getProgressBar()->advance($step); } @@ -272,11 +322,11 @@ public function progressFinish() /** * {@inheritdoc} */ - public function createProgressBar($max = 0) + public function createProgressBar(int $max = 0) { $progressBar = parent::createProgressBar($max); - if ('\\' !== DIRECTORY_SEPARATOR) { + if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) { $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 $progressBar->setProgressCharacter(''); $progressBar->setBarCharacter('▓'); // dark shade character \u2593 @@ -286,9 +336,17 @@ public function createProgressBar($max = 0) } /** - * @param Question $question - * - * @return string + * @see ProgressBar::iterate() + */ + public function progressIterate(iterable $iterable, int $max = null): iterable + { + yield from $this->createProgressBar()->iterate($iterable, $max); + + $this->newLine(2); + } + + /** + * @return mixed */ public function askQuestion(Question $question) { @@ -313,34 +371,62 @@ public function askQuestion(Question $question) /** * {@inheritdoc} */ - public function writeln($messages, $type = self::OUTPUT_NORMAL) + public function writeln($messages, int $type = self::OUTPUT_NORMAL) { - parent::writeln($messages, $type); - $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + if (!is_iterable($messages)) { + $messages = [$messages]; + } + + foreach ($messages as $message) { + parent::writeln($message, $type); + $this->writeBuffer($message, true, $type); + } } /** * {@inheritdoc} */ - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + public function write($messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) { - parent::write($messages, $newline, $type); - $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + if (!is_iterable($messages)) { + $messages = [$messages]; + } + + foreach ($messages as $message) { + parent::write($message, $newline, $type); + $this->writeBuffer($message, $newline, $type); + } } /** * {@inheritdoc} */ - public function newLine($count = 1) + public function newLine(int $count = 1) { parent::newLine($count); $this->bufferedOutput->write(str_repeat("\n", $count)); } /** - * @return ProgressBar + * Returns a new instance which makes use of stderr if available. + * + * @return self */ - private function getProgressBar() + public function getErrorStyle() + { + return new self($this->input, $this->getErrorOutput()); + } + + public function createTable(): Table + { + $output = $this->output instanceof ConsoleOutputInterface ? $this->output->section() : $this->output; + $style = clone Table::getStyleDefinition('symfony-style-guide'); + $style->setCellHeaderFormat('%s'); + + return (new Table($output))->setStyle($style); + } + + private function getProgressBar(): ProgressBar { if (!$this->progressBar) { throw new RuntimeException('The ProgressBar is not started.'); @@ -349,44 +435,43 @@ private function getProgressBar() return $this->progressBar; } - private function autoPrependBlock() + private function autoPrependBlock(): void { - $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + $chars = substr(str_replace(\PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); if (!isset($chars[0])) { - return $this->newLine(); //empty history, so we should start with a new line. + $this->newLine(); //empty history, so we should start with a new line. + + return; } //Prepend new line for each non LF chars (This means no blank line was output before) $this->newLine(2 - substr_count($chars, "\n")); } - private function autoPrependText() + private function autoPrependText(): void { $fetched = $this->bufferedOutput->fetch(); //Prepend new line if last char isn't EOL: - if ("\n" !== substr($fetched, -1)) { + if (!str_ends_with($fetched, "\n")) { $this->newLine(); } } - private function reduceBuffer($messages) + private function writeBuffer(string $message, bool $newLine, int $type): void { - // We need to know if the two last chars are PHP_EOL - // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer - return array_map(function ($value) { - return substr($value, -4); - }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); + // We need to know if the last chars are PHP_EOL + $this->bufferedOutput->write($message, $newLine, $type); } - private function createBlock($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = false) + private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array { $indentLength = 0; - $prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix); - $lines = array(); + $prefixLength = Helper::width(Helper::removeDecoration($this->getFormatter(), $prefix)); + $lines = []; if (null !== $type) { $type = sprintf('[%s] ', $type); - $indentLength = strlen($type); + $indentLength = \strlen($type); $lineIndentation = str_repeat(' ', $indentLength); } @@ -396,9 +481,14 @@ private function createBlock($messages, $type = null, $style = null, $prefix = ' $message = OutputFormatter::escape($message); } - $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, PHP_EOL, true))); + $decorationLength = Helper::width($message) - Helper::width(Helper::removeDecoration($this->getFormatter(), $message)); + $messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength); + $messageLines = explode(\PHP_EOL, wordwrap($message, $messageLineLength, \PHP_EOL, true)); + foreach ($messageLines as $messageLine) { + $lines[] = $messageLine; + } - if (count($messages) > 1 && $key < count($messages) - 1) { + if (\count($messages) > 1 && $key < \count($messages) - 1) { $lines[] = ''; } } @@ -416,7 +506,7 @@ private function createBlock($messages, $type = null, $style = null, $prefix = ' } $line = $prefix.$line; - $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + $line .= str_repeat(' ', max($this->lineLength - Helper::width(Helper::removeDecoration($this->getFormatter(), $line)), 0)); if ($style) { $line = sprintf('<%s>%s', $style, $line); diff --git a/tests/integration/vendor/symfony/console/Terminal.php b/tests/integration/vendor/symfony/console/Terminal.php index ddef70d7b..08c53535b 100644 --- a/tests/integration/vendor/symfony/console/Terminal.php +++ b/tests/integration/vendor/symfony/console/Terminal.php @@ -15,16 +15,18 @@ class Terminal { private static $width; private static $height; + private static $stty; /** * Gets the terminal width. * - * @return int|null + * @return int */ public function getWidth() { - if ($width = trim(getenv('COLUMNS'))) { - return (int) $width; + $width = getenv('COLUMNS'); + if (false !== $width) { + return (int) trim($width); } if (null === self::$width) { @@ -37,12 +39,13 @@ public function getWidth() /** * Gets the terminal height. * - * @return int|null + * @return int */ public function getHeight() { - if ($height = trim(getenv('LINES'))) { - return (int) $height; + $height = getenv('LINES'); + if (false !== $height) { + return (int) trim($height); } if (null === self::$height) { @@ -52,20 +55,61 @@ public function getHeight() return self::$height ?: 50; } + /** + * @internal + */ + public static function hasSttyAvailable(): bool + { + if (null !== self::$stty) { + return self::$stty; + } + + // skip check if exec function is disabled + if (!\function_exists('exec')) { + return false; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = 0 === $exitcode; + } + private static function initDimensions() { - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' === \DIRECTORY_SEPARATOR) { if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) { // extract [w, H] from "wxh (WxH)" // or [w, h] from "wxh" self::$width = (int) $matches[1]; self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2]; + } elseif (!self::hasVt100Support() && self::hasSttyAvailable()) { + // only use stty on Windows if the terminal does not support vt100 (e.g. Windows 7 + git-bash) + // testing for stty in a Windows 10 vt100-enabled console will implicitly disable vt100 support on STDOUT + self::initDimensionsUsingStty(); } elseif (null !== $dimensions = self::getConsoleMode()) { // extract [w, h] from "wxh" self::$width = (int) $dimensions[0]; self::$height = (int) $dimensions[1]; } - } elseif ($sttyString = self::getSttyColumns()) { + } else { + self::initDimensionsUsingStty(); + } + } + + /** + * Returns whether STDOUT has vt100 support (some Windows 10+ configurations). + */ + private static function hasVt100Support(): bool + { + return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w')); + } + + /** + * Initializes dimensions using the output of an stty columns line. + */ + private static function initDimensionsUsingStty() + { + if ($sttyString = self::getSttyColumns()) { if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { // extract [w, h] from "rows h; columns w;" self::$width = (int) $matches[2]; @@ -83,53 +127,46 @@ private static function initDimensions() * * @return int[]|null An array composed of the width and the height or null if it could not be parsed */ - private static function getConsoleMode() + private static function getConsoleMode(): ?array { - if (!function_exists('proc_open')) { - return; - } + $info = self::readFromProcess('mode CON'); - $descriptorspec = array( - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w'), - ); - $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); - if (is_resource($process)) { - $info = stream_get_contents($pipes[1]); - fclose($pipes[1]); - fclose($pipes[2]); - proc_close($process); - - if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { - return array((int) $matches[2], (int) $matches[1]); - } + if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return null; } + + return [(int) $matches[2], (int) $matches[1]]; } /** * Runs and parses stty -a if it's available, suppressing any error output. - * - * @return string|null */ - private static function getSttyColumns() + private static function getSttyColumns(): ?string { - if (!function_exists('proc_open')) { - return; - } + return self::readFromProcess('stty -a | grep columns'); + } - $descriptorspec = array( - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w'), - ); + private static function readFromProcess(string $command): ?string + { + if (!\function_exists('proc_open')) { + return null; + } - $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); - if (is_resource($process)) { - $info = stream_get_contents($pipes[1]); - fclose($pipes[1]); - fclose($pipes[2]); - proc_close($process); + $descriptorspec = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; - return $info; + $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + if (!\is_resource($process)) { + return null; } + + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; } } diff --git a/tests/integration/vendor/symfony/console/Tester/ApplicationTester.php b/tests/integration/vendor/symfony/console/Tester/ApplicationTester.php index c0f8c7207..19a95c8e4 100644 --- a/tests/integration/vendor/symfony/console/Tester/ApplicationTester.php +++ b/tests/integration/vendor/symfony/console/Tester/ApplicationTester.php @@ -13,10 +13,6 @@ use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\StreamOutput; /** * Eases the testing of console applications. @@ -30,14 +26,9 @@ */ class ApplicationTester { + use TesterTrait; + private $application; - private $input; - private $statusCode; - /** - * @var OutputInterface - */ - private $output; - private $captureStreamsIndependently = false; public function __construct(Application $application) { @@ -54,123 +45,21 @@ public function __construct(Application $application) * * verbosity: Sets the output verbosity flag * * capture_stderr_separately: Make output of stdOut and stdErr separately available * - * @param array $input An array of arguments and options - * @param array $options An array of options - * * @return int The command exit code */ - public function run(array $input, $options = array()) + public function run(array $input, array $options = []) { $this->input = new ArrayInput($input); if (isset($options['interactive'])) { $this->input->setInteractive($options['interactive']); } - $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; - if (!$this->captureStreamsIndependently) { - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) { - $this->output->setDecorated($options['decorated']); - } - if (isset($options['verbosity'])) { - $this->output->setVerbosity($options['verbosity']); - } - } else { - $this->output = new ConsoleOutput( - isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL, - isset($options['decorated']) ? $options['decorated'] : null - ); - - $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); - $errorOutput->setFormatter($this->output->getFormatter()); - $errorOutput->setVerbosity($this->output->getVerbosity()); - $errorOutput->setDecorated($this->output->isDecorated()); - - $reflectedOutput = new \ReflectionObject($this->output); - $strErrProperty = $reflectedOutput->getProperty('stderr'); - $strErrProperty->setAccessible(true); - $strErrProperty->setValue($this->output, $errorOutput); - - $reflectedParent = $reflectedOutput->getParentClass(); - $streamProperty = $reflectedParent->getProperty('stream'); - $streamProperty->setAccessible(true); - $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); - } - - return $this->statusCode = $this->application->run($this->input, $this->output); - } - - /** - * Gets the display returned by the last execution of the application. - * - * @param bool $normalize Whether to normalize end of lines to \n or not - * - * @return string The display - */ - public function getDisplay($normalize = false) - { - rewind($this->output->getStream()); - - $display = stream_get_contents($this->output->getStream()); - - if ($normalize) { - $display = str_replace(PHP_EOL, "\n", $display); - } - - return $display; - } - - /** - * Gets the output written to STDERR by the application. - * - * @param bool $normalize Whether to normalize end of lines to \n or not - * - * @return string - */ - public function getErrorOutput($normalize = false) - { - if (!$this->captureStreamsIndependently) { - throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); - } - - rewind($this->output->getErrorOutput()->getStream()); - - $display = stream_get_contents($this->output->getErrorOutput()->getStream()); - - if ($normalize) { - $display = str_replace(PHP_EOL, "\n", $display); + if ($this->inputs) { + $this->input->setStream(self::createStream($this->inputs)); } - return $display; - } + $this->initOutput($options); - /** - * Gets the input instance used by the last execution of the application. - * - * @return InputInterface The current input instance - */ - public function getInput() - { - return $this->input; - } - - /** - * Gets the output instance used by the last execution of the application. - * - * @return OutputInterface The current output instance - */ - public function getOutput() - { - return $this->output; - } - - /** - * Gets the status code returned by the last execution of the application. - * - * @return int The status code - */ - public function getStatusCode() - { - return $this->statusCode; + return $this->statusCode = $this->application->run($this->input, $this->output); } } diff --git a/tests/integration/vendor/symfony/console/Tester/CommandCompletionTester.php b/tests/integration/vendor/symfony/console/Tester/CommandCompletionTester.php new file mode 100644 index 000000000..ade732752 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Tester/CommandCompletionTester.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; + +/** + * Eases the testing of command completion. + * + * @author Jérôme Tamarelle + */ +class CommandCompletionTester +{ + private $command; + + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Create completion suggestions from input tokens. + */ + public function complete(array $input): array + { + $currentIndex = \count($input); + if ('' === end($input)) { + array_pop($input); + } + array_unshift($input, $this->command->getName()); + + $completionInput = CompletionInput::fromTokens($input, $currentIndex); + $completionInput->bind($this->command->getDefinition()); + $suggestions = new CompletionSuggestions(); + + $this->command->complete($completionInput, $suggestions); + + $options = []; + foreach ($suggestions->getOptionSuggestions() as $option) { + $options[] = '--'.$option->getName(); + } + + return array_map('strval', array_merge($options, $suggestions->getValueSuggestions())); + } +} diff --git a/tests/integration/vendor/symfony/console/Tester/CommandTester.php b/tests/integration/vendor/symfony/console/Tester/CommandTester.php index 2c547a8f6..6c15c25fb 100644 --- a/tests/integration/vendor/symfony/console/Tester/CommandTester.php +++ b/tests/integration/vendor/symfony/console/Tester/CommandTester.php @@ -13,9 +13,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\StreamOutput; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; /** * Eases the testing of console commands. @@ -25,17 +22,10 @@ */ class CommandTester { + use TesterTrait; + private $command; - private $input; - private $output; - private $inputs = array(); - private $statusCode; - /** - * Constructor. - * - * @param Command $command A Command instance to test - */ public function __construct(Command $command) { $this->command = $command; @@ -46,16 +36,17 @@ public function __construct(Command $command) * * Available execution options: * - * * interactive: Sets the input interactive flag - * * decorated: Sets the output decorated flag - * * verbosity: Sets the output verbosity flag + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available * * @param array $input An array of command arguments and options * @param array $options An array of execution options * * @return int The command exit code */ - public function execute(array $input, array $options = array()) + public function execute(array $input, array $options = []) { // set the command name automatically if the application requires // this argument and no command name was passed @@ -63,101 +54,23 @@ public function execute(array $input, array $options = array()) && (null !== $application = $this->command->getApplication()) && $application->getDefinition()->hasArgument('command') ) { - $input = array_merge(array('command' => $this->command->getName()), $input); + $input = array_merge(['command' => $this->command->getName()], $input); } $this->input = new ArrayInput($input); - if ($this->inputs) { - $this->input->setStream(self::createStream($this->inputs)); - } + // Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN. + $this->input->setStream(self::createStream($this->inputs)); if (isset($options['interactive'])) { $this->input->setInteractive($options['interactive']); } - $this->output = new StreamOutput(fopen('php://memory', 'w', false)); - if (isset($options['decorated'])) { - $this->output->setDecorated($options['decorated']); - } - if (isset($options['verbosity'])) { - $this->output->setVerbosity($options['verbosity']); + if (!isset($options['decorated'])) { + $options['decorated'] = false; } - return $this->statusCode = $this->command->run($this->input, $this->output); - } - - /** - * Gets the display returned by the last execution of the command. - * - * @param bool $normalize Whether to normalize end of lines to \n or not - * - * @return string The display - */ - public function getDisplay($normalize = false) - { - rewind($this->output->getStream()); - - $display = stream_get_contents($this->output->getStream()); - - if ($normalize) { - $display = str_replace(PHP_EOL, "\n", $display); - } - - return $display; - } - - /** - * Gets the input instance used by the last execution of the command. - * - * @return InputInterface The current input instance - */ - public function getInput() - { - return $this->input; - } + $this->initOutput($options); - /** - * Gets the output instance used by the last execution of the command. - * - * @return OutputInterface The current output instance - */ - public function getOutput() - { - return $this->output; - } - - /** - * Gets the status code returned by the last execution of the application. - * - * @return int The status code - */ - public function getStatusCode() - { - return $this->statusCode; - } - - /** - * Sets the user inputs. - * - * @param array An array of strings representing each input - * passed to the command input stream. - * - * @return CommandTester - */ - public function setInputs(array $inputs) - { - $this->inputs = $inputs; - - return $this; - } - - private static function createStream(array $inputs) - { - $stream = fopen('php://memory', 'r+', false); - - fputs($stream, implode(PHP_EOL, $inputs)); - rewind($stream); - - return $stream; + return $this->statusCode = $this->command->run($this->input, $this->output); } } diff --git a/tests/integration/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php b/tests/integration/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php new file mode 100644 index 000000000..a47324237 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use Symfony\Component\Console\Command\Command; + +final class CommandIsSuccessful extends Constraint +{ + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'is successful'; + } + + /** + * {@inheritdoc} + */ + protected function matches($other): bool + { + return Command::SUCCESS === $other; + } + + /** + * {@inheritdoc} + */ + protected function failureDescription($other): string + { + return 'the command '.$this->toString(); + } + + /** + * {@inheritdoc} + */ + protected function additionalFailureDescription($other): string + { + $mapping = [ + Command::FAILURE => 'Command failed.', + Command::INVALID => 'Command was invalid.', + ]; + + return $mapping[$other] ?? sprintf('Command returned exit status %d.', $other); + } +} diff --git a/tests/integration/vendor/symfony/console/Tester/TesterTrait.php b/tests/integration/vendor/symfony/console/Tester/TesterTrait.php new file mode 100644 index 000000000..40bc58177 --- /dev/null +++ b/tests/integration/vendor/symfony/console/Tester/TesterTrait.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use PHPUnit\Framework\Assert; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\Constraint\CommandIsSuccessful; + +/** + * @author Amrouche Hamza + */ +trait TesterTrait +{ + /** @var StreamOutput */ + private $output; + private $inputs = []; + private $captureStreamsIndependently = false; + /** @var InputInterface */ + private $input; + /** @var int */ + private $statusCode; + + /** + * Gets the display returned by the last execution of the command or application. + * + * @throws \RuntimeException If it's called before the execute method + * + * @return string + */ + public function getDisplay(bool $normalize = false) + { + if (null === $this->output) { + throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?'); + } + + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(\PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the output written to STDERR by the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string + */ + public function getErrorOutput(bool $normalize = false) + { + if (!$this->captureStreamsIndependently) { + throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); + } + + rewind($this->output->getErrorOutput()->getStream()); + + $display = stream_get_contents($this->output->getErrorOutput()->getStream()); + + if ($normalize) { + $display = str_replace(\PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command or application. + * + * @return InputInterface + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command or application. + * + * @return OutputInterface + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the command or application. + * + * @throws \RuntimeException If it's called before the execute method + * + * @return int + */ + public function getStatusCode() + { + if (null === $this->statusCode) { + throw new \RuntimeException('Status code not initialized, did you execute the command before requesting the status code?'); + } + + return $this->statusCode; + } + + public function assertCommandIsSuccessful(string $message = ''): void + { + Assert::assertThat($this->statusCode, new CommandIsSuccessful(), $message); + } + + /** + * Sets the user inputs. + * + * @param array $inputs An array of strings representing each input + * passed to the command input stream + * + * @return $this + */ + public function setInputs(array $inputs) + { + $this->inputs = $inputs; + + return $this; + } + + /** + * Initializes the output property. + * + * Available options: + * + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + */ + private function initOutput(array $options) + { + $this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; + if (!$this->captureStreamsIndependently) { + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + } else { + $this->output = new ConsoleOutput( + $options['verbosity'] ?? ConsoleOutput::VERBOSITY_NORMAL, + $options['decorated'] ?? null + ); + + $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); + $errorOutput->setFormatter($this->output->getFormatter()); + $errorOutput->setVerbosity($this->output->getVerbosity()); + $errorOutput->setDecorated($this->output->isDecorated()); + + $reflectedOutput = new \ReflectionObject($this->output); + $strErrProperty = $reflectedOutput->getProperty('stderr'); + $strErrProperty->setAccessible(true); + $strErrProperty->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $streamProperty->setAccessible(true); + $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); + } + } + + /** + * @return resource + */ + private static function createStream(array $inputs) + { + $stream = fopen('php://memory', 'r+', false); + + foreach ($inputs as $input) { + fwrite($stream, $input.\PHP_EOL); + } + + rewind($stream); + + return $stream; + } +} diff --git a/tests/integration/vendor/symfony/console/Tests/ApplicationTest.php b/tests/integration/vendor/symfony/console/Tests/ApplicationTest.php deleted file mode 100644 index b8f99f26f..000000000 --- a/tests/integration/vendor/symfony/console/Tests/ApplicationTest.php +++ /dev/null @@ -1,1232 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests; - -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Helper\FormatterHelper; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\NullOutput; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\StreamOutput; -use Symfony\Component\Console\Tester\ApplicationTester; -use Symfony\Component\Console\Event\ConsoleCommandEvent; -use Symfony\Component\Console\Event\ConsoleExceptionEvent; -use Symfony\Component\Console\Event\ConsoleTerminateEvent; -use Symfony\Component\EventDispatcher\EventDispatcher; - -class ApplicationTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); - require_once self::$fixturesPath.'/FooCommand.php'; - require_once self::$fixturesPath.'/Foo1Command.php'; - require_once self::$fixturesPath.'/Foo2Command.php'; - require_once self::$fixturesPath.'/Foo3Command.php'; - require_once self::$fixturesPath.'/Foo4Command.php'; - require_once self::$fixturesPath.'/Foo5Command.php'; - require_once self::$fixturesPath.'/FoobarCommand.php'; - require_once self::$fixturesPath.'/BarBucCommand.php'; - require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; - require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; - } - - protected function normalizeLineBreaks($text) - { - return str_replace(PHP_EOL, "\n", $text); - } - - /** - * Replaces the dynamic placeholders of the command help text with a static version. - * The placeholder %command.full_name% includes the script path that is not predictable - * and can not be tested against. - */ - protected function ensureStaticCommandHelp(Application $application) - { - foreach ($application->all() as $command) { - $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); - } - } - - public function testConstructor() - { - $application = new Application('foo', 'bar'); - $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); - $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); - $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); - } - - public function testSetGetName() - { - $application = new Application(); - $application->setName('foo'); - $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); - } - - public function testSetGetVersion() - { - $application = new Application(); - $application->setVersion('bar'); - $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); - } - - public function testGetLongVersion() - { - $application = new Application('foo', 'bar'); - $this->assertEquals('foo bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); - } - - public function testHelp() - { - $application = new Application(); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); - } - - public function testAll() - { - $application = new Application(); - $commands = $application->all(); - $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); - - $application->add(new \FooCommand()); - $commands = $application->all('foo'); - $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); - } - - public function testRegister() - { - $application = new Application(); - $command = $application->register('foo'); - $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); - } - - public function testAdd() - { - $application = new Application(); - $application->add($foo = new \FooCommand()); - $commands = $application->all(); - $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); - - $application = new Application(); - $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); - $commands = $application->all(); - $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. - */ - public function testAddCommandWithEmptyConstructor() - { - $application = new Application(); - $application->add(new \Foo5Command()); - } - - public function testHasGet() - { - $application = new Application(); - $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); - $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); - - $application->add($foo = new \FooCommand()); - $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); - $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); - $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); - - $application = new Application(); - $application->add($foo = new \FooCommand()); - // simulate --help - $r = new \ReflectionObject($application); - $p = $r->getProperty('wantHelps'); - $p->setAccessible(true); - $p->setValue($application, true); - $command = $application->get('foo:bar'); - $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); - } - - public function testSilentHelp() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $tester = new ApplicationTester($application); - $tester->run(array('-h' => true, '-q' => true), array('decorated' => false)); - - $this->assertEmpty($tester->getDisplay(true)); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException - * @expectedExceptionMessage The command "foofoo" does not exist. - */ - public function testGetInvalidCommand() - { - $application = new Application(); - $application->get('foofoo'); - } - - public function testGetNamespaces() - { - $application = new Application(); - $application->add(new \FooCommand()); - $application->add(new \Foo1Command()); - $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); - } - - public function testFindNamespace() - { - $application = new Application(); - $application->add(new \FooCommand()); - $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); - $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); - $application->add(new \Foo2Command()); - $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); - } - - public function testFindNamespaceWithSubnamespaces() - { - $application = new Application(); - $application->add(new \FooSubnamespaced1Command()); - $application->add(new \FooSubnamespaced2Command()); - $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException - * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). - */ - public function testFindAmbiguousNamespace() - { - $application = new Application(); - $application->add(new \BarBucCommand()); - $application->add(new \FooCommand()); - $application->add(new \Foo2Command()); - $application->findNamespace('f'); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException - * @expectedExceptionMessage There are no commands defined in the "bar" namespace. - */ - public function testFindInvalidNamespace() - { - $application = new Application(); - $application->findNamespace('bar'); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException - * @expectedExceptionMessage Command "foo1" is not defined - */ - public function testFindUniqueNameButNamespaceName() - { - $application = new Application(); - $application->add(new \FooCommand()); - $application->add(new \Foo1Command()); - $application->add(new \Foo2Command()); - - $application->find($commandName = 'foo1'); - } - - public function testFind() - { - $application = new Application(); - $application->add(new \FooCommand()); - - $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); - $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); - $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); - $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); - $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); - } - - /** - * @dataProvider provideAmbiguousAbbreviations - */ - public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) - { - $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); - - $application = new Application(); - $application->add(new \FooCommand()); - $application->add(new \Foo1Command()); - $application->add(new \Foo2Command()); - - $application->find($abbreviation); - } - - public function provideAmbiguousAbbreviations() - { - return array( - array('f', 'Command "f" is not defined.'), - array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'), - array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'), - ); - } - - public function testFindCommandEqualNamespace() - { - $application = new Application(); - $application->add(new \Foo3Command()); - $application->add(new \Foo4Command()); - - $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); - $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); - } - - public function testFindCommandWithAmbiguousNamespacesButUniqueName() - { - $application = new Application(); - $application->add(new \FooCommand()); - $application->add(new \FoobarCommand()); - - $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); - } - - public function testFindCommandWithMissingNamespace() - { - $application = new Application(); - $application->add(new \Foo4Command()); - - $this->assertInstanceOf('Foo4Command', $application->find('f::t')); - } - - /** - * @dataProvider provideInvalidCommandNamesSingle - * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException - * @expectedExceptionMessage Did you mean this - */ - public function testFindAlternativeExceptionMessageSingle($name) - { - $application = new Application(); - $application->add(new \Foo3Command()); - $application->find($name); - } - - public function provideInvalidCommandNamesSingle() - { - return array( - array('foo3:baR'), - array('foO3:bar'), - ); - } - - public function testFindAlternativeExceptionMessageMultiple() - { - $application = new Application(); - $application->add(new \FooCommand()); - $application->add(new \Foo1Command()); - $application->add(new \Foo2Command()); - - // Command + plural - try { - $application->find('foo:baR'); - $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - $this->assertRegExp('/foo1:bar/', $e->getMessage()); - $this->assertRegExp('/foo:bar/', $e->getMessage()); - } - - // Namespace + plural - try { - $application->find('foo2:bar'); - $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - $this->assertRegExp('/foo1/', $e->getMessage()); - } - - $application->add(new \Foo3Command()); - $application->add(new \Foo4Command()); - - // Subnamespace + plural - try { - $a = $application->find('foo3:'); - $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); - $this->assertRegExp('/foo3:bar/', $e->getMessage()); - $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); - } - } - - public function testFindAlternativeCommands() - { - $application = new Application(); - - $application->add(new \FooCommand()); - $application->add(new \Foo1Command()); - $application->add(new \Foo2Command()); - - try { - $application->find($commandName = 'Unknown command'); - $this->fail('->find() throws a CommandNotFoundException if command does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); - $this->assertSame(array(), $e->getAlternatives()); - $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); - } - - // Test if "bar1" command throw a "CommandNotFoundException" and does not contain - // "foo:bar" as alternative because "bar1" is too far from "foo:bar" - try { - $application->find($commandName = 'bar1'); - $this->fail('->find() throws a CommandNotFoundException if command does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); - $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives()); - $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); - $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); - $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); - $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); - } - } - - public function testFindAlternativeCommandsWithAnAlias() - { - $fooCommand = new \FooCommand(); - $fooCommand->setAliases(array('foo2')); - - $application = new Application(); - $application->add($fooCommand); - - $result = $application->find('foo'); - - $this->assertSame($fooCommand, $result); - } - - public function testFindAlternativeNamespace() - { - $application = new Application(); - - $application->add(new \FooCommand()); - $application->add(new \Foo1Command()); - $application->add(new \Foo2Command()); - $application->add(new \Foo3Command()); - - try { - $application->find('Unknown-namespace:Unknown-command'); - $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); - $this->assertSame(array(), $e->getAlternatives()); - $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); - } - - try { - $application->find('foo2:command'); - $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); - $this->assertCount(3, $e->getAlternatives()); - $this->assertContains('foo', $e->getAlternatives()); - $this->assertContains('foo1', $e->getAlternatives()); - $this->assertContains('foo3', $e->getAlternatives()); - $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); - $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); - $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); - $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); - } - } - - public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() - { - $application = $this->getMock('Symfony\Component\Console\Application', array('getNamespaces')); - $application->expects($this->once()) - ->method('getNamespaces') - ->will($this->returnValue(array('foo:sublong', 'bar:sub'))); - - $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException - * @expectedExceptionMessage Command "foo::bar" is not defined. - */ - public function testFindWithDoubleColonInNameThrowsException() - { - $application = new Application(); - $application->add(new \FooCommand()); - $application->add(new \Foo4Command()); - $application->find('foo::bar'); - } - - public function testSetCatchExceptions() - { - $application = new Application(); - $application->setAutoExit(false); - putenv('COLUMNS=120'); - $tester = new ApplicationTester($application); - - $application->setCatchExceptions(true); - $this->assertTrue($application->areExceptionsCaught()); - - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); - - $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag'); - $this->assertSame('', $tester->getDisplay(true)); - - $application->setCatchExceptions(false); - try { - $tester->run(array('command' => 'foo'), array('decorated' => false)); - $this->fail('->setCatchExceptions() sets the catch exception flag'); - } catch (\Exception $e) { - $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); - $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); - } - } - - public function testAutoExitSetting() - { - $application = new Application(); - $this->assertTrue($application->isAutoExitEnabled()); - - $application->setAutoExit(false); - $this->assertFalse($application->isAutoExitEnabled()); - } - - public function testRenderException() - { - $application = new Application(); - $application->setAutoExit(false); - putenv('COLUMNS=120'); - $tester = new ApplicationTester($application); - - $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception'); - - $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true)); - $this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); - - $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); - - $application->add(new \Foo3Command()); - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); - - $tester->run(array('command' => 'foo3:bar'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - $this->assertRegExp('/\[Exception\]\s*First exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is default and verbosity is verbose'); - $this->assertRegExp('/\[Exception\]\s*Second exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is 0 and verbosity is verbose'); - $this->assertRegExp('/\[Exception \(404\)\]\s*Third exception/', $tester->getDisplay(), '->renderException() renders a pretty exception with code exception when code exception is 404 and verbosity is verbose'); - - $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); - - $tester->run(array('command' => 'foo3:bar'), array('decorated' => true, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); - - $application = new Application(); - $application->setAutoExit(false); - putenv('COLUMNS=32'); - $tester = new ApplicationTester($application); - - $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); - putenv('COLUMNS=120'); - } - - public function testRenderExceptionWithDoubleWidthCharacters() - { - $application = new Application(); - $application->setAutoExit(false); - putenv('COLUMNS=120'); - $application->register('foo')->setCode(function () { - throw new \Exception('エラーメッセージ'); - }); - $tester = new ApplicationTester($application); - - $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); - - $tester->run(array('command' => 'foo'), array('decorated' => true, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); - - $application = new Application(); - $application->setAutoExit(false); - putenv('COLUMNS=32'); - $application->register('foo')->setCode(function () { - throw new \Exception('コマンドの実行中にエラーが発生しました。'); - }); - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo'), array('decorated' => false, 'capture_stderr_separately' => true)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); - putenv('COLUMNS=120'); - } - - public function testRun() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $application->add($command = new \Foo1Command()); - $_SERVER['argv'] = array('cli.php', 'foo:bar1'); - - ob_start(); - $application->run(); - ob_end_clean(); - - $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); - $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); - - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $this->ensureStaticCommandHelp($application); - $tester = new ApplicationTester($application); - - $tester->run(array(), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); - - $tester->run(array('--help' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); - - $tester->run(array('-h' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); - - $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); - - $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); - - $tester->run(array('--ansi' => true)); - $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); - - $tester->run(array('--no-ansi' => true)); - $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); - - $tester->run(array('--version' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); - - $tester->run(array('-V' => true), array('decorated' => false)); - $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); - - $tester->run(array('command' => 'list', '--quiet' => true)); - $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); - $this->assertFalse($tester->getInput()->isInteractive(), '->run() sets off the interactive mode if --quiet is passed'); - - $tester->run(array('command' => 'list', '-q' => true)); - $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); - $this->assertFalse($tester->getInput()->isInteractive(), '->run() sets off the interactive mode if -q is passed'); - - $tester->run(array('command' => 'list', '--verbose' => true)); - $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); - - $tester->run(array('command' => 'list', '--verbose' => 1)); - $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); - - $tester->run(array('command' => 'list', '--verbose' => 2)); - $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); - - $tester->run(array('command' => 'list', '--verbose' => 3)); - $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); - - $tester->run(array('command' => 'list', '--verbose' => 4)); - $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); - - $tester->run(array('command' => 'list', '-v' => true)); - $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); - - $tester->run(array('command' => 'list', '-vv' => true)); - $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); - - $tester->run(array('command' => 'list', '-vvv' => true)); - $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); - - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $application->add(new \FooCommand()); - $tester = new ApplicationTester($application); - - $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); - $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); - - $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); - $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); - } - - /** - * Issue #9285. - * - * If the "verbose" option is just before an argument in ArgvInput, - * an argument value should not be treated as verbosity value. - * This test will fail with "Not enough arguments." if broken - */ - public function testVerboseValueNotBreakArguments() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $application->add(new \FooCommand()); - - $output = new StreamOutput(fopen('php://memory', 'w', false)); - - $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); - $application->run($input, $output); - - $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); - $application->run($input, $output); - } - - public function testRunReturnsIntegerExitCode() - { - $exception = new \Exception('', 4); - - $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); - $application->setAutoExit(false); - $application->expects($this->once()) - ->method('doRun') - ->will($this->throwException($exception)); - - $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); - - $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); - } - - public function testRunReturnsExitCodeOneForExceptionCodeZero() - { - $exception = new \Exception('', 0); - - $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); - $application->setAutoExit(false); - $application->expects($this->once()) - ->method('doRun') - ->will($this->throwException($exception)); - - $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); - - $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage An option with shortcut "e" already exists. - */ - public function testAddingOptionWithDuplicateShortcut() - { - $dispatcher = new EventDispatcher(); - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $application->setDispatcher($dispatcher); - - $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); - - $application - ->register('foo') - ->setAliases(array('f')) - ->setDefinition(array(new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.'))) - ->setCode(function (InputInterface $input, OutputInterface $output) {}) - ; - - $input = new ArrayInput(array('command' => 'foo')); - $output = new NullOutput(); - - $application->run($input, $output); - } - - /** - * @expectedException \LogicException - * @dataProvider getAddingAlreadySetDefinitionElementData - */ - public function testAddingAlreadySetDefinitionElementData($def) - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - $application - ->register('foo') - ->setDefinition(array($def)) - ->setCode(function (InputInterface $input, OutputInterface $output) {}) - ; - - $input = new ArrayInput(array('command' => 'foo')); - $output = new NullOutput(); - $application->run($input, $output); - } - - public function getAddingAlreadySetDefinitionElementData() - { - return array( - array(new InputArgument('command', InputArgument::REQUIRED)), - array(new InputOption('quiet', '', InputOption::VALUE_NONE)), - array(new InputOption('query', 'q', InputOption::VALUE_NONE)), - ); - } - - public function testGetDefaultHelperSetReturnsDefaultValues() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $helperSet = $application->getHelperSet(); - - $this->assertTrue($helperSet->has('formatter')); - } - - public function testAddingSingleHelperSetOverwritesDefaultValues() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); - - $helperSet = $application->getHelperSet(); - - $this->assertTrue($helperSet->has('formatter')); - - // no other default helper set should be returned - $this->assertFalse($helperSet->has('dialog')); - $this->assertFalse($helperSet->has('progress')); - } - - public function testOverwritingDefaultHelperSetOverwritesDefaultValues() - { - $application = new CustomApplication(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); - - $helperSet = $application->getHelperSet(); - - $this->assertTrue($helperSet->has('formatter')); - - // no other default helper set should be returned - $this->assertFalse($helperSet->has('dialog')); - $this->assertFalse($helperSet->has('progress')); - } - - public function testGetDefaultInputDefinitionReturnsDefaultValues() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $inputDefinition = $application->getDefinition(); - - $this->assertTrue($inputDefinition->hasArgument('command')); - - $this->assertTrue($inputDefinition->hasOption('help')); - $this->assertTrue($inputDefinition->hasOption('quiet')); - $this->assertTrue($inputDefinition->hasOption('verbose')); - $this->assertTrue($inputDefinition->hasOption('version')); - $this->assertTrue($inputDefinition->hasOption('ansi')); - $this->assertTrue($inputDefinition->hasOption('no-ansi')); - $this->assertTrue($inputDefinition->hasOption('no-interaction')); - } - - public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() - { - $application = new CustomApplication(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $inputDefinition = $application->getDefinition(); - - // check whether the default arguments and options are not returned any more - $this->assertFalse($inputDefinition->hasArgument('command')); - - $this->assertFalse($inputDefinition->hasOption('help')); - $this->assertFalse($inputDefinition->hasOption('quiet')); - $this->assertFalse($inputDefinition->hasOption('verbose')); - $this->assertFalse($inputDefinition->hasOption('version')); - $this->assertFalse($inputDefinition->hasOption('ansi')); - $this->assertFalse($inputDefinition->hasOption('no-ansi')); - $this->assertFalse($inputDefinition->hasOption('no-interaction')); - - $this->assertTrue($inputDefinition->hasOption('custom')); - } - - public function testSettingCustomInputDefinitionOverwritesDefaultValues() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); - - $inputDefinition = $application->getDefinition(); - - // check whether the default arguments and options are not returned any more - $this->assertFalse($inputDefinition->hasArgument('command')); - - $this->assertFalse($inputDefinition->hasOption('help')); - $this->assertFalse($inputDefinition->hasOption('quiet')); - $this->assertFalse($inputDefinition->hasOption('verbose')); - $this->assertFalse($inputDefinition->hasOption('version')); - $this->assertFalse($inputDefinition->hasOption('ansi')); - $this->assertFalse($inputDefinition->hasOption('no-ansi')); - $this->assertFalse($inputDefinition->hasOption('no-interaction')); - - $this->assertTrue($inputDefinition->hasOption('custom')); - } - - public function testRunWithDispatcher() - { - $application = new Application(); - $application->setAutoExit(false); - $application->setDispatcher($this->getDispatcher()); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('foo.'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo')); - $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage caught - */ - public function testRunWithExceptionAndDispatcher() - { - $application = new Application(); - $application->setDispatcher($this->getDispatcher()); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - throw new \RuntimeException('foo'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo')); - } - - public function testRunDispatchesAllEventsWithException() - { - $application = new Application(); - $application->setDispatcher($this->getDispatcher()); - $application->setAutoExit(false); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('foo.'); - - throw new \RuntimeException('foo'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo')); - $this->assertContains('before.foo.caught.after.', $tester->getDisplay()); - } - - public function testRunWithError() - { - $this->setExpectedException('Exception', 'dymerr'); - - $application = new Application(); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('dym.'); - - throw new \Error('dymerr'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'dym')); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage caught - */ - public function testRunWithErrorAndDispatcher() - { - $application = new Application(); - $application->setDispatcher($this->getDispatcher()); - $application->setAutoExit(false); - $application->setCatchExceptions(false); - - $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('dym.'); - - throw new \Error('dymerr'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'dym')); - $this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); - } - - public function testRunDispatchesAllEventsWithError() - { - $application = new Application(); - $application->setDispatcher($this->getDispatcher()); - $application->setAutoExit(false); - - $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('dym.'); - - throw new \Error('dymerr'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'dym')); - $this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); - } - - public function testRunWithErrorFailingStatusCode() - { - $application = new Application(); - $application->setDispatcher($this->getDispatcher()); - $application->setAutoExit(false); - - $application->register('dus')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('dus.'); - - throw new \Error('duserr'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'dus')); - $this->assertSame(1, $tester->getStatusCode(), 'Status code should be 1'); - } - - public function testRunWithDispatcherSkippingCommand() - { - $application = new Application(); - $application->setDispatcher($this->getDispatcher(true)); - $application->setAutoExit(false); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('foo.'); - }); - - $tester = new ApplicationTester($application); - $exitCode = $tester->run(array('command' => 'foo')); - $this->assertContains('before.after.', $tester->getDisplay()); - $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); - } - - public function testRunWithDispatcherAccessingInputOptions() - { - $noInteractionValue = null; - $quietValue = null; - - $dispatcher = $this->getDispatcher(); - $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { - $input = $event->getInput(); - - $noInteractionValue = $input->getOption('no-interaction'); - $quietValue = $input->getOption('quiet'); - }); - - $application = new Application(); - $application->setDispatcher($dispatcher); - $application->setAutoExit(false); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('foo.'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo', '--no-interaction' => true)); - - $this->assertTrue($noInteractionValue); - $this->assertFalse($quietValue); - } - - public function testRunWithDispatcherAddingInputOptions() - { - $extraValue = null; - - $dispatcher = $this->getDispatcher(); - $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { - $definition = $event->getCommand()->getDefinition(); - $input = $event->getInput(); - - $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); - $input->bind($definition); - - $extraValue = $input->getOption('extra'); - }); - - $application = new Application(); - $application->setDispatcher($dispatcher); - $application->setAutoExit(false); - - $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { - $output->write('foo.'); - }); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'foo', '--extra' => 'some test value')); - - $this->assertEquals('some test value', $extraValue); - } - - /** - * @group legacy - */ - public function testTerminalDimensions() - { - $application = new Application(); - $originalDimensions = $application->getTerminalDimensions(); - $this->assertCount(2, $originalDimensions); - - $width = 80; - if ($originalDimensions[0] == $width) { - $width = 100; - } - - $application->setTerminalDimensions($width, 80); - $this->assertSame(array($width, 80), $application->getTerminalDimensions()); - } - - protected function getDispatcher($skipCommand = false) - { - $dispatcher = new EventDispatcher(); - $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { - $event->getOutput()->write('before.'); - - if ($skipCommand) { - $event->disableCommand(); - } - }); - $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { - $event->getOutput()->writeln('after.'); - - if (!$skipCommand) { - $event->setExitCode(113); - } - }); - $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { - $event->getOutput()->write('caught.'); - - $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); - }); - - return $dispatcher; - } - - public function testSetRunCustomDefaultCommand() - { - $command = new \FooCommand(); - - $application = new Application(); - $application->setAutoExit(false); - $application->add($command); - $application->setDefaultCommand($command->getName()); - - $tester = new ApplicationTester($application); - $tester->run(array()); - $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); - - $application = new CustomDefaultCommandApplication(); - $application->setAutoExit(false); - - $tester = new ApplicationTester($application); - $tester->run(array()); - - $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); - } - - public function testSetRunCustomSingleCommand() - { - $command = new \FooCommand(); - - $application = new Application(); - $application->setAutoExit(false); - $application->add($command); - $application->setDefaultCommand($command->getName(), true); - - $tester = new ApplicationTester($application); - - $tester->run(array()); - $this->assertContains('called', $tester->getDisplay()); - - $tester->run(array('--help' => true)); - $this->assertContains('The foo:bar command', $tester->getDisplay()); - } - - /** - * @requires function posix_isatty - */ - public function testCanCheckIfTerminalIsInteractive() - { - $application = new CustomDefaultCommandApplication(); - $application->setAutoExit(false); - - $tester = new ApplicationTester($application); - $tester->run(array('command' => 'help')); - - $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); - - $inputStream = $tester->getInput()->getStream(); - $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); - } -} - -class CustomApplication extends Application -{ - /** - * Overwrites the default input definition. - * - * @return InputDefinition An InputDefinition instance - */ - protected function getDefaultInputDefinition() - { - return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); - } - - /** - * Gets the default helper set with the helpers that should always be available. - * - * @return HelperSet A HelperSet instance - */ - protected function getDefaultHelperSet() - { - return new HelperSet(array(new FormatterHelper())); - } -} - -class CustomDefaultCommandApplication extends Application -{ - /** - * Overwrites the constructor in order to set a different default command. - */ - public function __construct() - { - parent::__construct(); - - $command = new \FooCommand(); - $this->add($command); - $this->setDefaultCommand($command->getName()); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Command/CommandTest.php b/tests/integration/vendor/symfony/console/Tests/Command/CommandTest.php deleted file mode 100644 index 3bab819f4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Command/CommandTest.php +++ /dev/null @@ -1,360 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Command; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Helper\FormatterHelper; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\StringInput; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Output\NullOutput; -use Symfony\Component\Console\Tester\CommandTester; - -class CommandTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = __DIR__.'/../Fixtures/'; - require_once self::$fixturesPath.'/TestCommand.php'; - } - - public function testConstructor() - { - $command = new Command('foo:bar'); - $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. - */ - public function testCommandNameCannotBeEmpty() - { - new Command(); - } - - public function testSetApplication() - { - $application = new Application(); - $command = new \TestCommand(); - $command->setApplication($application); - $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); - } - - public function testSetGetDefinition() - { - $command = new \TestCommand(); - $ret = $command->setDefinition($definition = new InputDefinition()); - $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); - $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); - $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); - $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); - $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); - $command->setDefinition(new InputDefinition()); - } - - public function testAddArgument() - { - $command = new \TestCommand(); - $ret = $command->addArgument('foo'); - $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); - $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); - } - - public function testAddOption() - { - $command = new \TestCommand(); - $ret = $command->addOption('foo'); - $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); - $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); - } - - public function testGetNamespaceGetNameSetName() - { - $command = new \TestCommand(); - $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); - $command->setName('foo'); - $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); - - $ret = $command->setName('foobar:bar'); - $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); - $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); - } - - /** - * @dataProvider provideInvalidCommandNames - */ - public function testInvalidCommandNames($name) - { - $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); - - $command = new \TestCommand(); - $command->setName($name); - } - - public function provideInvalidCommandNames() - { - return array( - array(''), - array('foo:'), - ); - } - - public function testGetSetDescription() - { - $command = new \TestCommand(); - $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); - $ret = $command->setDescription('description1'); - $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); - $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); - } - - public function testGetSetHelp() - { - $command = new \TestCommand(); - $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); - $ret = $command->setHelp('help1'); - $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); - $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); - $command->setHelp(''); - $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); - } - - public function testGetProcessedHelp() - { - $command = new \TestCommand(); - $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); - $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); - $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); - - $command = new \TestCommand(); - $command->setHelp(''); - $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); - } - - public function testGetSetAliases() - { - $command = new \TestCommand(); - $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); - $ret = $command->setAliases(array('name1')); - $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); - $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); - } - - public function testGetSynopsis() - { - $command = new \TestCommand(); - $command->addOption('foo'); - $command->addArgument('bar'); - $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); - } - - public function testGetHelper() - { - $application = new Application(); - $command = new \TestCommand(); - $command->setApplication($application); - $formatterHelper = new FormatterHelper(); - $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined. - */ - public function testGetHelperWithoutHelperSet() - { - $command = new \TestCommand(); - $command->getHelper('formatter'); - } - - public function testMergeApplicationDefinition() - { - $application1 = new Application(); - $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); - $application1->getDefinition()->addOptions(array(new InputOption('bar'))); - $command = new \TestCommand(); - $command->setApplication($application1); - $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); - - $r = new \ReflectionObject($command); - $m = $r->getMethod('mergeApplicationDefinition'); - $m->setAccessible(true); - $m->invoke($command); - $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); - $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); - $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); - $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); - - $m->invoke($command); - $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); - } - - public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() - { - $application1 = new Application(); - $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); - $application1->getDefinition()->addOptions(array(new InputOption('bar'))); - $command = new \TestCommand(); - $command->setApplication($application1); - $command->setDefinition($definition = new InputDefinition(array())); - - $r = new \ReflectionObject($command); - $m = $r->getMethod('mergeApplicationDefinition'); - $m->setAccessible(true); - $m->invoke($command, false); - $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); - $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); - - $m->invoke($command, true); - $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); - - $m->invoke($command); - $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); - } - - public function testRunInteractive() - { - $tester = new CommandTester(new \TestCommand()); - - $tester->execute(array(), array('interactive' => true)); - - $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); - } - - public function testRunNonInteractive() - { - $tester = new CommandTester(new \TestCommand()); - - $tester->execute(array(), array('interactive' => false)); - - $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage You must override the execute() method in the concrete command class. - */ - public function testExecuteMethodNeedsToBeOverridden() - { - $command = new Command('foo'); - $command->run(new StringInput(''), new NullOutput()); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\InvalidOptionException - * @expectedExceptionMessage The "--bar" option does not exist. - */ - public function testRunWithInvalidOption() - { - $command = new \TestCommand(); - $tester = new CommandTester($command); - $tester->execute(array('--bar' => true)); - } - - public function testRunReturnsIntegerExitCode() - { - $command = new \TestCommand(); - $exitCode = $command->run(new StringInput(''), new NullOutput()); - $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); - - $command = $this->getMock('TestCommand', array('execute')); - $command->expects($this->once()) - ->method('execute') - ->will($this->returnValue('2.3')); - $exitCode = $command->run(new StringInput(''), new NullOutput()); - $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); - } - - public function testRunWithApplication() - { - $command = new \TestCommand(); - $command->setApplication(new Application()); - $exitCode = $command->run(new StringInput(''), new NullOutput()); - - $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); - } - - public function testRunReturnsAlwaysInteger() - { - $command = new \TestCommand(); - - $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); - } - - public function testSetCode() - { - $command = new \TestCommand(); - $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { - $output->writeln('from the code...'); - }); - $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); - $tester = new CommandTester($command); - $tester->execute(array()); - $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); - } - - public function getSetCodeBindToClosureTests() - { - return array( - array(true, 'not bound to the command'), - array(false, 'bound to the command'), - ); - } - - /** - * @dataProvider getSetCodeBindToClosureTests - */ - public function testSetCodeBindToClosure($previouslyBound, $expected) - { - $code = createClosure(); - if ($previouslyBound) { - $code = $code->bindTo($this); - } - - $command = new \TestCommand(); - $command->setCode($code); - $tester = new CommandTester($command); - $tester->execute(array()); - $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); - } - - public function testSetCodeWithNonClosureCallable() - { - $command = new \TestCommand(); - $ret = $command->setCode(array($this, 'callableMethodCommand')); - $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); - $tester = new CommandTester($command); - $tester->execute(array()); - $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); - } - - public function callableMethodCommand(InputInterface $input, OutputInterface $output) - { - $output->writeln('from the code...'); - } -} - -// In order to get an unbound closure, we should create it outside a class -// scope. -function createClosure() -{ - return function (InputInterface $input, OutputInterface $output) { - $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); - }; -} diff --git a/tests/integration/vendor/symfony/console/Tests/Command/HelpCommandTest.php b/tests/integration/vendor/symfony/console/Tests/Command/HelpCommandTest.php deleted file mode 100644 index 10bb32419..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Command/HelpCommandTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Command; - -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Console\Command\HelpCommand; -use Symfony\Component\Console\Command\ListCommand; -use Symfony\Component\Console\Application; - -class HelpCommandTest extends \PHPUnit_Framework_TestCase -{ - public function testExecuteForCommandAlias() - { - $command = new HelpCommand(); - $command->setApplication(new Application()); - $commandTester = new CommandTester($command); - $commandTester->execute(array('command_name' => 'li'), array('decorated' => false)); - $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); - $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); - $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); - } - - public function testExecuteForCommand() - { - $command = new HelpCommand(); - $commandTester = new CommandTester($command); - $command->setCommand(new ListCommand()); - $commandTester->execute(array(), array('decorated' => false)); - $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - } - - public function testExecuteForCommandWithXmlOption() - { - $command = new HelpCommand(); - $commandTester = new CommandTester($command); - $command->setCommand(new ListCommand()); - $commandTester->execute(array('--format' => 'xml')); - $this->assertContains('getDisplay(), '->execute() returns an XML help text if --xml is passed'); - } - - public function testExecuteForApplicationCommand() - { - $application = new Application(); - $commandTester = new CommandTester($application->get('help')); - $commandTester->execute(array('command_name' => 'list')); - $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - } - - public function testExecuteForApplicationCommandWithXmlOption() - { - $application = new Application(); - $commandTester = new CommandTester($application->get('help')); - $commandTester->execute(array('command_name' => 'list', '--format' => 'xml')); - $this->assertContains('list [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); - $this->assertContains('getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Command/ListCommandTest.php b/tests/integration/vendor/symfony/console/Tests/Command/ListCommandTest.php deleted file mode 100644 index a166a0409..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Command/ListCommandTest.php +++ /dev/null @@ -1,112 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Command; - -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Console\Application; - -class ListCommandTest extends \PHPUnit_Framework_TestCase -{ - public function testExecuteListsCommands() - { - $application = new Application(); - $commandTester = new CommandTester($command = $application->get('list')); - $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); - - $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); - } - - public function testExecuteListsCommandsWithXmlOption() - { - $application = new Application(); - $commandTester = new CommandTester($command = $application->get('list')); - $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml')); - $this->assertRegExp('//', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); - } - - public function testExecuteListsCommandsWithRawOption() - { - $application = new Application(); - $commandTester = new CommandTester($command = $application->get('list')); - $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); - $output = <<<'EOF' -help Displays help for a command -list Lists commands - -EOF; - - $this->assertEquals($output, $commandTester->getDisplay(true)); - } - - public function testExecuteListsCommandsWithNamespaceArgument() - { - require_once realpath(__DIR__.'/../Fixtures/FooCommand.php'); - $application = new Application(); - $application->add(new \FooCommand()); - $commandTester = new CommandTester($command = $application->get('list')); - $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true)); - $output = <<<'EOF' -foo:bar The foo:bar command - -EOF; - - $this->assertEquals($output, $commandTester->getDisplay(true)); - } - - public function testExecuteListsCommandsOrder() - { - require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); - $application = new Application(); - $application->add(new \Foo6Command()); - $commandTester = new CommandTester($command = $application->get('list')); - $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); - $output = <<<'EOF' -Console Tool - -Usage: - command [options] [arguments] - -Options: - -h, --help Display this help message - -q, --quiet Do not output any message - -V, --version Display this application version - --ansi Force ANSI output - --no-ansi Disable ANSI output - -n, --no-interaction Do not ask any interactive question - -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug - -Available commands: - help Displays help for a command - list Lists commands - 0foo - 0foo:bar 0foo:bar command -EOF; - - $this->assertEquals($output, trim($commandTester->getDisplay(true))); - } - - public function testExecuteListsCommandsOrderRaw() - { - require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); - $application = new Application(); - $application->add(new \Foo6Command()); - $commandTester = new CommandTester($command = $application->get('list')); - $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); - $output = <<<'EOF' -help Displays help for a command -list Lists commands -0foo:bar 0foo:bar command -EOF; - - $this->assertEquals($output, trim($commandTester->getDisplay(true))); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Command/LockableTraitTest.php b/tests/integration/vendor/symfony/console/Tests/Command/LockableTraitTest.php deleted file mode 100644 index 828ba163b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Command/LockableTraitTest.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Command; - -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Filesystem\LockHandler; - -class LockableTraitTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = __DIR__.'/../Fixtures/'; - require_once self::$fixturesPath.'/FooLockCommand.php'; - require_once self::$fixturesPath.'/FooLock2Command.php'; - } - - public function testLockIsReleased() - { - $command = new \FooLockCommand(); - - $tester = new CommandTester($command); - $this->assertSame(2, $tester->execute(array())); - $this->assertSame(2, $tester->execute(array())); - } - - public function testLockReturnsFalseIfAlreadyLockedByAnotherCommand() - { - $command = new \FooLockCommand(); - - $lock = new LockHandler($command->getName()); - $lock->lock(); - - $tester = new CommandTester($command); - $this->assertSame(1, $tester->execute(array())); - - $lock->release(); - $this->assertSame(2, $tester->execute(array())); - } - - public function testMultipleLockCallsThrowLogicException() - { - $command = new \FooLock2Command(); - - $tester = new CommandTester($command); - $this->assertSame(1, $tester->execute(array())); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php b/tests/integration/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php deleted file mode 100644 index c36c4a8e5..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Descriptor/AbstractDescriptorTest.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Descriptor; - -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\BufferedOutput; - -abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase -{ - /** @dataProvider getDescribeInputArgumentTestData */ - public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) - { - $this->assertDescription($expectedDescription, $argument); - } - - /** @dataProvider getDescribeInputOptionTestData */ - public function testDescribeInputOption(InputOption $option, $expectedDescription) - { - $this->assertDescription($expectedDescription, $option); - } - - /** @dataProvider getDescribeInputDefinitionTestData */ - public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) - { - $this->assertDescription($expectedDescription, $definition); - } - - /** @dataProvider getDescribeCommandTestData */ - public function testDescribeCommand(Command $command, $expectedDescription) - { - $this->assertDescription($expectedDescription, $command); - } - - /** @dataProvider getDescribeApplicationTestData */ - public function testDescribeApplication(Application $application, $expectedDescription) - { - // Replaces the dynamic placeholders of the command help text with a static version. - // The placeholder %command.full_name% includes the script path that is not predictable - // and can not be tested against. - foreach ($application->all() as $command) { - $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); - } - - $this->assertDescription($expectedDescription, $application); - } - - public function getDescribeInputArgumentTestData() - { - return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); - } - - public function getDescribeInputOptionTestData() - { - return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); - } - - public function getDescribeInputDefinitionTestData() - { - return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); - } - - public function getDescribeCommandTestData() - { - return $this->getDescriptionTestData(ObjectsProvider::getCommands()); - } - - public function getDescribeApplicationTestData() - { - return $this->getDescriptionTestData(ObjectsProvider::getApplications()); - } - - abstract protected function getDescriptor(); - - abstract protected function getFormat(); - - private function getDescriptionTestData(array $objects) - { - $data = array(); - foreach ($objects as $name => $object) { - $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); - $data[] = array($object, $description); - } - - return $data; - } - - protected function assertDescription($expectedDescription, $describedObject) - { - $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); - $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); - $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php b/tests/integration/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php deleted file mode 100644 index f9a15612b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Descriptor/JsonDescriptorTest.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Descriptor; - -use Symfony\Component\Console\Descriptor\JsonDescriptor; -use Symfony\Component\Console\Output\BufferedOutput; - -class JsonDescriptorTest extends AbstractDescriptorTest -{ - protected function getDescriptor() - { - return new JsonDescriptor(); - } - - protected function getFormat() - { - return 'json'; - } - - protected function assertDescription($expectedDescription, $describedObject) - { - $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); - $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); - $this->assertEquals(json_decode(trim($expectedDescription), true), json_decode(trim(str_replace(PHP_EOL, "\n", $output->fetch())), true)); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php b/tests/integration/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php deleted file mode 100644 index c85e8a594..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Descriptor/MarkdownDescriptorTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Descriptor; - -use Symfony\Component\Console\Descriptor\MarkdownDescriptor; - -class MarkdownDescriptorTest extends AbstractDescriptorTest -{ - protected function getDescriptor() - { - return new MarkdownDescriptor(); - } - - protected function getFormat() - { - return 'md'; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php b/tests/integration/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php deleted file mode 100644 index 45b3b2fff..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Descriptor/ObjectsProvider.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Descriptor; - -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1; -use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2; -use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1; -use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2; - -/** - * @author Jean-François Simon - */ -class ObjectsProvider -{ - public static function getInputArguments() - { - return array( - 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED), - 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'), - 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'), - 'input_argument_4' => new InputArgument('argument_name', InputArgument::REQUIRED, "multiline\nargument description"), - ); - } - - public static function getInputOptions() - { - return array( - 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE), - 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'), - 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'), - 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()), - 'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"), - 'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'), - ); - } - - public static function getInputDefinitions() - { - return array( - 'input_definition_1' => new InputDefinition(), - 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))), - 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))), - 'input_definition_4' => new InputDefinition(array( - new InputArgument('argument_name', InputArgument::REQUIRED), - new InputOption('option_name', 'o', InputOption::VALUE_NONE), - )), - ); - } - - public static function getCommands() - { - return array( - 'command_1' => new DescriptorCommand1(), - 'command_2' => new DescriptorCommand2(), - ); - } - - public static function getApplications() - { - return array( - 'application_1' => new DescriptorApplication1(), - 'application_2' => new DescriptorApplication2(), - ); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php b/tests/integration/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php deleted file mode 100644 index 350b67950..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Descriptor/TextDescriptorTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Descriptor; - -use Symfony\Component\Console\Descriptor\TextDescriptor; - -class TextDescriptorTest extends AbstractDescriptorTest -{ - protected function getDescriptor() - { - return new TextDescriptor(); - } - - protected function getFormat() - { - return 'txt'; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php b/tests/integration/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php deleted file mode 100644 index 59a5d1ed8..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Descriptor/XmlDescriptorTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Descriptor; - -use Symfony\Component\Console\Descriptor\XmlDescriptor; - -class XmlDescriptorTest extends AbstractDescriptorTest -{ - protected function getDescriptor() - { - return new XmlDescriptor(); - } - - protected function getFormat() - { - return 'xml'; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php deleted file mode 100644 index 52b619e82..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/BarBucCommand.php +++ /dev/null @@ -1,11 +0,0 @@ -setName('bar:buc'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php deleted file mode 100644 index 132b6d57d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorApplication1.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Fixtures; - -use Symfony\Component\Console\Application; - -class DescriptorApplication1 extends Application -{ -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php deleted file mode 100644 index 0fe87a18e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorApplication2.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Fixtures; - -use Symfony\Component\Console\Application; - -class DescriptorApplication2 extends Application -{ - public function __construct() - { - parent::__construct('My Symfony application', 'v1.0'); - $this->add(new DescriptorCommand1()); - $this->add(new DescriptorCommand2()); - $this->add(new DescriptorCommand3()); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php deleted file mode 100644 index ede05d7a7..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand1.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Fixtures; - -use Symfony\Component\Console\Command\Command; - -class DescriptorCommand1 extends Command -{ - protected function configure() - { - $this - ->setName('descriptor:command1') - ->setAliases(array('alias1', 'alias2')) - ->setDescription('command 1 description') - ->setHelp('command 1 help') - ; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php deleted file mode 100644 index 51106b961..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand2.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Fixtures; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; - -class DescriptorCommand2 extends Command -{ - protected function configure() - { - $this - ->setName('descriptor:command2') - ->setDescription('command 2 description') - ->setHelp('command 2 help') - ->addUsage('-o|--option_name ') - ->addUsage('') - ->addArgument('argument_name', InputArgument::REQUIRED) - ->addOption('option_name', 'o', InputOption::VALUE_NONE) - ; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand3.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand3.php deleted file mode 100644 index 77f92e233..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/DescriptorCommand3.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Fixtures; - -use Symfony\Component\Console\Command\Command; - -class DescriptorCommand3 extends Command -{ - protected function configure() - { - $this - ->setName('descriptor:command3') - ->setDescription('command 3 description') - ->setHelp('command 3 help') - ->setHidden(true) - ; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/DummyOutput.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/DummyOutput.php deleted file mode 100644 index 0070c0a48..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/DummyOutput.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Fixtures; - -use Symfony\Component\Console\Output\BufferedOutput; - -/** - * Dummy output. - * - * @author Kévin Dunglas - */ -class DummyOutput extends BufferedOutput -{ - /** - * @return array - */ - public function getLogs() - { - $logs = array(); - foreach (explode("\n", trim($this->fetch())) as $message) { - preg_match('/^\[(.*)\] (.*)/', $message, $matches); - $logs[] = sprintf('%s %s', $matches[1], $matches[2]); - } - - return $logs; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo1Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo1Command.php deleted file mode 100644 index 254162f32..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo1Command.php +++ /dev/null @@ -1,26 +0,0 @@ -setName('foo:bar1') - ->setDescription('The foo:bar1 command') - ->setAliases(array('afoobar1')) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->input = $input; - $this->output = $output; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo2Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo2Command.php deleted file mode 100644 index 8071dc8fb..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo2Command.php +++ /dev/null @@ -1,21 +0,0 @@ -setName('foo1:bar') - ->setDescription('The foo1:bar command') - ->setAliases(array('afoobar2')) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo3Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo3Command.php deleted file mode 100644 index adb3a2d80..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo3Command.php +++ /dev/null @@ -1,29 +0,0 @@ -setName('foo3:bar') - ->setDescription('The foo3:bar command') - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - try { - try { - throw new \Exception('First exception

this is html

'); - } catch (\Exception $e) { - throw new \Exception('Second exception comment', 0, $e); - } - } catch (\Exception $e) { - throw new \Exception('Third exception comment', 404, $e); - } - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo4Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo4Command.php deleted file mode 100644 index 1c5463995..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo4Command.php +++ /dev/null @@ -1,11 +0,0 @@ -setName('foo3:bar:toh'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo5Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo5Command.php deleted file mode 100644 index a1c60827a..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Foo5Command.php +++ /dev/null @@ -1,10 +0,0 @@ -setName('0foo:bar')->setDescription('0foo:bar command'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooCommand.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/FooCommand.php deleted file mode 100644 index 355e0ad6d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooCommand.php +++ /dev/null @@ -1,33 +0,0 @@ -setName('foo:bar') - ->setDescription('The foo:bar command') - ->setAliases(array('afoobar')) - ; - } - - protected function interact(InputInterface $input, OutputInterface $output) - { - $output->writeln('interact called'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->input = $input; - $this->output = $output; - - $output->writeln('called'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooLock2Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/FooLock2Command.php deleted file mode 100644 index 4e4656f20..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooLock2Command.php +++ /dev/null @@ -1,28 +0,0 @@ -setName('foo:lock2'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - try { - $this->lock(); - $this->lock(); - } catch (LogicException $e) { - return 1; - } - - return 2; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooLockCommand.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/FooLockCommand.php deleted file mode 100644 index dfa28a6be..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooLockCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -setName('foo:lock'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - if (!$this->lock()) { - return 1; - } - - $this->release(); - - return 2; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php deleted file mode 100644 index fc50c72bf..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php +++ /dev/null @@ -1,26 +0,0 @@ -setName('foo:bar:baz') - ->setDescription('The foo:bar:baz command') - ->setAliases(array('foobarbaz')) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->input = $input; - $this->output = $output; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php deleted file mode 100644 index 1cf31ff11..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php +++ /dev/null @@ -1,26 +0,0 @@ -setName('foo:go:bret') - ->setDescription('The foo:bar:go command') - ->setAliases(array('foobargo')) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->input = $input; - $this->output = $output; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php deleted file mode 100644 index 968162804..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -setName('foobar:foo') - ->setDescription('The foobar:foo command') - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->input = $input; - $this->output = $output; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php deleted file mode 100644 index 8fe7c0771..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php +++ /dev/null @@ -1,11 +0,0 @@ -caution('Lorem ipsum dolor sit amet'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php deleted file mode 100644 index e5c700d60..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php +++ /dev/null @@ -1,13 +0,0 @@ -title('Title'); - $output->warning('Lorem ipsum dolor sit amet'); - $output->title('Title'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php deleted file mode 100644 index 3111873dd..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_10.php +++ /dev/null @@ -1,17 +0,0 @@ -block( - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', - 'CUSTOM', - 'fg=white;bg=green', - 'X ', - true - ); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php deleted file mode 100644 index 3ed897def..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_11.php +++ /dev/null @@ -1,12 +0,0 @@ -block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php deleted file mode 100644 index 8c458ae76..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_12.php +++ /dev/null @@ -1,13 +0,0 @@ -comment( - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' - ); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_13.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_13.php deleted file mode 100644 index 827cbad1d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_13.php +++ /dev/null @@ -1,14 +0,0 @@ -setDecorated(true); - $output = new SymfonyStyle($input, $output); - $output->comment( - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' - ); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_14.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_14.php deleted file mode 100644 index a893a48bf..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_14.php +++ /dev/null @@ -1,17 +0,0 @@ -block( - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', - null, - null, - '$ ', - true - ); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_15.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_15.php deleted file mode 100644 index 68402cd40..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_15.php +++ /dev/null @@ -1,14 +0,0 @@ -block( - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', - 'TEST' - ); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_16.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_16.php deleted file mode 100644 index 66e817963..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_16.php +++ /dev/null @@ -1,15 +0,0 @@ -setDecorated(true); - $output = new SymfonyStyle($input, $output); - $output->success( - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', - 'TEST' - ); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php deleted file mode 100644 index 791b626f2..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php +++ /dev/null @@ -1,16 +0,0 @@ -warning('Warning'); - $output->caution('Caution'); - $output->error('Error'); - $output->success('Success'); - $output->note('Note'); - $output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php deleted file mode 100644 index 99253a6c0..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php +++ /dev/null @@ -1,12 +0,0 @@ -title('First title'); - $output->title('Second title'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php deleted file mode 100644 index 0c5d3fb26..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php +++ /dev/null @@ -1,34 +0,0 @@ -write('Lorem ipsum dolor sit amet'); - $output->title('First title'); - - $output->writeln('Lorem ipsum dolor sit amet'); - $output->title('Second title'); - - $output->write('Lorem ipsum dolor sit amet'); - $output->write(''); - $output->title('Third title'); - - //Ensure edge case by appending empty strings to history: - $output->write('Lorem ipsum dolor sit amet'); - $output->write(array('', '', '')); - $output->title('Fourth title'); - - //Ensure have manual control over number of blank lines: - $output->writeln('Lorem ipsum dolor sit amet'); - $output->writeln(array('', '')); //Should append an extra blank line - $output->title('Fifth title'); - - $output->writeln('Lorem ipsum dolor sit amet'); - $output->newLine(2); //Should append an extra blank line - $output->title('Fifth title'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php deleted file mode 100644 index 92f358204..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php +++ /dev/null @@ -1,37 +0,0 @@ -writeln('Lorem ipsum dolor sit amet'); - $output->listing(array( - 'Lorem ipsum dolor sit amet', - 'consectetur adipiscing elit', - )); - - //Even using write: - $output->write('Lorem ipsum dolor sit amet'); - $output->listing(array( - 'Lorem ipsum dolor sit amet', - 'consectetur adipiscing elit', - )); - - $output->write('Lorem ipsum dolor sit amet'); - $output->text(array( - 'Lorem ipsum dolor sit amet', - 'consectetur adipiscing elit', - )); - - $output->newLine(); - - $output->write('Lorem ipsum dolor sit amet'); - $output->comment(array( - 'Lorem ipsum dolor sit amet', - 'consectetur adipiscing elit', - )); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php deleted file mode 100644 index 8031ec9c3..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php +++ /dev/null @@ -1,16 +0,0 @@ -listing(array( - 'Lorem ipsum dolor sit amet', - 'consectetur adipiscing elit', - )); - $output->success('Lorem ipsum dolor sit amet'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php deleted file mode 100644 index 203eb5b12..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php +++ /dev/null @@ -1,15 +0,0 @@ -title('Title'); - $output->askHidden('Hidden question'); - $output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1'); - $output->confirm('Confirmation with yes default', true); - $output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php deleted file mode 100644 index 922ef1f9d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_8.php +++ /dev/null @@ -1,26 +0,0 @@ - 3))), - array('ISBN', 'Title', 'Author'), - ); - - $rows = array( - array( - '978-0521567817', - 'De Monarchia', - new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), - ), - array('978-0804169127', 'Divine Comedy'), - ); - - $output = new SymfonyStyle($input, $output); - $output->table($headers, $rows); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php deleted file mode 100644 index 57afdf06b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_9.php +++ /dev/null @@ -1,11 +0,0 @@ -block(array('Custom block', 'Second custom block line'), 'CUSTOM', 'fg=white;bg=green', 'X ', true); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/interactive_command_1.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/interactive_command_1.php deleted file mode 100644 index c370c0010..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/interactive_command_1.php +++ /dev/null @@ -1,19 +0,0 @@ -setStream($stream); - - $output->ask('What\'s your name?'); - $output->ask('How are you?'); - $output->ask('Where do you come from?'); -}; diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/interactive_output_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/interactive_output_1.txt deleted file mode 100644 index 6fc7d7eb4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/interactive_output_1.txt +++ /dev/null @@ -1,7 +0,0 @@ - - What's your name?: - > - How are you?: - > - Where do you come from?: - > diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt deleted file mode 100644 index a42e0f792..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt +++ /dev/null @@ -1,3 +0,0 @@ - - ! [CAUTION] Lorem ipsum dolor sit amet - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt deleted file mode 100644 index 334875f78..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt +++ /dev/null @@ -1,9 +0,0 @@ - -Title -===== - - [WARNING] Lorem ipsum dolor sit amet - -Title -===== - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt deleted file mode 100644 index 385c6a283..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_10.txt +++ /dev/null @@ -1,7 +0,0 @@ - -X [CUSTOM] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et -X dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea -X commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat -X nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit -X anim id est laborum - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt deleted file mode 100644 index 190d78403..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_11.txt +++ /dev/null @@ -1,4 +0,0 @@ - - § [CUSTOM] Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophatto - § peristeralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt deleted file mode 100644 index 9983af832..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_12.txt +++ /dev/null @@ -1,6 +0,0 @@ - - // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna - // aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - // Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur - // sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt deleted file mode 100644 index 0f3704b74..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_13.txt +++ /dev/null @@ -1,7 +0,0 @@ - - // Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et  - // dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea  - // commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla  - // pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim - // id est laborum - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_14.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_14.txt deleted file mode 100644 index 1d0d37e7f..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_14.txt +++ /dev/null @@ -1,6 +0,0 @@ - -$ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna -$ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. -$ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint -$ occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_15.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_15.txt deleted file mode 100644 index 66404b815..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_15.txt +++ /dev/null @@ -1,7 +0,0 @@ - - [TEST] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - laborum - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_16.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_16.txt deleted file mode 100644 index a0d180165..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_16.txt +++ /dev/null @@ -1,8 +0,0 @@ - -  - [OK] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore  - magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo  - consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.  - Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum  -  - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt deleted file mode 100644 index ca609760c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt +++ /dev/null @@ -1,13 +0,0 @@ - - [WARNING] Warning - - ! [CAUTION] Caution - - [ERROR] Error - - [OK] Success - - ! [NOTE] Note - -X [CUSTOM] Custom block - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt deleted file mode 100644 index f4b6d5827..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt +++ /dev/null @@ -1,7 +0,0 @@ - -First title -=========== - -Second title -============ - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt deleted file mode 100644 index 2646d858e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt +++ /dev/null @@ -1,32 +0,0 @@ -Lorem ipsum dolor sit amet - -First title -=========== - -Lorem ipsum dolor sit amet - -Second title -============ - -Lorem ipsum dolor sit amet - -Third title -=========== - -Lorem ipsum dolor sit amet - -Fourth title -============ - -Lorem ipsum dolor sit amet - - -Fifth title -=========== - -Lorem ipsum dolor sit amet - - -Fifth title -=========== - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt deleted file mode 100644 index be4a2db60..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt +++ /dev/null @@ -1,18 +0,0 @@ -Lorem ipsum dolor sit amet - * Lorem ipsum dolor sit amet - * consectetur adipiscing elit - -Lorem ipsum dolor sit amet - * Lorem ipsum dolor sit amet - * consectetur adipiscing elit - -Lorem ipsum dolor sit amet - Lorem ipsum dolor sit amet - consectetur adipiscing elit - -Lorem ipsum dolor sit amet - - // Lorem ipsum dolor sit amet - // - // consectetur adipiscing elit - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt deleted file mode 100644 index 5f2d33c14..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt +++ /dev/null @@ -1,6 +0,0 @@ - - * Lorem ipsum dolor sit amet - * consectetur adipiscing elit - - [OK] Lorem ipsum dolor sit amet - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt deleted file mode 100644 index ecea9778b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt +++ /dev/null @@ -1,5 +0,0 @@ - -Title -===== - - Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt deleted file mode 100644 index 005b846ea..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_8.txt +++ /dev/null @@ -1,9 +0,0 @@ - ---------------- --------------- --------------------- - Main table title - ---------------- --------------- --------------------- - ISBN Title Author - ---------------- --------------- --------------------- - 978-0521567817 De Monarchia Dante Alighieri - 978-0804169127 Divine Comedy spans multiple rows - ---------------- --------------- --------------------- - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt deleted file mode 100644 index 069c0d511..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_9.txt +++ /dev/null @@ -1,5 +0,0 @@ - -X [CUSTOM] Custom block -X -X Second custom block line - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/TestCommand.php b/tests/integration/vendor/symfony/console/Tests/Fixtures/TestCommand.php deleted file mode 100644 index dcd32739c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/TestCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -setName('namespace:name') - ->setAliases(array('name')) - ->setDescription('description') - ->setHelp('help') - ; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $output->writeln('execute called'); - } - - protected function interact(InputInterface $input, OutputInterface $output) - { - $output->writeln('interact called'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.json deleted file mode 100644 index dab170a35..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "commands": [ - { - "name": "help", - "usage": [ - "help [--format FORMAT] [--raw] [--] []" - ], - "description": "Displays help for a command", - "help": "The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.", - "definition": { - "arguments": { - "command_name": { - "name": "command_name", - "is_required": false, - "is_array": false, - "description": "The command name", - "default": "help" - } - }, - "options": { - "format": { - "name": "--format", - "shortcut": "", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "The output format (txt, xml, json, or md)", - "default": "txt" - }, - "raw": { - "name": "--raw", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "To output raw command help", - "default": false - }, - "help": { - "name": "--help", - "shortcut": "-h", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this help message", - "default": false - }, - "quiet": { - "name": "--quiet", - "shortcut": "-q", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not output any message", - "default": false - }, - "verbose": { - "name": "--verbose", - "shortcut": "-v|-vv|-vvv", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", - "default": false - }, - "version": { - "name": "--version", - "shortcut": "-V", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this application version", - "default": false - }, - "ansi": { - "name": "--ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Force ANSI output", - "default": false - }, - "no-ansi": { - "name": "--no-ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Disable ANSI output", - "default": false - }, - "no-interaction": { - "name": "--no-interaction", - "shortcut": "-n", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not ask any interactive question", - "default": false - } - } - } - }, - { - "name": "list", - "usage": [ - "list [--raw] [--format FORMAT] [--] []" - ], - "description": "Lists commands", - "help": "The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>", - "definition": { - "arguments": { - "namespace": { - "name": "namespace", - "is_required": false, - "is_array": false, - "description": "The namespace name", - "default": null - } - }, - "options": { - "raw": { - "name": "--raw", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "To output raw command list", - "default": false - }, - "format": { - "name": "--format", - "shortcut": "", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "The output format (txt, xml, json, or md)", - "default": "txt" - } - } - } - } - ], - "namespaces": [ - { - "id": "_global", - "commands": [ - "help", - "list" - ] - } - ] -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.md deleted file mode 100644 index f1d88c5b7..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.md +++ /dev/null @@ -1,181 +0,0 @@ -UNKNOWN -======= - -* help -* list - -help ----- - -* Description: Displays help for a command -* Usage: - - * `help [--format FORMAT] [--raw] [--] []` - -The help command displays help for a given command: - - php app/console help list - -You can also output the help in other formats by using the --format option: - - php app/console help --format=xml list - -To display the list of available commands, please use the list command. - -### Arguments: - -**command_name:** - -* Name: command_name -* Is required: no -* Is array: no -* Description: The command name -* Default: `'help'` - -### Options: - -**format:** - -* Name: `--format` -* Shortcut: -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: The output format (txt, xml, json, or md) -* Default: `'txt'` - -**raw:** - -* Name: `--raw` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: To output raw command help -* Default: `false` - -**help:** - -* Name: `--help` -* Shortcut: `-h` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this help message -* Default: `false` - -**quiet:** - -* Name: `--quiet` -* Shortcut: `-q` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not output any message -* Default: `false` - -**verbose:** - -* Name: `--verbose` -* Shortcut: `-v|-vv|-vvv` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug -* Default: `false` - -**version:** - -* Name: `--version` -* Shortcut: `-V` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this application version -* Default: `false` - -**ansi:** - -* Name: `--ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Force ANSI output -* Default: `false` - -**no-ansi:** - -* Name: `--no-ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Disable ANSI output -* Default: `false` - -**no-interaction:** - -* Name: `--no-interaction` -* Shortcut: `-n` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not ask any interactive question -* Default: `false` - -list ----- - -* Description: Lists commands -* Usage: - - * `list [--raw] [--format FORMAT] [--] []` - -The list command lists all commands: - - php app/console list - -You can also display the commands for a specific namespace: - - php app/console list test - -You can also output the information in other formats by using the --format option: - - php app/console list --format=xml - -It's also possible to get raw list of commands (useful for embedding command runner): - - php app/console list --raw - -### Arguments: - -**namespace:** - -* Name: namespace -* Is required: no -* Is array: no -* Description: The namespace name -* Default: `NULL` - -### Options: - -**raw:** - -* Name: `--raw` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: To output raw command list -* Default: `false` - -**format:** - -* Name: `--format` -* Shortcut: -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: The output format (txt, xml, json, or md) -* Default: `'txt'` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.txt deleted file mode 100644 index 8a7b47e0c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.txt +++ /dev/null @@ -1,17 +0,0 @@ -Console Tool - -Usage: - command [options] [arguments] - -Options: - -h, --help Display this help message - -q, --quiet Do not output any message - -V, --version Display this application version - --ansi Force ANSI output - --no-ansi Disable ANSI output - -n, --no-interaction Do not ask any interactive question - -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug - -Available commands: - help Displays help for a command - list Lists commands diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.xml deleted file mode 100644 index 8514f233b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_1.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - help [--format FORMAT] [--raw] [--] [<command_name>] - - Displays help for a command - The <info>help</info> command displays help for a given command: - - <info>php app/console help list</info> - - You can also output the help in other formats by using the <comment>--format</comment> option: - - <info>php app/console help --format=xml list</info> - - To display the list of available commands, please use the <info>list</info> command. - - - The command name - - help - - - - - - - - - - - - - - - - - - list [--raw] [--format FORMAT] [--] [<namespace>] - - Lists commands - The <info>list</info> command lists all commands: - - <info>php app/console list</info> - - You can also display the commands for a specific namespace: - - <info>php app/console list test</info> - - You can also output the information in other formats by using the <comment>--format</comment> option: - - <info>php app/console list --format=xml</info> - - It's also possible to get raw list of commands (useful for embedding command runner): - - <info>php app/console list --raw</info> - - - The namespace name - - - - - - - - - - - - help - list - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.json deleted file mode 100644 index 398f1fd0d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.json +++ /dev/null @@ -1,336 +0,0 @@ -{ - "commands": [ - { - "name": "help", - "usage": [ - "help [--format FORMAT] [--raw] [--] []" - ], - "description": "Displays help for a command", - "help": "The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.", - "definition": { - "arguments": { - "command_name": { - "name": "command_name", - "is_required": false, - "is_array": false, - "description": "The command name", - "default": "help" - } - }, - "options": { - "format": { - "name": "--format", - "shortcut": "", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "The output format (txt, xml, json, or md)", - "default": "txt" - }, - "raw": { - "name": "--raw", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "To output raw command help", - "default": false - }, - "help": { - "name": "--help", - "shortcut": "-h", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this help message", - "default": false - }, - "quiet": { - "name": "--quiet", - "shortcut": "-q", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not output any message", - "default": false - }, - "verbose": { - "name": "--verbose", - "shortcut": "-v|-vv|-vvv", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", - "default": false - }, - "version": { - "name": "--version", - "shortcut": "-V", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this application version", - "default": false - }, - "ansi": { - "name": "--ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Force ANSI output", - "default": false - }, - "no-ansi": { - "name": "--no-ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Disable ANSI output", - "default": false - }, - "no-interaction": { - "name": "--no-interaction", - "shortcut": "-n", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not ask any interactive question", - "default": false - } - } - } - }, - { - "name": "list", - "usage": [ - "list [--raw] [--format FORMAT] [--] []" - ], - "description": "Lists commands", - "help": "The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>", - "definition": { - "arguments": { - "namespace": { - "name": "namespace", - "is_required": false, - "is_array": false, - "description": "The namespace name", - "default": null - } - }, - "options": { - "raw": { - "name": "--raw", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "To output raw command list", - "default": false - }, - "format": { - "name": "--format", - "shortcut": "", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "The output format (txt, xml, json, or md)", - "default": "txt" - } - } - } - }, - { - "name": "descriptor:command1", - "usage": [ - "descriptor:command1", - "alias1", - "alias2" - ], - "description": "command 1 description", - "help": "command 1 help", - "definition": { - "arguments": [], - "options": { - "help": { - "name": "--help", - "shortcut": "-h", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this help message", - "default": false - }, - "quiet": { - "name": "--quiet", - "shortcut": "-q", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not output any message", - "default": false - }, - "verbose": { - "name": "--verbose", - "shortcut": "-v|-vv|-vvv", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", - "default": false - }, - "version": { - "name": "--version", - "shortcut": "-V", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this application version", - "default": false - }, - "ansi": { - "name": "--ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Force ANSI output", - "default": false - }, - "no-ansi": { - "name": "--no-ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Disable ANSI output", - "default": false - }, - "no-interaction": { - "name": "--no-interaction", - "shortcut": "-n", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not ask any interactive question", - "default": false - } - } - } - }, - { - "name": "descriptor:command2", - "usage": [ - "descriptor:command2 [-o|--option_name] [--] ", - "descriptor:command2 -o|--option_name ", - "descriptor:command2 " - ], - "description": "command 2 description", - "help": "command 2 help", - "definition": { - "arguments": { - "argument_name": { - "name": "argument_name", - "is_required": true, - "is_array": false, - "description": "", - "default": null - } - }, - "options": { - "option_name": { - "name": "--option_name", - "shortcut": "-o", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "", - "default": false - }, - "help": { - "name": "--help", - "shortcut": "-h", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this help message", - "default": false - }, - "quiet": { - "name": "--quiet", - "shortcut": "-q", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not output any message", - "default": false - }, - "verbose": { - "name": "--verbose", - "shortcut": "-v|-vv|-vvv", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", - "default": false - }, - "version": { - "name": "--version", - "shortcut": "-V", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Display this application version", - "default": false - }, - "ansi": { - "name": "--ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Force ANSI output", - "default": false - }, - "no-ansi": { - "name": "--no-ansi", - "shortcut": "", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Disable ANSI output", - "default": false - }, - "no-interaction": { - "name": "--no-interaction", - "shortcut": "-n", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "Do not ask any interactive question", - "default": false - } - } - } - } - ], - "namespaces": [ - { - "id": "_global", - "commands": [ - "alias1", - "alias2", - "help", - "list" - ] - }, - { - "id": "descriptor", - "commands": [ - "descriptor:command1", - "descriptor:command2" - ] - } - ] -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.md deleted file mode 100644 index 63b2d9ab4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.md +++ /dev/null @@ -1,376 +0,0 @@ -My Symfony application -====================== - -* alias1 -* alias2 -* help -* list - -**descriptor:** - -* descriptor:command1 -* descriptor:command2 - -help ----- - -* Description: Displays help for a command -* Usage: - - * `help [--format FORMAT] [--raw] [--] []` - -The help command displays help for a given command: - - php app/console help list - -You can also output the help in other formats by using the --format option: - - php app/console help --format=xml list - -To display the list of available commands, please use the list command. - -### Arguments: - -**command_name:** - -* Name: command_name -* Is required: no -* Is array: no -* Description: The command name -* Default: `'help'` - -### Options: - -**format:** - -* Name: `--format` -* Shortcut: -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: The output format (txt, xml, json, or md) -* Default: `'txt'` - -**raw:** - -* Name: `--raw` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: To output raw command help -* Default: `false` - -**help:** - -* Name: `--help` -* Shortcut: `-h` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this help message -* Default: `false` - -**quiet:** - -* Name: `--quiet` -* Shortcut: `-q` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not output any message -* Default: `false` - -**verbose:** - -* Name: `--verbose` -* Shortcut: `-v|-vv|-vvv` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug -* Default: `false` - -**version:** - -* Name: `--version` -* Shortcut: `-V` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this application version -* Default: `false` - -**ansi:** - -* Name: `--ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Force ANSI output -* Default: `false` - -**no-ansi:** - -* Name: `--no-ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Disable ANSI output -* Default: `false` - -**no-interaction:** - -* Name: `--no-interaction` -* Shortcut: `-n` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not ask any interactive question -* Default: `false` - -list ----- - -* Description: Lists commands -* Usage: - - * `list [--raw] [--format FORMAT] [--] []` - -The list command lists all commands: - - php app/console list - -You can also display the commands for a specific namespace: - - php app/console list test - -You can also output the information in other formats by using the --format option: - - php app/console list --format=xml - -It's also possible to get raw list of commands (useful for embedding command runner): - - php app/console list --raw - -### Arguments: - -**namespace:** - -* Name: namespace -* Is required: no -* Is array: no -* Description: The namespace name -* Default: `NULL` - -### Options: - -**raw:** - -* Name: `--raw` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: To output raw command list -* Default: `false` - -**format:** - -* Name: `--format` -* Shortcut: -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: The output format (txt, xml, json, or md) -* Default: `'txt'` - -descriptor:command1 -------------------- - -* Description: command 1 description -* Usage: - - * `descriptor:command1` - * `alias1` - * `alias2` - -command 1 help - -### Options: - -**help:** - -* Name: `--help` -* Shortcut: `-h` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this help message -* Default: `false` - -**quiet:** - -* Name: `--quiet` -* Shortcut: `-q` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not output any message -* Default: `false` - -**verbose:** - -* Name: `--verbose` -* Shortcut: `-v|-vv|-vvv` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug -* Default: `false` - -**version:** - -* Name: `--version` -* Shortcut: `-V` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this application version -* Default: `false` - -**ansi:** - -* Name: `--ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Force ANSI output -* Default: `false` - -**no-ansi:** - -* Name: `--no-ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Disable ANSI output -* Default: `false` - -**no-interaction:** - -* Name: `--no-interaction` -* Shortcut: `-n` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not ask any interactive question -* Default: `false` - -descriptor:command2 -------------------- - -* Description: command 2 description -* Usage: - - * `descriptor:command2 [-o|--option_name] [--] ` - * `descriptor:command2 -o|--option_name ` - * `descriptor:command2 ` - -command 2 help - -### Arguments: - -**argument_name:** - -* Name: argument_name -* Is required: yes -* Is array: no -* Description: -* Default: `NULL` - -### Options: - -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: -* Default: `false` - -**help:** - -* Name: `--help` -* Shortcut: `-h` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this help message -* Default: `false` - -**quiet:** - -* Name: `--quiet` -* Shortcut: `-q` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not output any message -* Default: `false` - -**verbose:** - -* Name: `--verbose` -* Shortcut: `-v|-vv|-vvv` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug -* Default: `false` - -**version:** - -* Name: `--version` -* Shortcut: `-V` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Display this application version -* Default: `false` - -**ansi:** - -* Name: `--ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Force ANSI output -* Default: `false` - -**no-ansi:** - -* Name: `--no-ansi` -* Shortcut: -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Disable ANSI output -* Default: `false` - -**no-interaction:** - -* Name: `--no-interaction` -* Shortcut: `-n` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: Do not ask any interactive question -* Default: `false` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.txt deleted file mode 100644 index b28036599..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.txt +++ /dev/null @@ -1,20 +0,0 @@ -My Symfony application v1.0 - -Usage: - command [options] [arguments] - -Options: - -h, --help Display this help message - -q, --quiet Do not output any message - -V, --version Display this application version - --ansi Force ANSI output - --no-ansi Disable ANSI output - -n, --no-interaction Do not ask any interactive question - -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug - -Available commands: - help Displays help for a command - list Lists commands - descriptor - descriptor:command1 [alias1|alias2] command 1 description - descriptor:command2 command 2 description diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.xml deleted file mode 100644 index 62e3cfcbe..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_2.xml +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - help [--format FORMAT] [--raw] [--] [<command_name>] - - Displays help for a command - The <info>help</info> command displays help for a given command: - - <info>php app/console help list</info> - - You can also output the help in other formats by using the <comment>--format</comment> option: - - <info>php app/console help --format=xml list</info> - - To display the list of available commands, please use the <info>list</info> command. - - - The command name - - help - - - - - - - - - - - - - - - - - - list [--raw] [--format FORMAT] [--] [<namespace>] - - Lists commands - The <info>list</info> command lists all commands: - - <info>php app/console list</info> - - You can also display the commands for a specific namespace: - - <info>php app/console list test</info> - - You can also output the information in other formats by using the <comment>--format</comment> option: - - <info>php app/console list --format=xml</info> - - It's also possible to get raw list of commands (useful for embedding command runner): - - <info>php app/console list --raw</info> - - - The namespace name - - - - - - - - - - - descriptor:command1 - alias1 - alias2 - - command 1 description - command 1 help - - - - - - - - - - - - - - descriptor:command2 [-o|--option_name] [--] <argument_name> - descriptor:command2 -o|--option_name <argument_name> - descriptor:command2 <argument_name> - - command 2 description - command 2 help - - - - - - - - - - - - - - - - - - - - - alias1 - alias2 - help - list - - - descriptor:command1 - descriptor:command2 - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt deleted file mode 100644 index 5a5920d0e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_gethelp.txt +++ /dev/null @@ -1 +0,0 @@ -Console Tool \ No newline at end of file diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt deleted file mode 100644 index 919cec421..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception1.txt +++ /dev/null @@ -1,6 +0,0 @@ - - - [Symfony\Component\Console\Exception\CommandNotFoundException] - Command "foo" is not defined. - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt deleted file mode 100644 index 64561715e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception2.txt +++ /dev/null @@ -1,8 +0,0 @@ - - - [Symfony\Component\Console\Exception\InvalidOptionException] - The "--foo" option does not exist. - - -list [--raw] [--format FORMAT] [--] [] - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt deleted file mode 100644 index 8276137bd..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception3.txt +++ /dev/null @@ -1,18 +0,0 @@ - - - [Exception] - Third exception comment - - - - [Exception] - Second exception comment - - - - [Exception] - First exception

this is html

- - -foo3:bar - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt deleted file mode 100644 index b4a7b018a..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception3decorated.txt +++ /dev/null @@ -1,18 +0,0 @@ - -  - [Exception]  - Third exception comment  -  - -  - [Exception]  - Second exception comment  -  - -  - [Exception]  - First exception 

this is html

  -  - -foo3:bar - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt deleted file mode 100644 index cb080e9cb..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception4.txt +++ /dev/null @@ -1,7 +0,0 @@ - - - [Symfony\Component\Console\Exception\CommandNotFoundException] - Command "foo" is not define - d. - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt deleted file mode 100644 index 1ba5f8fdd..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1.txt +++ /dev/null @@ -1,8 +0,0 @@ - - - [Exception] - エラーメッセージ - - -foo - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt deleted file mode 100644 index 20644251c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt +++ /dev/null @@ -1,8 +0,0 @@ - -  - [Exception]  - エラーメッセージ  -  - -foo - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt deleted file mode 100644 index e41fcfcf6..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_renderexception_doublewidth2.txt +++ /dev/null @@ -1,9 +0,0 @@ - - - [Exception] - コマンドの実行中にエラーが - 発生しました。 - - -foo - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run1.txt deleted file mode 100644 index 0dc273098..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run1.txt +++ /dev/null @@ -1,17 +0,0 @@ -Console Tool - -Usage: - command [options] [arguments] - -Options: - -h, --help Display this help message - -q, --quiet Do not output any message - -V, --version Display this application version - --ansi Force ANSI output - --no-ansi Disable ANSI output - -n, --no-interaction Do not ask any interactive question - -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug - -Available commands: - help Displays help for a command - list Lists commands diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run2.txt deleted file mode 100644 index 65a685d08..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run2.txt +++ /dev/null @@ -1,26 +0,0 @@ -Usage: - list [options] [--] [] - -Arguments: - namespace The namespace name - -Options: - --raw To output raw command list - --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] - -Help: - The list command lists all commands: - - php app/console list - - You can also display the commands for a specific namespace: - - php app/console list test - - You can also output the information in other formats by using the --format option: - - php app/console list --format=xml - - It's also possible to get raw list of commands (useful for embedding command runner): - - php app/console list --raw diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run3.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run3.txt deleted file mode 100644 index 65a685d08..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run3.txt +++ /dev/null @@ -1,26 +0,0 @@ -Usage: - list [options] [--] [] - -Arguments: - namespace The namespace name - -Options: - --raw To output raw command list - --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] - -Help: - The list command lists all commands: - - php app/console list - - You can also display the commands for a specific namespace: - - php app/console list test - - You can also output the information in other formats by using the --format option: - - php app/console list --format=xml - - It's also possible to get raw list of commands (useful for embedding command runner): - - php app/console list --raw diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run4.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run4.txt deleted file mode 100644 index 47187fc26..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/application_run4.txt +++ /dev/null @@ -1 +0,0 @@ -Console Tool diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.json deleted file mode 100644 index 4cd37eeb4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "descriptor:command1", - "usage": [ - "descriptor:command1", - "alias1", - "alias2" - ], - "description": "command 1 description", - "help": "command 1 help", - "definition": { - "arguments": [], - "options": [] - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.md deleted file mode 100644 index 34ed3ea77..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.md +++ /dev/null @@ -1,11 +0,0 @@ -descriptor:command1 -------------------- - -* Description: command 1 description -* Usage: - - * `descriptor:command1` - * `alias1` - * `alias2` - -command 1 help diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.txt deleted file mode 100644 index e5e93ee09..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.txt +++ /dev/null @@ -1,7 +0,0 @@ -Usage: - descriptor:command1 - alias1 - alias2 - -Help: - command 1 help diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.xml deleted file mode 100644 index 838b9bd91..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_1.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - descriptor:command1 - alias1 - alias2 - - command 1 description - command 1 help - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.json deleted file mode 100644 index 74c1f1260..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "descriptor:command2", - "usage": [ - "descriptor:command2 [-o|--option_name] [--] ", - "descriptor:command2 -o|--option_name ", - "descriptor:command2 " - ], - "description": "command 2 description", - "help": "command 2 help", - "definition": { - "arguments": { - "argument_name": { - "name": "argument_name", - "is_required": true, - "is_array": false, - "description": "", - "default": null - } - }, - "options": { - "option_name": { - "name": "--option_name", - "shortcut": "-o", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "", - "default": false - } - } - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.md deleted file mode 100644 index 6f538b640..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.md +++ /dev/null @@ -1,33 +0,0 @@ -descriptor:command2 -------------------- - -* Description: command 2 description -* Usage: - - * `descriptor:command2 [-o|--option_name] [--] ` - * `descriptor:command2 -o|--option_name ` - * `descriptor:command2 ` - -command 2 help - -### Arguments: - -**argument_name:** - -* Name: argument_name -* Is required: yes -* Is array: no -* Description: -* Default: `NULL` - -### Options: - -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: -* Default: `false` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.txt deleted file mode 100644 index cad9cb45f..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.txt +++ /dev/null @@ -1,13 +0,0 @@ -Usage: - descriptor:command2 [options] [--] - descriptor:command2 -o|--option_name - descriptor:command2 - -Arguments: - argument_name - -Options: - -o, --option_name - -Help: - command 2 help diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.xml deleted file mode 100644 index 67364caa4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/command_2.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - descriptor:command2 [-o|--option_name] [--] <argument_name> - descriptor:command2 -o|--option_name <argument_name> - descriptor:command2 <argument_name> - - command 2 description - command 2 help - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.json deleted file mode 100644 index 0ab932960..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "argument_name", - "is_required": true, - "is_array": false, - "description": "", - "default": null -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.md deleted file mode 100644 index 88f311ab5..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.md +++ /dev/null @@ -1,7 +0,0 @@ -**argument_name:** - -* Name: argument_name -* Is required: yes -* Is array: no -* Description: -* Default: `NULL` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt deleted file mode 100644 index 55035183f..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.txt +++ /dev/null @@ -1 +0,0 @@ - argument_name diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml deleted file mode 100644 index cb37f812c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_1.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.json deleted file mode 100644 index 7450016ff..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "argument_name", - "is_required": false, - "is_array": true, - "description": "argument description", - "default": [] -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.md deleted file mode 100644 index 3cdb00cc8..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.md +++ /dev/null @@ -1,7 +0,0 @@ -**argument_name:** - -* Name: argument_name -* Is required: no -* Is array: yes -* Description: argument description -* Default: `array ()` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt deleted file mode 100644 index e71366074..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.txt +++ /dev/null @@ -1 +0,0 @@ - argument_name argument description diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml deleted file mode 100644 index 629da5a98..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_2.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - argument description - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.json deleted file mode 100644 index 9a83c5a54..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "argument_name", - "is_required": false, - "is_array": false, - "description": "argument description", - "default": "default_value" -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.md deleted file mode 100644 index be1c443ae..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.md +++ /dev/null @@ -1,7 +0,0 @@ -**argument_name:** - -* Name: argument_name -* Is required: no -* Is array: no -* Description: argument description -* Default: `'default_value'` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt deleted file mode 100644 index 6b76639e0..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.txt +++ /dev/null @@ -1 +0,0 @@ - argument_name argument description [default: "default_value"] diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml deleted file mode 100644 index 399a5c864..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_3.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - argument description - - default_value - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.json deleted file mode 100644 index cbcb19b39..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "argument_name", - "is_required": true, - "is_array": false, - "description": "multiline argument description", - "default": null -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.md deleted file mode 100644 index f026ab376..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.md +++ /dev/null @@ -1,8 +0,0 @@ -**argument_name:** - -* Name: argument_name -* Is required: yes -* Is array: no -* Description: multiline - argument description -* Default: `NULL` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt deleted file mode 100644 index fc7d669a1..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.txt +++ /dev/null @@ -1,2 +0,0 @@ - argument_name multiline - argument description diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml deleted file mode 100644 index 5ca135ec2..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_argument_4.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - multiline -argument description - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.json deleted file mode 100644 index 44aa2c2e1..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "arguments": [], - "options": [] -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml deleted file mode 100644 index b5481ce12..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_1.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.json deleted file mode 100644 index 7cfd57e56..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "arguments": { - "argument_name": { - "name": "argument_name", - "is_required": true, - "is_array": false, - "description": "", - "default": null - } - }, - "options": [] -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.md deleted file mode 100644 index 923191cdc..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.md +++ /dev/null @@ -1,9 +0,0 @@ -### Arguments: - -**argument_name:** - -* Name: argument_name -* Is required: yes -* Is array: no -* Description: -* Default: `NULL` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt deleted file mode 100644 index 73b0f308a..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.txt +++ /dev/null @@ -1,2 +0,0 @@ -Arguments: - argument_name diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml deleted file mode 100644 index 102efc148..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_2.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.json deleted file mode 100644 index 3b3cf73c5..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "arguments": [], - "options": { - "option_name": { - "name": "--option_name", - "shortcut": "-o", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "", - "default": false - } - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.md deleted file mode 100644 index 40fd7b0a9..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.md +++ /dev/null @@ -1,11 +0,0 @@ -### Options: - -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: -* Default: `false` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt deleted file mode 100644 index c02766fd3..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.txt +++ /dev/null @@ -1,2 +0,0 @@ -Options: - -o, --option_name diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml deleted file mode 100644 index bc9515154..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_3.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.json deleted file mode 100644 index d4a51e82e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "arguments": { - "argument_name": { - "name": "argument_name", - "is_required": true, - "is_array": false, - "description": "", - "default": null - } - }, - "options": { - "option_name": { - "name": "--option_name", - "shortcut": "-o", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "", - "default": false - } - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.md deleted file mode 100644 index a31feea47..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.md +++ /dev/null @@ -1,21 +0,0 @@ -### Arguments: - -**argument_name:** - -* Name: argument_name -* Is required: yes -* Is array: no -* Description: -* Default: `NULL` - -### Options: - -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: -* Default: `false` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt deleted file mode 100644 index 63aa81d2d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.txt +++ /dev/null @@ -1,5 +0,0 @@ -Arguments: - argument_name - -Options: - -o, --option_name diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml deleted file mode 100644 index cffceecef..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_definition_4.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.json deleted file mode 100644 index f86bf9671..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "--option_name", - "shortcut": "-o", - "accept_value": false, - "is_value_required": false, - "is_multiple": false, - "description": "", - "default": false -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.md deleted file mode 100644 index 6f9e9a7e0..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.md +++ /dev/null @@ -1,9 +0,0 @@ -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: no -* Is value required: no -* Is multiple: no -* Description: -* Default: `false` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.txt deleted file mode 100644 index 3a5e4eede..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.txt +++ /dev/null @@ -1 +0,0 @@ - -o, --option_name diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.xml deleted file mode 100644 index 8a64ea652..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_1.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.json deleted file mode 100644 index 32dbab21a..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "--option_name", - "shortcut": "-o", - "accept_value": true, - "is_value_required": false, - "is_multiple": false, - "description": "option description", - "default": "default_value" -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.md deleted file mode 100644 index 634ac0b03..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.md +++ /dev/null @@ -1,9 +0,0 @@ -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: yes -* Is value required: no -* Is multiple: no -* Description: option description -* Default: `'default_value'` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.txt deleted file mode 100644 index 1009eff16..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.txt +++ /dev/null @@ -1 +0,0 @@ - -o, --option_name[=OPTION_NAME] option description [default: "default_value"] diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.xml deleted file mode 100644 index 4afac5b04..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_2.xml +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.json deleted file mode 100644 index 6d55a6efe..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "--option_name", - "shortcut": "-o", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "option description", - "default": null -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.md deleted file mode 100644 index 34282896b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.md +++ /dev/null @@ -1,9 +0,0 @@ -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: option description -* Default: `NULL` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.txt deleted file mode 100644 index 947bb6527..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.txt +++ /dev/null @@ -1 +0,0 @@ - -o, --option_name=OPTION_NAME option description diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.xml deleted file mode 100644 index dcc0631cf..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_3.xml +++ /dev/null @@ -1,5 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.json deleted file mode 100644 index 788a8ed43..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "--option_name", - "shortcut": "-o", - "accept_value": true, - "is_value_required": false, - "is_multiple": true, - "description": "option description", - "default": [] -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.md deleted file mode 100644 index 8ffba56e7..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.md +++ /dev/null @@ -1,9 +0,0 @@ -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: yes -* Is value required: no -* Is multiple: yes -* Description: option description -* Default: `array ()` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.txt deleted file mode 100644 index 27edf77b4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.txt +++ /dev/null @@ -1 +0,0 @@ - -o, --option_name[=OPTION_NAME] option description (multiple values allowed) diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.xml deleted file mode 100644 index 5e2418b14..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_4.xml +++ /dev/null @@ -1,5 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.json deleted file mode 100644 index 9f34d8321..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "--option_name", - "shortcut": "-o", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "multiline option description", - "default": null -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.md deleted file mode 100644 index 82f51cad3..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.md +++ /dev/null @@ -1,10 +0,0 @@ -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o` -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: multiline - option description -* Default: `NULL` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.txt deleted file mode 100644 index 9563b4cab..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.txt +++ /dev/null @@ -1,2 +0,0 @@ - -o, --option_name=OPTION_NAME multiline - option description diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.xml deleted file mode 100644 index 90040ccd0..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_5.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.json b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.json deleted file mode 100644 index 0638de03e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "--option_name", - "shortcut": "-o|-O", - "accept_value": true, - "is_value_required": true, - "is_multiple": false, - "description": "option with multiple shortcuts", - "default": null -} diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.md b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.md deleted file mode 100644 index ed1ea1c84..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.md +++ /dev/null @@ -1,9 +0,0 @@ -**option_name:** - -* Name: `--option_name` -* Shortcut: `-o|-O` -* Accept value: yes -* Is value required: yes -* Is multiple: no -* Description: option with multiple shortcuts -* Default: `NULL` diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.txt b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.txt deleted file mode 100644 index 0e6c9759b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.txt +++ /dev/null @@ -1 +0,0 @@ - -o|O, --option_name=OPTION_NAME option with multiple shortcuts diff --git a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.xml b/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.xml deleted file mode 100644 index 06126a2f5..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Fixtures/input_option_6.xml +++ /dev/null @@ -1,5 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php b/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php deleted file mode 100644 index 774df268b..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleStackTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Formatter; - -use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; - -class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase -{ - public function testPush() - { - $stack = new OutputFormatterStyleStack(); - $stack->push($s1 = new OutputFormatterStyle('white', 'black')); - $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); - - $this->assertEquals($s2, $stack->getCurrent()); - - $stack->push($s3 = new OutputFormatterStyle('green', 'red')); - - $this->assertEquals($s3, $stack->getCurrent()); - } - - public function testPop() - { - $stack = new OutputFormatterStyleStack(); - $stack->push($s1 = new OutputFormatterStyle('white', 'black')); - $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); - - $this->assertEquals($s2, $stack->pop()); - $this->assertEquals($s1, $stack->pop()); - } - - public function testPopEmpty() - { - $stack = new OutputFormatterStyleStack(); - $style = new OutputFormatterStyle(); - - $this->assertEquals($style, $stack->pop()); - } - - public function testPopNotLast() - { - $stack = new OutputFormatterStyleStack(); - $stack->push($s1 = new OutputFormatterStyle('white', 'black')); - $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); - $stack->push($s3 = new OutputFormatterStyle('green', 'red')); - - $this->assertEquals($s2, $stack->pop($s2)); - $this->assertEquals($s1, $stack->pop()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testInvalidPop() - { - $stack = new OutputFormatterStyleStack(); - $stack->push(new OutputFormatterStyle('white', 'black')); - $stack->pop(new OutputFormatterStyle('yellow', 'blue')); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php b/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php deleted file mode 100644 index 0abfb3ce2..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Formatter; - -use Symfony\Component\Console\Formatter\OutputFormatterStyle; - -class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); - $this->assertEquals("\033[32;40;1;4mfoo\033[39;49;22;24m", $style->apply('foo')); - - $style = new OutputFormatterStyle('red', null, array('blink')); - $this->assertEquals("\033[31;5mfoo\033[39;25m", $style->apply('foo')); - - $style = new OutputFormatterStyle(null, 'white'); - $this->assertEquals("\033[47mfoo\033[49m", $style->apply('foo')); - } - - public function testForeground() - { - $style = new OutputFormatterStyle(); - - $style->setForeground('black'); - $this->assertEquals("\033[30mfoo\033[39m", $style->apply('foo')); - - $style->setForeground('blue'); - $this->assertEquals("\033[34mfoo\033[39m", $style->apply('foo')); - - $style->setForeground('default'); - $this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo')); - - $this->setExpectedException('InvalidArgumentException'); - $style->setForeground('undefined-color'); - } - - public function testBackground() - { - $style = new OutputFormatterStyle(); - - $style->setBackground('black'); - $this->assertEquals("\033[40mfoo\033[49m", $style->apply('foo')); - - $style->setBackground('yellow'); - $this->assertEquals("\033[43mfoo\033[49m", $style->apply('foo')); - - $style->setBackground('default'); - $this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo')); - - $this->setExpectedException('InvalidArgumentException'); - $style->setBackground('undefined-color'); - } - - public function testOptions() - { - $style = new OutputFormatterStyle(); - - $style->setOptions(array('reverse', 'conceal')); - $this->assertEquals("\033[7;8mfoo\033[27;28m", $style->apply('foo')); - - $style->setOption('bold'); - $this->assertEquals("\033[7;8;1mfoo\033[27;28;22m", $style->apply('foo')); - - $style->unsetOption('reverse'); - $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); - - $style->setOption('bold'); - $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); - - $style->setOptions(array('bold')); - $this->assertEquals("\033[1mfoo\033[22m", $style->apply('foo')); - - try { - $style->setOption('foo'); - $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); - $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); - } - - try { - $style->unsetOption('foo'); - $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); - $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); - } - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php b/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php deleted file mode 100644 index f3520dff8..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php +++ /dev/null @@ -1,334 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Formatter; - -use Symfony\Component\Console\Formatter\OutputFormatter; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; - -class OutputFormatterTest extends \PHPUnit_Framework_TestCase -{ - public function testEmptyTag() - { - $formatter = new OutputFormatter(true); - $this->assertEquals('foo<>bar', $formatter->format('foo<>bar')); - } - - public function testLGCharEscaping() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals('fooformat('foo\\assertEquals('some info', $formatter->format('\\some info\\')); - $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); - - $this->assertEquals( - "\033[33mSymfony\\Component\\Console does work very well!\033[39m", - $formatter->format('Symfony\Component\Console does work very well!') - ); - } - - public function testBundledStyles() - { - $formatter = new OutputFormatter(true); - - $this->assertTrue($formatter->hasStyle('error')); - $this->assertTrue($formatter->hasStyle('info')); - $this->assertTrue($formatter->hasStyle('comment')); - $this->assertTrue($formatter->hasStyle('question')); - - $this->assertEquals( - "\033[37;41msome error\033[39;49m", - $formatter->format('some error') - ); - $this->assertEquals( - "\033[32msome info\033[39m", - $formatter->format('some info') - ); - $this->assertEquals( - "\033[33msome comment\033[39m", - $formatter->format('some comment') - ); - $this->assertEquals( - "\033[30;46msome question\033[39;49m", - $formatter->format('some question') - ); - } - - public function testNestedStyles() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals( - "\033[37;41msome \033[39;49m\033[32msome info\033[39m\033[37;41m error\033[39;49m", - $formatter->format('some some info error') - ); - } - - public function testAdjacentStyles() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals( - "\033[37;41msome error\033[39;49m\033[32msome info\033[39m", - $formatter->format('some errorsome info') - ); - } - - public function testStyleMatchingNotGreedy() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals( - "(\033[32m>=2.0,<2.3\033[39m)", - $formatter->format('(>=2.0,<2.3)') - ); - } - - public function testStyleEscaping() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals( - "(\033[32mz>=2.0,<<format('('.$formatter->escape('z>=2.0,<\\<)') - ); - - $this->assertEquals( - "\033[32msome error\033[39m", - $formatter->format(''.$formatter->escape('some error').'') - ); - } - - public function testDeepNestedStyles() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals( - "\033[37;41merror\033[39;49m\033[32minfo\033[39m\033[33mcomment\033[39m\033[37;41merror\033[39;49m", - $formatter->format('errorinfocommenterror') - ); - } - - public function testNewStyle() - { - $formatter = new OutputFormatter(true); - - $style = new OutputFormatterStyle('blue', 'white'); - $formatter->setStyle('test', $style); - - $this->assertEquals($style, $formatter->getStyle('test')); - $this->assertNotEquals($style, $formatter->getStyle('info')); - - $style = new OutputFormatterStyle('blue', 'white'); - $formatter->setStyle('b', $style); - - $this->assertEquals("\033[34;47msome \033[39;49m\033[34;47mcustom\033[39;49m\033[34;47m msg\033[39;49m", $formatter->format('some custom msg')); - } - - public function testRedefineStyle() - { - $formatter = new OutputFormatter(true); - - $style = new OutputFormatterStyle('blue', 'white'); - $formatter->setStyle('info', $style); - - $this->assertEquals("\033[34;47msome custom msg\033[39;49m", $formatter->format('some custom msg')); - } - - public function testInlineStyle() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); - $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); - } - - /** - * @param string $tag - * @param string|null $expected - * @param string|null $input - * - * @dataProvider provideInlineStyleOptionsCases - */ - public function testInlineStyleOptions($tag, $expected = null, $input = null) - { - $styleString = substr($tag, 1, -1); - $formatter = new OutputFormatter(true); - $method = new \ReflectionMethod($formatter, 'createStyleFromString'); - $method->setAccessible(true); - $result = $method->invoke($formatter, $styleString); - if (null === $expected) { - $this->assertFalse($result); - $expected = $tag.$input.''; - $this->assertSame($expected, $formatter->format($expected)); - } else { - /* @var OutputFormatterStyle $result */ - $this->assertInstanceOf(OutputFormatterStyle::class, $result); - $this->assertSame($expected, $formatter->format($tag.$input.'')); - $this->assertSame($expected, $formatter->format($tag.$input.'')); - } - } - - public function provideInlineStyleOptionsCases() - { - return array( - array(''), - array(''), - array('', "\033[32m[test]\033[39m", '[test]'), - array('', "\033[32;44ma\033[39;49m", 'a'), - array('', "\033[32;1mb\033[39;22m", 'b'), - array('', "\033[32;7m\033[39;27m", ''), - array('', "\033[32;1;4mz\033[39;22;24m", 'z'), - array('', "\033[32;1;4;7md\033[39;22;24;27m", 'd'), - ); - } - - /** - * @group legacy - * @dataProvider provideInlineStyleTagsWithUnknownOptions - * @expectedDeprecation Unknown style options are deprecated since version 3.2 and will be removed in 4.0. Exception "Invalid option specified: "%s". Expected one of (bold, underscore, blink, reverse, conceal)". - */ - public function testInlineStyleOptionsUnknownAreDeprecated($tag, $option) - { - $formatter = new OutputFormatter(true); - $formatter->format($tag); - } - - public function provideInlineStyleTagsWithUnknownOptions() - { - return array( - array('', 'abc'), - array('', 'abc'), - array('', 'xyz'), - array('', 'efg'), - ); - } - - public function testNonStyleTag() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals("\033[32msome \033[39m\033[32m\033[39m\033[32m \033[39m\033[32m\033[39m\033[32m styled \033[39m\033[32m

\033[39m\033[32msingle-char tag\033[39m\033[32m

\033[39m", $formatter->format('some styled

single-char tag

')); - } - - public function testFormatLongString() - { - $formatter = new OutputFormatter(true); - $long = str_repeat('\\', 14000); - $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('some error'.$long)); - } - - public function testFormatToStringObject() - { - $formatter = new OutputFormatter(false); - $this->assertEquals( - 'some info', $formatter->format(new TableCell()) - ); - } - - public function testNotDecoratedFormatter() - { - $formatter = new OutputFormatter(false); - - $this->assertTrue($formatter->hasStyle('error')); - $this->assertTrue($formatter->hasStyle('info')); - $this->assertTrue($formatter->hasStyle('comment')); - $this->assertTrue($formatter->hasStyle('question')); - - $this->assertEquals( - 'some error', $formatter->format('some error') - ); - $this->assertEquals( - 'some info', $formatter->format('some info') - ); - $this->assertEquals( - 'some comment', $formatter->format('some comment') - ); - $this->assertEquals( - 'some question', $formatter->format('some question') - ); - - $formatter->setDecorated(true); - - $this->assertEquals( - "\033[37;41msome error\033[39;49m", $formatter->format('some error') - ); - $this->assertEquals( - "\033[32msome info\033[39m", $formatter->format('some info') - ); - $this->assertEquals( - "\033[33msome comment\033[39m", $formatter->format('some comment') - ); - $this->assertEquals( - "\033[30;46msome question\033[39;49m", $formatter->format('some question') - ); - } - - public function testContentWithLineBreaks() - { - $formatter = new OutputFormatter(true); - - $this->assertEquals(<<format(<<<'EOF' - -some text -EOF - )); - - $this->assertEquals(<<format(<<<'EOF' -some text - -EOF - )); - - $this->assertEquals(<<format(<<<'EOF' - -some text - -EOF - )); - - $this->assertEquals(<<format(<<<'EOF' - -some text -more text - -EOF - )); - } -} - -class TableCell -{ - public function __toString() - { - return 'some info'; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php deleted file mode 100644 index 625883863..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Input\StreamableInputInterface; - -abstract class AbstractQuestionHelperTest extends \PHPUnit_Framework_TestCase -{ - protected function createStreamableInputInterfaceMock($stream = null, $interactive = true) - { - $mock = $this->getMock(StreamableInputInterface::class); - $mock->expects($this->any()) - ->method('isInteractive') - ->will($this->returnValue($interactive)); - - if ($stream) { - $mock->expects($this->any()) - ->method('getStream') - ->willReturn($stream); - } - - return $mock; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php deleted file mode 100644 index 141dc1dcb..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/FormatterHelperTest.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\FormatterHelper; - -class FormatterHelperTest extends \PHPUnit_Framework_TestCase -{ - public function testFormatSection() - { - $formatter = new FormatterHelper(); - - $this->assertEquals( - '[cli] Some text to display', - $formatter->formatSection('cli', 'Some text to display'), - '::formatSection() formats a message in a section' - ); - } - - public function testFormatBlock() - { - $formatter = new FormatterHelper(); - - $this->assertEquals( - ' Some text to display ', - $formatter->formatBlock('Some text to display', 'error'), - '::formatBlock() formats a message in a block' - ); - - $this->assertEquals( - ' Some text to display '."\n". - ' foo bar ', - $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), - '::formatBlock() formats a message in a block' - ); - - $this->assertEquals( - ' '."\n". - ' Some text to display '."\n". - ' ', - $formatter->formatBlock('Some text to display', 'error', true), - '::formatBlock() formats a message in a block' - ); - } - - public function testFormatBlockWithDiacriticLetters() - { - $formatter = new FormatterHelper(); - - $this->assertEquals( - ' '."\n". - ' Du texte à afficher '."\n". - ' ', - $formatter->formatBlock('Du texte à afficher', 'error', true), - '::formatBlock() formats a message in a block' - ); - } - - public function testFormatBlockWithDoubleWidthDiacriticLetters() - { - $formatter = new FormatterHelper(); - $this->assertEquals( - ' '."\n". - ' 表示するテキスト '."\n". - ' ', - $formatter->formatBlock('表示するテキスト', 'error', true), - '::formatBlock() formats a message in a block' - ); - } - - public function testFormatBlockLGEscaping() - { - $formatter = new FormatterHelper(); - - $this->assertEquals( - ' '."\n". - ' \some info\ '."\n". - ' ', - $formatter->formatBlock('some info', 'error', true), - '::formatBlock() escapes \'<\' chars' - ); - } - - public function testTruncatingWithShorterLengthThanMessageWithSuffix() - { - $formatter = new FormatterHelper(); - $message = 'testing truncate'; - - $this->assertSame('test...', $formatter->truncate($message, 4)); - $this->assertSame('testing truncat...', $formatter->truncate($message, 15)); - $this->assertSame('testing truncate...', $formatter->truncate($message, 16)); - $this->assertSame('zażółć gęślą...', $formatter->truncate('zażółć gęślą jaźń', 12)); - } - - public function testTruncatingMessageWithCustomSuffix() - { - $formatter = new FormatterHelper(); - $message = 'testing truncate'; - - $this->assertSame('test!', $formatter->truncate($message, 4, '!')); - } - - public function testTruncatingWithLongerLengthThanMessageWithSuffix() - { - $formatter = new FormatterHelper(); - $message = 'test'; - - $this->assertSame($message, $formatter->truncate($message, 10)); - } - - public function testTruncatingWithNegativeLength() - { - $formatter = new FormatterHelper(); - $message = 'testing truncate'; - - $this->assertSame('testing tru...', $formatter->truncate($message, -5)); - $this->assertSame('...', $formatter->truncate($message, -100)); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/HelperSetTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/HelperSetTest.php deleted file mode 100644 index 998249dbc..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/HelperSetTest.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Command\Command; - -class HelperSetTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $mock_helper = $this->getGenericMockHelper('fake_helper'); - $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper)); - - $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers'); - $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper'); - } - - public function testSet() - { - $helperset = new HelperSet(); - $helperset->set($this->getGenericMockHelper('fake_helper', $helperset)); - $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers'); - - $helperset = new HelperSet(); - $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); - $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); - $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls'); - $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls'); - - $helperset = new HelperSet(); - $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias'); - $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set'); - $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set'); - } - - public function testHas() - { - $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper'))); - $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper'); - $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias'); - } - - public function testGet() - { - $helper_01 = $this->getGenericMockHelper('fake_helper_01'); - $helper_02 = $this->getGenericMockHelper('fake_helper_02'); - $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02)); - $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name'); - $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias'); - $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name'); - $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias'); - - $helperset = new HelperSet(); - try { - $helperset->get('foo'); - $this->fail('->get() throws InvalidArgumentException when helper not found'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found'); - $this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found'); - $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found'); - } - } - - public function testSetCommand() - { - $cmd_01 = new Command('foo'); - $cmd_02 = new Command('bar'); - - $helperset = new HelperSet(); - $helperset->setCommand($cmd_01); - $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command'); - - $helperset = new HelperSet(); - $helperset->setCommand($cmd_01); - $helperset->setCommand($cmd_02); - $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls'); - } - - public function testGetCommand() - { - $cmd = new Command('foo'); - $helperset = new HelperSet(); - $helperset->setCommand($cmd); - $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); - } - - public function testIteration() - { - $helperset = new HelperSet(); - $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); - $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); - - $helpers = array('fake_helper_01', 'fake_helper_02'); - $i = 0; - - foreach ($helperset as $helper) { - $this->assertEquals($helpers[$i++], $helper->getName()); - } - } - - private function getGenericMockHelper($name, HelperSet $helperset = null) - { - $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface'); - $mock_helper->expects($this->any()) - ->method('getName') - ->will($this->returnValue($name)); - - if ($helperset) { - $mock_helper->expects($this->any()) - ->method('setHelperSet') - ->with($this->equalTo($helperset)); - } - - return $mock_helper; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/HelperTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/HelperTest.php deleted file mode 100644 index 33fa22051..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/HelperTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\Helper; - -class HelperTest extends \PHPUnit_Framework_TestCase -{ - public function formatTimeProvider() - { - return array( - array(0, '< 1 sec'), - array(1, '1 sec'), - array(2, '2 secs'), - array(59, '59 secs'), - array(60, '1 min'), - array(61, '1 min'), - array(119, '1 min'), - array(120, '2 mins'), - array(121, '2 mins'), - array(3599, '59 mins'), - array(3600, '1 hr'), - array(7199, '1 hr'), - array(7200, '2 hrs'), - array(7201, '2 hrs'), - array(86399, '23 hrs'), - array(86400, '1 day'), - array(86401, '1 day'), - array(172799, '1 day'), - array(172800, '2 days'), - array(172801, '2 days'), - ); - } - - /** - * @dataProvider formatTimeProvider - * - * @param int $secs - * @param string $expectedFormat - */ - public function testFormatTime($secs, $expectedFormat) - { - $this->assertEquals($expectedFormat, Helper::formatTime($secs)); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php deleted file mode 100644 index 1dcddf8ba..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/ProcessHelperTest.php +++ /dev/null @@ -1,117 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\DebugFormatterHelper; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Output\StreamOutput; -use Symfony\Component\Console\Helper\ProcessHelper; -use Symfony\Component\Process\Process; - -class ProcessHelperTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider provideCommandsAndOutput - */ - public function testVariousProcessRuns($expected, $cmd, $verbosity, $error) - { - $helper = new ProcessHelper(); - $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); - $output = $this->getOutputStream($verbosity); - $helper->run($output, $cmd, $error); - $this->assertEquals($expected, $this->getOutput($output)); - } - - public function testPassedCallbackIsExecuted() - { - $helper = new ProcessHelper(); - $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); - $output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL); - - $executed = false; - $callback = function () use (&$executed) { $executed = true; }; - - $helper->run($output, 'php -r "echo 42;"', null, $callback); - $this->assertTrue($executed); - } - - public function provideCommandsAndOutput() - { - $successOutputVerbose = <<<'EOT' - RUN php -r "echo 42;" - RES Command ran successfully - -EOT; - $successOutputDebug = <<<'EOT' - RUN php -r "echo 42;" - OUT 42 - RES Command ran successfully - -EOT; - $successOutputDebugWithTags = <<<'EOT' - RUN php -r "echo '42';" - OUT 42 - RES Command ran successfully - -EOT; - $successOutputProcessDebug = <<<'EOT' - RUN 'php' '-r' 'echo 42;' - OUT 42 - RES Command ran successfully - -EOT; - $syntaxErrorOutputVerbose = <<<'EOT' - RUN php -r "fwrite(STDERR, 'error message');usleep(50000);fwrite(STDOUT, 'out message');exit(252);" - RES 252 Command did not run successfully - -EOT; - $syntaxErrorOutputDebug = <<<'EOT' - RUN php -r "fwrite(STDERR, 'error message');usleep(500000);fwrite(STDOUT, 'out message');exit(252);" - ERR error message - OUT out message - RES 252 Command did not run successfully - -EOT; - - $errorMessage = 'An error occurred'; - if ('\\' === DIRECTORY_SEPARATOR) { - $successOutputProcessDebug = str_replace("'", '"', $successOutputProcessDebug); - } - - return array( - array('', 'php -r "echo 42;"', StreamOutput::VERBOSITY_VERBOSE, null), - array($successOutputVerbose, 'php -r "echo 42;"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), - array($successOutputDebug, 'php -r "echo 42;"', StreamOutput::VERBOSITY_DEBUG, null), - array($successOutputDebugWithTags, 'php -r "echo \'42\';"', StreamOutput::VERBOSITY_DEBUG, null), - array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null), - array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), - array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null), - array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage), - array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage), - array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage), - array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null), - array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null), - ); - } - - private function getOutputStream($verbosity) - { - return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false); - } - - private function getOutput(StreamOutput $output) - { - rewind($output->getStream()); - - return stream_get_contents($output->getStream()); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/ProgressBarTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/ProgressBarTest.php deleted file mode 100644 index 1f9f31a3e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/ProgressBarTest.php +++ /dev/null @@ -1,756 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\ProgressBar; -use Symfony\Component\Console\Helper\Helper; -use Symfony\Component\Console\Output\StreamOutput; - -/** - * @group time-sensitive - */ -class ProgressBarTest extends \PHPUnit_Framework_TestCase -{ - public function testMultipleStart() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(); - $bar->start(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 1 [->--------------------------]'). - $this->generateOutput(' 0 [>---------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testAdvance() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 1 [->--------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testAdvanceWithStep() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(5); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 5 [----->----------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testAdvanceMultipleTimes() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(3); - $bar->advance(2); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 3 [--->------------------------]'). - $this->generateOutput(' 5 [----->----------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testAdvanceOverMax() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 10); - $bar->setProgress(9); - $bar->advance(); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 9/10 [=========================>--] 90%'. - $this->generateOutput(' 10/10 [============================] 100%'). - $this->generateOutput(' 11/11 [============================] 100%'), - stream_get_contents($output->getStream()) - ); - } - - public function testRegress() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(); - $bar->advance(); - $bar->advance(-1); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 1 [->--------------------------]'). - $this->generateOutput(' 2 [-->-------------------------]'). - $this->generateOutput(' 1 [->--------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testRegressWithStep() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(4); - $bar->advance(4); - $bar->advance(-2); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 4 [---->-----------------------]'). - $this->generateOutput(' 8 [-------->-------------------]'). - $this->generateOutput(' 6 [------>---------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testRegressMultipleTimes() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->advance(3); - $bar->advance(3); - $bar->advance(-1); - $bar->advance(-2); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 3 [--->------------------------]'). - $this->generateOutput(' 6 [------>---------------------]'). - $this->generateOutput(' 5 [----->----------------------]'). - $this->generateOutput(' 3 [--->------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testRegressBelowMin() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 10); - $bar->setProgress(1); - $bar->advance(-1); - $bar->advance(-1); - - rewind($output->getStream()); - $this->assertEquals( - ' 1/10 [==>-------------------------] 10%'. - $this->generateOutput(' 0/10 [>---------------------------] 0%'), - stream_get_contents($output->getStream()) - ); - } - - public function testFormat() - { - $expected = - ' 0/10 [>---------------------------] 0%'. - $this->generateOutput(' 10/10 [============================] 100%'). - $this->generateOutput(' 10/10 [============================] 100%') - ; - - // max in construct, no format - $bar = new ProgressBar($output = $this->getOutputStream(), 10); - $bar->start(); - $bar->advance(10); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals($expected, stream_get_contents($output->getStream())); - - // max in start, no format - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(10); - $bar->advance(10); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals($expected, stream_get_contents($output->getStream())); - - // max in construct, explicit format before - $bar = new ProgressBar($output = $this->getOutputStream(), 10); - $bar->setFormat('normal'); - $bar->start(); - $bar->advance(10); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals($expected, stream_get_contents($output->getStream())); - - // max in start, explicit format before - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->setFormat('normal'); - $bar->start(10); - $bar->advance(10); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals($expected, stream_get_contents($output->getStream())); - } - - public function testCustomizations() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 10); - $bar->setBarWidth(10); - $bar->setBarCharacter('_'); - $bar->setEmptyBarCharacter(' '); - $bar->setProgressCharacter('/'); - $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); - $bar->start(); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/10 [/ ] 0%'. - $this->generateOutput(' 1/10 [_/ ] 10%'), - stream_get_contents($output->getStream()) - ); - } - - public function testDisplayWithoutStart() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 50); - $bar->display(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------] 0%', - stream_get_contents($output->getStream()) - ); - } - - public function testDisplayWithQuietVerbosity() - { - $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50); - $bar->display(); - - rewind($output->getStream()); - $this->assertEquals( - '', - stream_get_contents($output->getStream()) - ); - } - - public function testFinishWithoutStart() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 50); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - ' 50/50 [============================] 100%', - stream_get_contents($output->getStream()) - ); - } - - public function testPercent() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 50); - $bar->start(); - $bar->display(); - $bar->advance(); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------] 0%'. - $this->generateOutput(' 0/50 [>---------------------------] 0%'). - $this->generateOutput(' 1/50 [>---------------------------] 2%'). - $this->generateOutput(' 2/50 [=>--------------------------] 4%'), - stream_get_contents($output->getStream()) - ); - } - - public function testOverwriteWithShorterLine() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 50); - $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); - $bar->start(); - $bar->display(); - $bar->advance(); - - // set shorter format - $bar->setFormat(' %current%/%max% [%bar%]'); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------] 0%'. - $this->generateOutput(' 0/50 [>---------------------------] 0%'). - $this->generateOutput(' 1/50 [>---------------------------] 2%'). - $this->generateOutput(' 2/50 [=>--------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testStartWithMax() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->setFormat('%current%/%max% [%bar%]'); - $bar->start(50); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------]'. - $this->generateOutput(' 1/50 [>---------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testSetCurrentProgress() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 50); - $bar->start(); - $bar->display(); - $bar->advance(); - $bar->setProgress(15); - $bar->setProgress(25); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------] 0%'. - $this->generateOutput(' 0/50 [>---------------------------] 0%'). - $this->generateOutput(' 1/50 [>---------------------------] 2%'). - $this->generateOutput(' 15/50 [========>-------------------] 30%'). - $this->generateOutput(' 25/50 [==============>-------------] 50%'), - stream_get_contents($output->getStream()) - ); - } - - public function testSetCurrentBeforeStarting() - { - $bar = new ProgressBar($this->getOutputStream()); - $bar->setProgress(15); - $this->assertNotNull($bar->getStartTime()); - } - - public function testRedrawFrequency() - { - $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream(), 6)); - $bar->expects($this->exactly(4))->method('display'); - - $bar->setRedrawFrequency(2); - $bar->start(); - $bar->setProgress(1); - $bar->advance(2); - $bar->advance(2); - $bar->advance(1); - } - - public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() - { - $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); - - $bar->expects($this->exactly(2))->method('display'); - $bar->setRedrawFrequency(0); - $bar->start(); - $bar->advance(); - } - - public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() - { - $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); - - $bar->expects($this->exactly(2))->method('display'); - $bar->setRedrawFrequency(0.9); - $bar->start(); - $bar->advance(); - } - - public function testMultiByteSupport() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->start(); - $bar->setBarCharacter('■'); - $bar->advance(3); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'. - $this->generateOutput(' 3 [■■■>------------------------]'), - stream_get_contents($output->getStream()) - ); - } - - public function testClear() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 50); - $bar->start(); - $bar->setProgress(25); - $bar->clear(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------] 0%'. - $this->generateOutput(' 25/50 [==============>-------------] 50%'). - $this->generateOutput(''), - stream_get_contents($output->getStream()) - ); - } - - public function testPercentNotHundredBeforeComplete() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 200); - $bar->start(); - $bar->display(); - $bar->advance(199); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/200 [>---------------------------] 0%'. - $this->generateOutput(' 0/200 [>---------------------------] 0%'). - $this->generateOutput(' 199/200 [===========================>] 99%'). - $this->generateOutput(' 200/200 [============================] 100%'), - stream_get_contents($output->getStream()) - ); - } - - public function testNonDecoratedOutput() - { - $bar = new ProgressBar($output = $this->getOutputStream(false), 200); - $bar->start(); - - for ($i = 0; $i < 200; ++$i) { - $bar->advance(); - } - - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/200 [>---------------------------] 0%'.PHP_EOL. - ' 20/200 [==>-------------------------] 10%'.PHP_EOL. - ' 40/200 [=====>----------------------] 20%'.PHP_EOL. - ' 60/200 [========>-------------------] 30%'.PHP_EOL. - ' 80/200 [===========>----------------] 40%'.PHP_EOL. - ' 100/200 [==============>-------------] 50%'.PHP_EOL. - ' 120/200 [================>-----------] 60%'.PHP_EOL. - ' 140/200 [===================>--------] 70%'.PHP_EOL. - ' 160/200 [======================>-----] 80%'.PHP_EOL. - ' 180/200 [=========================>--] 90%'.PHP_EOL. - ' 200/200 [============================] 100%', - stream_get_contents($output->getStream()) - ); - } - - public function testNonDecoratedOutputWithClear() - { - $bar = new ProgressBar($output = $this->getOutputStream(false), 50); - $bar->start(); - $bar->setProgress(25); - $bar->clear(); - $bar->setProgress(50); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/50 [>---------------------------] 0%'.PHP_EOL. - ' 25/50 [==============>-------------] 50%'.PHP_EOL. - ' 50/50 [============================] 100%', - stream_get_contents($output->getStream()) - ); - } - - public function testNonDecoratedOutputWithoutMax() - { - $bar = new ProgressBar($output = $this->getOutputStream(false)); - $bar->start(); - $bar->advance(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]'.PHP_EOL. - ' 1 [->--------------------------]', - stream_get_contents($output->getStream()) - ); - } - - public function testParallelBars() - { - $output = $this->getOutputStream(); - $bar1 = new ProgressBar($output, 2); - $bar2 = new ProgressBar($output, 3); - $bar2->setProgressCharacter('#'); - $bar3 = new ProgressBar($output); - - $bar1->start(); - $output->write("\n"); - $bar2->start(); - $output->write("\n"); - $bar3->start(); - - for ($i = 1; $i <= 3; ++$i) { - // up two lines - $output->write("\033[2A"); - if ($i <= 2) { - $bar1->advance(); - } - $output->write("\n"); - $bar2->advance(); - $output->write("\n"); - $bar3->advance(); - } - $output->write("\033[2A"); - $output->write("\n"); - $output->write("\n"); - $bar3->finish(); - - rewind($output->getStream()); - $this->assertEquals( - ' 0/2 [>---------------------------] 0%'."\n". - ' 0/3 [#---------------------------] 0%'."\n". - rtrim(' 0 [>---------------------------]'). - - "\033[2A". - $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n". - $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n". - rtrim($this->generateOutput(' 1 [->--------------------------]')). - - "\033[2A". - $this->generateOutput(' 2/2 [============================] 100%')."\n". - $this->generateOutput(' 2/3 [==================#---------] 66%')."\n". - rtrim($this->generateOutput(' 2 [-->-------------------------]')). - - "\033[2A". - "\n". - $this->generateOutput(' 3/3 [============================] 100%')."\n". - rtrim($this->generateOutput(' 3 [--->------------------------]')). - - "\033[2A". - "\n". - "\n". - rtrim($this->generateOutput(' 3 [============================]')), - stream_get_contents($output->getStream()) - ); - } - - public function testWithoutMax() - { - $output = $this->getOutputStream(); - - $bar = new ProgressBar($output); - $bar->start(); - $bar->advance(); - $bar->advance(); - $bar->advance(); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - rtrim(' 0 [>---------------------------]'). - rtrim($this->generateOutput(' 1 [->--------------------------]')). - rtrim($this->generateOutput(' 2 [-->-------------------------]')). - rtrim($this->generateOutput(' 3 [--->------------------------]')). - rtrim($this->generateOutput(' 3 [============================]')), - stream_get_contents($output->getStream()) - ); - } - - public function testWithSmallScreen() - { - $output = $this->getOutputStream(); - - $bar = new ProgressBar($output); - putenv('COLUMNS=12'); - $bar->start(); - $bar->advance(); - putenv('COLUMNS=120'); - - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---]'. - $this->generateOutput(' 1 [->--]'), - stream_get_contents($output->getStream()) - ); - } - - public function testAddingPlaceholderFormatter() - { - ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { - return $bar->getMaxSteps() - $bar->getProgress(); - }); - $bar = new ProgressBar($output = $this->getOutputStream(), 3); - $bar->setFormat(' %remaining_steps% [%bar%]'); - - $bar->start(); - $bar->advance(); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - ' 3 [>---------------------------]'. - $this->generateOutput(' 2 [=========>------------------]'). - $this->generateOutput(' 0 [============================]'), - stream_get_contents($output->getStream()) - ); - } - - public function testMultilineFormat() - { - $bar = new ProgressBar($output = $this->getOutputStream(), 3); - $bar->setFormat("%bar%\nfoobar"); - - $bar->start(); - $bar->advance(); - $bar->clear(); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - ">---------------------------\nfoobar". - $this->generateOutput("=========>------------------\nfoobar"). - "\x0D\x1B[2K\x1B[1A\x1B[2K". - $this->generateOutput("============================\nfoobar"), - stream_get_contents($output->getStream()) - ); - } - - public function testAnsiColorsAndEmojis() - { - putenv('COLUMNS=156'); - - $bar = new ProgressBar($output = $this->getOutputStream(), 15); - ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) { - static $i = 0; - $mem = 100000 * $i; - $colors = $i++ ? '41;37' : '44;37'; - - return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m"; - }); - $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%"); - $bar->setBarCharacter($done = "\033[32m●\033[0m"); - $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m"); - $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m"); - - $bar->setMessage('Starting the demo... fingers crossed', 'title'); - $bar->start(); - - rewind($output->getStream()); - $this->assertEquals( - " \033[44;37m Starting the demo... fingers crossed \033[0m\n". - ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". - " \xf0\x9f\x8f\x81 < 1 sec \033[44;37m 0 B \033[0m", - stream_get_contents($output->getStream()) - ); - ftruncate($output->getStream(), 0); - rewind($output->getStream()); - - $bar->setMessage('Looks good to me...', 'title'); - $bar->advance(4); - - rewind($output->getStream()); - $this->assertEquals( - $this->generateOutput( - " \033[44;37m Looks good to me... \033[0m\n". - ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". - " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 97 KiB \033[0m" - ), - stream_get_contents($output->getStream()) - ); - ftruncate($output->getStream(), 0); - rewind($output->getStream()); - - $bar->setMessage('Thanks, bye', 'title'); - $bar->finish(); - - rewind($output->getStream()); - $this->assertEquals( - $this->generateOutput( - " \033[44;37m Thanks, bye \033[0m\n". - ' 15/15 '.str_repeat($done, 28)." 100%\n". - " \xf0\x9f\x8f\x81 < 1 sec \033[41;37m 195 KiB \033[0m" - ), - stream_get_contents($output->getStream()) - ); - putenv('COLUMNS=120'); - } - - public function testSetFormat() - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->setFormat('normal'); - $bar->start(); - rewind($output->getStream()); - $this->assertEquals( - ' 0 [>---------------------------]', - stream_get_contents($output->getStream()) - ); - - $bar = new ProgressBar($output = $this->getOutputStream(), 10); - $bar->setFormat('normal'); - $bar->start(); - rewind($output->getStream()); - $this->assertEquals( - ' 0/10 [>---------------------------] 0%', - stream_get_contents($output->getStream()) - ); - } - - /** - * @dataProvider provideFormat - */ - public function testFormatsWithoutMax($format) - { - $bar = new ProgressBar($output = $this->getOutputStream()); - $bar->setFormat($format); - $bar->start(); - - rewind($output->getStream()); - $this->assertNotEmpty(stream_get_contents($output->getStream())); - } - - /** - * Provides each defined format. - * - * @return array - */ - public function provideFormat() - { - return array( - array('normal'), - array('verbose'), - array('very_verbose'), - array('debug'), - ); - } - - protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) - { - return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); - } - - protected function generateOutput($expected) - { - $count = substr_count($expected, "\n"); - - return "\x0D\x1B[2K".($count ? str_repeat("\x1B[1A\x1B[2K", $count) : '').$expected; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php deleted file mode 100644 index 2f72d3094..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/ProgressIndicatorTest.php +++ /dev/null @@ -1,182 +0,0 @@ -getOutputStream()); - $bar->start('Starting...'); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->setMessage('Advancing...'); - $bar->advance(); - $bar->finish('Done...'); - $bar->start('Starting Again...'); - usleep(101000); - $bar->advance(); - $bar->finish('Done Again...'); - - rewind($output->getStream()); - - $this->assertEquals( - $this->generateOutput(' - Starting...'). - $this->generateOutput(' \\ Starting...'). - $this->generateOutput(' | Starting...'). - $this->generateOutput(' / Starting...'). - $this->generateOutput(' - Starting...'). - $this->generateOutput(' \\ Starting...'). - $this->generateOutput(' \\ Advancing...'). - $this->generateOutput(' | Advancing...'). - $this->generateOutput(' | Done...'). - PHP_EOL. - $this->generateOutput(' - Starting Again...'). - $this->generateOutput(' \\ Starting Again...'). - $this->generateOutput(' \\ Done Again...'). - PHP_EOL, - stream_get_contents($output->getStream()) - ); - } - - public function testNonDecoratedOutput() - { - $bar = new ProgressIndicator($output = $this->getOutputStream(false)); - - $bar->start('Starting...'); - $bar->advance(); - $bar->advance(); - $bar->setMessage('Midway...'); - $bar->advance(); - $bar->advance(); - $bar->finish('Done...'); - - rewind($output->getStream()); - - $this->assertEquals( - ' Starting...'.PHP_EOL. - ' Midway...'.PHP_EOL. - ' Done...'.PHP_EOL.PHP_EOL, - stream_get_contents($output->getStream()) - ); - } - - public function testCustomIndicatorValues() - { - $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c')); - - $bar->start('Starting...'); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->advance(); - usleep(101000); - $bar->advance(); - - rewind($output->getStream()); - - $this->assertEquals( - $this->generateOutput(' a Starting...'). - $this->generateOutput(' b Starting...'). - $this->generateOutput(' c Starting...'). - $this->generateOutput(' a Starting...'), - stream_get_contents($output->getStream()) - ); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Must have at least 2 indicator value characters. - */ - public function testCannotSetInvalidIndicatorCharacters() - { - $bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1')); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Progress indicator already started. - */ - public function testCannotStartAlreadyStartedIndicator() - { - $bar = new ProgressIndicator($this->getOutputStream()); - $bar->start('Starting...'); - $bar->start('Starting Again.'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Progress indicator has not yet been started. - */ - public function testCannotAdvanceUnstartedIndicator() - { - $bar = new ProgressIndicator($this->getOutputStream()); - $bar->advance(); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Progress indicator has not yet been started. - */ - public function testCannotFinishUnstartedIndicator() - { - $bar = new ProgressIndicator($this->getOutputStream()); - $bar->finish('Finished'); - } - - /** - * @dataProvider provideFormat - */ - public function testFormats($format) - { - $bar = new ProgressIndicator($output = $this->getOutputStream(), $format); - $bar->start('Starting...'); - $bar->advance(); - - rewind($output->getStream()); - - $this->assertNotEmpty(stream_get_contents($output->getStream())); - } - - /** - * Provides each defined format. - * - * @return array - */ - public function provideFormat() - { - return array( - array('normal'), - array('verbose'), - array('very_verbose'), - array('debug'), - ); - } - - protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) - { - return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); - } - - protected function generateOutput($expected) - { - $count = substr_count($expected, "\n"); - - return "\x0D\x1B[2K".($count ? sprintf("\033[%dA", $count) : '').$expected; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php deleted file mode 100644 index 8c753cc7a..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php +++ /dev/null @@ -1,805 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Formatter\OutputFormatter; -use Symfony\Component\Console\Helper\QuestionHelper; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Helper\FormatterHelper; -use Symfony\Component\Console\Output\StreamOutput; -use Symfony\Component\Console\Question\ChoiceQuestion; -use Symfony\Component\Console\Question\ConfirmationQuestion; -use Symfony\Component\Console\Question\Question; - -/** - * @group tty - */ -class QuestionHelperTest extends AbstractQuestionHelperTest -{ - public function testAskChoice() - { - $questionHelper = new QuestionHelper(); - - $helperSet = new HelperSet(array(new FormatterHelper())); - $questionHelper->setHelperSet($helperSet); - - $heroes = array('Superman', 'Batman', 'Spiderman'); - - $inputStream = $this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); - $question->setMaxAttempts(1); - // first answer is an empty answer, we're supposed to receive the default value - $this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setMaxAttempts(1); - $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setErrorMessage('Input "%s" is not a superhero!'); - $question->setMaxAttempts(2); - $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); - - rewind($output->getStream()); - $stream = stream_get_contents($output->getStream()); - $this->assertContains('Input "Fabien" is not a superhero!', $stream); - - try { - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); - $question->setMaxAttempts(1); - $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); - } - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - } - - public function testAsk() - { - $dialog = new QuestionHelper(); - - $inputStream = $this->getInputStream("\n8AM\n"); - - $question = new Question('What time is it?', '2PM'); - $this->assertEquals('2PM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new Question('What time is it?', '2PM'); - $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); - - rewind($output->getStream()); - $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); - } - - public function testAskWithAutocomplete() - { - if (!$this->hasSttyAvailable()) { - $this->markTestSkipped('`stty` is required to test autocomplete functionality'); - } - - // Acm - // AcsTest - // - // - // Test - // - // S - // F00oo - $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); - - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new Question('Please select a bundle', 'FrameworkBundle'); - $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); - - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - } - - public function testAskWithAutocompleteWithNonSequentialKeys() - { - if (!$this->hasSttyAvailable()) { - $this->markTestSkipped('`stty` is required to test autocomplete functionality'); - } - - // - $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); - - $dialog = new QuestionHelper(); - $dialog->setHelperSet(new HelperSet(array(new FormatterHelper()))); - - $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle')); - $question->setMaxAttempts(1); - - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - } - - public function testAskHiddenResponse() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test is not supported on Windows'); - } - - $dialog = new QuestionHelper(); - - $question = new Question('What time is it?'); - $question->setHidden(true); - - $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("8AM\n")), $this->createOutputInterface(), $question)); - } - - /** - * @dataProvider getAskConfirmationData - */ - public function testAskConfirmation($question, $expected, $default = true) - { - $dialog = new QuestionHelper(); - - $inputStream = $this->getInputStream($question."\n"); - $question = new ConfirmationQuestion('Do you like French fries?', $default); - $this->assertEquals($expected, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); - } - - public function getAskConfirmationData() - { - return array( - array('', true), - array('', false, false), - array('y', true), - array('yes', true), - array('n', false), - array('no', false), - ); - } - - public function testAskConfirmationWithCustomTrueAnswer() - { - $dialog = new QuestionHelper(); - - $inputStream = $this->getInputStream("j\ny\n"); - $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); - $this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); - $this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - } - - public function testAskAndValidate() - { - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $error = 'This is not a color!'; - $validator = function ($color) use ($error) { - if (!in_array($color, array('white', 'black'))) { - throw new \InvalidArgumentException($error); - } - - return $color; - }; - - $question = new Question('What color was the white horse of Henry IV?', 'white'); - $question->setValidator($validator); - $question->setMaxAttempts(2); - - $inputStream = $this->getInputStream("\nblack\n"); - $this->assertEquals('white', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('black', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - try { - $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("green\nyellow\norange\n")), $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals($error, $e->getMessage()); - } - } - - /** - * @dataProvider simpleAnswerProvider - */ - public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) - { - $possibleChoices = array( - 'My environment 1', - 'My environment 2', - 'My environment 3', - ); - - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - public function simpleAnswerProvider() - { - return array( - array(0, 'My environment 1'), - array(1, 'My environment 2'), - array(2, 'My environment 3'), - array('My environment 1', 'My environment 1'), - array('My environment 2', 'My environment 2'), - array('My environment 3', 'My environment 3'), - ); - } - - /** - * @dataProvider mixedKeysChoiceListAnswerProvider - */ - public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) - { - $possibleChoices = array( - '0' => 'No environment', - '1' => 'My environment 1', - 'env_2' => 'My environment 2', - 3 => 'My environment 3', - ); - - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - public function mixedKeysChoiceListAnswerProvider() - { - return array( - array('0', '0'), - array('No environment', '0'), - array('1', '1'), - array('env_2', 'env_2'), - array(3, '3'), - array('My environment 1', '1'), - ); - } - - /** - * @dataProvider answerProvider - */ - public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) - { - $possibleChoices = array( - 'env_1' => 'My environment 1', - 'env_2' => 'My environment', - 'env_3' => 'My environment', - ); - - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. - */ - public function testAmbiguousChoiceFromChoicelist() - { - $possibleChoices = array( - 'env_1' => 'My first environment', - 'env_2' => 'My environment', - 'env_3' => 'My environment', - ); - - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - - $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("My environment\n")), $this->createOutputInterface(), $question); - } - - public function answerProvider() - { - return array( - array('env_1', 'env_1'), - array('env_2', 'env_2'), - array('env_3', 'env_3'), - array('My environment 1', 'env_1'), - ); - } - - public function testNoInteraction() - { - $dialog = new QuestionHelper(); - $question = new Question('Do you have a job?', 'not yet'); - $this->assertEquals('not yet', $dialog->ask($this->createStreamableInputInterfaceMock(null, false), $this->createOutputInterface(), $question)); - } - - /** - * @requires function mb_strwidth - */ - public function testChoiceOutputFormattingQuestionForUtf8Keys() - { - $question = 'Lorem ipsum?'; - $possibleChoices = array( - 'foo' => 'foo', - 'żółw' => 'bar', - 'łabądź' => 'baz', - ); - $outputShown = array( - $question, - ' [foo ] foo', - ' [żółw ] bar', - ' [łabądź] baz', - ); - $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); - $output->method('getFormatter')->willReturn(new OutputFormatter()); - - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); - - $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); - $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("\n")), $output, $question); - } - - /** - * @group legacy - */ - public function testLegacyAskChoice() - { - $questionHelper = new QuestionHelper(); - - $helperSet = new HelperSet(array(new FormatterHelper())); - $questionHelper->setHelperSet($helperSet); - - $heroes = array('Superman', 'Batman', 'Spiderman'); - - $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); - $question->setMaxAttempts(1); - // first answer is an empty answer, we're supposed to receive the default value - $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setMaxAttempts(1); - $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setErrorMessage('Input "%s" is not a superhero!'); - $question->setMaxAttempts(2); - $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); - - rewind($output->getStream()); - $stream = stream_get_contents($output->getStream()); - $this->assertContains('Input "Fabien" is not a superhero!', $stream); - - try { - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); - $question->setMaxAttempts(1); - $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); - } - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAsk() - { - $dialog = new QuestionHelper(); - - $dialog->setInputStream($this->getInputStream("\n8AM\n")); - - $question = new Question('What time is it?', '2PM'); - $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $question = new Question('What time is it?', '2PM'); - $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); - - rewind($output->getStream()); - $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); - } - - /** - * @group legacy - */ - public function testLegacyAskWithAutocomplete() - { - if (!$this->hasSttyAvailable()) { - $this->markTestSkipped('`stty` is required to test autocomplete functionality'); - } - - // Acm - // AcsTest - // - // - // Test - // - // S - // F00oo - $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($inputStream); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new Question('Please select a bundle', 'FrameworkBundle'); - $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); - - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAskWithAutocompleteWithNonSequentialKeys() - { - if (!$this->hasSttyAvailable()) { - $this->markTestSkipped('`stty` is required to test autocomplete functionality'); - } - - // - $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($inputStream); - $dialog->setHelperSet(new HelperSet(array(new FormatterHelper()))); - - $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle')); - $question->setMaxAttempts(1); - - $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAskHiddenResponse() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test is not supported on Windows'); - } - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream("8AM\n")); - - $question = new Question('What time is it?'); - $question->setHidden(true); - - $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - * @dataProvider getAskConfirmationData - */ - public function testLegacyAskConfirmation($question, $expected, $default = true) - { - $dialog = new QuestionHelper(); - - $dialog->setInputStream($this->getInputStream($question."\n")); - $question = new ConfirmationQuestion('Do you like French fries?', $default); - $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); - } - - /** - * @group legacy - */ - public function testLegacyAskConfirmationWithCustomTrueAnswer() - { - $dialog = new QuestionHelper(); - - $dialog->setInputStream($this->getInputStream("j\ny\n")); - $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); - $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); - $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - } - - /** - * @group legacy - */ - public function testLegacyAskAndValidate() - { - $dialog = new QuestionHelper(); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $error = 'This is not a color!'; - $validator = function ($color) use ($error) { - if (!in_array($color, array('white', 'black'))) { - throw new \InvalidArgumentException($error); - } - - return $color; - }; - - $question = new Question('What color was the white horse of Henry IV?', 'white'); - $question->setValidator($validator); - $question->setMaxAttempts(2); - - $dialog->setInputStream($this->getInputStream("\nblack\n")); - $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); - - $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); - try { - $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals($error, $e->getMessage()); - } - } - - /** - * @group legacy - * @dataProvider simpleAnswerProvider - */ - public function testLegacySelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) - { - $possibleChoices = array( - 'My environment 1', - 'My environment 2', - 'My environment 3', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @group legacy - * @dataProvider mixedKeysChoiceListAnswerProvider - */ - public function testLegacyChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) - { - $possibleChoices = array( - '0' => 'No environment', - '1' => 'My environment 1', - 'env_2' => 'My environment 2', - 3 => 'My environment 3', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @group legacy - * @dataProvider answerProvider - */ - public function testLegacySelectChoiceFromChoiceList($providedAnswer, $expectedValue) - { - $possibleChoices = array( - 'env_1' => 'My environment 1', - 'env_2' => 'My environment', - 'env_3' => 'My environment', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - - $this->assertSame($expectedValue, $answer); - } - - /** - * @group legacy - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. - */ - public function testLegacyAmbiguousChoiceFromChoicelist() - { - $possibleChoices = array( - 'env_1' => 'My first environment', - 'env_2' => 'My environment', - 'env_3' => 'My environment', - ); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream("My environment\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); - $question->setMaxAttempts(1); - - $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); - } - - /** - * @requires function mb_strwidth - * @group legacy - */ - public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys() - { - $question = 'Lorem ipsum?'; - $possibleChoices = array( - 'foo' => 'foo', - 'żółw' => 'bar', - 'łabądź' => 'baz', - ); - $outputShown = array( - $question, - ' [foo ] foo', - ' [żółw ] bar', - ' [łabądź] baz', - ); - $output = $this->getMock('\Symfony\Component\Console\Output\OutputInterface'); - $output->method('getFormatter')->willReturn(new OutputFormatter()); - - $dialog = new QuestionHelper(); - $dialog->setInputStream($this->getInputStream("\n")); - $helperSet = new HelperSet(array(new FormatterHelper())); - $dialog->setHelperSet($helperSet); - - $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); - - $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); - $dialog->ask($this->createInputInterfaceMock(), $output, $question); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\RuntimeException - * @expectedExceptionMessage Aborted - */ - public function testAskThrowsExceptionOnMissingInput() - { - $dialog = new QuestionHelper(); - $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?')); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\RuntimeException - * @expectedExceptionMessage Aborted - */ - public function testAskThrowsExceptionOnMissingInputWithValidator() - { - $dialog = new QuestionHelper(); - - $question = new Question('What\'s your name?'); - $question->setValidator(function () { - if (!$value) { - throw new \Exception('A value is required.'); - } - }); - - $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question); - } - - protected function getInputStream($input) - { - $stream = fopen('php://memory', 'r+', false); - fwrite($stream, $input); - rewind($stream); - - return $stream; - } - - protected function createOutputInterface() - { - return new StreamOutput(fopen('php://memory', 'r+', false)); - } - - protected function createInputInterfaceMock($interactive = true) - { - $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); - $mock->expects($this->any()) - ->method('isInteractive') - ->will($this->returnValue($interactive)); - - return $mock; - } - - private function hasSttyAvailable() - { - exec('stty 2>&1', $output, $exitcode); - - return $exitcode === 0; - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php deleted file mode 100644 index 4298dd2df..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php +++ /dev/null @@ -1,147 +0,0 @@ -setHelperSet($helperSet); - - $heroes = array('Superman', 'Batman', 'Spiderman'); - - $inputStream = $this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); - $question->setMaxAttempts(1); - // first answer is an empty answer, we're supposed to receive the default value - $this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); - $this->assertOutputContains('What is your favorite superhero? [Spiderman]', $output); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setMaxAttempts(1); - $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); - $question->setErrorMessage('Input "%s" is not a superhero!'); - $question->setMaxAttempts(2); - $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); - $this->assertOutputContains('Input "Fabien" is not a superhero!', $output); - - try { - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); - $question->setMaxAttempts(1); - $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question); - $this->fail(); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); - } - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); - $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output); - - $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); - $question->setMaxAttempts(1); - $question->setMultiselect(true); - - $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); - $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output); - } - - public function testAskReturnsNullIfValidatorAllowsIt() - { - $questionHelper = new SymfonyQuestionHelper(); - $question = new Question('What is your favorite superhero?'); - $question->setValidator(function ($value) { return $value; }); - $input = $this->createStreamableInputInterfaceMock($this->getInputStream("\n")); - $this->assertNull($questionHelper->ask($input, $this->createOutputInterface(), $question)); - } - - public function testAskEscapeDefaultValue() - { - $helper = new SymfonyQuestionHelper(); - $input = $this->createStreamableInputInterfaceMock($this->getInputStream('\\')); - $helper->ask($input, $output = $this->createOutputInterface(), new Question('Can I have a backslash?', '\\')); - - $this->assertOutputContains('Can I have a backslash? [\]', $output); - } - - public function testAskEscapeLabel() - { - $helper = new SymfonyQuestionHelper(); - $input = $this->createStreamableInputInterfaceMock($this->getInputStream('sure')); - $helper->ask($input, $output = $this->createOutputInterface(), new Question('Do you want a \?')); - - $this->assertOutputContains('Do you want a \?', $output); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\RuntimeException - * @expectedExceptionMessage Aborted - */ - public function testAskThrowsExceptionOnMissingInput() - { - $dialog = new SymfonyQuestionHelper(); - $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?')); - } - - protected function getInputStream($input) - { - $stream = fopen('php://memory', 'r+', false); - fwrite($stream, $input); - rewind($stream); - - return $stream; - } - - protected function createOutputInterface() - { - $output = new StreamOutput(fopen('php://memory', 'r+', false)); - $output->setDecorated(false); - - return $output; - } - - protected function createInputInterfaceMock($interactive = true) - { - $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); - $mock->expects($this->any()) - ->method('isInteractive') - ->will($this->returnValue($interactive)); - - return $mock; - } - - private function assertOutputContains($expected, StreamOutput $output) - { - rewind($output->getStream()); - $stream = stream_get_contents($output->getStream()); - $this->assertContains($expected, $stream); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/TableStyleTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/TableStyleTest.php deleted file mode 100644 index 587d8414e..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/TableStyleTest.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\TableStyle; - -class TableStyleTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH). - */ - public function testSetPadTypeWithInvalidType() - { - $style = new TableStyle(); - $style->setPadType('TEST'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Helper/TableTest.php b/tests/integration/vendor/symfony/console/Tests/Helper/TableTest.php deleted file mode 100644 index 1ea7b11f6..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Helper/TableTest.php +++ /dev/null @@ -1,727 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Helper; - -use Symfony\Component\Console\Helper\Table; -use Symfony\Component\Console\Helper\TableStyle; -use Symfony\Component\Console\Helper\TableSeparator; -use Symfony\Component\Console\Helper\TableCell; -use Symfony\Component\Console\Output\StreamOutput; - -class TableTest extends \PHPUnit_Framework_TestCase -{ - protected $stream; - - protected function setUp() - { - $this->stream = fopen('php://memory', 'r+'); - } - - protected function tearDown() - { - fclose($this->stream); - $this->stream = null; - } - - /** - * @dataProvider testRenderProvider - */ - public function testRender($headers, $rows, $style, $expected) - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders($headers) - ->setRows($rows) - ->setStyle($style) - ; - $table->render(); - - $this->assertEquals($expected, $this->getOutputContent($output)); - } - - /** - * @dataProvider testRenderProvider - */ - public function testRenderAddRows($headers, $rows, $style, $expected) - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders($headers) - ->addRows($rows) - ->setStyle($style) - ; - $table->render(); - - $this->assertEquals($expected, $this->getOutputContent($output)); - } - - /** - * @dataProvider testRenderProvider - */ - public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected) - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders($headers) - ->setStyle($style) - ; - foreach ($rows as $row) { - $table->addRow($row); - } - $table->render(); - - $this->assertEquals($expected, $this->getOutputContent($output)); - } - - public function testRenderProvider() - { - $books = array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), - array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), - array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), - ); - - return array( - array( - array('ISBN', 'Title', 'Author'), - $books, - 'default', -<<<'TABLE' -+---------------+--------------------------+------------------+ -| ISBN | Title | Author | -+---------------+--------------------------+------------------+ -| 99921-58-10-7 | Divine Comedy | Dante Alighieri | -| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | -| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | -| 80-902734-1-6 | And Then There Were None | Agatha Christie | -+---------------+--------------------------+------------------+ - -TABLE - ), - array( - array('ISBN', 'Title', 'Author'), - $books, - 'compact', -<<<'TABLE' - ISBN Title Author - 99921-58-10-7 Divine Comedy Dante Alighieri - 9971-5-0210-0 A Tale of Two Cities Charles Dickens - 960-425-059-0 The Lord of the Rings J. R. R. Tolkien - 80-902734-1-6 And Then There Were None Agatha Christie - -TABLE - ), - array( - array('ISBN', 'Title', 'Author'), - $books, - 'borderless', -<<<'TABLE' - =============== ========================== ================== - ISBN Title Author - =============== ========================== ================== - 99921-58-10-7 Divine Comedy Dante Alighieri - 9971-5-0210-0 A Tale of Two Cities Charles Dickens - 960-425-059-0 The Lord of the Rings J. R. R. Tolkien - 80-902734-1-6 And Then There Were None Agatha Christie - =============== ========================== ================== - -TABLE - ), - array( - array('ISBN', 'Title'), - array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0'), - array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), - array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), - ), - 'default', -<<<'TABLE' -+---------------+--------------------------+------------------+ -| ISBN | Title | | -+---------------+--------------------------+------------------+ -| 99921-58-10-7 | Divine Comedy | Dante Alighieri | -| 9971-5-0210-0 | | | -| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | -| 80-902734-1-6 | And Then There Were None | Agatha Christie | -+---------------+--------------------------+------------------+ - -TABLE - ), - array( - array(), - array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0'), - array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), - array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), - ), - 'default', -<<<'TABLE' -+---------------+--------------------------+------------------+ -| 99921-58-10-7 | Divine Comedy | Dante Alighieri | -| 9971-5-0210-0 | | | -| 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | -| 80-902734-1-6 | And Then There Were None | Agatha Christie | -+---------------+--------------------------+------------------+ - -TABLE - ), - array( - array('ISBN', 'Title', 'Author'), - array( - array('99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'), - array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."), - array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."), - array('960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"), - ), - 'default', -<<<'TABLE' -+---------------+----------------------------+-----------------+ -| ISBN | Title | Author | -+---------------+----------------------------+-----------------+ -| 99921-58-10-7 | Divine | Dante Alighieri | -| | Comedy | | -| 9971-5-0210-2 | Harry Potter | Rowling | -| | and the Chamber of Secrets | Joanne K. | -| 9971-5-0210-2 | Harry Potter | Rowling | -| | and the Chamber of Secrets | Joanne K. | -| 960-425-059-0 | The Lord of the Rings | J. R. R. | -| | | Tolkien | -+---------------+----------------------------+-----------------+ - -TABLE - ), - array( - array('ISBN', 'Title'), - array(), - 'default', -<<<'TABLE' -+------+-------+ -| ISBN | Title | -+------+-------+ - -TABLE - ), - array( - array(), - array(), - 'default', - '', - ), - 'Cell text with tags used for Output styling' => array( - array('ISBN', 'Title', 'Author'), - array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), - ), - 'default', -<<<'TABLE' -+---------------+----------------------+-----------------+ -| ISBN | Title | Author | -+---------------+----------------------+-----------------+ -| 99921-58-10-7 | Divine Comedy | Dante Alighieri | -| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | -+---------------+----------------------+-----------------+ - -TABLE - ), - 'Cell text with tags not used for Output styling' => array( - array('ISBN', 'Title', 'Author'), - array( - array('99921-58-10-700', 'Divine Com', 'Dante Alighieri'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), - ), - 'default', -<<<'TABLE' -+----------------------------------+----------------------+-----------------+ -| ISBN | Title | Author | -+----------------------------------+----------------------+-----------------+ -| 99921-58-10-700 | Divine Com | Dante Alighieri | -| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | -+----------------------------------+----------------------+-----------------+ - -TABLE - ), - 'Cell with colspan' => array( - array('ISBN', 'Title', 'Author'), - array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), - new TableSeparator(), - array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))), - new TableSeparator(), - array( - new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)), - 'Mark Schmidt', - ), - new TableSeparator(), - array( - '9971-5-0210-0', - new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), - ), - new TableSeparator(), - array( - new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)), - ), - ), - 'default', -<<<'TABLE' -+-------------------------------+-------------------------------+-----------------------------+ -| ISBN | Title | Author | -+-------------------------------+-------------------------------+-----------------------------+ -| 99921-58-10-7 | Divine Comedy | Dante Alighieri | -+-------------------------------+-------------------------------+-----------------------------+ -| Divine Comedy(Dante Alighieri) | -+-------------------------------+-------------------------------+-----------------------------+ -| Arduino: A Quick-Start Guide | Mark Schmidt | -+-------------------------------+-------------------------------+-----------------------------+ -| 9971-5-0210-0 | A Tale of | -| | Two Cities | -+-------------------------------+-------------------------------+-----------------------------+ -| Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil! | -+-------------------------------+-------------------------------+-----------------------------+ - -TABLE - ), - 'Cell with rowspan' => array( - array('ISBN', 'Title', 'Author'), - array( - array( - new TableCell('9971-5-0210-0', array('rowspan' => 3)), - 'Divine Comedy', - 'Dante Alighieri', - ), - array('A Tale of Two Cities', 'Charles Dickens'), - array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"), - new TableSeparator(), - array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'), - array('80-902734-1-7', 'Test'), - ), - 'default', -<<<'TABLE' -+---------------+----------------------+-----------------+ -| ISBN | Title | Author | -+---------------+----------------------+-----------------+ -| 9971-5-0210-0 | Divine Comedy | Dante Alighieri | -| | A Tale of Two Cities | Charles Dickens | -| | The Lord of | J. R. | -| | the Rings | R. Tolkien | -+---------------+----------------------+-----------------+ -| 80-902734-1-6 | And Then | Agatha Christie | -| 80-902734-1-7 | There | Test | -| | Were None | | -+---------------+----------------------+-----------------+ - -TABLE - ), - 'Cell with rowspan and colspan' => array( - array('ISBN', 'Title', 'Author'), - array( - array( - new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)), - 'Dante Alighieri', - ), - array('Charles Dickens'), - new TableSeparator(), - array( - 'Dante Alighieri', - new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)), - ), - array('J. R. R. Tolkien'), - array('J. R. R'), - ), - 'default', -<<<'TABLE' -+------------------+---------+-----------------+ -| ISBN | Title | Author | -+------------------+---------+-----------------+ -| 9971-5-0210-0 | Dante Alighieri | -| | Charles Dickens | -+------------------+---------+-----------------+ -| Dante Alighieri | 9971-5-0210-0 | -| J. R. R. Tolkien | | -| J. R. R | | -+------------------+---------+-----------------+ - -TABLE - ), - 'Cell with rowspan and colspan contains new line break' => array( - array('ISBN', 'Title', 'Author'), - array( - array( - new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), - 'Dante Alighieri', - ), - array('Charles Dickens'), - new TableSeparator(), - array( - 'Dante Alighieri', - new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), - ), - array('Charles Dickens'), - new TableSeparator(), - array( - new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), - new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)), - ), - ), - 'default', -<<<'TABLE' -+-----------------+-------+-----------------+ -| ISBN | Title | Author | -+-----------------+-------+-----------------+ -| 9971 | Dante Alighieri | -| -5- | Charles Dickens | -| 021 | | -| 0-0 | | -+-----------------+-------+-----------------+ -| Dante Alighieri | 9971 | -| Charles Dickens | -5- | -| | 021 | -| | 0-0 | -+-----------------+-------+-----------------+ -| 9971 | Dante | -| -5- | Alighieri | -| 021 | | -| 0-0 | | -+-----------------+-------+-----------------+ - -TABLE - ), - 'Cell with rowspan and colspan without using TableSeparator' => array( - array('ISBN', 'Title', 'Author'), - array( - array( - new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), - 'Dante Alighieri', - ), - array('Charles Dickens'), - array( - 'Dante Alighieri', - new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), - ), - array('Charles Dickens'), - ), - 'default', -<<<'TABLE' -+-----------------+-------+-----------------+ -| ISBN | Title | Author | -+-----------------+-------+-----------------+ -| 9971 | Dante Alighieri | -| -5- | Charles Dickens | -| 021 | | -| 0-0 | | -| Dante Alighieri | 9971 | -| Charles Dickens | -5- | -| | 021 | -| | 0-0 | -+-----------------+-------+-----------------+ - -TABLE - ), - 'Cell with rowspan and colspan with separator inside a rowspan' => array( - array('ISBN', 'Author'), - array( - array( - new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), - 'Dante Alighieri', - ), - array(new TableSeparator()), - array('Charles Dickens'), - ), - 'default', -<<<'TABLE' -+---------------+-----------------+ -| ISBN | Author | -+---------------+-----------------+ -| 9971-5-0210-0 | Dante Alighieri | -| |-----------------| -| | Charles Dickens | -+---------------+-----------------+ - -TABLE - ), - 'Multiple header lines' => array( - array( - array(new TableCell('Main title', array('colspan' => 3))), - array('ISBN', 'Title', 'Author'), - ), - array(), - 'default', -<<<'TABLE' -+------+-------+--------+ -| Main title | -+------+-------+--------+ -| ISBN | Title | Author | -+------+-------+--------+ - -TABLE - ), - 'Row with multiple cells' => array( - array(), - array( - array( - new TableCell('1', array('colspan' => 3)), - new TableCell('2', array('colspan' => 2)), - new TableCell('3', array('colspan' => 2)), - new TableCell('4', array('colspan' => 2)), - ), - ), - 'default', -<<<'TABLE' -+---+--+--+---+--+---+--+---+--+ -| 1 | 2 | 3 | 4 | -+---+--+--+---+--+---+--+---+--+ - -TABLE - ), - ); - } - - public function testRenderMultiByte() - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders(array('■■')) - ->setRows(array(array(1234))) - ->setStyle('default') - ; - $table->render(); - - $expected = -<<<'TABLE' -+------+ -| ■■ | -+------+ -| 1234 | -+------+ - -TABLE; - - $this->assertEquals($expected, $this->getOutputContent($output)); - } - - public function testStyle() - { - $style = new TableStyle(); - $style - ->setHorizontalBorderChar('.') - ->setVerticalBorderChar('.') - ->setCrossingChar('.') - ; - - Table::setStyleDefinition('dotfull', $style); - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders(array('Foo')) - ->setRows(array(array('Bar'))) - ->setStyle('dotfull'); - $table->render(); - - $expected = -<<<'TABLE' -....... -. Foo . -....... -. Bar . -....... - -TABLE; - - $this->assertEquals($expected, $this->getOutputContent($output)); - } - - public function testRowSeparator() - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders(array('Foo')) - ->setRows(array( - array('Bar1'), - new TableSeparator(), - array('Bar2'), - new TableSeparator(), - array('Bar3'), - )); - $table->render(); - - $expected = -<<<'TABLE' -+------+ -| Foo | -+------+ -| Bar1 | -+------+ -| Bar2 | -+------+ -| Bar3 | -+------+ - -TABLE; - - $this->assertEquals($expected, $this->getOutputContent($output)); - - $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); - } - - public function testRenderMultiCalls() - { - $table = new Table($output = $this->getOutputStream()); - $table->setRows(array( - array(new TableCell('foo', array('colspan' => 2))), - )); - $table->render(); - $table->render(); - $table->render(); - - $expected = -<<
assertEquals($expected, $this->getOutputContent($output)); - } - - public function testColumnStyle() - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) - ->setRows(array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), - )); - - $style = new TableStyle(); - $style->setPadType(STR_PAD_LEFT); - $table->setColumnStyle(3, $style); - - $table->render(); - - $expected = - <<
assertEquals($expected, $this->getOutputContent($output)); - } - - public function testColumnWith() - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) - ->setRows(array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), - )) - ->setColumnWidth(0, 15) - ->setColumnWidth(3, 10); - - $style = new TableStyle(); - $style->setPadType(STR_PAD_LEFT); - $table->setColumnStyle(3, $style); - - $table->render(); - - $expected = - <<
assertEquals($expected, $this->getOutputContent($output)); - } - - public function testColumnWiths() - { - $table = new Table($output = $this->getOutputStream()); - $table - ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) - ->setRows(array( - array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), - array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), - )) - ->setColumnWidths(array(15, 0, -1, 10)); - - $style = new TableStyle(); - $style->setPadType(STR_PAD_LEFT); - $table->setColumnStyle(3, $style); - - $table->render(); - - $expected = - <<
assertEquals($expected, $this->getOutputContent($output)); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException - * @expectedExceptionMessage Style "absent" is not defined. - */ - public function testIsNotDefinedStyleException() - { - $table = new Table($this->getOutputStream()); - $table->setStyle('absent'); - } - - /** - * @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException - * @expectedExceptionMessage Style "absent" is not defined. - */ - public function testGetStyleDefinition() - { - Table::getStyleDefinition('absent'); - } - - protected function getOutputStream() - { - return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); - } - - protected function getOutputContent(StreamOutput $output) - { - rewind($output->getStream()); - - return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/ArgvInputTest.php b/tests/integration/vendor/symfony/console/Tests/Input/ArgvInputTest.php deleted file mode 100644 index ecbe41df5..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/ArgvInputTest.php +++ /dev/null @@ -1,384 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; - -class ArgvInputTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $_SERVER['argv'] = array('cli.php', 'foo'); - $input = new ArgvInput(); - $r = new \ReflectionObject($input); - $p = $r->getProperty('tokens'); - $p->setAccessible(true); - - $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); - } - - public function testParseArguments() - { - $input = new ArgvInput(array('cli.php', 'foo')); - $input->bind(new InputDefinition(array(new InputArgument('name')))); - $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); - - $input->bind(new InputDefinition(array(new InputArgument('name')))); - $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); - } - - /** - * @dataProvider provideOptions - */ - public function testParseOptions($input, $options, $expectedOptions, $message) - { - $input = new ArgvInput($input); - $input->bind(new InputDefinition($options)); - - $this->assertEquals($expectedOptions, $input->getOptions(), $message); - } - - public function provideOptions() - { - return array( - array( - array('cli.php', '--foo'), - array(new InputOption('foo')), - array('foo' => true), - '->parse() parses long options without a value', - ), - array( - array('cli.php', '--foo=bar'), - array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), - array('foo' => 'bar'), - '->parse() parses long options with a required value (with a = separator)', - ), - array( - array('cli.php', '--foo', 'bar'), - array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), - array('foo' => 'bar'), - '->parse() parses long options with a required value (with a space separator)', - ), - array( - array('cli.php', '--foo='), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), - array('foo' => null), - '->parse() parses long options with optional value which is empty (with a = separator) as null', - ), - array( - array('cli.php', '--foo=', 'bar'), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)), - array('foo' => null), - '->parse() parses long options with optional value which is empty (with a = separator) followed by an argument', - ), - array( - array('cli.php', '-f'), - array(new InputOption('foo', 'f')), - array('foo' => true), - '->parse() parses short options without a value', - ), - array( - array('cli.php', '-fbar'), - array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), - array('foo' => 'bar'), - '->parse() parses short options with a required value (with no separator)', - ), - array( - array('cli.php', '-f', 'bar'), - array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), - array('foo' => 'bar'), - '->parse() parses short options with a required value (with a space separator)', - ), - array( - array('cli.php', '-f', ''), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), - array('foo' => ''), - '->parse() parses short options with an optional empty value', - ), - array( - array('cli.php', '-f', '', 'foo'), - array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), - array('foo' => ''), - '->parse() parses short options with an optional empty value followed by an argument', - ), - array( - array('cli.php', '-f', '', '-b'), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), - array('foo' => '', 'bar' => true), - '->parse() parses short options with an optional empty value followed by an option', - ), - array( - array('cli.php', '-f', '-b', 'foo'), - array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), - array('foo' => null, 'bar' => true), - '->parse() parses short options with an optional value which is not present', - ), - array( - array('cli.php', '-fb'), - array(new InputOption('foo', 'f'), new InputOption('bar', 'b')), - array('foo' => true, 'bar' => true), - '->parse() parses short options when they are aggregated as a single one', - ), - array( - array('cli.php', '-fb', 'bar'), - array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)), - array('foo' => true, 'bar' => 'bar'), - '->parse() parses short options when they are aggregated as a single one and the last one has a required value', - ), - array( - array('cli.php', '-fb', 'bar'), - array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), - array('foo' => true, 'bar' => 'bar'), - '->parse() parses short options when they are aggregated as a single one and the last one has an optional value', - ), - array( - array('cli.php', '-fbbar'), - array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), - array('foo' => true, 'bar' => 'bar'), - '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator', - ), - array( - array('cli.php', '-fbbar'), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), - array('foo' => 'bbar', 'bar' => null), - '->parse() parses short options when they are aggregated as a single one and one of them takes a value', - ), - ); - } - - /** - * @dataProvider provideInvalidInput - */ - public function testInvalidInput($argv, $definition, $expectedExceptionMessage) - { - $this->setExpectedException('RuntimeException', $expectedExceptionMessage); - - $input = new ArgvInput($argv); - $input->bind($definition); - } - - public function provideInvalidInput() - { - return array( - array( - array('cli.php', '--foo'), - new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), - 'The "--foo" option requires a value.', - ), - array( - array('cli.php', '-f'), - new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), - 'The "--foo" option requires a value.', - ), - array( - array('cli.php', '-ffoo'), - new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), - 'The "-o" option does not exist.', - ), - array( - array('cli.php', '--foo=bar'), - new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), - 'The "--foo" option does not accept a value.', - ), - array( - array('cli.php', 'foo', 'bar'), - new InputDefinition(), - 'No arguments expected, got "foo".', - ), - array( - array('cli.php', 'foo', 'bar'), - new InputDefinition(array(new InputArgument('number'))), - 'Too many arguments, expected arguments "number".', - ), - array( - array('cli.php', 'foo', 'bar', 'zzz'), - new InputDefinition(array(new InputArgument('number'), new InputArgument('county'))), - 'Too many arguments, expected arguments "number" "county".', - ), - array( - array('cli.php', '--foo'), - new InputDefinition(), - 'The "--foo" option does not exist.', - ), - array( - array('cli.php', '-f'), - new InputDefinition(), - 'The "-f" option does not exist.', - ), - array( - array('cli.php', '-1'), - new InputDefinition(array(new InputArgument('number'))), - 'The "-1" option does not exist.', - ), - ); - } - - public function testParseArrayArgument() - { - $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); - $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); - - $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); - } - - public function testParseArrayOption() - { - $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); - $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); - - $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); - - $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz')); - $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); - $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); - - $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=')); - $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); - $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)'); - - $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption')); - $input->bind(new InputDefinition(array( - new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), - new InputOption('anotherOption', null, InputOption::VALUE_NONE), - ))); - $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)'); - } - - public function testParseNegativeNumberAfterDoubleDash() - { - $input = new ArgvInput(array('cli.php', '--', '-1')); - $input->bind(new InputDefinition(array(new InputArgument('number')))); - $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); - - $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); - $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); - $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); - $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); - } - - public function testParseEmptyStringArgument() - { - $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); - $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); - - $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); - } - - public function testGetFirstArgument() - { - $input = new ArgvInput(array('cli.php', '-fbbar')); - $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); - - $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); - $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); - } - - public function testHasParameterOption() - { - $input = new ArgvInput(array('cli.php', '-f', 'foo')); - $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); - - $input = new ArgvInput(array('cli.php', '--foo', 'foo')); - $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); - - $input = new ArgvInput(array('cli.php', 'foo')); - $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); - - $input = new ArgvInput(array('cli.php', '--foo=bar')); - $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input'); - } - - public function testHasParameterOptionOnlyOptions() - { - $input = new ArgvInput(array('cli.php', '-f', 'foo')); - $this->assertTrue($input->hasParameterOption('-f', true), '->hasParameterOption() returns true if the given short option is in the raw input'); - - $input = new ArgvInput(array('cli.php', '--foo', '--', 'foo')); - $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option is in the raw input'); - - $input = new ArgvInput(array('cli.php', '--foo=bar', 'foo')); - $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option with provided value is in the raw input'); - - $input = new ArgvInput(array('cli.php', '--', '--foo')); - $this->assertFalse($input->hasParameterOption('--foo', true), '->hasParameterOption() returns false if the given option is in the raw input but after an end of options signal'); - } - - public function testToString() - { - $input = new ArgvInput(array('cli.php', '-f', 'foo')); - $this->assertEquals('-f foo', (string) $input); - - $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C")); - $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); - } - - /** - * @dataProvider provideGetParameterOptionValues - */ - public function testGetParameterOptionEqualSign($argv, $key, $onlyParams, $expected) - { - $input = new ArgvInput($argv); - $this->assertEquals($expected, $input->getParameterOption($key, false, $onlyParams), '->getParameterOption() returns the expected value'); - } - - public function provideGetParameterOptionValues() - { - return array( - array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', false, 'dev'), - array(array('app/console', 'foo:bar', '--env=dev'), '--env', false, 'dev'), - array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), false, 'dev'), - array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), false, 'dev'), - array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), false, '1'), - array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), false, '1'), - array(array('app/console', 'foo:bar', '--env', 'val'), '--env', false, 'val'), - array(array('app/console', 'foo:bar', '--env', 'val', '--dummy'), '--env', false, 'val'), - array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', false, 'dev'), - array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', true, false), - ); - } - - public function testParseSingleDashAsArgument() - { - $input = new ArgvInput(array('cli.php', '-')); - $input->bind(new InputDefinition(array(new InputArgument('file')))); - $this->assertEquals(array('file' => '-'), $input->getArguments(), '->parse() parses single dash as an argument'); - } - - public function testParseOptionWithValueOptionalGivenEmptyAndRequiredArgument() - { - $input = new ArgvInput(array('cli.php', '--foo=', 'bar')); - $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)))); - $this->assertEquals(array('foo' => null), $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->parse() parses required arguments'); - - $input = new ArgvInput(array('cli.php', '--foo=0', 'bar')); - $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)))); - $this->assertEquals(array('foo' => '0'), $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->parse() parses required arguments'); - } - - public function testParseOptionWithValueOptionalGivenEmptyAndOptionalArgument() - { - $input = new ArgvInput(array('cli.php', '--foo=', 'bar')); - $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::OPTIONAL)))); - $this->assertEquals(array('foo' => null), $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->parse() parses optional arguments'); - - $input = new ArgvInput(array('cli.php', '--foo=0', 'bar')); - $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::OPTIONAL)))); - $this->assertEquals(array('foo' => '0'), $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->parse() parses optional arguments'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/ArrayInputTest.php b/tests/integration/vendor/symfony/console/Tests/Input/ArrayInputTest.php deleted file mode 100644 index afb9913a4..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/ArrayInputTest.php +++ /dev/null @@ -1,159 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; - -class ArrayInputTest extends \PHPUnit_Framework_TestCase -{ - public function testGetFirstArgument() - { - $input = new ArrayInput(array()); - $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); - $input = new ArrayInput(array('name' => 'Fabien')); - $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); - $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); - $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); - } - - public function testHasParameterOption() - { - $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); - $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); - $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); - - $input = new ArrayInput(array('--foo')); - $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); - - $input = new ArrayInput(array('--foo', '--', '--bar')); - $this->assertTrue($input->hasParameterOption('--bar'), '->hasParameterOption() returns true if an option is present in the passed parameters'); - $this->assertFalse($input->hasParameterOption('--bar', true), '->hasParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); - } - - public function testGetParameterOption() - { - $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); - $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); - $this->assertFalse($input->getParameterOption('--bar'), '->getParameterOption() returns the default if an option is not present in the passed parameters'); - - $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); - $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); - - $input = new ArrayInput(array('--foo', '--', '--bar' => 'woop')); - $this->assertEquals('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); - $this->assertFalse($input->getParameterOption('--bar', false, true), '->getParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); - } - - public function testParseArguments() - { - $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); - - $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); - } - - /** - * @dataProvider provideOptions - */ - public function testParseOptions($input, $options, $expectedOptions, $message) - { - $input = new ArrayInput($input, new InputDefinition($options)); - - $this->assertEquals($expectedOptions, $input->getOptions(), $message); - } - - public function provideOptions() - { - return array( - array( - array('--foo' => 'bar'), - array(new InputOption('foo')), - array('foo' => 'bar'), - '->parse() parses long options', - ), - array( - array('--foo' => 'bar'), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), - array('foo' => 'bar'), - '->parse() parses long options with a default value', - ), - array( - array('--foo' => null), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), - array('foo' => 'default'), - '->parse() parses long options with a default value', - ), - array( - array('-f' => 'bar'), - array(new InputOption('foo', 'f')), - array('foo' => 'bar'), - '->parse() parses short options', - ), - array( - array('--' => null, '-f' => 'bar'), - array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), - array('foo' => 'default'), - '->parse() does not parse opts after an end of options signal', - ), - array( - array('--' => null), - array(), - array(), - '->parse() does not choke on end of options signal', - ), - ); - } - - /** - * @dataProvider provideInvalidInput - */ - public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage) - { - $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); - - new ArrayInput($parameters, $definition); - } - - public function provideInvalidInput() - { - return array( - array( - array('foo' => 'foo'), - new InputDefinition(array(new InputArgument('name'))), - 'The "foo" argument does not exist.', - ), - array( - array('--foo' => null), - new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), - 'The "--foo" option requires a value.', - ), - array( - array('--foo' => 'foo'), - new InputDefinition(), - 'The "--foo" option does not exist.', - ), - array( - array('-o' => 'foo'), - new InputDefinition(), - 'The "-o" option does not exist.', - ), - ); - } - - public function testToString() - { - $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C")); - $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/InputArgumentTest.php b/tests/integration/vendor/symfony/console/Tests/Input/InputArgumentTest.php deleted file mode 100644 index cfb37cd41..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/InputArgumentTest.php +++ /dev/null @@ -1,111 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\InputArgument; - -class InputArgumentTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $argument = new InputArgument('foo'); - $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); - } - - public function testModes() - { - $argument = new InputArgument('foo'); - $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); - - $argument = new InputArgument('foo', null); - $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); - - $argument = new InputArgument('foo', InputArgument::OPTIONAL); - $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); - - $argument = new InputArgument('foo', InputArgument::REQUIRED); - $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); - } - - /** - * @dataProvider provideInvalidModes - */ - public function testInvalidModes($mode) - { - $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); - - new InputArgument('foo', $mode); - } - - public function provideInvalidModes() - { - return array( - array('ANOTHER_ONE'), - array(-1), - ); - } - - public function testIsArray() - { - $argument = new InputArgument('foo', InputArgument::IS_ARRAY); - $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); - $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); - $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); - $argument = new InputArgument('foo', InputArgument::OPTIONAL); - $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); - } - - public function testGetDescription() - { - $argument = new InputArgument('foo', null, 'Some description'); - $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); - } - - public function testGetDefault() - { - $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); - $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); - } - - public function testSetDefault() - { - $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); - $argument->setDefault(null); - $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); - $argument->setDefault('another'); - $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); - - $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); - $argument->setDefault(array(1, 2)); - $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode. - */ - public function testSetDefaultWithRequiredArgument() - { - $argument = new InputArgument('foo', InputArgument::REQUIRED); - $argument->setDefault('default'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage A default value for an array argument must be an array. - */ - public function testSetDefaultWithArrayArgument() - { - $argument = new InputArgument('foo', InputArgument::IS_ARRAY); - $argument->setDefault('default'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/InputDefinitionTest.php b/tests/integration/vendor/symfony/console/Tests/Input/InputDefinitionTest.php deleted file mode 100644 index 739e107de..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/InputDefinitionTest.php +++ /dev/null @@ -1,405 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; - -class InputDefinitionTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixtures; - - protected $foo; - protected $bar; - protected $foo1; - protected $foo2; - - public static function setUpBeforeClass() - { - self::$fixtures = __DIR__.'/../Fixtures/'; - } - - public function testConstructorArguments() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); - - $definition = new InputDefinition(array($this->foo, $this->bar)); - $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); - } - - public function testConstructorOptions() - { - $this->initializeOptions(); - - $definition = new InputDefinition(); - $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); - - $definition = new InputDefinition(array($this->foo, $this->bar)); - $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); - } - - public function testSetArguments() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->setArguments(array($this->foo)); - $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); - $definition->setArguments(array($this->bar)); - - $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); - } - - public function testAddArguments() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArguments(array($this->foo)); - $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); - $definition->addArguments(array($this->bar)); - $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); - } - - public function testAddArgument() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArgument($this->foo); - $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); - $definition->addArgument($this->bar); - $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage An argument with name "foo" already exists. - */ - public function testArgumentsMustHaveDifferentNames() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArgument($this->foo); - $definition->addArgument($this->foo1); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Cannot add an argument after an array argument. - */ - public function testArrayArgumentHasToBeLast() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); - $definition->addArgument(new InputArgument('anotherbar')); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Cannot add a required argument after an optional one. - */ - public function testRequiredArgumentCannotFollowAnOptionalOne() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArgument($this->foo); - $definition->addArgument($this->foo2); - } - - public function testGetArgument() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArguments(array($this->foo)); - $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "bar" argument does not exist. - */ - public function testGetInvalidArgument() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArguments(array($this->foo)); - $definition->getArgument('bar'); - } - - public function testHasArgument() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArguments(array($this->foo)); - - $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); - $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); - } - - public function testGetArgumentRequiredCount() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArgument($this->foo2); - $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); - $definition->addArgument($this->foo); - $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); - } - - public function testGetArgumentCount() - { - $this->initializeArguments(); - - $definition = new InputDefinition(); - $definition->addArgument($this->foo2); - $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); - $definition->addArgument($this->foo); - $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); - } - - public function testGetArgumentDefaults() - { - $definition = new InputDefinition(array( - new InputArgument('foo1', InputArgument::OPTIONAL), - new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), - new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), - // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), - )); - $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); - - $definition = new InputDefinition(array( - new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), - )); - $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); - } - - public function testSetOptions() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); - $definition->setOptions(array($this->bar)); - $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "-f" option does not exist. - */ - public function testSetOptionsClearsOptions() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $definition->setOptions(array($this->bar)); - $definition->getOptionForShortcut('f'); - } - - public function testAddOptions() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); - $definition->addOptions(array($this->bar)); - $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); - } - - public function testAddOption() - { - $this->initializeOptions(); - - $definition = new InputDefinition(); - $definition->addOption($this->foo); - $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); - $definition->addOption($this->bar); - $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage An option named "foo" already exists. - */ - public function testAddDuplicateOption() - { - $this->initializeOptions(); - - $definition = new InputDefinition(); - $definition->addOption($this->foo); - $definition->addOption($this->foo2); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage An option with shortcut "f" already exists. - */ - public function testAddDuplicateShortcutOption() - { - $this->initializeOptions(); - - $definition = new InputDefinition(); - $definition->addOption($this->foo); - $definition->addOption($this->foo1); - } - - public function testGetOption() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "--bar" option does not exist. - */ - public function testGetInvalidOption() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $definition->getOption('bar'); - } - - public function testHasOption() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); - $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); - } - - public function testHasShortcut() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); - $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); - } - - public function testGetOptionForShortcut() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); - } - - public function testGetOptionForMultiShortcut() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->multi)); - $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); - $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "-l" option does not exist. - */ - public function testGetOptionForInvalidShortcut() - { - $this->initializeOptions(); - - $definition = new InputDefinition(array($this->foo)); - $definition->getOptionForShortcut('l'); - } - - public function testGetOptionDefaults() - { - $definition = new InputDefinition(array( - new InputOption('foo1', null, InputOption::VALUE_NONE), - new InputOption('foo2', null, InputOption::VALUE_REQUIRED), - new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), - new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), - new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), - new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), - new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), - )); - $defaults = array( - 'foo1' => false, - 'foo2' => null, - 'foo3' => 'default', - 'foo4' => null, - 'foo5' => 'default', - 'foo6' => array(), - 'foo7' => array(1, 2), - ); - $this->assertSame($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); - } - - /** - * @dataProvider getGetSynopsisData - */ - public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) - { - $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); - } - - public function getGetSynopsisData() - { - return array( - array(new InputDefinition(array(new InputOption('foo'))), '[--foo]', 'puts optional options in square brackets'), - array(new InputDefinition(array(new InputOption('foo', 'f'))), '[-f|--foo]', 'separates shortcut with a pipe'), - array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), '[-f|--foo FOO]', 'uses shortcut as value placeholder'), - array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))), '[-f|--foo [FOO]]', 'puts optional values in square brackets'), - - array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))), '', 'puts arguments in angle brackets'), - array(new InputDefinition(array(new InputArgument('foo'))), '[]', 'puts optional arguments in square brackets'), - array(new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))), '[]...', 'uses an ellipsis for array arguments'), - array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))), ' ()...', 'uses parenthesis and ellipsis for required array arguments'), - - array(new InputDefinition(array(new InputOption('foo'), new InputArgument('foo', InputArgument::REQUIRED))), '[--foo] [--] ', 'puts [--] between options and arguments'), - ); - } - - public function testGetShortSynopsis() - { - $definition = new InputDefinition(array(new InputOption('foo'), new InputOption('bar'), new InputArgument('cat'))); - $this->assertEquals('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); - } - - protected function initializeArguments() - { - $this->foo = new InputArgument('foo'); - $this->bar = new InputArgument('bar'); - $this->foo1 = new InputArgument('foo'); - $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); - } - - protected function initializeOptions() - { - $this->foo = new InputOption('foo', 'f'); - $this->bar = new InputOption('bar', 'b'); - $this->foo1 = new InputOption('fooBis', 'f'); - $this->foo2 = new InputOption('foo', 'p'); - $this->multi = new InputOption('multi', 'm|mm|mmm'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/InputOptionTest.php b/tests/integration/vendor/symfony/console/Tests/Input/InputOptionTest.php deleted file mode 100644 index 53ce1df8c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/InputOptionTest.php +++ /dev/null @@ -1,204 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\InputOption; - -class InputOptionTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $option = new InputOption('foo'); - $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); - $option = new InputOption('--foo'); - $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value. - */ - public function testArrayModeWithoutValue() - { - new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); - } - - public function testShortcut() - { - $option = new InputOption('foo', 'f'); - $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); - $option = new InputOption('foo', '-f|-ff|fff'); - $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); - $option = new InputOption('foo', array('f', 'ff', '-fff')); - $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); - $option = new InputOption('foo'); - $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); - } - - public function testModes() - { - $option = new InputOption('foo', 'f'); - $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); - $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); - $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); - - $option = new InputOption('foo', 'f', null); - $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); - $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); - $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); - - $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); - $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); - $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); - $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); - - $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); - $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); - $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); - $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); - - $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); - $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); - $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); - $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); - } - - /** - * @dataProvider provideInvalidModes - */ - public function testInvalidModes($mode) - { - $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); - - new InputOption('foo', 'f', $mode); - } - - public function provideInvalidModes() - { - return array( - array('ANOTHER_ONE'), - array(-1), - ); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testEmptyNameIsInvalid() - { - new InputOption(''); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testDoubleDashNameIsInvalid() - { - new InputOption('--'); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testSingleDashOptionIsInvalid() - { - new InputOption('foo', '-'); - } - - public function testIsArray() - { - $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); - $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); - $option = new InputOption('foo', null, InputOption::VALUE_NONE); - $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); - } - - public function testGetDescription() - { - $option = new InputOption('foo', 'f', null, 'Some description'); - $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); - } - - public function testGetDefault() - { - $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); - $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); - - $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); - $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); - - $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); - $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); - - $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); - $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); - - $option = new InputOption('foo', null, InputOption::VALUE_NONE); - $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); - } - - public function testSetDefault() - { - $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); - $option->setDefault(null); - $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); - $option->setDefault('another'); - $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); - - $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); - $option->setDefault(array(1, 2)); - $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode. - */ - public function testDefaultValueWithValueNoneMode() - { - $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); - $option->setDefault('default'); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage A default value for an array option must be an array. - */ - public function testDefaultValueWithIsArrayMode() - { - $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); - $option->setDefault('default'); - } - - public function testEquals() - { - $option = new InputOption('foo', 'f', null, 'Some description'); - $option2 = new InputOption('foo', 'f', null, 'Alternative description'); - $this->assertTrue($option->equals($option2)); - - $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); - $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); - $this->assertFalse($option->equals($option2)); - - $option = new InputOption('foo', 'f', null, 'Some description'); - $option2 = new InputOption('bar', 'f', null, 'Some description'); - $this->assertFalse($option->equals($option2)); - - $option = new InputOption('foo', 'f', null, 'Some description'); - $option2 = new InputOption('foo', '', null, 'Some description'); - $this->assertFalse($option->equals($option2)); - - $option = new InputOption('foo', 'f', null, 'Some description'); - $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); - $this->assertFalse($option->equals($option2)); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/InputTest.php b/tests/integration/vendor/symfony/console/Tests/Input/InputTest.php deleted file mode 100644 index ef7e5c430..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/InputTest.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; - -class InputTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); - $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); - } - - public function testOptions() - { - $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); - $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); - - $input->setOption('name', 'bar'); - $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); - $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); - - $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); - $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); - $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "foo" option does not exist. - */ - public function testSetInvalidOption() - { - $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); - $input->setOption('foo', 'bar'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "foo" option does not exist. - */ - public function testGetInvalidOption() - { - $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); - $input->getOption('foo'); - } - - public function testArguments() - { - $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); - $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); - - $input->setArgument('name', 'bar'); - $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); - $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); - - $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); - $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); - $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "foo" argument does not exist. - */ - public function testSetInvalidArgument() - { - $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); - $input->setArgument('foo', 'bar'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The "foo" argument does not exist. - */ - public function testGetInvalidArgument() - { - $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); - $input->getArgument('foo'); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Not enough arguments (missing: "name"). - */ - public function testValidateWithMissingArguments() - { - $input = new ArrayInput(array()); - $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); - $input->validate(); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Not enough arguments (missing: "name"). - */ - public function testValidateWithMissingRequiredArguments() - { - $input = new ArrayInput(array('bar' => 'baz')); - $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED), new InputArgument('bar', InputArgument::OPTIONAL)))); - $input->validate(); - } - - public function testValidate() - { - $input = new ArrayInput(array('name' => 'foo')); - $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); - - $this->assertNull($input->validate()); - } - - public function testSetGetInteractive() - { - $input = new ArrayInput(array()); - $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); - $input->setInteractive(false); - $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); - } - - public function testSetGetStream() - { - $input = new ArrayInput(array()); - $stream = fopen('php://memory', 'r+', false); - $input->setStream($stream); - $this->assertSame($stream, $input->getStream()); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Input/StringInputTest.php b/tests/integration/vendor/symfony/console/Tests/Input/StringInputTest.php deleted file mode 100644 index 7059cf05d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Input/StringInputTest.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Input; - -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\StringInput; - -class StringInputTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getTokenizeData - */ - public function testTokenize($input, $tokens, $message) - { - $input = new StringInput($input); - $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); - $p = $r->getProperty('tokens'); - $p->setAccessible(true); - $this->assertEquals($tokens, $p->getValue($input), $message); - } - - public function testInputOptionWithGivenString() - { - $definition = new InputDefinition( - array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) - ); - - // call to bind - $input = new StringInput('--foo=bar'); - $input->bind($definition); - $this->assertEquals('bar', $input->getOption('foo')); - } - - public function getTokenizeData() - { - return array( - array('', array(), '->tokenize() parses an empty string'), - array('foo', array('foo'), '->tokenize() parses arguments'), - array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), - array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), - array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), - array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'), - array("'a'\r'b'\n'c'\t'd'", array('a', 'b', 'c', 'd'), '->tokenize() parses whitespace chars between args as spaces'), - array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), - array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), - array('-a', array('-a'), '->tokenize() parses short options'), - array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), - array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), - array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), - array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), - array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), - array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), - array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), - array('--long-option', array('--long-option'), '->tokenize() parses long options'), - array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), - array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), - array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), - array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), - array("--long-option='foo bar''another'", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), - array("--long-option='foo bar'\"another\"", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), - array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), - ); - } - - public function testToString() - { - $input = new StringInput('-f foo'); - $this->assertEquals('-f foo', (string) $input); - - $input = new StringInput('-f --bar=foo "a b c d"'); - $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); - - $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); - $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php b/tests/integration/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php deleted file mode 100644 index 43fdb627c..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Logger; - -use Psr\Log\Test\LoggerInterfaceTest; -use Psr\Log\LogLevel; -use Symfony\Component\Console\Logger\ConsoleLogger; -use Symfony\Component\Console\Output\BufferedOutput; -use Symfony\Component\Console\Tests\Fixtures\DummyOutput; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Console logger test. - * - * @author Kévin Dunglas - */ -class ConsoleLoggerTest extends LoggerInterfaceTest -{ - /** - * @var DummyOutput - */ - protected $output; - - /** - * {@inheritdoc} - */ - public function getLogger() - { - $this->output = new DummyOutput(OutputInterface::VERBOSITY_VERBOSE); - - return new ConsoleLogger($this->output, array( - LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, - LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, - LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, - LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, - LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, - LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, - LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, - LogLevel::DEBUG => OutputInterface::VERBOSITY_NORMAL, - )); - } - - /** - * {@inheritdoc} - */ - public function getLogs() - { - return $this->output->getLogs(); - } - - /** - * @dataProvider provideOutputMappingParams - */ - public function testOutputMapping($logLevel, $outputVerbosity, $isOutput, $addVerbosityLevelMap = array()) - { - $out = new BufferedOutput($outputVerbosity); - $logger = new ConsoleLogger($out, $addVerbosityLevelMap); - $logger->log($logLevel, 'foo bar'); - $logs = $out->fetch(); - $this->assertEquals($isOutput ? "[$logLevel] foo bar\n" : '', $logs); - } - - public function provideOutputMappingParams() - { - $quietMap = array(LogLevel::EMERGENCY => OutputInterface::VERBOSITY_QUIET); - - return array( - array(LogLevel::EMERGENCY, OutputInterface::VERBOSITY_NORMAL, true), - array(LogLevel::WARNING, OutputInterface::VERBOSITY_NORMAL, true), - array(LogLevel::INFO, OutputInterface::VERBOSITY_NORMAL, false), - array(LogLevel::DEBUG, OutputInterface::VERBOSITY_NORMAL, false), - array(LogLevel::INFO, OutputInterface::VERBOSITY_VERBOSE, false), - array(LogLevel::INFO, OutputInterface::VERBOSITY_VERY_VERBOSE, true), - array(LogLevel::DEBUG, OutputInterface::VERBOSITY_VERY_VERBOSE, false), - array(LogLevel::DEBUG, OutputInterface::VERBOSITY_DEBUG, true), - array(LogLevel::ALERT, OutputInterface::VERBOSITY_QUIET, false), - array(LogLevel::EMERGENCY, OutputInterface::VERBOSITY_QUIET, false), - array(LogLevel::ALERT, OutputInterface::VERBOSITY_QUIET, false, $quietMap), - array(LogLevel::EMERGENCY, OutputInterface::VERBOSITY_QUIET, true, $quietMap), - ); - } - - public function testHasErrored() - { - $logger = new ConsoleLogger(new BufferedOutput()); - - $this->assertFalse($logger->hasErrored()); - - $logger->warning('foo'); - $this->assertFalse($logger->hasErrored()); - - $logger->error('bar'); - $this->assertTrue($logger->hasErrored()); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php b/tests/integration/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php deleted file mode 100644 index 1afbbb6e6..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Output; - -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Console\Output\Output; - -class ConsoleOutputTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); - $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); - $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Output/NullOutputTest.php b/tests/integration/vendor/symfony/console/Tests/Output/NullOutputTest.php deleted file mode 100644 index b20ae4e8d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Output/NullOutputTest.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Output; - -use Symfony\Component\Console\Output\NullOutput; -use Symfony\Component\Console\Output\OutputInterface; - -class NullOutputTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $output = new NullOutput(); - - ob_start(); - $output->write('foo'); - $buffer = ob_get_clean(); - - $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)'); - $this->assertFalse($output->isDecorated(), '->isDecorated() returns false'); - } - - public function testVerbosity() - { - $output = new NullOutput(); - $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); - - $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); - $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Output/OutputTest.php b/tests/integration/vendor/symfony/console/Tests/Output/OutputTest.php deleted file mode 100644 index 45e6ddc7d..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Output/OutputTest.php +++ /dev/null @@ -1,175 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Output; - -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; - -class OutputTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $output = new TestOutput(Output::VERBOSITY_QUIET, true); - $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); - $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); - } - - public function testSetIsDecorated() - { - $output = new TestOutput(); - $output->setDecorated(true); - $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); - } - - public function testSetGetVerbosity() - { - $output = new TestOutput(); - $output->setVerbosity(Output::VERBOSITY_QUIET); - $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); - - $this->assertTrue($output->isQuiet()); - $this->assertFalse($output->isVerbose()); - $this->assertFalse($output->isVeryVerbose()); - $this->assertFalse($output->isDebug()); - - $output->setVerbosity(Output::VERBOSITY_NORMAL); - $this->assertFalse($output->isQuiet()); - $this->assertFalse($output->isVerbose()); - $this->assertFalse($output->isVeryVerbose()); - $this->assertFalse($output->isDebug()); - - $output->setVerbosity(Output::VERBOSITY_VERBOSE); - $this->assertFalse($output->isQuiet()); - $this->assertTrue($output->isVerbose()); - $this->assertFalse($output->isVeryVerbose()); - $this->assertFalse($output->isDebug()); - - $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); - $this->assertFalse($output->isQuiet()); - $this->assertTrue($output->isVerbose()); - $this->assertTrue($output->isVeryVerbose()); - $this->assertFalse($output->isDebug()); - - $output->setVerbosity(Output::VERBOSITY_DEBUG); - $this->assertFalse($output->isQuiet()); - $this->assertTrue($output->isVerbose()); - $this->assertTrue($output->isVeryVerbose()); - $this->assertTrue($output->isDebug()); - } - - public function testWriteWithVerbosityQuiet() - { - $output = new TestOutput(Output::VERBOSITY_QUIET); - $output->writeln('foo'); - $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); - } - - public function testWriteAnArrayOfMessages() - { - $output = new TestOutput(); - $output->writeln(array('foo', 'bar')); - $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); - } - - /** - * @dataProvider provideWriteArguments - */ - public function testWriteRawMessage($message, $type, $expectedOutput) - { - $output = new TestOutput(); - $output->writeln($message, $type); - $this->assertEquals($expectedOutput, $output->output); - } - - public function provideWriteArguments() - { - return array( - array('foo', Output::OUTPUT_RAW, "foo\n"), - array('foo', Output::OUTPUT_PLAIN, "foo\n"), - ); - } - - public function testWriteWithDecorationTurnedOff() - { - $output = new TestOutput(); - $output->setDecorated(false); - $output->writeln('foo'); - $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); - } - - public function testWriteDecoratedMessage() - { - $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); - $output = new TestOutput(); - $output->getFormatter()->setStyle('FOO', $fooStyle); - $output->setDecorated(true); - $output->writeln('foo'); - $this->assertEquals("\033[33;41;5mfoo\033[39;49;25m\n", $output->output, '->writeln() decorates the output'); - } - - public function testWriteWithInvalidStyle() - { - $output = new TestOutput(); - - $output->clear(); - $output->write('foo'); - $this->assertEquals('foo', $output->output, '->write() do nothing when a style does not exist'); - - $output->clear(); - $output->writeln('foo'); - $this->assertEquals("foo\n", $output->output, '->writeln() do nothing when a style does not exist'); - } - - /** - * @dataProvider verbosityProvider - */ - public function testWriteWithVerbosityOption($verbosity, $expected, $msg) - { - $output = new TestOutput(); - - $output->setVerbosity($verbosity); - $output->clear(); - $output->write('1', false); - $output->write('2', false, Output::VERBOSITY_QUIET); - $output->write('3', false, Output::VERBOSITY_NORMAL); - $output->write('4', false, Output::VERBOSITY_VERBOSE); - $output->write('5', false, Output::VERBOSITY_VERY_VERBOSE); - $output->write('6', false, Output::VERBOSITY_DEBUG); - $this->assertEquals($expected, $output->output, $msg); - } - - public function verbosityProvider() - { - return array( - array(Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'), - array(Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'), - array(Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'), - array(Output::VERBOSITY_VERY_VERBOSE, '12345', '->write() in VERY_VERBOSE mode outputs anything below an explicit DEBUG verbosity'), - array(Output::VERBOSITY_DEBUG, '123456', '->write() in DEBUG mode outputs everything'), - ); - } -} - -class TestOutput extends Output -{ - public $output = ''; - - public function clear() - { - $this->output = ''; - } - - protected function doWrite($message, $newline) - { - $this->output .= $message.($newline ? "\n" : ''); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Output/StreamOutputTest.php b/tests/integration/vendor/symfony/console/Tests/Output/StreamOutputTest.php deleted file mode 100644 index 2fd4f6121..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Output/StreamOutputTest.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Output; - -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Output\StreamOutput; - -class StreamOutputTest extends \PHPUnit_Framework_TestCase -{ - protected $stream; - - protected function setUp() - { - $this->stream = fopen('php://memory', 'a', false); - } - - protected function tearDown() - { - $this->stream = null; - } - - public function testConstructor() - { - $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); - $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); - $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument. - */ - public function testStreamIsRequired() - { - new StreamOutput('foo'); - } - - public function testGetStream() - { - $output = new StreamOutput($this->stream); - $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); - } - - public function testDoWrite() - { - $output = new StreamOutput($this->stream); - $output->writeln('foo'); - rewind($output->getStream()); - $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php b/tests/integration/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php deleted file mode 100644 index 3338f4b05..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Style/SymfonyStyleTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Style; - -use PHPUnit_Framework_TestCase; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Tester\CommandTester; - -class SymfonyStyleTest extends PHPUnit_Framework_TestCase -{ - /** @var Command */ - protected $command; - /** @var CommandTester */ - protected $tester; - - protected function setUp() - { - putenv('COLUMNS=121'); - $this->command = new Command('sfstyle'); - $this->tester = new CommandTester($this->command); - } - - protected function tearDown() - { - $this->command = null; - $this->tester = null; - } - - /** - * @dataProvider inputCommandToOutputFilesProvider - */ - public function testOutputs($inputCommandFilepath, $outputFilepath) - { - $code = require $inputCommandFilepath; - $this->command->setCode($code); - $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); - $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); - } - - /** - * @dataProvider inputInteractiveCommandToOutputFilesProvider - */ - public function testInteractiveOutputs($inputCommandFilepath, $outputFilepath) - { - $code = require $inputCommandFilepath; - $this->command->setCode($code); - $this->tester->execute(array(), array('interactive' => true, 'decorated' => false)); - $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); - } - - public function inputInteractiveCommandToOutputFilesProvider() - { - $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; - - return array_map(null, glob($baseDir.'/command/interactive_command_*.php'), glob($baseDir.'/output/interactive_output_*.txt')); - } - - public function inputCommandToOutputFilesProvider() - { - $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; - - return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt')); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/TerminalTest.php b/tests/integration/vendor/symfony/console/Tests/TerminalTest.php deleted file mode 100644 index f13102244..000000000 --- a/tests/integration/vendor/symfony/console/Tests/TerminalTest.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests; - -use Symfony\Component\Console\Terminal; - -class TerminalTest extends \PHPUnit_Framework_TestCase -{ - public function test() - { - putenv('COLUMNS=100'); - putenv('LINES=50'); - $terminal = new Terminal(); - $this->assertSame(100, $terminal->getWidth()); - $this->assertSame(50, $terminal->getHeight()); - - putenv('COLUMNS=120'); - putenv('LINES=60'); - $terminal = new Terminal(); - $this->assertSame(120, $terminal->getWidth()); - $this->assertSame(60, $terminal->getHeight()); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php b/tests/integration/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php deleted file mode 100644 index a8389dd18..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Tester/ApplicationTesterTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Tester; - -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Tester\ApplicationTester; - -class ApplicationTesterTest extends \PHPUnit_Framework_TestCase -{ - protected $application; - protected $tester; - - protected function setUp() - { - $this->application = new Application(); - $this->application->setAutoExit(false); - $this->application->register('foo') - ->addArgument('foo') - ->setCode(function ($input, $output) { $output->writeln('foo'); }) - ; - - $this->tester = new ApplicationTester($this->application); - $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - } - - protected function tearDown() - { - $this->application = null; - $this->tester = null; - } - - public function testRun() - { - $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); - $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); - $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); - } - - public function testGetInput() - { - $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); - } - - public function testGetOutput() - { - rewind($this->tester->getOutput()->getStream()); - $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); - } - - public function testGetDisplay() - { - $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); - } - - public function testGetStatusCode() - { - $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); - } -} diff --git a/tests/integration/vendor/symfony/console/Tests/Tester/CommandTesterTest.php b/tests/integration/vendor/symfony/console/Tests/Tester/CommandTesterTest.php deleted file mode 100644 index c605729b8..000000000 --- a/tests/integration/vendor/symfony/console/Tests/Tester/CommandTesterTest.php +++ /dev/null @@ -1,162 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Console\Tests\Tester; - -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Console\Question\Question; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Helper\QuestionHelper; -use Symfony\Component\Console\Style\SymfonyStyle; - -class CommandTesterTest extends \PHPUnit_Framework_TestCase -{ - protected $command; - protected $tester; - - protected function setUp() - { - $this->command = new Command('foo'); - $this->command->addArgument('command'); - $this->command->addArgument('foo'); - $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); - - $this->tester = new CommandTester($this->command); - $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); - } - - protected function tearDown() - { - $this->command = null; - $this->tester = null; - } - - public function testExecute() - { - $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); - $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); - $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); - } - - public function testGetInput() - { - $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); - } - - public function testGetOutput() - { - rewind($this->tester->getOutput()->getStream()); - $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); - } - - public function testGetDisplay() - { - $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); - } - - public function testGetStatusCode() - { - $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); - } - - public function testCommandFromApplication() - { - $application = new Application(); - $application->setAutoExit(false); - - $command = new Command('foo'); - $command->setCode(function ($input, $output) { $output->writeln('foo'); }); - - $application->add($command); - - $tester = new CommandTester($application->find('foo')); - - // check that there is no need to pass the command name here - $this->assertEquals(0, $tester->execute(array())); - } - - public function testCommandWithInputs() - { - $questions = array( - 'What\'s your name?', - 'How are you?', - 'Where do you come from?', - ); - - $command = new Command('foo'); - $command->setHelperSet(new HelperSet(array(new QuestionHelper()))); - $command->setCode(function ($input, $output) use ($questions, $command) { - $helper = $command->getHelper('question'); - $helper->ask($input, $output, new Question($questions[0])); - $helper->ask($input, $output, new Question($questions[1])); - $helper->ask($input, $output, new Question($questions[2])); - }); - - $tester = new CommandTester($command); - $tester->setInputs(array('Bobby', 'Fine', 'France')); - $tester->execute(array()); - - $this->assertEquals(0, $tester->getStatusCode()); - $this->assertEquals(implode('', $questions), $tester->getDisplay(true)); - } - - /** - * @expectedException \RuntimeException - * @expectedMessage Aborted - */ - public function testCommandWithWrongInputsNumber() - { - $questions = array( - 'What\'s your name?', - 'How are you?', - 'Where do you come from?', - ); - - $command = new Command('foo'); - $command->setHelperSet(new HelperSet(array(new QuestionHelper()))); - $command->setCode(function ($input, $output) use ($questions, $command) { - $helper = $command->getHelper('question'); - $helper->ask($input, $output, new Question($questions[0])); - $helper->ask($input, $output, new Question($questions[1])); - $helper->ask($input, $output, new Question($questions[2])); - }); - - $tester = new CommandTester($command); - $tester->setInputs(array('Bobby', 'Fine')); - $tester->execute(array()); - } - - public function testSymfonyStyleCommandWithInputs() - { - $questions = array( - 'What\'s your name?', - 'How are you?', - 'Where do you come from?', - ); - - $command = new Command('foo'); - $command->setCode(function ($input, $output) use ($questions, $command) { - $io = new SymfonyStyle($input, $output); - $io->ask($questions[0]); - $io->ask($questions[1]); - $io->ask($questions[2]); - }); - - $tester = new CommandTester($command); - $tester->setInputs(array('Bobby', 'Fine', 'France')); - $tester->execute(array()); - - $this->assertEquals(0, $tester->getStatusCode()); - } -} diff --git a/tests/integration/vendor/symfony/console/composer.json b/tests/integration/vendor/symfony/console/composer.json index fab17b8ab..9a565068c 100644 --- a/tests/integration/vendor/symfony/console/composer.json +++ b/tests/integration/vendor/symfony/console/composer.json @@ -1,8 +1,8 @@ { "name": "symfony/console", "type": "library", - "description": "Symfony Console Component", - "keywords": [], + "description": "Eases the creation of beautiful and testable command line interfaces", + "keywords": ["console", "cli", "command line", "terminal"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ @@ -16,32 +16,45 @@ } ], "require": { - "php": ">=5.5.9", + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/debug": "~2.8|~3.0" + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" }, "require-dev": { - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/filesystem": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0", - "psr/log": "~1.0" + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0", + "psr/log": "^1|^2" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" }, "suggest": { "symfony/event-dispatcher": "", - "symfony/filesystem": "", + "symfony/lock": "", "symfony/process": "", "psr/log": "For using the console logger" }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } + "minimum-stability": "dev" } diff --git a/tests/integration/vendor/symfony/console/phpunit.xml.dist b/tests/integration/vendor/symfony/console/phpunit.xml.dist deleted file mode 100644 index 8c09554f8..000000000 --- a/tests/integration/vendor/symfony/console/phpunit.xml.dist +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - - - - - - Symfony\Component\Console - - - - - diff --git a/tests/integration/vendor/symfony/debug/BufferingLogger.php b/tests/integration/vendor/symfony/debug/BufferingLogger.php deleted file mode 100644 index a2ed75b9d..000000000 --- a/tests/integration/vendor/symfony/debug/BufferingLogger.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use Psr\Log\AbstractLogger; - -/** - * A buffering logger that stacks logs for later. - * - * @author Nicolas Grekas - */ -class BufferingLogger extends AbstractLogger -{ - private $logs = array(); - - public function log($level, $message, array $context = array()) - { - $this->logs[] = array($level, $message, $context); - } - - public function cleanLogs() - { - $logs = $this->logs; - $this->logs = array(); - - return $logs; - } -} diff --git a/tests/integration/vendor/symfony/debug/CHANGELOG.md b/tests/integration/vendor/symfony/debug/CHANGELOG.md deleted file mode 100644 index 70f7802a4..000000000 --- a/tests/integration/vendor/symfony/debug/CHANGELOG.md +++ /dev/null @@ -1,54 +0,0 @@ -CHANGELOG -========= - -3.2.0 ------ - -* `FlattenException::getTrace()` now returns additional type descriptions - `integer` and `float`. - - -3.0.0 ------ - -* removed classes, methods and interfaces deprecated in 2.x - -2.8.0 ------ - -* added BufferingLogger for errors that happen before a proper logger is configured -* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);` -* deprecate ExceptionHandler::createResponse - -2.7.0 ------ - -* added deprecations checking for parent interfaces/classes to DebugClassLoader -* added ZTS support to symfony_debug extension -* added symfony_debug_backtrace() to symfony_debug extension - to track the backtrace of fatal errors - -2.6.0 ------ - -* generalized ErrorHandler and ExceptionHandler, - with some new methods and others deprecated -* enhanced error messages for uncaught exceptions - -2.5.0 ------ - -* added ExceptionHandler::setHandler() -* added UndefinedMethodFatalErrorHandler -* deprecated DummyException - -2.4.0 ------ - - * added a DebugClassLoader able to wrap any autoloader providing a findFile method - * improved error messages for not found classes and functions - -2.3.0 ------ - - * added the component diff --git a/tests/integration/vendor/symfony/debug/Debug.php b/tests/integration/vendor/symfony/debug/Debug.php deleted file mode 100644 index e3665ae5f..000000000 --- a/tests/integration/vendor/symfony/debug/Debug.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -/** - * Registers all the debug tools. - * - * @author Fabien Potencier - */ -class Debug -{ - private static $enabled = false; - - /** - * Enables the debug tools. - * - * This method registers an error handler and an exception handler. - * - * If the Symfony ClassLoader component is available, a special - * class loader is also registered. - * - * @param int $errorReportingLevel The level of error reporting you want - * @param bool $displayErrors Whether to display errors (for development) or just log them (for production) - */ - public static function enable($errorReportingLevel = E_ALL, $displayErrors = true) - { - if (static::$enabled) { - return; - } - - static::$enabled = true; - - if (null !== $errorReportingLevel) { - error_reporting($errorReportingLevel); - } else { - error_reporting(E_ALL); - } - - if ('cli' !== PHP_SAPI) { - ini_set('display_errors', 0); - ExceptionHandler::register(); - } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { - // CLI - display errors only if they're not already logged to STDERR - ini_set('display_errors', 1); - } - if ($displayErrors) { - ErrorHandler::register(new ErrorHandler(new BufferingLogger())); - } else { - ErrorHandler::register()->throwAt(0, true); - } - - DebugClassLoader::enable(); - } -} diff --git a/tests/integration/vendor/symfony/debug/DebugClassLoader.php b/tests/integration/vendor/symfony/debug/DebugClassLoader.php deleted file mode 100644 index 9fd688718..000000000 --- a/tests/integration/vendor/symfony/debug/DebugClassLoader.php +++ /dev/null @@ -1,314 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -/** - * Autoloader checking if the class is really defined in the file found. - * - * The ClassLoader will wrap all registered autoloaders - * and will throw an exception if a file is found but does - * not declare the class. - * - * @author Fabien Potencier - * @author Christophe Coevoet - * @author Nicolas Grekas - */ -class DebugClassLoader -{ - private $classLoader; - private $isFinder; - private static $caseCheck; - private static $deprecated = array(); - private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null'); - private static $darwinCache = array('/' => array('/', array())); - - /** - * Constructor. - * - * @param callable $classLoader A class loader - */ - public function __construct(callable $classLoader) - { - $this->classLoader = $classLoader; - $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); - - if (!isset(self::$caseCheck)) { - $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR); - $i = strrpos($file, DIRECTORY_SEPARATOR); - $dir = substr($file, 0, 1 + $i); - $file = substr($file, 1 + $i); - $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); - $test = realpath($dir.$test); - - if (false === $test || false === $i) { - // filesystem is case sensitive - self::$caseCheck = 0; - } elseif (substr($test, -strlen($file)) === $file) { - // filesystem is case insensitive and realpath() normalizes the case of characters - self::$caseCheck = 1; - } elseif (false !== stripos(PHP_OS, 'darwin')) { - // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters - self::$caseCheck = 2; - } else { - // filesystem case checks failed, fallback to disabling them - self::$caseCheck = 0; - } - } - } - - /** - * Gets the wrapped class loader. - * - * @return callable The wrapped class loader - */ - public function getClassLoader() - { - return $this->classLoader; - } - - /** - * Wraps all autoloaders. - */ - public static function enable() - { - // Ensures we don't hit https://bugs.php.net/42098 - class_exists('Symfony\Component\Debug\ErrorHandler'); - class_exists('Psr\Log\LogLevel'); - - if (!is_array($functions = spl_autoload_functions())) { - return; - } - - foreach ($functions as $function) { - spl_autoload_unregister($function); - } - - foreach ($functions as $function) { - if (!is_array($function) || !$function[0] instanceof self) { - $function = array(new static($function), 'loadClass'); - } - - spl_autoload_register($function); - } - } - - /** - * Disables the wrapping. - */ - public static function disable() - { - if (!is_array($functions = spl_autoload_functions())) { - return; - } - - foreach ($functions as $function) { - spl_autoload_unregister($function); - } - - foreach ($functions as $function) { - if (is_array($function) && $function[0] instanceof self) { - $function = $function[0]->getClassLoader(); - } - - spl_autoload_register($function); - } - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * - * @return bool|null True, if loaded - * - * @throws \RuntimeException - */ - public function loadClass($class) - { - ErrorHandler::stackErrors(); - - try { - if ($this->isFinder) { - if ($file = $this->classLoader[0]->findFile($class)) { - require_once $file; - } - } else { - call_user_func($this->classLoader, $class); - $file = false; - } - } finally { - ErrorHandler::unstackErrors(); - } - - $exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); - - if ('\\' === $class[0]) { - $class = substr($class, 1); - } - - if ($exists) { - $refl = new \ReflectionClass($class); - $name = $refl->getName(); - - if ($name !== $class && 0 === strcasecmp($name, $class)) { - throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name)); - } - - if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) { - @trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); - } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) { - self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]); - } else { - if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) { - $len = 0; - $ns = ''; - } else { - switch ($ns = substr($name, 0, $len)) { - case 'Symfony\Bridge\\': - case 'Symfony\Bundle\\': - case 'Symfony\Component\\': - $ns = 'Symfony\\'; - $len = strlen($ns); - break; - } - } - $parent = get_parent_class($class); - - if (!$parent || strncmp($ns, $parent, $len)) { - if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) { - @trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED); - } - - $parentInterfaces = array(); - $deprecatedInterfaces = array(); - if ($parent) { - foreach (class_implements($parent) as $interface) { - $parentInterfaces[$interface] = 1; - } - } - - foreach ($refl->getInterfaceNames() as $interface) { - if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) { - $deprecatedInterfaces[] = $interface; - } - foreach (class_implements($interface) as $interface) { - $parentInterfaces[$interface] = 1; - } - } - - foreach ($deprecatedInterfaces as $interface) { - if (!isset($parentInterfaces[$interface])) { - @trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED); - } - } - } - } - } - - if ($file) { - if (!$exists) { - if (false !== strpos($class, '/')) { - throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); - } - - throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); - } - if (self::$caseCheck) { - $real = explode('\\', $class.strrchr($file, '.')); - $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file)); - - $i = count($tail) - 1; - $j = count($real) - 1; - - while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { - --$i; - --$j; - } - - array_splice($tail, 0, $i + 1); - } - if (self::$caseCheck && $tail) { - $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail); - $tailLen = strlen($tail); - $real = $refl->getFileName(); - - if (2 === self::$caseCheck) { - // realpath() on MacOSX doesn't normalize the case of characters - - $i = 1 + strrpos($real, '/'); - $file = substr($real, $i); - $real = substr($real, 0, $i); - - if (isset(self::$darwinCache[$real])) { - $kDir = $real; - } else { - $kDir = strtolower($real); - - if (isset(self::$darwinCache[$kDir])) { - $real = self::$darwinCache[$kDir][0]; - } else { - $dir = getcwd(); - chdir($real); - $real = getcwd().'/'; - chdir($dir); - - $dir = $real; - $k = $kDir; - $i = strlen($dir) - 1; - while (!isset(self::$darwinCache[$k])) { - self::$darwinCache[$k] = array($dir, array()); - self::$darwinCache[$dir] = &self::$darwinCache[$k]; - - while ('/' !== $dir[--$i]) { - } - $k = substr($k, 0, ++$i); - $dir = substr($dir, 0, $i--); - } - } - } - - $dirFiles = self::$darwinCache[$kDir][1]; - - if (isset($dirFiles[$file])) { - $kFile = $file; - } else { - $kFile = strtolower($file); - - if (!isset($dirFiles[$kFile])) { - foreach (scandir($real, 2) as $f) { - if ('.' !== $f[0]) { - $dirFiles[$f] = $f; - if ($f === $file) { - $kFile = $k = $file; - } elseif ($f !== $k = strtolower($f)) { - $dirFiles[$k] = $f; - } - } - } - self::$darwinCache[$kDir][1] = $dirFiles; - } - } - - $real .= $dirFiles[$kFile]; - } - - if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) - && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) - ) { - throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1))); - } - } - - return true; - } - } -} diff --git a/tests/integration/vendor/symfony/debug/ErrorHandler.php b/tests/integration/vendor/symfony/debug/ErrorHandler.php deleted file mode 100644 index 5728b5d01..000000000 --- a/tests/integration/vendor/symfony/debug/ErrorHandler.php +++ /dev/null @@ -1,670 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use Psr\Log\LogLevel; -use Psr\Log\LoggerInterface; -use Symfony\Component\Debug\Exception\ContextErrorException; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\Exception\FatalThrowableError; -use Symfony\Component\Debug\Exception\OutOfMemoryException; -use Symfony\Component\Debug\Exception\SilencedErrorContext; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; -use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; -use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; - -/** - * A generic ErrorHandler for the PHP engine. - * - * Provides five bit fields that control how errors are handled: - * - thrownErrors: errors thrown as \ErrorException - * - loggedErrors: logged errors, when not @-silenced - * - scopedErrors: errors thrown or logged with their local context - * - tracedErrors: errors logged with their stack trace - * - screamedErrors: never @-silenced errors - * - * Each error level can be logged by a dedicated PSR-3 logger object. - * Screaming only applies to logging. - * Throwing takes precedence over logging. - * Uncaught exceptions are logged as E_ERROR. - * E_DEPRECATED and E_USER_DEPRECATED levels never throw. - * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. - * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. - * As errors have a performance cost, repeated errors are all logged, so that the developer - * can see them and weight them as more important to fix than others of the same level. - * - * @author Nicolas Grekas - * @author Grégoire Pineau - */ -class ErrorHandler -{ - private $levels = array( - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User Deprecated', - E_NOTICE => 'Notice', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Runtime Notice', - E_WARNING => 'Warning', - E_USER_WARNING => 'User Warning', - E_COMPILE_WARNING => 'Compile Warning', - E_CORE_WARNING => 'Core Warning', - E_USER_ERROR => 'User Error', - E_RECOVERABLE_ERROR => 'Catchable Fatal Error', - E_COMPILE_ERROR => 'Compile Error', - E_PARSE => 'Parse Error', - E_ERROR => 'Error', - E_CORE_ERROR => 'Core Error', - ); - - private $loggers = array( - E_DEPRECATED => array(null, LogLevel::INFO), - E_USER_DEPRECATED => array(null, LogLevel::INFO), - E_NOTICE => array(null, LogLevel::WARNING), - E_USER_NOTICE => array(null, LogLevel::WARNING), - E_STRICT => array(null, LogLevel::WARNING), - E_WARNING => array(null, LogLevel::WARNING), - E_USER_WARNING => array(null, LogLevel::WARNING), - E_COMPILE_WARNING => array(null, LogLevel::WARNING), - E_CORE_WARNING => array(null, LogLevel::WARNING), - E_USER_ERROR => array(null, LogLevel::CRITICAL), - E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), - E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), - E_PARSE => array(null, LogLevel::CRITICAL), - E_ERROR => array(null, LogLevel::CRITICAL), - E_CORE_ERROR => array(null, LogLevel::CRITICAL), - ); - - private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED - private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED - private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE - private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE - private $loggedErrors = 0; - private $traceReflector; - - private $isRecursive = 0; - private $isRoot = false; - private $exceptionHandler; - private $bootstrappingLogger; - - private static $reservedMemory; - private static $stackedErrors = array(); - private static $stackedErrorLevels = array(); - private static $toStringException = null; - - /** - * Registers the error handler. - * - * @param self|null $handler The handler to register - * @param bool $replace Whether to replace or not any existing handler - * - * @return self The registered error handler - */ - public static function register(self $handler = null, $replace = true) - { - if (null === self::$reservedMemory) { - self::$reservedMemory = str_repeat('x', 10240); - register_shutdown_function(__CLASS__.'::handleFatalError'); - } - - if ($handlerIsNew = null === $handler) { - $handler = new static(); - } - - if (null === $prev = set_error_handler(array($handler, 'handleError'))) { - restore_error_handler(); - // Specifying the error types earlier would expose us to https://bugs.php.net/63206 - set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors); - $handler->isRoot = true; - } - - if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) { - $handler = $prev[0]; - $replace = false; - } - if ($replace || !$prev) { - $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); - } else { - restore_error_handler(); - } - - $handler->throwAt(E_ALL & $handler->thrownErrors, true); - - return $handler; - } - - public function __construct(BufferingLogger $bootstrappingLogger = null) - { - if ($bootstrappingLogger) { - $this->bootstrappingLogger = $bootstrappingLogger; - $this->setDefaultLogger($bootstrappingLogger); - } - $this->traceReflector = new \ReflectionProperty('Exception', 'trace'); - $this->traceReflector->setAccessible(true); - } - - /** - * Sets a logger to non assigned errors levels. - * - * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels - * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants - * @param bool $replace Whether to replace or not any existing logger - */ - public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false) - { - $loggers = array(); - - if (is_array($levels)) { - foreach ($levels as $type => $logLevel) { - if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { - $loggers[$type] = array($logger, $logLevel); - } - } - } else { - if (null === $levels) { - $levels = E_ALL; - } - foreach ($this->loggers as $type => $log) { - if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { - $log[0] = $logger; - $loggers[$type] = $log; - } - } - } - - $this->setLoggers($loggers); - } - - /** - * Sets a logger for each error level. - * - * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map - * - * @return array The previous map - * - * @throws \InvalidArgumentException - */ - public function setLoggers(array $loggers) - { - $prevLogged = $this->loggedErrors; - $prev = $this->loggers; - $flush = array(); - - foreach ($loggers as $type => $log) { - if (!isset($prev[$type])) { - throw new \InvalidArgumentException('Unknown error type: '.$type); - } - if (!is_array($log)) { - $log = array($log); - } elseif (!array_key_exists(0, $log)) { - throw new \InvalidArgumentException('No logger provided'); - } - if (null === $log[0]) { - $this->loggedErrors &= ~$type; - } elseif ($log[0] instanceof LoggerInterface) { - $this->loggedErrors |= $type; - } else { - throw new \InvalidArgumentException('Invalid logger provided'); - } - $this->loggers[$type] = $log + $prev[$type]; - - if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { - $flush[$type] = $type; - } - } - $this->reRegister($prevLogged | $this->thrownErrors); - - if ($flush) { - foreach ($this->bootstrappingLogger->cleanLogs() as $log) { - $type = $log[2]['exception']->getSeverity(); - if (!isset($flush[$type])) { - $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); - } elseif ($this->loggers[$type][0]) { - $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); - } - } - } - - return $prev; - } - - /** - * Sets a user exception handler. - * - * @param callable $handler A handler that will be called on Exception - * - * @return callable|null The previous exception handler - */ - public function setExceptionHandler(callable $handler = null) - { - $prev = $this->exceptionHandler; - $this->exceptionHandler = $handler; - - return $prev; - } - - /** - * Sets the PHP error levels that throw an exception when a PHP error occurs. - * - * @param int $levels A bit field of E_* constants for thrown errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function throwAt($levels, $replace = false) - { - $prev = $this->thrownErrors; - $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; - if (!$replace) { - $this->thrownErrors |= $prev; - } - $this->reRegister($prev | $this->loggedErrors); - - return $prev; - } - - /** - * Sets the PHP error levels for which local variables are preserved. - * - * @param int $levels A bit field of E_* constants for scoped errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function scopeAt($levels, $replace = false) - { - $prev = $this->scopedErrors; - $this->scopedErrors = (int) $levels; - if (!$replace) { - $this->scopedErrors |= $prev; - } - - return $prev; - } - - /** - * Sets the PHP error levels for which the stack trace is preserved. - * - * @param int $levels A bit field of E_* constants for traced errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function traceAt($levels, $replace = false) - { - $prev = $this->tracedErrors; - $this->tracedErrors = (int) $levels; - if (!$replace) { - $this->tracedErrors |= $prev; - } - - return $prev; - } - - /** - * Sets the error levels where the @-operator is ignored. - * - * @param int $levels A bit field of E_* constants for screamed errors - * @param bool $replace Replace or amend the previous value - * - * @return int The previous value - */ - public function screamAt($levels, $replace = false) - { - $prev = $this->screamedErrors; - $this->screamedErrors = (int) $levels; - if (!$replace) { - $this->screamedErrors |= $prev; - } - - return $prev; - } - - /** - * Re-registers as a PHP error handler if levels changed. - */ - private function reRegister($prev) - { - if ($prev !== $this->thrownErrors | $this->loggedErrors) { - $handler = set_error_handler('var_dump'); - $handler = is_array($handler) ? $handler[0] : null; - restore_error_handler(); - if ($handler === $this) { - restore_error_handler(); - if ($this->isRoot) { - set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors); - } else { - set_error_handler(array($this, 'handleError')); - } - } - } - } - - /** - * Handles errors by filtering then logging them according to the configured bit fields. - * - * @param int $type One of the E_* constants - * @param string $message - * @param string $file - * @param int $line - * @param array $context - * @param array $backtrace - * - * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself - * - * @throws \ErrorException When $this->thrownErrors requests so - * - * @internal - */ - public function handleError($type, $message, $file, $line, array $context, array $backtrace = null) - { - // Level is the current error reporting level to manage silent error. - // Strong errors are not authorized to be silenced. - $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; - $log = $this->loggedErrors & $type; - $throw = $this->thrownErrors & $type & $level; - $type &= $level | $this->screamedErrors; - - if (!$type || (!$log && !$throw)) { - return $type && $log; - } - - if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) { - unset($context['GLOBALS']); - } - - if (null !== $backtrace && $type & E_ERROR) { - // E_ERROR fatal errors are triggered on HHVM when - // hhvm.error_handling.call_user_handler_on_fatals=1 - // which is the way to get their backtrace. - $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); - - return true; - } - - $logMessage = $this->levels[$type].': '.$message; - - if (null !== self::$toStringException) { - $errorAsException = self::$toStringException; - self::$toStringException = null; - } elseif (!$throw && !($type & $level)) { - $errorAsException = new SilencedErrorContext($type, $file, $line); - } else { - if ($this->scopedErrors & $type) { - $errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context); - } else { - $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); - } - - // Clean the trace by removing function arguments and the first frames added by the error handler itself. - if ($throw || $this->tracedErrors & $type) { - $backtrace = $backtrace ?: $errorAsException->getTrace(); - $lightTrace = $backtrace; - - for ($i = 0; isset($backtrace[$i]); ++$i) { - if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { - $lightTrace = array_slice($lightTrace, 1 + $i); - break; - } - } - if (!($throw || $this->scopedErrors & $type)) { - for ($i = 0; isset($lightTrace[$i]); ++$i) { - unset($lightTrace[$i]['args']); - } - } - $this->traceReflector->setValue($errorAsException, $lightTrace); - } else { - $this->traceReflector->setValue($errorAsException, array()); - } - } - - if ($throw) { - if (E_USER_ERROR & $type) { - for ($i = 1; isset($backtrace[$i]); ++$i) { - if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) - && '__toString' === $backtrace[$i]['function'] - && '->' === $backtrace[$i]['type'] - && !isset($backtrace[$i - 1]['class']) - && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) - ) { - // Here, we know trigger_error() has been called from __toString(). - // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. - // A small convention allows working around the limitation: - // given a caught $e exception in __toString(), quitting the method with - // `return trigger_error($e, E_USER_ERROR);` allows this error handler - // to make $e get through the __toString() barrier. - - foreach ($context as $e) { - if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { - if (1 === $i) { - // On HHVM - $errorAsException = $e; - break; - } - self::$toStringException = $e; - - return true; - } - } - - if (1 < $i) { - // On PHP (not on HHVM), display the original error message instead of the default one. - $this->handleException($errorAsException); - - // Stop the process by giving back the error to the native handler. - return false; - } - } - } - } - - throw $errorAsException; - } - - if ($this->isRecursive) { - $log = 0; - } elseif (self::$stackedErrorLevels) { - self::$stackedErrors[] = array( - $this->loggers[$type][0], - ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, - $logMessage, - array('exception' => $errorAsException), - ); - } else { - try { - $this->isRecursive = true; - $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; - $this->loggers[$type][0]->log($level, $logMessage, array('exception' => $errorAsException)); - } finally { - $this->isRecursive = false; - } - } - - return $type && $log; - } - - /** - * Handles an exception by logging then forwarding it to another handler. - * - * @param \Exception|\Throwable $exception An exception to handle - * @param array $error An array as returned by error_get_last() - * - * @internal - */ - public function handleException($exception, array $error = null) - { - if (!$exception instanceof \Exception) { - $exception = new FatalThrowableError($exception); - } - $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; - - if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { - if ($exception instanceof FatalErrorException) { - if ($exception instanceof FatalThrowableError) { - $error = array( - 'type' => $type, - 'message' => $message = $exception->getMessage(), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), - ); - } else { - $message = 'Fatal '.$exception->getMessage(); - } - } elseif ($exception instanceof \ErrorException) { - $message = 'Uncaught '.$exception->getMessage(); - if ($exception instanceof ContextErrorException) { - $e['context'] = $exception->getContext(); - } - } else { - $message = 'Uncaught Exception: '.$exception->getMessage(); - } - } - if ($this->loggedErrors & $type) { - $this->loggers[$type][0]->log($this->loggers[$type][1], $message, array('exception' => $exception)); - } - if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { - foreach ($this->getFatalErrorHandlers() as $handler) { - if ($e = $handler->handleError($error, $exception)) { - $exception = $e; - break; - } - } - } - if (empty($this->exceptionHandler)) { - throw $exception; // Give back $exception to the native handler - } - try { - call_user_func($this->exceptionHandler, $exception); - } catch (\Exception $handlerException) { - } catch (\Throwable $handlerException) { - } - if (isset($handlerException)) { - $this->exceptionHandler = null; - $this->handleException($handlerException); - } - } - - /** - * Shutdown registered function for handling PHP fatal errors. - * - * @param array $error An array as returned by error_get_last() - * - * @internal - */ - public static function handleFatalError(array $error = null) - { - if (null === self::$reservedMemory) { - return; - } - - self::$reservedMemory = null; - - $handler = set_error_handler('var_dump'); - $handler = is_array($handler) ? $handler[0] : null; - restore_error_handler(); - - if (!$handler instanceof self) { - return; - } - - if (null === $error) { - $error = error_get_last(); - } - - try { - while (self::$stackedErrorLevels) { - static::unstackErrors(); - } - } catch (\Exception $exception) { - // Handled below - } catch (\Throwable $exception) { - // Handled below - } - - if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { - // Let's not throw anymore but keep logging - $handler->throwAt(0, true); - $trace = isset($error['backtrace']) ? $error['backtrace'] : null; - - if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { - $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); - } else { - $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); - } - } elseif (!isset($exception)) { - return; - } - - try { - $handler->handleException($exception, $error); - } catch (FatalErrorException $e) { - // Ignore this re-throw - } - } - - /** - * Configures the error handler for delayed handling. - * Ensures also that non-catchable fatal errors are never silenced. - * - * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 - * PHP has a compile stage where it behaves unusually. To workaround it, - * we plug an error handler that only stacks errors for later. - * - * The most important feature of this is to prevent - * autoloading until unstackErrors() is called. - */ - public static function stackErrors() - { - self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); - } - - /** - * Unstacks stacked errors and forwards to the logger. - */ - public static function unstackErrors() - { - $level = array_pop(self::$stackedErrorLevels); - - if (null !== $level) { - $errorReportingLevel = error_reporting($level); - if ($errorReportingLevel !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) { - // If the user changed the error level, do not overwrite it - error_reporting($errorReportingLevel); - } - } - - if (empty(self::$stackedErrorLevels)) { - $errors = self::$stackedErrors; - self::$stackedErrors = array(); - - foreach ($errors as $error) { - $error[0]->log($error[1], $error[2], $error[3]); - } - } - } - - /** - * Gets the fatal error handlers. - * - * Override this method if you want to define more fatal error handlers. - * - * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface - */ - protected function getFatalErrorHandlers() - { - return array( - new UndefinedFunctionFatalErrorHandler(), - new UndefinedMethodFatalErrorHandler(), - new ClassNotFoundFatalErrorHandler(), - ); - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/ClassNotFoundException.php b/tests/integration/vendor/symfony/debug/Exception/ClassNotFoundException.php deleted file mode 100644 index b91bf4663..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/ClassNotFoundException.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Class (or Trait or Interface) Not Found Exception. - * - * @author Konstanton Myakshin - */ -class ClassNotFoundException extends FatalErrorException -{ - public function __construct($message, \ErrorException $previous) - { - parent::__construct( - $message, - $previous->getCode(), - $previous->getSeverity(), - $previous->getFile(), - $previous->getLine(), - $previous->getPrevious() - ); - $this->setTrace($previous->getTrace()); - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/ContextErrorException.php b/tests/integration/vendor/symfony/debug/Exception/ContextErrorException.php deleted file mode 100644 index 54f0198f1..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/ContextErrorException.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Error Exception with Variable Context. - * - * @author Christian Sciberras - */ -class ContextErrorException extends \ErrorException -{ - private $context = array(); - - public function __construct($message, $code, $severity, $filename, $lineno, $context = array()) - { - parent::__construct($message, $code, $severity, $filename, $lineno); - $this->context = $context; - } - - /** - * @return array Array of variables that existed when the exception occurred - */ - public function getContext() - { - return $this->context; - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/FatalErrorException.php b/tests/integration/vendor/symfony/debug/Exception/FatalErrorException.php deleted file mode 100644 index f24a54e77..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/FatalErrorException.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Fatal Error Exception. - * - * @author Konstanton Myakshin - */ -class FatalErrorException extends \ErrorException -{ - public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null) - { - parent::__construct($message, $code, $severity, $filename, $lineno); - - if (null !== $trace) { - if (!$traceArgs) { - foreach ($trace as &$frame) { - unset($frame['args'], $frame['this'], $frame); - } - } - - $this->setTrace($trace); - } elseif (null !== $traceOffset) { - if (function_exists('xdebug_get_function_stack')) { - $trace = xdebug_get_function_stack(); - if (0 < $traceOffset) { - array_splice($trace, -$traceOffset); - } - - foreach ($trace as &$frame) { - if (!isset($frame['type'])) { - // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 - if (isset($frame['class'])) { - $frame['type'] = '::'; - } - } elseif ('dynamic' === $frame['type']) { - $frame['type'] = '->'; - } elseif ('static' === $frame['type']) { - $frame['type'] = '::'; - } - - // XDebug also has a different name for the parameters array - if (!$traceArgs) { - unset($frame['params'], $frame['args']); - } elseif (isset($frame['params']) && !isset($frame['args'])) { - $frame['args'] = $frame['params']; - unset($frame['params']); - } - } - - unset($frame); - $trace = array_reverse($trace); - } elseif (function_exists('symfony_debug_backtrace')) { - $trace = symfony_debug_backtrace(); - if (0 < $traceOffset) { - array_splice($trace, 0, $traceOffset); - } - } else { - $trace = array(); - } - - $this->setTrace($trace); - } - } - - protected function setTrace($trace) - { - $traceReflector = new \ReflectionProperty('Exception', 'trace'); - $traceReflector->setAccessible(true); - $traceReflector->setValue($this, $trace); - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/FatalThrowableError.php b/tests/integration/vendor/symfony/debug/Exception/FatalThrowableError.php deleted file mode 100644 index 34f43b17b..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/FatalThrowableError.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Fatal Throwable Error. - * - * @author Nicolas Grekas - */ -class FatalThrowableError extends FatalErrorException -{ - public function __construct(\Throwable $e) - { - if ($e instanceof \ParseError) { - $message = 'Parse error: '.$e->getMessage(); - $severity = E_PARSE; - } elseif ($e instanceof \TypeError) { - $message = 'Type error: '.$e->getMessage(); - $severity = E_RECOVERABLE_ERROR; - } else { - $message = $e->getMessage(); - $severity = E_ERROR; - } - - \ErrorException::__construct( - $message, - $e->getCode(), - $severity, - $e->getFile(), - $e->getLine() - ); - - $this->setTrace($e->getTrace()); - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/FlattenException.php b/tests/integration/vendor/symfony/debug/Exception/FlattenException.php deleted file mode 100644 index 51baeb9be..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/FlattenException.php +++ /dev/null @@ -1,260 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; - -/** - * FlattenException wraps a PHP Exception to be able to serialize it. - * - * Basically, this class removes all objects from the trace. - * - * @author Fabien Potencier - */ -class FlattenException -{ - private $message; - private $code; - private $previous; - private $trace; - private $class; - private $statusCode; - private $headers; - private $file; - private $line; - - public static function create(\Exception $exception, $statusCode = null, array $headers = array()) - { - $e = new static(); - $e->setMessage($exception->getMessage()); - $e->setCode($exception->getCode()); - - if ($exception instanceof HttpExceptionInterface) { - $statusCode = $exception->getStatusCode(); - $headers = array_merge($headers, $exception->getHeaders()); - } - - if (null === $statusCode) { - $statusCode = 500; - } - - $e->setStatusCode($statusCode); - $e->setHeaders($headers); - $e->setTraceFromException($exception); - $e->setClass(get_class($exception)); - $e->setFile($exception->getFile()); - $e->setLine($exception->getLine()); - - $previous = $exception->getPrevious(); - - if ($previous instanceof \Exception) { - $e->setPrevious(static::create($previous)); - } elseif ($previous instanceof \Throwable) { - $e->setPrevious(static::create(new FatalThrowableError($previous))); - } - - return $e; - } - - public function toArray() - { - $exceptions = array(); - foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) { - $exceptions[] = array( - 'message' => $exception->getMessage(), - 'class' => $exception->getClass(), - 'trace' => $exception->getTrace(), - ); - } - - return $exceptions; - } - - public function getStatusCode() - { - return $this->statusCode; - } - - public function setStatusCode($code) - { - $this->statusCode = $code; - } - - public function getHeaders() - { - return $this->headers; - } - - public function setHeaders(array $headers) - { - $this->headers = $headers; - } - - public function getClass() - { - return $this->class; - } - - public function setClass($class) - { - $this->class = $class; - } - - public function getFile() - { - return $this->file; - } - - public function setFile($file) - { - $this->file = $file; - } - - public function getLine() - { - return $this->line; - } - - public function setLine($line) - { - $this->line = $line; - } - - public function getMessage() - { - return $this->message; - } - - public function setMessage($message) - { - $this->message = $message; - } - - public function getCode() - { - return $this->code; - } - - public function setCode($code) - { - $this->code = $code; - } - - public function getPrevious() - { - return $this->previous; - } - - public function setPrevious(FlattenException $previous) - { - $this->previous = $previous; - } - - public function getAllPrevious() - { - $exceptions = array(); - $e = $this; - while ($e = $e->getPrevious()) { - $exceptions[] = $e; - } - - return $exceptions; - } - - public function getTrace() - { - return $this->trace; - } - - public function setTraceFromException(\Exception $exception) - { - $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine()); - } - - public function setTrace($trace, $file, $line) - { - $this->trace = array(); - $this->trace[] = array( - 'namespace' => '', - 'short_class' => '', - 'class' => '', - 'type' => '', - 'function' => '', - 'file' => $file, - 'line' => $line, - 'args' => array(), - ); - foreach ($trace as $entry) { - $class = ''; - $namespace = ''; - if (isset($entry['class'])) { - $parts = explode('\\', $entry['class']); - $class = array_pop($parts); - $namespace = implode('\\', $parts); - } - - $this->trace[] = array( - 'namespace' => $namespace, - 'short_class' => $class, - 'class' => isset($entry['class']) ? $entry['class'] : '', - 'type' => isset($entry['type']) ? $entry['type'] : '', - 'function' => isset($entry['function']) ? $entry['function'] : null, - 'file' => isset($entry['file']) ? $entry['file'] : null, - 'line' => isset($entry['line']) ? $entry['line'] : null, - 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(), - ); - } - } - - private function flattenArgs($args, $level = 0, &$count = 0) - { - $result = array(); - foreach ($args as $key => $value) { - if (++$count > 1e4) { - return array('array', '*SKIPPED over 10000 entries*'); - } - if ($value instanceof \__PHP_Incomplete_Class) { - // is_object() returns false on PHP<=7.1 - $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value)); - } elseif (is_object($value)) { - $result[$key] = array('object', get_class($value)); - } elseif (is_array($value)) { - if ($level > 10) { - $result[$key] = array('array', '*DEEP NESTED ARRAY*'); - } else { - $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count)); - } - } elseif (null === $value) { - $result[$key] = array('null', null); - } elseif (is_bool($value)) { - $result[$key] = array('boolean', $value); - } elseif (is_integer($value)) { - $result[$key] = array('integer', $value); - } elseif (is_float($value)) { - $result[$key] = array('float', $value); - } elseif (is_resource($value)) { - $result[$key] = array('resource', get_resource_type($value)); - } else { - $result[$key] = array('string', (string) $value); - } - } - - return $result; - } - - private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) - { - $array = new \ArrayObject($value); - - return $array['__PHP_Incomplete_Class_Name']; - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/OutOfMemoryException.php b/tests/integration/vendor/symfony/debug/Exception/OutOfMemoryException.php deleted file mode 100644 index fec197983..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/OutOfMemoryException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Out of memory exception. - * - * @author Nicolas Grekas - */ -class OutOfMemoryException extends FatalErrorException -{ -} diff --git a/tests/integration/vendor/symfony/debug/Exception/SilencedErrorContext.php b/tests/integration/vendor/symfony/debug/Exception/SilencedErrorContext.php deleted file mode 100644 index 0c3a0e1d9..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/SilencedErrorContext.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Data Object that represents a Silenced Error. - * - * @author Grégoire Pineau - */ -class SilencedErrorContext implements \JsonSerializable -{ - private $severity; - private $file; - private $line; - - public function __construct($severity, $file, $line) - { - $this->severity = $severity; - $this->file = $file; - $this->line = $line; - } - - public function getSeverity() - { - return $this->severity; - } - - public function getFile() - { - return $this->file; - } - - public function getLine() - { - return $this->line; - } - - public function JsonSerialize() - { - return array( - 'severity' => $this->severity, - 'file' => $this->file, - 'line' => $this->line, - ); - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/UndefinedFunctionException.php b/tests/integration/vendor/symfony/debug/Exception/UndefinedFunctionException.php deleted file mode 100644 index a66ae2a38..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/UndefinedFunctionException.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Undefined Function Exception. - * - * @author Konstanton Myakshin - */ -class UndefinedFunctionException extends FatalErrorException -{ - public function __construct($message, \ErrorException $previous) - { - parent::__construct( - $message, - $previous->getCode(), - $previous->getSeverity(), - $previous->getFile(), - $previous->getLine(), - $previous->getPrevious() - ); - $this->setTrace($previous->getTrace()); - } -} diff --git a/tests/integration/vendor/symfony/debug/Exception/UndefinedMethodException.php b/tests/integration/vendor/symfony/debug/Exception/UndefinedMethodException.php deleted file mode 100644 index 350dc3187..000000000 --- a/tests/integration/vendor/symfony/debug/Exception/UndefinedMethodException.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Exception; - -/** - * Undefined Method Exception. - * - * @author Grégoire Pineau - */ -class UndefinedMethodException extends FatalErrorException -{ - public function __construct($message, \ErrorException $previous) - { - parent::__construct( - $message, - $previous->getCode(), - $previous->getSeverity(), - $previous->getFile(), - $previous->getLine(), - $previous->getPrevious() - ); - $this->setTrace($previous->getTrace()); - } -} diff --git a/tests/integration/vendor/symfony/debug/ExceptionHandler.php b/tests/integration/vendor/symfony/debug/ExceptionHandler.php deleted file mode 100644 index a6b9d09a3..000000000 --- a/tests/integration/vendor/symfony/debug/ExceptionHandler.php +++ /dev/null @@ -1,402 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -use Symfony\Component\Debug\Exception\FlattenException; -use Symfony\Component\Debug\Exception\OutOfMemoryException; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; - -/** - * ExceptionHandler converts an exception to a Response object. - * - * It is mostly useful in debug mode to replace the default PHP/XDebug - * output with something prettier and more useful. - * - * As this class is mainly used during Kernel boot, where nothing is yet - * available, the Response content is always HTML. - * - * @author Fabien Potencier - * @author Nicolas Grekas - */ -class ExceptionHandler -{ - private $debug; - private $charset; - private $handler; - private $caughtBuffer; - private $caughtLength; - private $fileLinkFormat; - - public function __construct($debug = true, $charset = null, $fileLinkFormat = null) - { - $this->debug = $debug; - $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; - $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); - } - - /** - * Registers the exception handler. - * - * @param bool $debug Enable/disable debug mode, where the stack trace is displayed - * @param string|null $charset The charset used by exception messages - * @param string|null $fileLinkFormat The IDE link template - * - * @return ExceptionHandler The registered exception handler - */ - public static function register($debug = true, $charset = null, $fileLinkFormat = null) - { - $handler = new static($debug, $charset, $fileLinkFormat); - - $prev = set_exception_handler(array($handler, 'handle')); - if (is_array($prev) && $prev[0] instanceof ErrorHandler) { - restore_exception_handler(); - $prev[0]->setExceptionHandler(array($handler, 'handle')); - } - - return $handler; - } - - /** - * Sets a user exception handler. - * - * @param callable $handler An handler that will be called on Exception - * - * @return callable|null The previous exception handler if any - */ - public function setHandler(callable $handler = null) - { - $old = $this->handler; - $this->handler = $handler; - - return $old; - } - - /** - * Sets the format for links to source files. - * - * @param string|FileLinkFormatter $format The format for links to source files - * - * @return string The previous file link format - */ - public function setFileLinkFormat($fileLinkFormat) - { - $old = $this->fileLinkFormat; - $this->fileLinkFormat = $fileLinkFormat; - - return $old; - } - - /** - * Sends a response for the given Exception. - * - * To be as fail-safe as possible, the exception is first handled - * by our simple exception handler, then by the user exception handler. - * The latter takes precedence and any output from the former is cancelled, - * if and only if nothing bad happens in this handling path. - */ - public function handle(\Exception $exception) - { - if (null === $this->handler || $exception instanceof OutOfMemoryException) { - $this->sendPhpResponse($exception); - - return; - } - - $caughtLength = $this->caughtLength = 0; - - ob_start(function ($buffer) { - $this->caughtBuffer = $buffer; - - return ''; - }); - - $this->sendPhpResponse($exception); - while (null === $this->caughtBuffer && ob_end_flush()) { - // Empty loop, everything is in the condition - } - if (isset($this->caughtBuffer[0])) { - ob_start(function ($buffer) { - if ($this->caughtLength) { - // use substr_replace() instead of substr() for mbstring overloading resistance - $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength); - if (isset($cleanBuffer[0])) { - $buffer = $cleanBuffer; - } - } - - return $buffer; - }); - - echo $this->caughtBuffer; - $caughtLength = ob_get_length(); - } - $this->caughtBuffer = null; - - try { - call_user_func($this->handler, $exception); - $this->caughtLength = $caughtLength; - } catch (\Exception $e) { - if (!$caughtLength) { - // All handlers failed. Let PHP handle that now. - throw $exception; - } - } - } - - /** - * Sends the error associated with the given Exception as a plain PHP response. - * - * This method uses plain PHP functions like header() and echo to output - * the response. - * - * @param \Exception|FlattenException $exception An \Exception or FlattenException instance - */ - public function sendPhpResponse($exception) - { - if (!$exception instanceof FlattenException) { - $exception = FlattenException::create($exception); - } - - if (!headers_sent()) { - header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); - foreach ($exception->getHeaders() as $name => $value) { - header($name.': '.$value, false); - } - header('Content-Type: text/html; charset='.$this->charset); - } - - echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); - } - - /** - * Gets the full HTML content associated with the given exception. - * - * @param \Exception|FlattenException $exception An \Exception or FlattenException instance - * - * @return string The HTML content as a string - */ - public function getHtml($exception) - { - if (!$exception instanceof FlattenException) { - $exception = FlattenException::create($exception); - } - - return $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); - } - - /** - * Gets the HTML content associated with the given exception. - * - * @param FlattenException $exception A FlattenException instance - * - * @return string The content as a string - */ - public function getContent(FlattenException $exception) - { - switch ($exception->getStatusCode()) { - case 404: - $title = 'Sorry, the page you are looking for could not be found.'; - break; - default: - $title = 'Whoops, looks like something went wrong.'; - } - - $content = ''; - if ($this->debug) { - try { - $count = count($exception->getAllPrevious()); - $total = $count + 1; - foreach ($exception->toArray() as $position => $e) { - $ind = $count - $position + 1; - $class = $this->formatClass($e['class']); - $message = nl2br($this->escapeHtml($e['message'])); - $content .= sprintf(<<<'EOF' -

- %d/%d - %s%s: - %s -

-
-
    - -EOF - , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message); - foreach ($e['trace'] as $trace) { - $content .= '
  1. '; - if ($trace['function']) { - $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); - } - if (isset($trace['file']) && isset($trace['line'])) { - $content .= $this->formatPath($trace['file'], $trace['line']); - } - $content .= "
  2. \n"; - } - - $content .= "
\n
\n"; - } - } catch (\Exception $e) { - // something nasty happened and we cannot throw an exception anymore - if ($this->debug) { - $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); - } else { - $title = 'Whoops, looks like something went wrong.'; - } - } - } - - return << -

$title

- $content - -EOF; - } - - /** - * Gets the stylesheet associated with the given exception. - * - * @param FlattenException $exception A FlattenException instance - * - * @return string The stylesheet as a string - */ - public function getStylesheet(FlattenException $exception) - { - return <<<'EOF' - .sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 } - .sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; } - .sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; } - .sf-reset .clear_fix { display:inline-block; } - .sf-reset * html .clear_fix { height:1%; } - .sf-reset .clear_fix { display:block; } - .sf-reset, .sf-reset .block { margin: auto } - .sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; } - .sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px } - .sf-reset strong { font-weight:bold; } - .sf-reset a { color:#6c6159; cursor: default; } - .sf-reset a img { border:none; } - .sf-reset a:hover { text-decoration:underline; } - .sf-reset em { font-style:italic; } - .sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif } - .sf-reset .exception_counter { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; float: left; display: block; } - .sf-reset .exception_title { margin-left: 3em; margin-bottom: 0.7em; display: block; } - .sf-reset .exception_message { margin-left: 3em; display: block; } - .sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; } - .sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px; - border-bottom-right-radius: 16px; - border-bottom-left-radius: 16px; - border-bottom:1px solid #ccc; - border-right:1px solid #ccc; - border-left:1px solid #ccc; - word-wrap: break-word; - } - .sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px; - border-top-left-radius: 16px; - border-top-right-radius: 16px; - border-top:1px solid #ccc; - border-right:1px solid #ccc; - border-left:1px solid #ccc; - overflow: hidden; - word-wrap: break-word; - } - .sf-reset a { background:none; color:#868686; text-decoration:none; } - .sf-reset a:hover { background:none; color:#313131; text-decoration:underline; } - .sf-reset ol { padding: 10px 0; } - .sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px; - border-radius: 10px; - border: 1px solid #ccc; - } -EOF; - } - - private function decorate($content, $css) - { - return << - - - - - - - - $content - - -EOF; - } - - private function formatClass($class) - { - $parts = explode('\\', $class); - - return sprintf('%s', $class, array_pop($parts)); - } - - private function formatPath($path, $line) - { - $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path); - $fmt = $this->fileLinkFormat; - - if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $path, '%l' => $line)) : $fmt->format($path, $line)) { - return sprintf(' in
%s line %d', $this->escapeHtml($link), $file, $line); - } - - return sprintf(' in %s line %d', $this->escapeHtml($path), $file, $line); - } - - /** - * Formats an array as a string. - * - * @param array $args The argument array - * - * @return string - */ - private function formatArgs(array $args) - { - $result = array(); - foreach ($args as $key => $item) { - if ('object' === $item[0]) { - $formattedValue = sprintf('object(%s)', $this->formatClass($item[1])); - } elseif ('array' === $item[0]) { - $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); - } elseif ('null' === $item[0]) { - $formattedValue = 'null'; - } elseif ('boolean' === $item[0]) { - $formattedValue = ''.strtolower(var_export($item[1], true)).''; - } elseif ('resource' === $item[0]) { - $formattedValue = 'resource'; - } else { - $formattedValue = str_replace("\n", '', $this->escapeHtml(var_export($item[1], true))); - } - - $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); - } - - return implode(', ', $result); - } - - /** - * HTML-encodes a string. - */ - private function escapeHtml($str) - { - return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset); - } -} diff --git a/tests/integration/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/tests/integration/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php deleted file mode 100644 index c48d0d3fa..000000000 --- a/tests/integration/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php +++ /dev/null @@ -1,206 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\ClassNotFoundException; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\DebugClassLoader; -use Composer\Autoload\ClassLoader as ComposerClassLoader; -use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; - -/** - * ErrorHandler for classes that do not exist. - * - * @author Fabien Potencier - */ -class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function handleError(array $error, FatalErrorException $exception) - { - $messageLen = strlen($error['message']); - $notFoundSuffix = '\' not found'; - $notFoundSuffixLen = strlen($notFoundSuffix); - if ($notFoundSuffixLen > $messageLen) { - return; - } - - if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { - return; - } - - foreach (array('class', 'interface', 'trait') as $typeName) { - $prefix = ucfirst($typeName).' \''; - $prefixLen = strlen($prefix); - if (0 !== strpos($error['message'], $prefix)) { - continue; - } - - $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); - if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { - $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); - $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); - $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); - $tail = ' for another namespace?'; - } else { - $className = $fullyQualifiedClassName; - $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); - $tail = '?'; - } - - if ($candidates = $this->getClassCandidates($className)) { - $tail = array_pop($candidates).'"?'; - if ($candidates) { - $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; - } else { - $tail = ' for "'.$tail; - } - } - $message .= "\nDid you forget a \"use\" statement".$tail; - - return new ClassNotFoundException($message, $exception); - } - } - - /** - * Tries to guess the full namespace for a given class name. - * - * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer - * autoloader (that should cover all common cases). - * - * @param string $class A class name (without its namespace) - * - * @return array An array of possible fully qualified class names - */ - private function getClassCandidates($class) - { - if (!is_array($functions = spl_autoload_functions())) { - return array(); - } - - // find Symfony and Composer autoloaders - $classes = array(); - - foreach ($functions as $function) { - if (!is_array($function)) { - continue; - } - // get class loaders wrapped by DebugClassLoader - if ($function[0] instanceof DebugClassLoader) { - $function = $function[0]->getClassLoader(); - - if (!is_array($function)) { - continue; - } - } - - if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) { - foreach ($function[0]->getPrefixes() as $prefix => $paths) { - foreach ($paths as $path) { - $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); - } - } - } - if ($function[0] instanceof ComposerClassLoader) { - foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { - foreach ($paths as $path) { - $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); - } - } - } - } - - return array_unique($classes); - } - - /** - * @param string $path - * @param string $class - * @param string $prefix - * - * @return array - */ - private function findClassInPath($path, $class, $prefix) - { - if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { - return array(); - } - - $classes = array(); - $filename = $class.'.php'; - foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { - if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { - $classes[] = $class; - } - } - - return $classes; - } - - /** - * @param string $path - * @param string $file - * @param string $prefix - * - * @return string|null - */ - private function convertFileToClass($path, $file, $prefix) - { - $candidates = array( - // namespaced class - $namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file), - // namespaced class (with target dir) - $prefix.$namespacedClass, - // namespaced class (with target dir and separator) - $prefix.'\\'.$namespacedClass, - // PEAR class - str_replace('\\', '_', $namespacedClass), - // PEAR class (with target dir) - str_replace('\\', '_', $prefix.$namespacedClass), - // PEAR class (with target dir and separator) - str_replace('\\', '_', $prefix.'\\'.$namespacedClass), - ); - - if ($prefix) { - $candidates = array_filter($candidates, function ($candidate) use ($prefix) {return 0 === strpos($candidate, $prefix);}); - } - - // We cannot use the autoloader here as most of them use require; but if the class - // is not found, the new autoloader call will require the file again leading to a - // "cannot redeclare class" error. - foreach ($candidates as $candidate) { - if ($this->classExists($candidate)) { - return $candidate; - } - } - - require_once $file; - - foreach ($candidates as $candidate) { - if ($this->classExists($candidate)) { - return $candidate; - } - } - } - - /** - * @param string $class - * - * @return bool - */ - private function classExists($class) - { - return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); - } -} diff --git a/tests/integration/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/tests/integration/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php deleted file mode 100644 index 6b87eb30a..000000000 --- a/tests/integration/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\FatalErrorException; - -/** - * Attempts to convert fatal errors to exceptions. - * - * @author Fabien Potencier - */ -interface FatalErrorHandlerInterface -{ - /** - * Attempts to convert an error into an exception. - * - * @param array $error An array as returned by error_get_last() - * @param FatalErrorException $exception A FatalErrorException instance - * - * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise - */ - public function handleError(array $error, FatalErrorException $exception); -} diff --git a/tests/integration/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/tests/integration/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php deleted file mode 100644 index c6f391a79..000000000 --- a/tests/integration/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\UndefinedFunctionException; -use Symfony\Component\Debug\Exception\FatalErrorException; - -/** - * ErrorHandler for undefined functions. - * - * @author Fabien Potencier - */ -class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function handleError(array $error, FatalErrorException $exception) - { - $messageLen = strlen($error['message']); - $notFoundSuffix = '()'; - $notFoundSuffixLen = strlen($notFoundSuffix); - if ($notFoundSuffixLen > $messageLen) { - return; - } - - if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { - return; - } - - $prefix = 'Call to undefined function '; - $prefixLen = strlen($prefix); - if (0 !== strpos($error['message'], $prefix)) { - return; - } - - $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); - if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { - $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); - $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); - $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); - } else { - $functionName = $fullyQualifiedFunctionName; - $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); - } - - $candidates = array(); - foreach (get_defined_functions() as $type => $definedFunctionNames) { - foreach ($definedFunctionNames as $definedFunctionName) { - if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { - $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); - } else { - $definedFunctionNameBasename = $definedFunctionName; - } - - if ($definedFunctionNameBasename === $functionName) { - $candidates[] = '\\'.$definedFunctionName; - } - } - } - - if ($candidates) { - sort($candidates); - $last = array_pop($candidates).'"?'; - if ($candidates) { - $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; - } else { - $candidates = '"'.$last; - } - $message .= "\nDid you mean to call ".$candidates; - } - - return new UndefinedFunctionException($message, $exception); - } -} diff --git a/tests/integration/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/tests/integration/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php deleted file mode 100644 index f734d6bb7..000000000 --- a/tests/integration/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\Exception\UndefinedMethodException; - -/** - * ErrorHandler for undefined methods. - * - * @author Grégoire Pineau - */ -class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function handleError(array $error, FatalErrorException $exception) - { - preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches); - if (!$matches) { - return; - } - - $className = $matches[1]; - $methodName = $matches[2]; - - $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); - - $candidates = array(); - foreach (get_class_methods($className) as $definedMethodName) { - $lev = levenshtein($methodName, $definedMethodName); - if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) { - $candidates[] = $definedMethodName; - } - } - - if ($candidates) { - sort($candidates); - $last = array_pop($candidates).'"?'; - if ($candidates) { - $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; - } else { - $candidates = '"'.$last; - } - $message .= "\nDid you mean to call ".$candidates; - } - - return new UndefinedMethodException($message, $exception); - } -} diff --git a/tests/integration/vendor/symfony/debug/LICENSE b/tests/integration/vendor/symfony/debug/LICENSE deleted file mode 100644 index 12a74531e..000000000 --- a/tests/integration/vendor/symfony/debug/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2016 Fabien Potencier - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -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. diff --git a/tests/integration/vendor/symfony/debug/README.md b/tests/integration/vendor/symfony/debug/README.md deleted file mode 100644 index a1d16175c..000000000 --- a/tests/integration/vendor/symfony/debug/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Debug Component -=============== - -The Debug component provides tools to ease debugging PHP code. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/debug/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/README.md b/tests/integration/vendor/symfony/debug/Resources/ext/README.md deleted file mode 100644 index 25dccf076..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/README.md +++ /dev/null @@ -1,134 +0,0 @@ -Symfony Debug Extension for PHP 5 -================================= - -This extension publishes several functions to help building powerful debugging tools. -It is compatible with PHP 5.3, 5.4, 5.5 and 5.6; with ZTS and non-ZTS modes. -It is not required thus not provided for PHP 7. - -symfony_zval_info() -------------------- - -- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP, -- does work with references, preventing memory copying. - -Its behavior is about the same as: - -```php - gettype($array[$key]), - 'zval_hash' => /* hashed memory address of $array[$key] */, - 'zval_refcount' => /* internal zval refcount of $array[$key] */, - 'zval_isref' => /* is_ref status of $array[$key] */, - ); - - switch ($info['type']) { - case 'object': - $info += array( - 'object_class' => get_class($array[$key]), - 'object_refcount' => /* internal object refcount of $array[$key] */, - 'object_hash' => spl_object_hash($array[$key]), - 'object_handle' => /* internal object handle $array[$key] */, - ); - break; - - case 'resource': - $info += array( - 'resource_handle' => (int) $array[$key], - 'resource_type' => get_resource_type($array[$key]), - 'resource_refcount' => /* internal resource refcount of $array[$key] */, - ); - break; - - case 'array': - $info += array( - 'array_count' => count($array[$key]), - ); - break; - - case 'string': - $info += array( - 'strlen' => strlen($array[$key]), - ); - break; - } - - return $info; -} -``` - -symfony_debug_backtrace() -------------------------- - -This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors: - -```php -function foo() { fatal(); } -function bar() { foo(); } - -function sd() { var_dump(symfony_debug_backtrace()); } - -register_shutdown_function('sd'); - -bar(); - -/* Will output -Fatal error: Call to undefined function fatal() in foo.php on line 42 -array(3) { - [0]=> - array(2) { - ["function"]=> - string(2) "sd" - ["args"]=> - array(0) { - } - } - [1]=> - array(4) { - ["file"]=> - string(7) "foo.php" - ["line"]=> - int(1) - ["function"]=> - string(3) "foo" - ["args"]=> - array(0) { - } - } - [2]=> - array(4) { - ["file"]=> - string(102) "foo.php" - ["line"]=> - int(2) - ["function"]=> - string(3) "bar" - ["args"]=> - array(0) { - } - } -} -*/ -``` - -Usage ------ - -To enable the extension from source, run: - -``` - phpize - ./configure - make - sudo make install -``` diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/config.m4 b/tests/integration/vendor/symfony/debug/Resources/ext/config.m4 deleted file mode 100644 index 3c5604715..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/config.m4 +++ /dev/null @@ -1,63 +0,0 @@ -dnl $Id$ -dnl config.m4 for extension symfony_debug - -dnl Comments in this file start with the string 'dnl'. -dnl Remove where necessary. This file will not work -dnl without editing. - -dnl If your extension references something external, use with: - -dnl PHP_ARG_WITH(symfony_debug, for symfony_debug support, -dnl Make sure that the comment is aligned: -dnl [ --with-symfony_debug Include symfony_debug support]) - -dnl Otherwise use enable: - -PHP_ARG_ENABLE(symfony_debug, whether to enable symfony_debug support, -dnl Make sure that the comment is aligned: -[ --enable-symfony_debug Enable symfony_debug support]) - -if test "$PHP_SYMFONY_DEBUG" != "no"; then - dnl Write more examples of tests here... - - dnl # --with-symfony_debug -> check with-path - dnl SEARCH_PATH="/usr/local /usr" # you might want to change this - dnl SEARCH_FOR="/include/symfony_debug.h" # you most likely want to change this - dnl if test -r $PHP_SYMFONY_DEBUG/$SEARCH_FOR; then # path given as parameter - dnl SYMFONY_DEBUG_DIR=$PHP_SYMFONY_DEBUG - dnl else # search default path list - dnl AC_MSG_CHECKING([for symfony_debug files in default path]) - dnl for i in $SEARCH_PATH ; do - dnl if test -r $i/$SEARCH_FOR; then - dnl SYMFONY_DEBUG_DIR=$i - dnl AC_MSG_RESULT(found in $i) - dnl fi - dnl done - dnl fi - dnl - dnl if test -z "$SYMFONY_DEBUG_DIR"; then - dnl AC_MSG_RESULT([not found]) - dnl AC_MSG_ERROR([Please reinstall the symfony_debug distribution]) - dnl fi - - dnl # --with-symfony_debug -> add include path - dnl PHP_ADD_INCLUDE($SYMFONY_DEBUG_DIR/include) - - dnl # --with-symfony_debug -> check for lib and symbol presence - dnl LIBNAME=symfony_debug # you may want to change this - dnl LIBSYMBOL=symfony_debug # you most likely want to change this - - dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, - dnl [ - dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SYMFONY_DEBUG_DIR/lib, SYMFONY_DEBUG_SHARED_LIBADD) - dnl AC_DEFINE(HAVE_SYMFONY_DEBUGLIB,1,[ ]) - dnl ],[ - dnl AC_MSG_ERROR([wrong symfony_debug lib version or lib not found]) - dnl ],[ - dnl -L$SYMFONY_DEBUG_DIR/lib -lm - dnl ]) - dnl - dnl PHP_SUBST(SYMFONY_DEBUG_SHARED_LIBADD) - - PHP_NEW_EXTENSION(symfony_debug, symfony_debug.c, $ext_shared) -fi diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/config.w32 b/tests/integration/vendor/symfony/debug/Resources/ext/config.w32 deleted file mode 100644 index 487e69138..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/config.w32 +++ /dev/null @@ -1,13 +0,0 @@ -// $Id$ -// vim:ft=javascript - -// If your extension references something external, use ARG_WITH -// ARG_WITH("symfony_debug", "for symfony_debug support", "no"); - -// Otherwise, use ARG_ENABLE -// ARG_ENABLE("symfony_debug", "enable symfony_debug support", "no"); - -if (PHP_SYMFONY_DEBUG != "no") { - EXTENSION("symfony_debug", "symfony_debug.c"); -} - diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/php_symfony_debug.h b/tests/integration/vendor/symfony/debug/Resources/ext/php_symfony_debug.h deleted file mode 100644 index 26d0e8c01..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/php_symfony_debug.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#ifndef PHP_SYMFONY_DEBUG_H -#define PHP_SYMFONY_DEBUG_H - -extern zend_module_entry symfony_debug_module_entry; -#define phpext_symfony_debug_ptr &symfony_debug_module_entry - -#define PHP_SYMFONY_DEBUG_VERSION "2.7" - -#ifdef PHP_WIN32 -# define PHP_SYMFONY_DEBUG_API __declspec(dllexport) -#elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_SYMFONY_DEBUG_API __attribute__ ((visibility("default"))) -#else -# define PHP_SYMFONY_DEBUG_API -#endif - -#ifdef ZTS -#include "TSRM.h" -#endif - -ZEND_BEGIN_MODULE_GLOBALS(symfony_debug) - intptr_t req_rand_init; - void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); - zval *debug_bt; -ZEND_END_MODULE_GLOBALS(symfony_debug) - -PHP_MINIT_FUNCTION(symfony_debug); -PHP_MSHUTDOWN_FUNCTION(symfony_debug); -PHP_RINIT_FUNCTION(symfony_debug); -PHP_RSHUTDOWN_FUNCTION(symfony_debug); -PHP_MINFO_FUNCTION(symfony_debug); -PHP_GINIT_FUNCTION(symfony_debug); -PHP_GSHUTDOWN_FUNCTION(symfony_debug); - -PHP_FUNCTION(symfony_zval_info); -PHP_FUNCTION(symfony_debug_backtrace); - -static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC); -static const char *_symfony_debug_zval_type(zval *); -static const char* _symfony_debug_get_resource_type(long TSRMLS_DC); -static int _symfony_debug_get_resource_refcount(long TSRMLS_DC); - -void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); - -#ifdef ZTS -#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v) -#else -#define SYMFONY_DEBUG_G(v) (symfony_debug_globals.v) -#endif - -#endif /* PHP_SYMFONY_DEBUG_H */ diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/symfony_debug.c b/tests/integration/vendor/symfony/debug/Resources/ext/symfony_debug.c deleted file mode 100644 index 0d7cb6023..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/symfony_debug.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#ifdef ZTS -#include "TSRM.h" -#endif -#include "php_ini.h" -#include "ext/standard/info.h" -#include "php_symfony_debug.h" -#include "ext/standard/php_rand.h" -#include "ext/standard/php_lcg.h" -#include "ext/spl/php_spl.h" -#include "Zend/zend_gc.h" -#include "Zend/zend_builtin_functions.h" -#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ -#include "ext/standard/php_array.h" -#include "Zend/zend_interfaces.h" -#include "SAPI.h" - -#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626 - -ZEND_DECLARE_MODULE_GLOBALS(symfony_debug) - -ZEND_BEGIN_ARG_INFO_EX(symfony_zval_arginfo, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_ARRAY_INFO(0, array, 0) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -const zend_function_entry symfony_debug_functions[] = { - PHP_FE(symfony_zval_info, symfony_zval_arginfo) - PHP_FE(symfony_debug_backtrace, NULL) - PHP_FE_END -}; - -PHP_FUNCTION(symfony_debug_backtrace) -{ - if (zend_parse_parameters_none() == FAILURE) { - return; - } -#if IS_PHP_53 - zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC); -#else - zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC); -#endif - - if (!SYMFONY_DEBUG_G(debug_bt)) { - return; - } - - php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC); -} - -PHP_FUNCTION(symfony_zval_info) -{ - zval *key = NULL, *arg = NULL; - zval **data = NULL; - HashTable *array = NULL; - long options = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) { - return; - } - - switch (Z_TYPE_P(key)) { - case IS_STRING: - if (zend_symtable_find(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&data) == FAILURE) { - return; - } - break; - case IS_LONG: - if (zend_hash_index_find(array, Z_LVAL_P(key), (void **)&data)) { - return; - } - break; - } - - arg = *data; - - array_init(return_value); - - add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1); - add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0); - add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg)); - add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg)); - - if (Z_TYPE_P(arg) == IS_OBJECT) { - char hash[33] = {0}; - - php_spl_object_hash(arg, (char *)hash TSRMLS_CC); - add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1); - add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount); - add_assoc_string(return_value, "object_hash", hash, 1); - add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg)); - } else if (Z_TYPE_P(arg) == IS_ARRAY) { - add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg))); - } else if(Z_TYPE_P(arg) == IS_RESOURCE) { - add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg)); - add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1); - add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC)); - } else if (Z_TYPE_P(arg) == IS_STRING) { - add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg)); - } -} - -void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) -{ - TSRMLS_FETCH(); - zval *retval; - - switch (type) { - case E_ERROR: - case E_PARSE: - case E_CORE_ERROR: - case E_CORE_WARNING: - case E_COMPILE_ERROR: - case E_COMPILE_WARNING: - ALLOC_INIT_ZVAL(retval); -#if IS_PHP_53 - zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC); -#else - zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC); -#endif - SYMFONY_DEBUG_G(debug_bt) = retval; - } - - SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args); -} - -static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC) -{ - const char *res_type; - res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC); - - if (!res_type) { - return "Unknown"; - } - - return res_type; -} - -static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC) -{ - zend_rsrc_list_entry *le; - - if (zend_hash_index_find(&EG(regular_list), rsid, (void **) &le)==SUCCESS) { - return le->refcount; - } - - return 0; -} - -static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC) -{ - char *result = NULL; - intptr_t address_rand; - - if (!SYMFONY_DEBUG_G(req_rand_init)) { - if (!BG(mt_rand_is_seeded)) { - php_mt_srand(GENERATE_SEED() TSRMLS_CC); - } - SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C); - } - - address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init); - - spprintf(&result, 17, "%016zx", address_rand); - - return result; -} - -static const char *_symfony_debug_zval_type(zval *zv) -{ - switch (Z_TYPE_P(zv)) { - case IS_NULL: - return "NULL"; - break; - - case IS_BOOL: - return "boolean"; - break; - - case IS_LONG: - return "integer"; - break; - - case IS_DOUBLE: - return "double"; - break; - - case IS_STRING: - return "string"; - break; - - case IS_ARRAY: - return "array"; - break; - - case IS_OBJECT: - return "object"; - - case IS_RESOURCE: - return "resource"; - - default: - return "unknown type"; - } -} - -zend_module_entry symfony_debug_module_entry = { - STANDARD_MODULE_HEADER, - "symfony_debug", - symfony_debug_functions, - PHP_MINIT(symfony_debug), - PHP_MSHUTDOWN(symfony_debug), - PHP_RINIT(symfony_debug), - PHP_RSHUTDOWN(symfony_debug), - PHP_MINFO(symfony_debug), - PHP_SYMFONY_DEBUG_VERSION, - PHP_MODULE_GLOBALS(symfony_debug), - PHP_GINIT(symfony_debug), - PHP_GSHUTDOWN(symfony_debug), - NULL, - STANDARD_MODULE_PROPERTIES_EX -}; - -#ifdef COMPILE_DL_SYMFONY_DEBUG -ZEND_GET_MODULE(symfony_debug) -#endif - -PHP_GINIT_FUNCTION(symfony_debug) -{ - memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals)); -} - -PHP_GSHUTDOWN_FUNCTION(symfony_debug) -{ - -} - -PHP_MINIT_FUNCTION(symfony_debug) -{ - SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb; - zend_error_cb = symfony_debug_error_cb; - - return SUCCESS; -} - -PHP_MSHUTDOWN_FUNCTION(symfony_debug) -{ - zend_error_cb = SYMFONY_DEBUG_G(old_error_cb); - - return SUCCESS; -} - -PHP_RINIT_FUNCTION(symfony_debug) -{ - return SUCCESS; -} - -PHP_RSHUTDOWN_FUNCTION(symfony_debug) -{ - return SUCCESS; -} - -PHP_MINFO_FUNCTION(symfony_debug) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "Symfony Debug support", "enabled"); - php_info_print_table_header(2, "Symfony Debug version", PHP_SYMFONY_DEBUG_VERSION); - php_info_print_table_end(); -} diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/tests/001.phpt b/tests/integration/vendor/symfony/debug/Resources/ext/tests/001.phpt deleted file mode 100644 index 15e183a70..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/tests/001.phpt +++ /dev/null @@ -1,153 +0,0 @@ ---TEST-- -Test symfony_zval_info API ---SKIPIF-- - ---FILE-- - $int, - 'float' => $float, - 'str' => $str, - 'object' => $object, - 'array' => $array, - 'resource' => $resource, - 'null' => $null, - 'bool' => $bool, - 'refcount' => &$refcount2, -); - -var_dump(symfony_zval_info('int', $var)); -var_dump(symfony_zval_info('float', $var)); -var_dump(symfony_zval_info('str', $var)); -var_dump(symfony_zval_info('object', $var)); -var_dump(symfony_zval_info('array', $var)); -var_dump(symfony_zval_info('resource', $var)); -var_dump(symfony_zval_info('null', $var)); -var_dump(symfony_zval_info('bool', $var)); - -var_dump(symfony_zval_info('refcount', $var)); -var_dump(symfony_zval_info('not-exist', $var)); -?> ---EXPECTF-- -array(4) { - ["type"]=> - string(7) "integer" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(4) { - ["type"]=> - string(6) "double" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(5) { - ["type"]=> - string(6) "string" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["strlen"]=> - int(6) -} -array(8) { - ["type"]=> - string(6) "object" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["object_class"]=> - string(8) "stdClass" - ["object_refcount"]=> - int(1) - ["object_hash"]=> - string(32) "%s" - ["object_handle"]=> - int(%d) -} -array(5) { - ["type"]=> - string(5) "array" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["array_count"]=> - int(2) -} -array(7) { - ["type"]=> - string(8) "resource" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) - ["resource_handle"]=> - int(%d) - ["resource_type"]=> - string(6) "stream" - ["resource_refcount"]=> - int(1) -} -array(4) { - ["type"]=> - string(4) "NULL" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(4) { - ["type"]=> - string(7) "boolean" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(2) - ["zval_isref"]=> - bool(false) -} -array(4) { - ["type"]=> - string(7) "integer" - ["zval_hash"]=> - string(16) "%s" - ["zval_refcount"]=> - int(3) - ["zval_isref"]=> - bool(true) -} -NULL diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/tests/002.phpt b/tests/integration/vendor/symfony/debug/Resources/ext/tests/002.phpt deleted file mode 100644 index 2bc6d7127..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/tests/002.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -Test symfony_debug_backtrace in case of fatal error ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -Fatal error: Call to undefined function notexist() in %s on line %d -Array -( - [0] => Array - ( - [function] => bt - [args] => Array - ( - ) - - ) - - [1] => Array - ( - [file] => %s - [line] => %d - [function] => foo - [args] => Array - ( - ) - - ) - - [2] => Array - ( - [file] => %s - [line] => %d - [function] => bar - [args] => Array - ( - ) - - ) - -) diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/tests/002_1.phpt b/tests/integration/vendor/symfony/debug/Resources/ext/tests/002_1.phpt deleted file mode 100644 index 4e9e34f1b..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/tests/002_1.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -Test symfony_debug_backtrace in case of non fatal error ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -Array -( - [0] => Array - ( - [file] => %s - [line] => %d - [function] => bt - [args] => Array - ( - ) - - ) - - [1] => Array - ( - [file] => %s - [line] => %d - [function] => bar - [args] => Array - ( - ) - - ) - -) diff --git a/tests/integration/vendor/symfony/debug/Resources/ext/tests/003.phpt b/tests/integration/vendor/symfony/debug/Resources/ext/tests/003.phpt deleted file mode 100644 index 2a494e27a..000000000 --- a/tests/integration/vendor/symfony/debug/Resources/ext/tests/003.phpt +++ /dev/null @@ -1,85 +0,0 @@ ---TEST-- -Test ErrorHandler in case of fatal error ---SKIPIF-- - ---FILE-- -setExceptionHandler('print_r'); - -if (function_exists('xdebug_disable')) { - xdebug_disable(); -} - -bar(); -?> ---EXPECTF-- -Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d -Symfony\Component\Debug\Exception\UndefinedFunctionException Object -( - [message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug". - [string:Exception:private] => - [code:protected] => 0 - [file:protected] => %s - [line:protected] => %d - [trace:Exception:private] => Array - ( - [0] => Array - ( -%A [function] => Symfony\Component\Debug\foo -%A [args] => Array - ( - ) - - ) - - [1] => Array - ( -%A [function] => Symfony\Component\Debug\bar -%A [args] => Array - ( - ) - - ) -%A - ) - - [previous:Exception:private] => - [severity:protected] => 1 -) diff --git a/tests/integration/vendor/symfony/debug/Tests/DebugClassLoaderTest.php b/tests/integration/vendor/symfony/debug/Tests/DebugClassLoaderTest.php deleted file mode 100644 index 6bdbaaf83..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/DebugClassLoaderTest.php +++ /dev/null @@ -1,315 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use Symfony\Component\Debug\DebugClassLoader; -use Symfony\Component\Debug\ErrorHandler; - -class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var int Error reporting level before running tests - */ - private $errorReporting; - - private $loader; - - protected function setUp() - { - $this->errorReporting = error_reporting(E_ALL); - $this->loader = new ClassLoader(); - spl_autoload_register(array($this->loader, 'loadClass'), true, true); - DebugClassLoader::enable(); - } - - protected function tearDown() - { - DebugClassLoader::disable(); - spl_autoload_unregister(array($this->loader, 'loadClass')); - error_reporting($this->errorReporting); - } - - public function testIdempotence() - { - DebugClassLoader::enable(); - - $functions = spl_autoload_functions(); - foreach ($functions as $function) { - if (is_array($function) && $function[0] instanceof DebugClassLoader) { - $reflClass = new \ReflectionClass($function[0]); - $reflProp = $reflClass->getProperty('classLoader'); - $reflProp->setAccessible(true); - - $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0])); - - return; - } - } - - $this->fail('DebugClassLoader did not register'); - } - - public function testUnsilencing() - { - if (PHP_VERSION_ID >= 70000) { - $this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.'); - } - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM is not handled in this test case.'); - } - - ob_start(); - - $this->iniSet('log_errors', 0); - $this->iniSet('display_errors', 1); - - // See below: this will fail with parse error - // but this should not be @-silenced. - @class_exists(__NAMESPACE__.'\TestingUnsilencing', true); - - $output = ob_get_clean(); - - $this->assertStringMatchesFormat('%aParse error%a', $output); - } - - public function testStacking() - { - // the ContextErrorException must not be loaded to test the workaround - // for https://bugs.php.net/65322. - if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) { - $this->markTestSkipped('The ContextErrorException class is already loaded.'); - } - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM is not handled in this test case.'); - } - - ErrorHandler::register(); - - try { - // Trigger autoloading + E_STRICT at compile time - // which in turn triggers $errorHandler->handle() - // that again triggers autoloading for ContextErrorException. - // Error stacking works around the bug above and everything is fine. - - eval(' - namespace '.__NAMESPACE__.'; - class ChildTestingStacking extends TestingStacking { function foo($bar) {} } - '); - $this->fail('ContextErrorException expected'); - } catch (\ErrorException $exception) { - // if an exception is thrown, the test passed - $this->assertStringStartsWith(__FILE__, $exception->getFile()); - if (PHP_VERSION_ID < 70000) { - $this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage()); - $this->assertEquals(E_STRICT, $exception->getSeverity()); - } else { - $this->assertRegExp('/^Warning: Declaration/', $exception->getMessage()); - $this->assertEquals(E_WARNING, $exception->getSeverity()); - } - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - /** - * @expectedException \RuntimeException - */ - public function testNameCaseMismatch() - { - class_exists(__NAMESPACE__.'\TestingCaseMismatch', true); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage Case mismatch between class and real file names - */ - public function testFileCaseMismatch() - { - if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { - $this->markTestSkipped('Can only be run on case insensitive filesystems'); - } - - class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); - } - - /** - * @expectedException \RuntimeException - */ - public function testPsr4CaseMismatch() - { - class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true); - } - - public function testNotPsr0() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true)); - } - - public function testNotPsr0Bis() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true)); - } - - public function testClassAlias() - { - $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true)); - } - - /** - * @dataProvider provideDeprecatedSuper - */ - public function testDeprecatedSuper($class, $super, $type) - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_DEPRECATED); - - class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = array( - 'type' => E_USER_DEPRECATED, - 'message' => 'The Test\Symfony\Component\Debug\Tests\\'.$class.' class '.$type.' Symfony\Component\Debug\Tests\Fixtures\\'.$super.' that is deprecated but this is a test deprecation notice.', - ); - - $this->assertSame($xError, $lastError); - } - - public function provideDeprecatedSuper() - { - return array( - array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'), - array('DeprecatedParentClass', 'DeprecatedClass', 'extends'), - ); - } - - public function testInterfaceExtendsDeprecatedInterface() - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = array( - 'type' => E_USER_NOTICE, - 'message' => '', - ); - - $this->assertSame($xError, $lastError); - } - - public function testDeprecatedSuperInSameNamespace() - { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = array( - 'type' => E_USER_NOTICE, - 'message' => '', - ); - - $this->assertSame($xError, $lastError); - } - - public function testReservedForPhp7() - { - if (PHP_VERSION_ID >= 70000) { - $this->markTestSkipped('PHP7 already prevents using reserved names.'); - } - - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); - - class_exists('Test\\'.__NAMESPACE__.'\\Float', true); - - error_reporting($e); - restore_error_handler(); - - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - - $xError = array( - 'type' => E_USER_DEPRECATED, - 'message' => 'Test\Symfony\Component\Debug\Tests\Float uses a reserved class name (Float) that will break on PHP 7 and higher', - ); - - $this->assertSame($xError, $lastError); - } -} - -class ClassLoader -{ - public function loadClass($class) - { - } - - public function getClassMap() - { - return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'); - } - - public function findFile($class) - { - $fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR; - - if (__NAMESPACE__.'\TestingUnsilencing' === $class) { - eval('-- parse error --'); - } elseif (__NAMESPACE__.'\TestingStacking' === $class) { - eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }'); - } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { - eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); - } elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) { - return $fixtureDir.'CaseMismatch.php'; - } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { - return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php'; - } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { - return $fixtureDir.'reallyNotPsr0.php'; - } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { - return $fixtureDir.'notPsr0Bis.php'; - } elseif (__NAMESPACE__.'\Fixtures\DeprecatedInterface' === $class) { - return $fixtureDir.'DeprecatedInterface.php'; - } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) { - eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); - } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); - } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}'); - } elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}'); - } elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) { - eval('namespace Test\\'.__NAMESPACE__.'; class Float {}'); - } - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/ErrorHandlerTest.php b/tests/integration/vendor/symfony/debug/Tests/ErrorHandlerTest.php deleted file mode 100644 index 759514831..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/ErrorHandlerTest.php +++ /dev/null @@ -1,511 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use Psr\Log\LogLevel; -use Symfony\Component\Debug\BufferingLogger; -use Symfony\Component\Debug\ErrorHandler; -use Symfony\Component\Debug\Exception\ContextErrorException; -use Symfony\Component\Debug\Exception\SilencedErrorContext; - -/** - * ErrorHandlerTest. - * - * @author Robert Schönthal - * @author Nicolas Grekas - */ -class ErrorHandlerTest extends \PHPUnit_Framework_TestCase -{ - public function testRegister() - { - $handler = ErrorHandler::register(); - - try { - $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); - $this->assertSame($handler, ErrorHandler::register()); - - $newHandler = new ErrorHandler(); - - $this->assertSame($newHandler, ErrorHandler::register($newHandler, false)); - $h = set_error_handler('var_dump'); - restore_error_handler(); - $this->assertSame(array($handler, 'handleError'), $h); - - try { - $this->assertSame($newHandler, ErrorHandler::register($newHandler, true)); - $h = set_error_handler('var_dump'); - restore_error_handler(); - $this->assertSame(array($newHandler, 'handleError'), $h); - } catch (\Exception $e) { - } - - restore_error_handler(); - restore_exception_handler(); - - if (isset($e)) { - throw $e; - } - } catch (\Exception $e) { - } - - restore_error_handler(); - restore_exception_handler(); - - if (isset($e)) { - throw $e; - } - } - - public function testNotice() - { - ErrorHandler::register(); - - try { - self::triggerNotice($this); - $this->fail('ContextErrorException expected'); - } catch (ContextErrorException $exception) { - // if an exception is thrown, the test passed - $this->assertEquals(E_NOTICE, $exception->getSeverity()); - $this->assertEquals(__FILE__, $exception->getFile()); - $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); - $this->assertArrayHasKey('foobar', $exception->getContext()); - - $trace = $exception->getTrace(); - - $this->assertEquals(__FILE__, $trace[0]['file']); - $this->assertEquals(__CLASS__, $trace[0]['class']); - $this->assertEquals('triggerNotice', $trace[0]['function']); - $this->assertEquals('::', $trace[0]['type']); - - $this->assertEquals(__FILE__, $trace[0]['file']); - $this->assertEquals(__CLASS__, $trace[1]['class']); - $this->assertEquals(__FUNCTION__, $trace[1]['function']); - $this->assertEquals('->', $trace[1]['type']); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - // dummy function to test trace in error handler. - private static function triggerNotice($that) - { - // dummy variable to check for in error handler. - $foobar = 123; - $that->assertSame('', $foo.$foo.$bar); - } - - public function testConstruct() - { - try { - $handler = ErrorHandler::register(); - $handler->throwAt(3, true); - $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0)); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testDefaultLogger() - { - try { - $handler = ErrorHandler::register(); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $handler->setDefaultLogger($logger, E_NOTICE); - $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL)); - - $loggers = array( - E_DEPRECATED => array(null, LogLevel::INFO), - E_USER_DEPRECATED => array(null, LogLevel::INFO), - E_NOTICE => array($logger, LogLevel::WARNING), - E_USER_NOTICE => array($logger, LogLevel::CRITICAL), - E_STRICT => array(null, LogLevel::WARNING), - E_WARNING => array(null, LogLevel::WARNING), - E_USER_WARNING => array(null, LogLevel::WARNING), - E_COMPILE_WARNING => array(null, LogLevel::WARNING), - E_CORE_WARNING => array(null, LogLevel::WARNING), - E_USER_ERROR => array(null, LogLevel::CRITICAL), - E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), - E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), - E_PARSE => array(null, LogLevel::CRITICAL), - E_ERROR => array(null, LogLevel::CRITICAL), - E_CORE_ERROR => array(null, LogLevel::CRITICAL), - ); - $this->assertSame($loggers, $handler->setLoggers(array())); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testHandleError() - { - try { - $handler = ErrorHandler::register(); - $handler->throwAt(0, true); - $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array())); - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(3, true); - $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array())); - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(3, true); - try { - $handler->handleError(4, 'foo', 'foo.php', 12, array()); - } catch (\ErrorException $e) { - $this->assertSame('Parse Error: foo', $e->getMessage()); - $this->assertSame(4, $e->getSeverity()); - $this->assertSame('foo.php', $e->getFile()); - $this->assertSame(12, $e->getLine()); - } - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(E_USER_DEPRECATED, true); - $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); - - restore_error_handler(); - restore_exception_handler(); - - $handler = ErrorHandler::register(); - $handler->throwAt(E_DEPRECATED, true); - $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array())); - - restore_error_handler(); - restore_exception_handler(); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $warnArgCheck = function ($logLevel, $message, $context) { - $this->assertEquals('info', $logLevel); - $this->assertEquals('User Deprecated: foo', $message); - $this->assertArrayHasKey('exception', $context); - $exception = $context['exception']; - $this->assertInstanceOf(\ErrorException::class, $exception); - $this->assertSame('User Deprecated: foo', $exception->getMessage()); - $this->assertSame(E_USER_DEPRECATED, $exception->getSeverity()); - }; - - $logger - ->expects($this->once()) - ->method('log') - ->will($this->returnCallback($warnArgCheck)) - ; - - $handler = ErrorHandler::register(); - $handler->setDefaultLogger($logger, E_USER_DEPRECATED); - $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); - - restore_error_handler(); - restore_exception_handler(); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $logArgCheck = function ($level, $message, $context) { - $this->assertEquals('Notice: Undefined variable: undefVar', $message); - $this->assertArrayHasKey('exception', $context); - $exception = $context['exception']; - $this->assertInstanceOf(SilencedErrorContext::class, $exception); - $this->assertSame(E_NOTICE, $exception->getSeverity()); - }; - - $logger - ->expects($this->once()) - ->method('log') - ->will($this->returnCallback($logArgCheck)) - ; - - $handler = ErrorHandler::register(); - $handler->setDefaultLogger($logger, E_NOTICE); - $handler->screamAt(E_NOTICE); - unset($undefVar); - @$undefVar++; - - restore_error_handler(); - restore_exception_handler(); - } catch (\Exception $e) { - restore_error_handler(); - restore_exception_handler(); - - throw $e; - } - } - - public function testHandleUserError() - { - try { - $handler = ErrorHandler::register(); - $handler->throwAt(0, true); - - $e = null; - $x = new \Exception('Foo'); - - try { - $f = new Fixtures\ToStringThrower($x); - $f .= ''; // Trigger $f->__toString() - } catch (\Exception $e) { - } - - $this->assertSame($x, $e); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testHandleDeprecation() - { - $logArgCheck = function ($level, $message, $context) { - $this->assertEquals(LogLevel::INFO, $level); - $this->assertArrayHasKey('exception', $context); - $exception = $context['exception']; - $this->assertInstanceOf(\ErrorException::class, $exception); - $this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage()); - }; - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - $logger - ->expects($this->once()) - ->method('log') - ->will($this->returnCallback($logArgCheck)) - ; - - $handler = new ErrorHandler(); - $handler->setDefaultLogger($logger); - @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array()); - } - - public function testHandleException() - { - try { - $handler = ErrorHandler::register(); - - $exception = new \Exception('foo'); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $logArgCheck = function ($level, $message, $context) { - $this->assertSame('Uncaught Exception: foo', $message); - $this->assertArrayHasKey('exception', $context); - $this->assertInstanceOf(\Exception::class, $context['exception']); - }; - - $logger - ->expects($this->exactly(2)) - ->method('log') - ->will($this->returnCallback($logArgCheck)) - ; - - $handler->setDefaultLogger($logger, E_ERROR); - - try { - $handler->handleException($exception); - $this->fail('Exception expected'); - } catch (\Exception $e) { - $this->assertSame($exception, $e); - } - - $handler->setExceptionHandler(function ($e) use ($exception) { - $this->assertSame($exception, $e); - }); - - $handler->handleException($exception); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testErrorStacking() - { - try { - $handler = ErrorHandler::register(); - $handler->screamAt(E_USER_WARNING); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $logger - ->expects($this->exactly(2)) - ->method('log') - ->withConsecutive( - array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')), - array($this->equalTo(LogLevel::DEBUG), $this->equalTo('User Warning: Silenced warning')) - ) - ; - - $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING)); - - ErrorHandler::stackErrors(); - @trigger_error('Silenced warning', E_USER_WARNING); - $logger->log(LogLevel::WARNING, 'Dummy log'); - ErrorHandler::unstackErrors(); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } - - public function testBootstrappingLogger() - { - $bootLogger = new BufferingLogger(); - $handler = new ErrorHandler($bootLogger); - - $loggers = array( - E_DEPRECATED => array($bootLogger, LogLevel::INFO), - E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO), - E_NOTICE => array($bootLogger, LogLevel::WARNING), - E_USER_NOTICE => array($bootLogger, LogLevel::WARNING), - E_STRICT => array($bootLogger, LogLevel::WARNING), - E_WARNING => array($bootLogger, LogLevel::WARNING), - E_USER_WARNING => array($bootLogger, LogLevel::WARNING), - E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING), - E_CORE_WARNING => array($bootLogger, LogLevel::WARNING), - E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL), - E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL), - E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL), - E_PARSE => array($bootLogger, LogLevel::CRITICAL), - E_ERROR => array($bootLogger, LogLevel::CRITICAL), - E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL), - ); - - $this->assertSame($loggers, $handler->setLoggers(array())); - - $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array()); - - $logs = $bootLogger->cleanLogs(); - - $this->assertCount(1, $logs); - $log = $logs[0]; - $this->assertSame('info', $log[0]); - $this->assertSame('Deprecated: Foo message', $log[1]); - $this->assertArrayHasKey('exception', $log[2]); - $exception = $log[2]['exception']; - $this->assertInstanceOf(\ErrorException::class, $exception); - $this->assertSame('Deprecated: Foo message', $exception->getMessage()); - $this->assertSame(__FILE__, $exception->getFile()); - $this->assertSame(123, $exception->getLine()); - $this->assertSame(E_DEPRECATED, $exception->getSeverity()); - - $bootLogger->log(LogLevel::WARNING, 'Foo message', array('exception' => $exception)); - - $mockLogger = $this->getMock('Psr\Log\LoggerInterface'); - $mockLogger->expects($this->once()) - ->method('log') - ->with(LogLevel::WARNING, 'Foo message', array('exception' => $exception)); - - $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING))); - } - - public function testHandleFatalError() - { - try { - $handler = ErrorHandler::register(); - - $error = array( - 'type' => E_PARSE, - 'message' => 'foo', - 'file' => 'bar', - 'line' => 123, - ); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $logArgCheck = function ($level, $message, $context) { - $this->assertEquals('Fatal Parse Error: foo', $message); - $this->assertArrayHasKey('exception', $context); - $this->assertInstanceOf(\Exception::class, $context['exception']); - }; - - $logger - ->expects($this->once()) - ->method('log') - ->will($this->returnCallback($logArgCheck)) - ; - - $handler->setDefaultLogger($logger, E_PARSE); - - $handler->handleFatalError($error); - - restore_error_handler(); - restore_exception_handler(); - } catch (\Exception $e) { - restore_error_handler(); - restore_exception_handler(); - - throw $e; - } - } - - /** - * @requires PHP 7 - */ - public function testHandleErrorException() - { - $exception = new \Error("Class 'Foo' not found"); - - $handler = new ErrorHandler(); - $handler->setExceptionHandler(function () use (&$args) { - $args = func_get_args(); - }); - - $handler->handleException($exception); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); - $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); - } - - public function testHandleFatalErrorOnHHVM() - { - try { - $handler = ErrorHandler::register(); - - $logger = $this->getMock('Psr\Log\LoggerInterface'); - $logger - ->expects($this->once()) - ->method('log') - ->with( - $this->equalTo(LogLevel::CRITICAL), - $this->equalTo('Fatal Error: foo') - ) - ; - - $handler->setDefaultLogger($logger, E_ERROR); - - $error = array( - 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors - 'message' => 'foo', - 'file' => 'bar', - 'line' => 123, - 'context' => array(123), - 'backtrace' => array(456), - ); - - call_user_func_array(array($handler, 'handleError'), $error); - $handler->handleFatalError($error); - } finally { - restore_error_handler(); - restore_exception_handler(); - } - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php b/tests/integration/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php deleted file mode 100644 index aa4c2d0d1..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php +++ /dev/null @@ -1,294 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\Exception; - -use Symfony\Component\Debug\Exception\FlattenException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; -use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; -use Symfony\Component\HttpKernel\Exception\ConflictHttpException; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; -use Symfony\Component\HttpKernel\Exception\GoneHttpException; -use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException; -use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; -use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException; -use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; -use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; -use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; - -class FlattenExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testStatusCode() - { - $flattened = FlattenException::create(new \RuntimeException(), 403); - $this->assertEquals('403', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new \RuntimeException()); - $this->assertEquals('500', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new NotFoundHttpException()); - $this->assertEquals('404', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); - $this->assertEquals('401', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new BadRequestHttpException()); - $this->assertEquals('400', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new NotAcceptableHttpException()); - $this->assertEquals('406', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new ConflictHttpException()); - $this->assertEquals('409', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); - $this->assertEquals('405', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new AccessDeniedHttpException()); - $this->assertEquals('403', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new GoneHttpException()); - $this->assertEquals('410', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new LengthRequiredHttpException()); - $this->assertEquals('411', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new PreconditionFailedHttpException()); - $this->assertEquals('412', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new PreconditionRequiredHttpException()); - $this->assertEquals('428', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new ServiceUnavailableHttpException()); - $this->assertEquals('503', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new TooManyRequestsHttpException()); - $this->assertEquals('429', $flattened->getStatusCode()); - - $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException()); - $this->assertEquals('415', $flattened->getStatusCode()); - } - - public function testHeadersForHttpException() - { - $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); - $this->assertEquals(array('Allow' => 'POST'), $flattened->getHeaders()); - - $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); - $this->assertEquals(array('WWW-Authenticate' => 'Basic realm="My Realm"'), $flattened->getHeaders()); - - $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); - $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); - - $flattened = FlattenException::create(new ServiceUnavailableHttpException(120)); - $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); - - $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); - $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); - - $flattened = FlattenException::create(new TooManyRequestsHttpException(120)); - $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testFlattenHttpException(\Exception $exception, $statusCode) - { - $flattened = FlattenException::create($exception); - $flattened2 = FlattenException::create($exception); - - $flattened->setPrevious($flattened2); - - $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.'); - $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.'); - $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception'); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testPrevious(\Exception $exception, $statusCode) - { - $flattened = FlattenException::create($exception); - $flattened2 = FlattenException::create($exception); - - $flattened->setPrevious($flattened2); - - $this->assertSame($flattened2, $flattened->getPrevious()); - - $this->assertSame(array($flattened2), $flattened->getAllPrevious()); - } - - /** - * @requires PHP 7.0 - */ - public function testPreviousError() - { - $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); - - $flattened = FlattenException::create($exception)->getPrevious(); - - $this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.'); - $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.'); - $this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception'); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testLine(\Exception $exception) - { - $flattened = FlattenException::create($exception); - $this->assertSame($exception->getLine(), $flattened->getLine()); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testFile(\Exception $exception) - { - $flattened = FlattenException::create($exception); - $this->assertSame($exception->getFile(), $flattened->getFile()); - } - - /** - * @dataProvider flattenDataProvider - */ - public function testToArray(\Exception $exception, $statusCode) - { - $flattened = FlattenException::create($exception); - $flattened->setTrace(array(), 'foo.php', 123); - - $this->assertEquals(array( - array( - 'message' => 'test', - 'class' => 'Exception', - 'trace' => array(array( - 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123, - 'args' => array(), - )), - ), - ), $flattened->toArray()); - } - - public function flattenDataProvider() - { - return array( - array(new \Exception('test', 123), 500), - ); - } - - public function testArguments() - { - $dh = opendir(__DIR__); - $fh = tmpfile(); - - $incomplete = unserialize('O:14:"BogusTestClass":0:{}'); - - $exception = $this->createException(array( - (object) array('foo' => 1), - new NotFoundHttpException(), - $incomplete, - $dh, - $fh, - function () {}, - array(1, 2), - array('foo' => 123), - null, - true, - false, - 0, - 0.0, - '0', - '', - INF, - NAN, - )); - - $flattened = FlattenException::create($exception); - $trace = $flattened->getTrace(); - $args = $trace[1]['args']; - $array = $args[0][1]; - - closedir($dh); - fclose($fh); - - $i = 0; - $this->assertSame(array('object', 'stdClass'), $array[$i++]); - $this->assertSame(array('object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'), $array[$i++]); - $this->assertSame(array('incomplete-object', 'BogusTestClass'), $array[$i++]); - $this->assertSame(array('resource', defined('HHVM_VERSION') ? 'Directory' : 'stream'), $array[$i++]); - $this->assertSame(array('resource', 'stream'), $array[$i++]); - - $args = $array[$i++]; - $this->assertSame($args[0], 'object'); - $this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.'); - - $this->assertSame(array('array', array(array('integer', 1), array('integer', 2))), $array[$i++]); - $this->assertSame(array('array', array('foo' => array('integer', 123))), $array[$i++]); - $this->assertSame(array('null', null), $array[$i++]); - $this->assertSame(array('boolean', true), $array[$i++]); - $this->assertSame(array('boolean', false), $array[$i++]); - $this->assertSame(array('integer', 0), $array[$i++]); - $this->assertSame(array('float', 0.0), $array[$i++]); - $this->assertSame(array('string', '0'), $array[$i++]); - $this->assertSame(array('string', ''), $array[$i++]); - $this->assertSame(array('float', INF), $array[$i++]); - - // assertEquals() does not like NAN values. - $this->assertEquals($array[$i][0], 'float'); - $this->assertTrue(is_nan($array[$i++][1])); - } - - public function testRecursionInArguments() - { - $a = array('foo', array(2, &$a)); - $exception = $this->createException($a); - - $flattened = FlattenException::create($exception); - $trace = $flattened->getTrace(); - $this->assertContains('*DEEP NESTED ARRAY*', serialize($trace)); - } - - public function testTooBigArray() - { - $a = array(); - for ($i = 0; $i < 20; ++$i) { - for ($j = 0; $j < 50; ++$j) { - for ($k = 0; $k < 10; ++$k) { - $a[$i][$j][$k] = 'value'; - } - } - } - $a[20] = 'value'; - $a[21] = 'value1'; - $exception = $this->createException($a); - - $flattened = FlattenException::create($exception); - $trace = $flattened->getTrace(); - - $this->assertSame($trace[1]['args'][0], array('array', array('array', '*SKIPPED over 10000 entries*'))); - - $serializeTrace = serialize($trace); - - $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace); - $this->assertNotContains('*value1*', $serializeTrace); - } - - private function createException($foo) - { - return new \Exception(); - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/ExceptionHandlerTest.php b/tests/integration/vendor/symfony/debug/Tests/ExceptionHandlerTest.php deleted file mode 100644 index 1703da6eb..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/ExceptionHandlerTest.php +++ /dev/null @@ -1,132 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use Symfony\Component\Debug\ExceptionHandler; -use Symfony\Component\Debug\Exception\OutOfMemoryException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; - -require_once __DIR__.'/HeaderMock.php'; - -class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase -{ - protected function setUp() - { - testHeader(); - } - - protected function tearDown() - { - testHeader(); - } - - public function testDebug() - { - $handler = new ExceptionHandler(false); - - ob_start(); - $handler->sendPhpResponse(new \RuntimeException('Foo')); - $response = ob_get_clean(); - - $this->assertContains('

Whoops, looks like something went wrong.

', $response); - $this->assertNotContains('

', $response); - - $handler = new ExceptionHandler(true); - - ob_start(); - $handler->sendPhpResponse(new \RuntimeException('Foo')); - $response = ob_get_clean(); - - $this->assertContains('

Whoops, looks like something went wrong.

', $response); - $this->assertContains('

', $response); - } - - public function testStatusCode() - { - $handler = new ExceptionHandler(false, 'iso8859-1'); - - ob_start(); - $handler->sendPhpResponse(new NotFoundHttpException('Foo')); - $response = ob_get_clean(); - - $this->assertContains('Sorry, the page you are looking for could not be found.', $response); - - $expectedHeaders = array( - array('HTTP/1.0 404', true, null), - array('Content-Type: text/html; charset=iso8859-1', true, null), - ); - - $this->assertSame($expectedHeaders, testHeader()); - } - - public function testHeaders() - { - $handler = new ExceptionHandler(false, 'iso8859-1'); - - ob_start(); - $handler->sendPhpResponse(new MethodNotAllowedHttpException(array('POST'))); - $response = ob_get_clean(); - - $expectedHeaders = array( - array('HTTP/1.0 405', true, null), - array('Allow: POST', false, null), - array('Content-Type: text/html; charset=iso8859-1', true, null), - ); - - $this->assertSame($expectedHeaders, testHeader()); - } - - public function testNestedExceptions() - { - $handler = new ExceptionHandler(true); - ob_start(); - $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); - $response = ob_get_clean(); - - $this->assertStringMatchesFormat('%AFoo%ABar%A', $response); - } - - public function testHandle() - { - $exception = new \Exception('foo'); - - $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); - $handler - ->expects($this->exactly(2)) - ->method('sendPhpResponse'); - - $handler->handle($exception); - - $handler->setHandler(function ($e) use ($exception) { - $this->assertSame($exception, $e); - }); - - $handler->handle($exception); - } - - public function testHandleOutOfMemoryException() - { - $exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__); - - $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); - $handler - ->expects($this->once()) - ->method('sendPhpResponse'); - - $handler->setHandler(function ($e) { - $this->fail('OutOfMemoryException should bypass the handler'); - }); - - $handler->handle($exception); - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php deleted file mode 100644 index 360e6edf0..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php +++ /dev/null @@ -1,178 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; -use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; -use Symfony\Component\Debug\DebugClassLoader; -use Composer\Autoload\ClassLoader as ComposerClassLoader; - -class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase -{ - public static function setUpBeforeClass() - { - foreach (spl_autoload_functions() as $function) { - if (!is_array($function)) { - continue; - } - - // get class loaders wrapped by DebugClassLoader - if ($function[0] instanceof DebugClassLoader) { - $function = $function[0]->getClassLoader(); - } - - if ($function[0] instanceof ComposerClassLoader) { - $function[0]->add('Symfony_Component_Debug_Tests_Fixtures', dirname(dirname(dirname(dirname(dirname(__DIR__)))))); - break; - } - } - } - - /** - * @dataProvider provideClassNotFoundData - */ - public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null) - { - if ($autoloader) { - // Unregister all autoloaders to ensure the custom provided - // autoloader is the only one to be used during the test run. - $autoloaders = spl_autoload_functions(); - array_map('spl_autoload_unregister', $autoloaders); - spl_autoload_register($autoloader); - } - - $handler = new ClassNotFoundFatalErrorHandler(); - - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - if ($autoloader) { - spl_autoload_unregister($autoloader); - array_map('spl_autoload_register', $autoloaders); - } - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); - $this->assertSame($translatedMessage, $exception->getMessage()); - $this->assertSame($error['type'], $exception->getSeverity()); - $this->assertSame($error['file'], $exception->getFile()); - $this->assertSame($error['line'], $exception->getLine()); - } - - public function provideClassNotFoundData() - { - $prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception')); - - $symfonyAutoloader = new SymfonyClassLoader(); - $symfonyAutoloader->addPrefixes($prefixes); - - $debugClassLoader = new DebugClassLoader(array($symfonyAutoloader, 'loadClass')); - - return array( - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'WhizBangFactory\' not found', - ), - "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found', - ), - "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'UndefinedFunctionException\' not found', - ), - "Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'PEARClass\' not found', - ), - "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ), - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ), - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - array($symfonyAutoloader, 'loadClass'), - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ), - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", - array($debugClassLoader, 'loadClass'), - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', - ), - "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", - function ($className) { /* do nothing here */ }, - ), - ); - } - - public function testCannotRedeclareClass() - { - if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) { - $this->markTestSkipped('Can only be run on case insensitive filesystems'); - } - - require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP'; - - $error = array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found', - ); - - $handler = new ClassNotFoundFatalErrorHandler(); - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php deleted file mode 100644 index 795b74781..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; - -class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider provideUndefinedFunctionData - */ - public function testUndefinedFunction($error, $translatedMessage) - { - $handler = new UndefinedFunctionFatalErrorHandler(); - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); - // class names are case insensitive and PHP/HHVM do not return the same - $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage())); - $this->assertSame($error['type'], $exception->getSeverity()); - $this->assertSame($error['file'], $exception->getFile()); - $this->assertSame($error['line'], $exception->getLine()); - } - - public function provideUndefinedFunctionData() - { - return array( - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function test_namespaced_function()', - ), - "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', - ), - "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function foo()', - ), - 'Attempted to call function "foo" from the global namespace.', - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', - ), - 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".', - ), - ); - } -} - -function test_namespaced_function() -{ -} diff --git a/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php deleted file mode 100644 index de7b21c69..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests\FatalErrorHandler; - -use Symfony\Component\Debug\Exception\FatalErrorException; -use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; - -class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider provideUndefinedMethodData - */ - public function testUndefinedMethod($error, $translatedMessage) - { - $handler = new UndefinedMethodFatalErrorHandler(); - $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); - - $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception); - $this->assertSame($translatedMessage, $exception->getMessage()); - $this->assertSame($error['type'], $exception->getSeverity()); - $this->assertSame($error['file'], $exception->getFile()); - $this->assertSame($error['line'], $exception->getLine()); - } - - public function provideUndefinedMethodData() - { - return array( - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined method SplObjectStorage::what()', - ), - 'Attempted to call an undefined method named "what" of class "SplObjectStorage".', - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined method SplObjectStorage::walid()', - ), - "Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?", - ), - array( - array( - 'type' => 1, - 'line' => 12, - 'file' => 'foo.php', - 'message' => 'Call to undefined method SplObjectStorage::offsetFet()', - ), - "Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?", - ), - ); - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php b/tests/integration/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php deleted file mode 100644 index 9d6dbaa71..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php +++ /dev/null @@ -1,3 +0,0 @@ -exception = $e; - } - - public function __toString() - { - try { - throw $this->exception; - } catch (\Exception $e) { - // Using user_error() here is on purpose so we do not forget - // that this alias also should work alongside with trigger_error(). - return user_error($e, E_USER_ERROR); - } - } -} diff --git a/tests/integration/vendor/symfony/debug/Tests/Fixtures/casemismatch.php b/tests/integration/vendor/symfony/debug/Tests/Fixtures/casemismatch.php deleted file mode 100644 index 691d660fd..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/Fixtures/casemismatch.php +++ /dev/null @@ -1,7 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug; - -function headers_sent() -{ - return false; -} - -function header($str, $replace = true, $status = null) -{ - Tests\testHeader($str, $replace, $status); -} - -namespace Symfony\Component\Debug\Tests; - -function testHeader() -{ - static $headers = array(); - - if (!$h = func_get_args()) { - $h = $headers; - $headers = array(); - - return $h; - } - - $headers[] = func_get_args(); -} diff --git a/tests/integration/vendor/symfony/debug/Tests/MockExceptionHandler.php b/tests/integration/vendor/symfony/debug/Tests/MockExceptionHandler.php deleted file mode 100644 index 2d6ce564d..000000000 --- a/tests/integration/vendor/symfony/debug/Tests/MockExceptionHandler.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Debug\Tests; - -use Symfony\Component\Debug\ExceptionHandler; - -class MockExceptionHandler extends ExceptionHandler -{ - public $e; - - public function handle(\Exception $e) - { - $this->e = $e; - } -} diff --git a/tests/integration/vendor/symfony/debug/composer.json b/tests/integration/vendor/symfony/debug/composer.json deleted file mode 100644 index 5c40bea32..000000000 --- a/tests/integration/vendor/symfony/debug/composer.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "symfony/debug", - "type": "library", - "description": "Symfony Debug Component", - "keywords": [], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.5.9", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" - }, - "autoload": { - "psr-4": { "Symfony\\Component\\Debug\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } -} diff --git a/tests/integration/vendor/symfony/debug/phpunit.xml.dist b/tests/integration/vendor/symfony/debug/phpunit.xml.dist deleted file mode 100644 index e99c4ddf4..000000000 --- a/tests/integration/vendor/symfony/debug/phpunit.xml.dist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - ./Tests/ - - - ./Resources/ext/tests/ - - - - - - ./ - - ./Tests - ./vendor - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/.gitignore b/tests/integration/vendor/symfony/dependency-injection/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/tests/integration/vendor/symfony/dependency-injection/Alias.php b/tests/integration/vendor/symfony/dependency-injection/Alias.php index eaf7f00cc..d1c9989ec 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Alias.php +++ b/tests/integration/vendor/symfony/dependency-injection/Alias.php @@ -11,18 +11,19 @@ namespace Symfony\Component\DependencyInjection; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + class Alias { + private const DEFAULT_DEPRECATION_TEMPLATE = 'The "%alias_id%" service alias is deprecated. You should stop using it, as it will be removed in the future.'; + private $id; private $public; + private $deprecation = []; - /** - * @param string $id Alias identifier - * @param bool $public If this alias is public - */ - public function __construct($id, $public = true) + public function __construct(string $id, bool $public = false) { - $this->id = strtolower($id); + $this->id = $id; $this->public = $public; } @@ -39,11 +40,113 @@ public function isPublic() /** * Sets if this Alias is public. * - * @param bool $boolean If this Alias should be public + * @return $this + */ + public function setPublic(bool $boolean) + { + $this->public = $boolean; + + return $this; + } + + /** + * Sets if this Alias is private. + * + * @return $this + * + * @deprecated since Symfony 5.2, use setPublic() instead + */ + public function setPrivate(bool $boolean) + { + trigger_deprecation('symfony/dependency-injection', '5.2', 'The "%s()" method is deprecated, use "setPublic()" instead.', __METHOD__); + + return $this->setPublic(!$boolean); + } + + /** + * Whether this alias is private. + * + * @return bool + */ + public function isPrivate() + { + return !$this->public; + } + + /** + * Whether this alias is deprecated, that means it should not be referenced + * anymore. + * + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The deprecation message to use + * + * @return $this + * + * @throws InvalidArgumentException when the message template is invalid + */ + public function setDeprecated(/* string $package, string $version, string $message */) + { + $args = \func_get_args(); + + if (\func_num_args() < 3) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + $status = $args[0] ?? true; + + if (!$status) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); + } + + $message = (string) ($args[1] ?? null); + $package = $version = ''; + } else { + $status = true; + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) $args[2]; + } + + if ('' !== $message) { + if (preg_match('#[\r\n]|\*/#', $message)) { + throw new InvalidArgumentException('Invalid characters found in deprecation template.'); + } + + if (!str_contains($message, '%alias_id%')) { + throw new InvalidArgumentException('The deprecation template must contain the "%alias_id%" placeholder.'); + } + } + + $this->deprecation = $status ? ['package' => $package, 'version' => $version, 'message' => $message ?: self::DEFAULT_DEPRECATION_TEMPLATE] : []; + + return $this; + } + + public function isDeprecated(): bool + { + return (bool) $this->deprecation; + } + + /** + * @deprecated since Symfony 5.1, use "getDeprecation()" instead. + */ + public function getDeprecationMessage(string $id): string + { + trigger_deprecation('symfony/dependency-injection', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); + + return $this->getDeprecation($id)['message']; + } + + /** + * @param string $id Service id relying on this definition */ - public function setPublic($boolean) + public function getDeprecation(string $id): array { - $this->public = (bool) $boolean; + return [ + 'package' => $this->deprecation['package'], + 'version' => $this->deprecation['version'], + 'message' => str_replace('%alias_id%', $id, $this->deprecation['message']), + ]; } /** diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/AbstractArgument.php b/tests/integration/vendor/symfony/dependency-injection/Argument/AbstractArgument.php new file mode 100644 index 000000000..3ba5ff33b --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/AbstractArgument.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * Represents an abstract service argument, which have to be set by a compiler pass or a DI extension. + */ +final class AbstractArgument +{ + private $text; + private $context; + + public function __construct(string $text = '') + { + $this->text = trim($text, '. '); + } + + public function setContext(string $context): void + { + $this->context = $context.' is abstract'.('' === $this->text ? '' : ': '); + } + + public function getText(): string + { + return $this->text; + } + + public function getTextWithContext(): string + { + return $this->context.$this->text.'.'; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php b/tests/integration/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php new file mode 100644 index 000000000..b46eb77be --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * Represents a complex argument containing nested values. + * + * @author Titouan Galopin + */ +interface ArgumentInterface +{ + /** + * @return array + */ + public function getValues(); + + public function setValues(array $values); +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/BoundArgument.php b/tests/integration/vendor/symfony/dependency-injection/Argument/BoundArgument.php new file mode 100644 index 000000000..c2afe2cfa --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/BoundArgument.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * @author Guilhem Niot + */ +final class BoundArgument implements ArgumentInterface +{ + public const SERVICE_BINDING = 0; + public const DEFAULTS_BINDING = 1; + public const INSTANCEOF_BINDING = 2; + + private static $sequence = 0; + + private $value; + private $identifier; + private $used; + private $type; + private $file; + + public function __construct($value, bool $trackUsage = true, int $type = 0, string $file = null) + { + $this->value = $value; + if ($trackUsage) { + $this->identifier = ++self::$sequence; + } else { + $this->used = true; + } + $this->type = $type; + $this->file = $file; + } + + /** + * {@inheritdoc} + */ + public function getValues(): array + { + return [$this->value, $this->identifier, $this->used, $this->type, $this->file]; + } + + /** + * {@inheritdoc} + */ + public function setValues(array $values) + { + if (5 === \count($values)) { + [$this->value, $this->identifier, $this->used, $this->type, $this->file] = $values; + } else { + [$this->value, $this->identifier, $this->used] = $values; + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/IteratorArgument.php b/tests/integration/vendor/symfony/dependency-injection/Argument/IteratorArgument.php new file mode 100644 index 000000000..d413678a1 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/IteratorArgument.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * Represents a collection of values to lazily iterate over. + * + * @author Titouan Galopin + */ +class IteratorArgument implements ArgumentInterface +{ + use ReferenceSetArgumentTrait; +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php b/tests/integration/vendor/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php new file mode 100644 index 000000000..777e40566 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Titouan Galopin + * @author Nicolas Grekas + */ +trait ReferenceSetArgumentTrait +{ + private $values; + + /** + * @param Reference[] $values + */ + public function __construct(array $values) + { + $this->setValues($values); + } + + /** + * @return Reference[] The values in the set + */ + public function getValues() + { + return $this->values; + } + + /** + * @param Reference[] $values The service references to put in the set + */ + public function setValues(array $values) + { + foreach ($values as $k => $v) { + if (null !== $v && !$v instanceof Reference) { + throw new InvalidArgumentException(sprintf('A "%s" must hold only Reference instances, "%s" given.', __CLASS__, get_debug_type($v))); + } + } + + $this->values = $values; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php b/tests/integration/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php new file mode 100644 index 000000000..41fec786f --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * @internal + */ +class RewindableGenerator implements \IteratorAggregate, \Countable +{ + private $generator; + private $count; + + /** + * @param int|callable $count + */ + public function __construct(callable $generator, $count) + { + $this->generator = $generator; + $this->count = $count; + } + + public function getIterator(): \Traversable + { + $g = $this->generator; + + return $g(); + } + + public function count(): int + { + if (\is_callable($count = $this->count)) { + $this->count = $count(); + } + + return $this->count; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php b/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php new file mode 100644 index 000000000..6331affa4 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Represents a service wrapped in a memoizing closure. + * + * @author Nicolas Grekas + */ +class ServiceClosureArgument implements ArgumentInterface +{ + private $values; + + public function __construct(Reference $reference) + { + $this->values = [$reference]; + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function setValues(array $values) + { + if ([0] !== array_keys($values) || !($values[0] instanceof Reference || null === $values[0])) { + throw new InvalidArgumentException('A ServiceClosureArgument must hold one and only one Reference.'); + } + + $this->values = $values; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceLocator.php b/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceLocator.php new file mode 100644 index 000000000..4f3c19eb3 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceLocator.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class ServiceLocator extends BaseServiceLocator +{ + private $factory; + private $serviceMap; + private $serviceTypes; + + public function __construct(\Closure $factory, array $serviceMap, array $serviceTypes = null) + { + $this->factory = $factory; + $this->serviceMap = $serviceMap; + $this->serviceTypes = $serviceTypes; + parent::__construct($serviceMap); + } + + /** + * {@inheritdoc} + * + * @return mixed + */ + public function get($id) + { + return isset($this->serviceMap[$id]) ? ($this->factory)(...$this->serviceMap[$id]) : parent::get($id); + } + + /** + * {@inheritdoc} + */ + public function getProvidedServices(): array + { + return $this->serviceTypes ?? $this->serviceTypes = array_map(function () { return '?'; }, $this->serviceMap); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceLocatorArgument.php b/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceLocatorArgument.php new file mode 100644 index 000000000..fcbf478c6 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/ServiceLocatorArgument.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\Reference; + +/** + * Represents a closure acting as a service locator. + * + * @author Nicolas Grekas + */ +class ServiceLocatorArgument implements ArgumentInterface +{ + use ReferenceSetArgumentTrait; + + private $taggedIteratorArgument; + + /** + * @param Reference[]|TaggedIteratorArgument $values + */ + public function __construct($values = []) + { + if ($values instanceof TaggedIteratorArgument) { + $this->taggedIteratorArgument = $values; + $this->values = []; + } else { + $this->setValues($values); + } + } + + public function getTaggedIteratorArgument(): ?TaggedIteratorArgument + { + return $this->taggedIteratorArgument; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php b/tests/integration/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php new file mode 100644 index 000000000..1ba8de790 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Argument; + +/** + * Represents a collection of services found by tag name to lazily iterate over. + * + * @author Roland Franssen + */ +class TaggedIteratorArgument extends IteratorArgument +{ + private $tag; + private $indexAttribute; + private $defaultIndexMethod; + private $defaultPriorityMethod; + private $needsIndexes = false; + + /** + * @param string $tag The name of the tag identifying the target services + * @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection + * @param string|null $defaultIndexMethod The static method that should be called to get each service's key when their tag doesn't define the previous attribute + * @param bool $needsIndexes Whether indexes are required and should be generated when computing the map + * @param string|null $defaultPriorityMethod The static method that should be called to get each service's priority when their tag doesn't define the "priority" attribute + */ + public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null) + { + parent::__construct([]); + + if (null === $indexAttribute && $needsIndexes) { + $indexAttribute = preg_match('/[^.]++$/', $tag, $m) ? $m[0] : $tag; + } + + $this->tag = $tag; + $this->indexAttribute = $indexAttribute; + $this->defaultIndexMethod = $defaultIndexMethod ?: ($indexAttribute ? 'getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute))).'Name' : null); + $this->needsIndexes = $needsIndexes; + $this->defaultPriorityMethod = $defaultPriorityMethod ?: ($indexAttribute ? 'getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute))).'Priority' : null); + } + + public function getTag() + { + return $this->tag; + } + + public function getIndexAttribute(): ?string + { + return $this->indexAttribute; + } + + public function getDefaultIndexMethod(): ?string + { + return $this->defaultIndexMethod; + } + + public function needsIndexes(): bool + { + return $this->needsIndexes; + } + + public function getDefaultPriorityMethod(): ?string + { + return $this->defaultPriorityMethod; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/CHANGELOG.md b/tests/integration/vendor/symfony/dependency-injection/CHANGELOG.md index 43f445736..4da43e0a6 100644 --- a/tests/integration/vendor/symfony/dependency-injection/CHANGELOG.md +++ b/tests/integration/vendor/symfony/dependency-injection/CHANGELOG.md @@ -1,6 +1,210 @@ CHANGELOG ========= +5.2.0 +----- + + * added `param()` and `abstract_arg()` in the PHP-DSL + * deprecated `Definition::setPrivate()` and `Alias::setPrivate()`, use `setPublic()` instead + * added support for the `#[Required]` attribute + +5.1.0 +----- + + * deprecated `inline()` in favor of `inline_service()` and `ref()` in favor of `service()` when using the PHP-DSL + * allow decorators to reference their decorated service using the special `.inner` id + * added support to autowire public typed properties in php 7.4 + * added support for defining method calls, a configurator, and property setters in `InlineServiceConfigurator` + * added possibility to define abstract service arguments + * allowed mixing "parent" and instanceof-conditionals/defaults/bindings + * updated the signature of method `Definition::setDeprecated()` to `Definition::setDeprecation(string $package, string $version, string $message)` + * updated the signature of method `Alias::setDeprecated()` to `Alias::setDeprecation(string $package, string $version, string $message)` + * updated the signature of method `DeprecateTrait::deprecate()` to `DeprecateTrait::deprecation(string $package, string $version, string $message)` + * deprecated the `Psr\Container\ContainerInterface` and `Symfony\Component\DependencyInjection\ContainerInterface` aliases of the `service_container` service, + configure them explicitly instead + * added class `Symfony\Component\DependencyInjection\Dumper\Preloader` to help with preloading on PHP 7.4+ + * added tags `container.preload`/`.no_preload` to declare extra classes to preload/services to not preload + * allowed loading and dumping tags with an attribute named "name" + * deprecated `Definition::getDeprecationMessage()`, use `Definition::getDeprecation()` instead + * deprecated `Alias::getDeprecationMessage()`, use `Alias::getDeprecation()` instead + * added support of PHP8 static return type for withers + * added `AliasDeprecatedPublicServicesPass` to deprecate public services to private + +5.0.0 +----- + + * removed support for auto-discovered extension configuration class which does not implement `ConfigurationInterface` + * removed support for non-string default env() parameters + * moved `ServiceSubscriberInterface` to the `Symfony\Contracts\Service` namespace + * removed `RepeatedPass` and `RepeatablePassInterface` + * removed support for short factory/configurator syntax from `YamlFileLoader` + * removed `ResettableContainerInterface`, use `ResetInterface` instead + * added argument `$returnsClone` to `Definition::addMethodCall()` + * removed `tagged`, use `tagged_iterator` instead + +4.4.0 +----- + + * added `CheckTypeDeclarationsPass` to check injected parameters type during compilation + * added support for opcache.preload by generating a preloading script in the cache folder + * added support for dumping the container in one file instead of many files + * deprecated support for short factories and short configurators in Yaml + * added `tagged_iterator` alias for `tagged` which might be deprecated in a future version + * deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` + * added support for binding iterable and tagged services + * made singly-implemented interfaces detection be scoped by file + * added ability to define a static priority method for tagged service + * added support for improved syntax to define method calls in Yaml + * made the `%env(base64:...)%` processor able to decode base64url + * added ability to choose behavior of decorations on non existent decorated services + +4.3.0 +----- + + * added `%env(trim:...)%` processor to trim a string value + * added `%env(default:param_name:...)%` processor to fallback to a parameter or to null when using `%env(default::...)%` + * added `%env(url:...)%` processor to convert an URL or DNS into an array of components + * added `%env(query_string:...)%` processor to convert a query string into an array of key values + * added support for deprecating aliases + * made `ContainerParametersResource` final and not implement `Serializable` anymore + * added `ReverseContainer`: a container that turns services back to their ids + * added ability to define an index for a tagged collection + * added ability to define an index for services in an injected service locator argument + * made `ServiceLocator` implement `ServiceProviderInterface` + * deprecated support for non-string default env() parameters + * added `%env(require:...)%` processor to `require()` a PHP file and use the value returned from it + +4.2.0 +----- + + * added `ContainerBuilder::registerAliasForArgument()` to support autowiring by type+name + * added support for binding by type+name + * added `ServiceSubscriberTrait` to ease implementing `ServiceSubscriberInterface` using methods' return types + * added `ServiceLocatorArgument` and `!service_locator` config tag for creating optimized service-locators + * added support for autoconfiguring bindings + * added `%env(key:...)%` processor to fetch a specific key from an array + * deprecated `ServiceSubscriberInterface`, use the same interface from the `Symfony\Contracts\Service` namespace instead + * deprecated `ResettableContainerInterface`, use `Symfony\Contracts\Service\ResetInterface` instead + +4.1.0 +----- + + * added support for variadics in named arguments + * added PSR-11 `ContainerBagInterface` and its `ContainerBag` implementation to access parameters as-a-service + * added support for service's decorators autowiring + * deprecated the `TypedReference::canBeAutoregistered()` and `TypedReference::getRequiringClass()` methods + * environment variables are validated when used in extension configuration + * deprecated support for auto-discovered extension configuration class which does not implement `ConfigurationInterface` + +4.0.0 +----- + + * Relying on service auto-registration while autowiring is not supported anymore. + Explicitly inject your dependencies or create services whose ids are + their fully-qualified class name. + + Before: + + ```php + namespace App\Controller; + + use App\Mailer; + + class DefaultController + { + public function __construct(Mailer $mailer) { + // ... + } + + // ... + } + ``` + ```yml + services: + App\Controller\DefaultController: + autowire: true + ``` + + After: + + ```php + // same PHP code + ``` + ```yml + services: + App\Controller\DefaultController: + autowire: true + + # or + # App\Controller\DefaultController: + # arguments: { $mailer: "@App\Mailer" } + + App\Mailer: + autowire: true + ``` + * removed autowiring services based on the types they implement + * added a third `$methodName` argument to the `getProxyFactoryCode()` method + of the `DumperInterface` + * removed support for autowiring types + * removed `Container::isFrozen` + * removed support for dumping an ucompiled container in `PhpDumper` + * removed support for generating a dumped `Container` without populating the method map + * removed support for case insensitive service identifiers + * removed the `DefinitionDecorator` class, replaced by `ChildDefinition` + * removed the `AutowireServiceResource` class and related `AutowirePass::createResourceForClass()` method + * removed `LoggingFormatter`, `Compiler::getLoggingFormatter()` and `addLogMessage()` class and methods, use the `ContainerBuilder::log()` method instead + * removed `FactoryReturnTypePass` + * removed `ContainerBuilder::addClassResource()`, use the `addObjectResource()` or the `getReflectionClass()` method instead. + * removed support for top-level anonymous services + * removed silent behavior for unused attributes and elements + * removed support for setting and accessing private services in `Container` + * removed support for setting pre-defined services in `Container` + * removed support for case insensitivity of parameter names + * removed `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead + +3.4.0 +----- + + * moved the `ExtensionCompilerPass` to before-optimization passes with priority -1000 + * deprecated "public-by-default" definitions and aliases, the new default will be "private" in 4.0 + * added `EnvVarProcessorInterface` and corresponding "container.env_var_processor" tag for processing env vars + * added support for ignore-on-uninitialized references + * deprecated service auto-registration while autowiring + * deprecated the ability to check for the initialization of a private service with the `Container::initialized()` method + * deprecated support for top-level anonymous services in XML + * deprecated case insensitivity of parameter names + * deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass` + * added `TaggedIteratorArgument` with YAML (`!tagged foo`) and XML (``) support + * deprecated `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead + +3.3.0 +----- + + * deprecated autowiring services based on the types they implement; + rename (or alias) your services to their FQCN id to make them autowirable + * added "ServiceSubscriberInterface" - to allow for per-class explicit service-locator definitions + * added "container.service_locator" tag for defining service-locator services + * added anonymous services support in YAML configuration files using the `!service` tag. + * added "TypedReference" and "ServiceClosureArgument" for creating service-locator services + * added `ServiceLocator` - a PSR-11 container holding a set of services to be lazily loaded + * added "instanceof" section for local interface-defined configs + * added prototype services for PSR4-based discovery and registration + * added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info + * deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead + * added `ContainerBuilder::fileExists()` for checking and tracking file or directory existence + * deprecated autowiring-types, use aliases instead + * added support for omitting the factory class name in a service definition if the definition class is set + * deprecated case insensitivity of service identifiers + * added "iterator" argument type for lazy iteration over a set of values and services + * added file-wide configurable defaults for service attributes "public", "tags", + "autowire" and "autoconfigure" + * made the "class" attribute optional, using the "id" as fallback + * using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and + will not be supported anymore in 4.0 + * deprecated the `DefinitionDecorator` class in favor of `ChildDefinition` + * allow config files to be loaded using a glob pattern + * [BC BREAK] the `NullDumper` class is now final + 3.2.0 ----- @@ -9,6 +213,7 @@ CHANGELOG * deprecated the ability to set or unset a private service with the `Container::set()` method * deprecated the ability to check for the existence of a private service with the `Container::has()` method * deprecated the ability to request a private service with the `Container::get()` method + * deprecated support for generating a dumped `Container` without populating the method map 3.0.0 ----- @@ -40,8 +245,8 @@ CHANGELOG 2.5.0 ----- -* added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService()) -* deprecated SimpleXMLElement class. + * added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService()) + * deprecated SimpleXMLElement class. 2.4.0 ----- diff --git a/tests/integration/vendor/symfony/dependency-injection/ChildDefinition.php b/tests/integration/vendor/symfony/dependency-injection/ChildDefinition.php new file mode 100644 index 000000000..868cd1413 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/ChildDefinition.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +/** + * This definition extends another definition. + * + * @author Johannes M. Schmitt + */ +class ChildDefinition extends Definition +{ + private $parent; + + /** + * @param string $parent The id of Definition instance to decorate + */ + public function __construct(string $parent) + { + $this->parent = $parent; + } + + /** + * Returns the Definition to inherit from. + * + * @return string + */ + public function getParent() + { + return $this->parent; + } + + /** + * Sets the Definition to inherit from. + * + * @return $this + */ + public function setParent(string $parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Gets an argument to pass to the service constructor/factory method. + * + * If replaceArgument() has been used to replace an argument, this method + * will return the replacement value. + * + * @param int|string $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + */ + public function getArgument($index) + { + if (\array_key_exists('index_'.$index, $this->arguments)) { + return $this->arguments['index_'.$index]; + } + + return parent::getArgument($index); + } + + /** + * You should always use this method when overwriting existing arguments + * of the parent definition. + * + * If you directly call setArguments() keep in mind that you must follow + * certain conventions when you want to overwrite the arguments of the + * parent definition, otherwise your arguments will only be appended. + * + * @param int|string $index + * @param mixed $value + * + * @return $this + * + * @throws InvalidArgumentException when $index isn't an integer + */ + public function replaceArgument($index, $value) + { + if (\is_int($index)) { + $this->arguments['index_'.$index] = $value; + } elseif (str_starts_with($index, '$')) { + $this->arguments[$index] = $value; + } else { + throw new InvalidArgumentException('The argument must be an existing index or the name of a constructor\'s parameter.'); + } + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php new file mode 100644 index 000000000..85478da5e --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractRecursivePass implements CompilerPassInterface +{ + /** + * @var ContainerBuilder + */ + protected $container; + protected $currentId; + + private $processExpressions = false; + private $expressionLanguage; + private $inExpression = false; + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + + try { + $this->processValue($container->getDefinitions(), true); + } finally { + $this->container = null; + } + } + + protected function enableExpressionProcessing() + { + $this->processExpressions = true; + } + + protected function inExpression(bool $reset = true): bool + { + $inExpression = $this->inExpression; + if ($reset) { + $this->inExpression = false; + } + + return $inExpression; + } + + /** + * Processes a value found in a definition tree. + * + * @param mixed $value + * + * @return mixed The processed value + */ + protected function processValue($value, bool $isRoot = false) + { + if (\is_array($value)) { + foreach ($value as $k => $v) { + if ($isRoot) { + $this->currentId = $k; + } + if ($v !== $processedValue = $this->processValue($v, $isRoot)) { + $value[$k] = $processedValue; + } + } + } elseif ($value instanceof ArgumentInterface) { + $value->setValues($this->processValue($value->getValues())); + } elseif ($value instanceof Expression && $this->processExpressions) { + $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']); + } elseif ($value instanceof Definition) { + $value->setArguments($this->processValue($value->getArguments())); + $value->setProperties($this->processValue($value->getProperties())); + $value->setMethodCalls($this->processValue($value->getMethodCalls())); + + $changes = $value->getChanges(); + if (isset($changes['factory'])) { + $value->setFactory($this->processValue($value->getFactory())); + } + if (isset($changes['configurator'])) { + $value->setConfigurator($this->processValue($value->getConfigurator())); + } + } + + return $value; + } + + /** + * @return \ReflectionFunctionAbstract|null + * + * @throws RuntimeException + */ + protected function getConstructor(Definition $definition, bool $required) + { + if ($definition->isSynthetic()) { + return null; + } + + if (\is_string($factory = $definition->getFactory())) { + if (!\function_exists($factory)) { + throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory)); + } + $r = new \ReflectionFunction($factory); + if (false !== $r->getFileName() && file_exists($r->getFileName())) { + $this->container->fileExists($r->getFileName()); + } + + return $r; + } + + if ($factory) { + [$class, $method] = $factory; + if ($class instanceof Reference) { + $class = $this->container->findDefinition((string) $class)->getClass(); + } elseif ($class instanceof Definition) { + $class = $class->getClass(); + } elseif (null === $class) { + $class = $definition->getClass(); + } + + if ('__construct' === $method) { + throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId)); + } + + return $this->getReflectionMethod(new Definition($class), $method); + } + + $class = $definition->getClass(); + + try { + if (!$r = $this->container->getReflectionClass($class)) { + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); + } + } catch (\ReflectionException $e) { + throw new RuntimeException(sprintf('Invalid service "%s": ', $this->currentId).lcfirst($e->getMessage())); + } + if (!$r = $r->getConstructor()) { + if ($required) { + throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class))); + } + } elseif (!$r->isPublic()) { + throw new RuntimeException(sprintf('Invalid service "%s": ', $this->currentId).sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class).' must be public.'); + } + + return $r; + } + + /** + * @throws RuntimeException + * + * @return \ReflectionFunctionAbstract + */ + protected function getReflectionMethod(Definition $definition, string $method) + { + if ('__construct' === $method) { + return $this->getConstructor($definition, true); + } + + if (!$class = $definition->getClass()) { + throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId)); + } + + if (!$r = $this->container->getReflectionClass($class)) { + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); + } + + if (!$r->hasMethod($method)) { + throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); + } + + $r = $r->getMethod($method); + if (!$r->isPublic()) { + throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); + } + + return $r; + } + + private function getExpressionLanguage(): ExpressionLanguage + { + if (null === $this->expressionLanguage) { + if (!class_exists(ExpressionLanguage::class)) { + throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + + $providers = $this->container->getExpressionLanguageProviders(); + $this->expressionLanguage = new ExpressionLanguage(null, $providers, function (string $arg): string { + if ('""' === substr_replace($arg, '', 1, -1)) { + $id = stripcslashes(substr($arg, 1, -1)); + $this->inExpression = true; + $arg = $this->processValue(new Reference($id)); + $this->inExpression = false; + if (!$arg instanceof Reference) { + throw new RuntimeException(sprintf('"%s::processValue()" must return a Reference when processing an expression, "%s" returned for service("%s").', static::class, get_debug_type($arg), $id)); + } + $arg = sprintf('"%s"', $arg); + } + + return sprintf('$this->get(%s)', $arg); + }); + } + + return $this->expressionLanguage; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AliasDeprecatedPublicServicesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AliasDeprecatedPublicServicesPass.php new file mode 100644 index 000000000..a44767de3 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AliasDeprecatedPublicServicesPass.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +final class AliasDeprecatedPublicServicesPass extends AbstractRecursivePass +{ + private $tagName; + + private $aliases = []; + + public function __construct(string $tagName = 'container.private') + { + $this->tagName = $tagName; + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Reference && isset($this->aliases[$id = (string) $value])) { + return new Reference($this->aliases[$id], $value->getInvalidBehavior()); + } + + return parent::processValue($value, $isRoot); + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->findTaggedServiceIds($this->tagName) as $id => $tags) { + if (null === $package = $tags[0]['package'] ?? null) { + throw new InvalidArgumentException(sprintf('The "package" attribute is mandatory for the "%s" tag on the "%s" service.', $this->tagName, $id)); + } + + if (null === $version = $tags[0]['version'] ?? null) { + throw new InvalidArgumentException(sprintf('The "version" attribute is mandatory for the "%s" tag on the "%s" service.', $this->tagName, $id)); + } + + $definition = $container->getDefinition($id); + if (!$definition->isPublic() || $definition->isPrivate()) { + continue; + } + + $container + ->setAlias($id, $aliasId = '.'.$this->tagName.'.'.$id) + ->setPublic(true) + ->setDeprecated($package, $version, 'Accessing the "%alias_id%" service directly from the container is deprecated, use dependency injection instead.'); + + $container->setDefinition($aliasId, $definition); + + $this->aliases[$id] = $aliasId; + } + + parent::process($container); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php index 717fc378e..f7dbe6c8a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php @@ -11,9 +11,12 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Run this pass before passes that need to know more about the relation of @@ -23,124 +26,166 @@ * retrieve the graph in other passes from the compiler. * * @author Johannes M. Schmitt + * @author Nicolas Grekas */ -class AnalyzeServiceReferencesPass implements RepeatablePassInterface +class AnalyzeServiceReferencesPass extends AbstractRecursivePass { private $graph; - private $container; - private $currentId; private $currentDefinition; - private $repeatedPass; private $onlyConstructorArguments; + private $hasProxyDumper; + private $lazy; + private $byConstructor; + private $byFactory; + private $definitions; + private $aliases; /** * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls */ - public function __construct($onlyConstructorArguments = false) - { - $this->onlyConstructorArguments = (bool) $onlyConstructorArguments; - } - - /** - * {@inheritdoc} - */ - public function setRepeatedPass(RepeatedPass $repeatedPass) + public function __construct(bool $onlyConstructorArguments = false, bool $hasProxyDumper = true) { - $this->repeatedPass = $repeatedPass; + $this->onlyConstructorArguments = $onlyConstructorArguments; + $this->hasProxyDumper = $hasProxyDumper; + $this->enableExpressionProcessing(); } /** * Processes a ContainerBuilder object to populate the service reference graph. - * - * @param ContainerBuilder $container */ public function process(ContainerBuilder $container) { $this->container = $container; $this->graph = $container->getCompiler()->getServiceReferenceGraph(); $this->graph->clear(); + $this->lazy = false; + $this->byConstructor = false; + $this->byFactory = false; + $this->definitions = $container->getDefinitions(); + $this->aliases = $container->getAliases(); + + foreach ($this->aliases as $id => $alias) { + $targetId = $this->getDefinitionId((string) $alias); + $this->graph->connect($id, $alias, $targetId, null !== $targetId ? $this->container->getDefinition($targetId) : null, null); + } - foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isSynthetic() || $definition->isAbstract()) { - continue; - } + try { + parent::process($container); + } finally { + $this->aliases = $this->definitions = []; + } + } + + protected function processValue($value, bool $isRoot = false) + { + $lazy = $this->lazy; + $inExpression = $this->inExpression(); - $this->currentId = $id; - $this->currentDefinition = $definition; + if ($value instanceof ArgumentInterface) { + $this->lazy = !$this->byFactory || !$value instanceof IteratorArgument; + parent::processValue($value->getValues()); + $this->lazy = $lazy; - $this->processArguments($definition->getArguments()); - if (is_array($definition->getFactory())) { - $this->processArguments($definition->getFactory()); + return $value; + } + if ($value instanceof Reference) { + $targetId = $this->getDefinitionId((string) $value); + $targetDefinition = null !== $targetId ? $this->container->getDefinition($targetId) : null; + + $this->graph->connect( + $this->currentId, + $this->currentDefinition, + $targetId, + $targetDefinition, + $value, + $this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()), + ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(), + $this->byConstructor + ); + + if ($inExpression) { + $this->graph->connect( + '.internal.reference_in_expression', + null, + $targetId, + $targetDefinition, + $value, + $this->lazy || ($targetDefinition && $targetDefinition->isLazy()), + true + ); } - if (!$this->onlyConstructorArguments) { - $this->processArguments($definition->getMethodCalls()); - $this->processArguments($definition->getProperties()); - if ($definition->getConfigurator()) { - $this->processArguments(array($definition->getConfigurator())); - } + return $value; + } + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + if ($isRoot) { + if ($value->isSynthetic() || $value->isAbstract()) { + return $value; } + $this->currentDefinition = $value; + } elseif ($this->currentDefinition === $value) { + return $value; } + $this->lazy = false; + + $byConstructor = $this->byConstructor; + $this->byConstructor = $isRoot || $byConstructor; + + $byFactory = $this->byFactory; + $this->byFactory = true; + $this->processValue($value->getFactory()); + $this->byFactory = $byFactory; + $this->processValue($value->getArguments()); - foreach ($container->getAliases() as $id => $alias) { - $this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null); + $properties = $value->getProperties(); + $setters = $value->getMethodCalls(); + + // Any references before a "wither" are part of the constructor-instantiation graph + $lastWitherIndex = null; + foreach ($setters as $k => $call) { + if ($call[2] ?? false) { + $lastWitherIndex = $k; + } } - } - /** - * Processes service definitions for arguments to find relationships for the service graph. - * - * @param array $arguments An array of Reference or Definition objects relating to service definitions - */ - private function processArguments(array $arguments) - { - foreach ($arguments as $argument) { - if (is_array($argument)) { - $this->processArguments($argument); - } elseif ($argument instanceof Reference) { - $this->graph->connect( - $this->currentId, - $this->currentDefinition, - $this->getDefinitionId((string) $argument), - $this->getDefinition((string) $argument), - $argument - ); - } elseif ($argument instanceof Definition) { - $this->processArguments($argument->getArguments()); - $this->processArguments($argument->getMethodCalls()); - $this->processArguments($argument->getProperties()); - - if (is_array($argument->getFactory())) { - $this->processArguments($argument->getFactory()); + if (null !== $lastWitherIndex) { + $this->processValue($properties); + $setters = $properties = []; + + foreach ($value->getMethodCalls() as $k => $call) { + if (null === $lastWitherIndex) { + $setters[] = $call; + continue; + } + + if ($lastWitherIndex === $k) { + $lastWitherIndex = null; } + + $this->processValue($call); } } - } - /** - * Returns a service definition given the full name or an alias. - * - * @param string $id A full id or alias for a service definition - * - * @return Definition|null The definition related to the supplied id - */ - private function getDefinition($id) - { - $id = $this->getDefinitionId($id); + $this->byConstructor = $byConstructor; - return null === $id ? null : $this->container->getDefinition($id); + if (!$this->onlyConstructorArguments) { + $this->processValue($properties); + $this->processValue($setters); + $this->processValue($value->getConfigurator()); + } + $this->lazy = $lazy; + + return $value; } - private function getDefinitionId($id) + private function getDefinitionId(string $id): ?string { - while ($this->container->hasAlias($id)) { - $id = (string) $this->container->getAlias($id); - } - - if (!$this->container->hasDefinition($id)) { - return; + while (isset($this->aliases[$id])) { + $id = (string) $this->aliases[$id]; } - return $id; + return isset($this->definitions[$id]) ? $id : null; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php index c1f05e03e..03420683a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php @@ -33,7 +33,7 @@ public function process(ContainerBuilder $container) $aliasId = $container->getParameterBag()->resolveValue($tag['format']); if ($container->hasDefinition($aliasId) || $container->hasAlias($aliasId)) { - $container->setAlias($serviceId, new Alias($aliasId)); + $container->setAlias($serviceId, new Alias($aliasId, true)); } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowirePass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowirePass.php index d1b1fd292..052cfc49a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowirePass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowirePass.php @@ -11,184 +11,330 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; +use Symfony\Component\Config\Resource\ClassExistenceResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; -use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\TypedReference; /** - * Guesses constructor arguments of services definitions and try to instantiate services if necessary. + * Inspects existing service definitions and wires the autowired ones using the type hints of their classes. * * @author Kévin Dunglas + * @author Nicolas Grekas */ -class AutowirePass implements CompilerPassInterface +class AutowirePass extends AbstractRecursivePass { - private $container; - private $reflectionClasses = array(); - private $definedTypes = array(); private $types; - private $ambiguousServiceTypes = array(); + private $ambiguousServiceTypes; + private $lastFailure; + private $throwOnAutowiringException; + private $decoratedClass; + private $decoratedId; + private $methodCalls; + private $getPreviousValue; + private $decoratedMethodIndex; + private $decoratedMethodArgumentIndex; + private $typesClone; + + public function __construct(bool $throwOnAutowireException = true) + { + $this->throwOnAutowiringException = $throwOnAutowireException; + } /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { - $throwingAutoloader = function ($class) { throw new \ReflectionException(sprintf('Class %s does not exist', $class)); }; - spl_autoload_register($throwingAutoloader); + try { + $this->typesClone = clone $this; + parent::process($container); + } finally { + $this->decoratedClass = null; + $this->decoratedId = null; + $this->methodCalls = null; + $this->getPreviousValue = null; + $this->decoratedMethodIndex = null; + $this->decoratedMethodArgumentIndex = null; + $this->typesClone = null; + } + } + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { try { - $this->container = $container; - foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isAutowired()) { - $this->completeDefinition($id, $definition); - } + return $this->doProcessValue($value, $isRoot); + } catch (AutowiringFailedException $e) { + if ($this->throwOnAutowiringException) { + throw $e; } - } finally { - spl_autoload_unregister($throwingAutoloader); - // Free memory and remove circular reference to container - $this->container = null; - $this->reflectionClasses = array(); - $this->definedTypes = array(); - $this->types = null; - $this->ambiguousServiceTypes = array(); + $this->container->getDefinition($this->currentId)->addError($e->getMessageCallback() ?? $e->getMessage()); + + return parent::processValue($value, $isRoot); } } /** - * Creates a resource to help know if this service has changed. - * - * @param \ReflectionClass $reflectionClass - * - * @return AutowireServiceResource + * @return mixed */ - public static function createResourceForClass(\ReflectionClass $reflectionClass) + private function doProcessValue($value, bool $isRoot = false) { - $metadata = array(); + if ($value instanceof TypedReference) { + if ($ref = $this->getAutowiredReference($value)) { + return $ref; + } + if (ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { + $message = $this->createTypeNotFoundMessageCallback($value, 'it'); + + // since the error message varies by referenced id and $this->currentId, so should the id of the dummy errored definition + $this->container->register($id = sprintf('.errored.%s.%s', $this->currentId, (string) $value), $value->getType()) + ->addError($message); + + return new TypedReference($id, $value->getType(), $value->getInvalidBehavior(), $value->getName()); + } + } + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + $this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass())); + + return $value; + } - if ($constructor = $reflectionClass->getConstructor()) { - $metadata['__construct'] = self::getResourceMetadataForMethod($constructor); + $this->methodCalls = $value->getMethodCalls(); + + try { + $constructor = $this->getConstructor($value, false); + } catch (RuntimeException $e) { + throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e); } - foreach (self::getSetters($reflectionClass) as $reflectionMethod) { - $metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod); + if ($constructor) { + array_unshift($this->methodCalls, [$constructor, $value->getArguments()]); } - return new AutowireServiceResource($reflectionClass->name, $reflectionClass->getFileName(), $metadata); + $this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot); + + if ($constructor) { + [, $arguments] = array_shift($this->methodCalls); + + if ($arguments !== $value->getArguments()) { + $value->setArguments($arguments); + } + } + + if ($this->methodCalls !== $value->getMethodCalls()) { + $value->setMethodCalls($this->methodCalls); + } + + return $value; } - /** - * Wires the given definition. - * - * @param string $id - * @param Definition $definition - * - * @throws RuntimeException - */ - private function completeDefinition($id, Definition $definition) + private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot): array { - if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { - return; + $this->decoratedId = null; + $this->decoratedClass = null; + $this->getPreviousValue = null; + + if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && null !== ($this->decoratedId = $definition->innerServiceId) && $this->container->has($this->decoratedId)) { + $this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass(); } - if ($this->container->isTrackingResources()) { - $this->container->addResource(static::createResourceForClass($reflectionClass)); + foreach ($this->methodCalls as $i => $call) { + $this->decoratedMethodIndex = $i; + [$method, $arguments] = $call; + + if ($method instanceof \ReflectionFunctionAbstract) { + $reflectionMethod = $method; + } else { + $definition = new Definition($reflectionClass->name); + try { + $reflectionMethod = $this->getReflectionMethod($definition, $method); + } catch (RuntimeException $e) { + if ($definition->getFactory()) { + continue; + } + throw $e; + } + } + + $arguments = $this->autowireMethod($reflectionMethod, $arguments); + + if ($arguments !== $call[1]) { + $this->methodCalls[$i][1] = $arguments; + } } - if (!$constructor = $reflectionClass->getConstructor()) { - return; + return $this->methodCalls; + } + + /** + * Autowires the constructor or a method. + * + * @return array The autowired arguments + * + * @throws AutowiringFailedException + */ + private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments): array + { + $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; + $method = $reflectionMethod->name; + $parameters = $reflectionMethod->getParameters(); + if ($reflectionMethod->isVariadic()) { + array_pop($parameters); } - $arguments = $definition->getArguments(); - foreach ($constructor->getParameters() as $index => $parameter) { - if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { + foreach ($parameters as $index => $parameter) { + if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } - try { - if (!$typeHint = $parameter->getClass()) { - // no default value? Then fail - if (!$parameter->isOptional()) { - throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id)); - } - - // specifically pass the default value - $arguments[$index] = $parameter->getDefaultValue(); + $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true); + if (!$type) { + if (isset($arguments[$index])) { continue; } - if (null === $this->types) { - $this->populateAvailableTypes(); + // no default value? Then fail + if (!$parameter->isDefaultValueAvailable()) { + // For core classes, isDefaultValueAvailable() can + // be false when isOptional() returns true. If the + // argument *is* optional, allow it to be missing + if ($parameter->isOptional()) { + continue; + } + $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false); + $type = $type ? sprintf('is type-hinted "%s"', ltrim($type, '\\')) : 'has no type-hint'; + + throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type)); } - if (isset($this->types[$typeHint->name])) { - $value = new Reference($this->types[$typeHint->name]); - } else { - try { - $value = $this->createAutowiredDefinition($typeHint, $id); - } catch (RuntimeException $e) { - if ($parameter->allowsNull()) { - $value = null; - } elseif ($parameter->isDefaultValueAvailable()) { - $value = $parameter->getDefaultValue(); - } else { - throw $e; - } + // specifically pass the default value + $arguments[$index] = $parameter->getDefaultValue(); + + continue; + } + + $getValue = function () use ($type, $parameter, $class, $method) { + if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $parameter->name))) { + $failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); + + if ($parameter->isDefaultValueAvailable()) { + $value = $parameter->getDefaultValue(); + } elseif (!$parameter->allowsNull()) { + throw new AutowiringFailedException($this->currentId, $failureMessage); } } - } catch (\ReflectionException $e) { - // Typehint against a non-existing class - if (!$parameter->isDefaultValueAvailable()) { - throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $e->getMessage()), 0, $e); - } + return $value; + }; + + if ($this->decoratedClass && $isDecorated = is_a($this->decoratedClass, $type, true)) { + if ($this->getPreviousValue) { + // The inner service is injected only if there is only 1 argument matching the type of the decorated class + // across all arguments of all autowired methods. + // If a second matching argument is found, the default behavior is restored. + + $getPreviousValue = $this->getPreviousValue; + $this->methodCalls[$this->decoratedMethodIndex][1][$this->decoratedMethodArgumentIndex] = $getPreviousValue(); + $this->decoratedClass = null; // Prevent further checks + } else { + $arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass); + $this->getPreviousValue = $getValue; + $this->decoratedMethodArgumentIndex = $index; - $value = $parameter->getDefaultValue(); + continue; + } } - $arguments[$index] = $value; + $arguments[$index] = $getValue(); + } + + if ($parameters && !isset($arguments[++$index])) { + while (0 <= --$index) { + $parameter = $parameters[$index]; + if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) { + break; + } + unset($arguments[$index]); + } } // it's possible index 1 was set, then index 0, then 2, etc // make sure that we re-order so they're injected as expected ksort($arguments); - $definition->setArguments($arguments); + + return $arguments; + } + + /** + * Returns a reference to the service matching the given type, if any. + */ + private function getAutowiredReference(TypedReference $reference): ?TypedReference + { + $this->lastFailure = null; + $type = $reference->getType(); + + if ($type !== (string) $reference) { + return $reference; + } + + if (null !== $name = $reference->getName()) { + if ($this->container->has($alias = $type.' $'.$name) && !$this->container->findDefinition($alias)->isAbstract()) { + return new TypedReference($alias, $type, $reference->getInvalidBehavior()); + } + + if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) { + foreach ($this->container->getAliases() as $id => $alias) { + if ($name === (string) $alias && str_starts_with($id, $type.' $')) { + return new TypedReference($name, $type, $reference->getInvalidBehavior()); + } + } + } + } + + if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) { + return new TypedReference($type, $type, $reference->getInvalidBehavior()); + } + + return null; } /** * Populates the list of available types. */ - private function populateAvailableTypes() + private function populateAvailableTypes(ContainerBuilder $container) { - $this->types = array(); + $this->types = []; + $this->ambiguousServiceTypes = []; - foreach ($this->container->getDefinitions() as $id => $definition) { - $this->populateAvailableType($id, $definition); + foreach ($container->getDefinitions() as $id => $definition) { + $this->populateAvailableType($container, $id, $definition); } } /** * Populates the list of available types for a given definition. - * - * @param string $id - * @param Definition $definition */ - private function populateAvailableType($id, Definition $definition) + private function populateAvailableType(ContainerBuilder $container, string $id, Definition $definition) { // Never use abstract services if ($definition->isAbstract()) { return; } - foreach ($definition->getAutowiringTypes() as $type) { - $this->definedTypes[$type] = true; - $this->types[$type] = $id; - } - - if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { + if ('' === $id || '.' === $id[0] || $definition->isDeprecated() || !$reflectionClass = $container->getReflectionClass($definition->getClass(), false)) { return; } @@ -203,155 +349,127 @@ private function populateAvailableType($id, Definition $definition) /** * Associates a type and a service id if applicable. - * - * @param string $type - * @param string $id */ - private function set($type, $id) + private function set(string $type, string $id) { - if (isset($this->definedTypes[$type])) { - return; - } - // is this already a type/class that is known to match multiple services? if (isset($this->ambiguousServiceTypes[$type])) { - $this->addServiceToAmbiguousType($id, $type); + $this->ambiguousServiceTypes[$type][] = $id; return; } // check to make sure the type doesn't match multiple services - if (isset($this->types[$type])) { - if ($this->types[$type] === $id) { - return; - } + if (!isset($this->types[$type]) || $this->types[$type] === $id) { + $this->types[$type] = $id; - // keep an array of all services matching this type - $this->addServiceToAmbiguousType($id, $type); + return; + } + // keep an array of all services matching this type + if (!isset($this->ambiguousServiceTypes[$type])) { + $this->ambiguousServiceTypes[$type] = [$this->types[$type]]; unset($this->types[$type]); + } + $this->ambiguousServiceTypes[$type][] = $id; + } - return; + private function createTypeNotFoundMessageCallback(TypedReference $reference, string $label): \Closure + { + if (null === $this->typesClone->container) { + $this->typesClone->container = new ContainerBuilder($this->container->getParameterBag()); + $this->typesClone->container->setAliases($this->container->getAliases()); + $this->typesClone->container->setDefinitions($this->container->getDefinitions()); + $this->typesClone->container->setResourceTracking(false); } + $currentId = $this->currentId; - $this->types[$type] = $id; + return (function () use ($reference, $label, $currentId) { + return $this->createTypeNotFoundMessage($reference, $label, $currentId); + })->bindTo($this->typesClone); } - /** - * Registers a definition for the type if possible or throws an exception. - * - * @param \ReflectionClass $typeHint - * @param string $id - * - * @return Reference A reference to the registered definition - * - * @throws RuntimeException - */ - private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) + private function createTypeNotFoundMessage(TypedReference $reference, string $label, string $currentId): string { - if (isset($this->ambiguousServiceTypes[$typeHint->name])) { - $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; - $matchingServices = implode(', ', $this->ambiguousServiceTypes[$typeHint->name]); + if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) { + // either $type does not exist or a parent class does not exist + try { + $resource = new ClassExistenceResource($type, false); + // isFresh() will explode ONLY if a parent class/trait does not exist + $resource->isFresh(0); + $parentMsg = false; + } catch (\ReflectionException $e) { + $parentMsg = $e->getMessage(); + } - throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Multiple services exist for this %s (%s).', $typeHint->name, $id, $classOrInterface, $matchingServices)); - } + $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found'); + } else { + $alternatives = $this->createTypeAlternatives($this->container, $reference); + $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists'; + $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives); - if (!$typeHint->isInstantiable()) { - $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; - throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface)); + if ($r->isInterface() && !$alternatives) { + $message .= ' Did you create a class that implements this interface?'; + } } - $argumentId = sprintf('autowired.%s', $typeHint->name); - - $argumentDefinition = $this->container->register($argumentId, $typeHint->name); - $argumentDefinition->setPublic(false); - - $this->populateAvailableType($argumentId, $argumentDefinition); + $message = sprintf('Cannot autowire service "%s": %s %s', $currentId, $label, $message); - try { - $this->completeDefinition($argumentId, $argumentDefinition); - } catch (RuntimeException $e) { - $classOrInterface = $typeHint->isInterface() ? 'interface' : 'class'; - $message = sprintf('Unable to autowire argument of type "%s" for the service "%s". No services were found matching this %s and it cannot be auto-registered.', $typeHint->name, $id, $classOrInterface); - throw new RuntimeException($message, 0, $e); + if (null !== $this->lastFailure) { + $message = $this->lastFailure."\n".$message; + $this->lastFailure = null; } - return new Reference($argumentId); + return $message; } - /** - * Retrieves the reflection class associated with the given service. - * - * @param string $id - * @param Definition $definition - * - * @return \ReflectionClass|false - */ - private function getReflectionClass($id, Definition $definition) + private function createTypeAlternatives(ContainerBuilder $container, TypedReference $reference): string { - if (isset($this->reflectionClasses[$id])) { - return $this->reflectionClasses[$id]; + // try suggesting available aliases first + if ($message = $this->getAliasesSuggestionForType($container, $type = $reference->getType())) { + return ' '.$message; } - - // Cannot use reflection if the class isn't set - if (!$class = $definition->getClass()) { - return false; + if (null === $this->ambiguousServiceTypes) { + $this->populateAvailableTypes($container); } - $class = $this->container->getParameterBag()->resolveValue($class); - - try { - $reflector = new \ReflectionClass($class); - } catch (\ReflectionException $e) { - $reflector = false; + $servicesAndAliases = $container->getServiceIds(); + if (!$container->has($type) && false !== $key = array_search(strtolower($type), array_map('strtolower', $servicesAndAliases))) { + return sprintf(' Did you mean "%s"?', $servicesAndAliases[$key]); + } elseif (isset($this->ambiguousServiceTypes[$type])) { + $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); + } elseif (isset($this->types[$type])) { + $message = sprintf('the existing "%s" service', $this->types[$type]); + } else { + return ''; } - return $this->reflectionClasses[$id] = $reflector; + return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message); } - private function addServiceToAmbiguousType($id, $type) + private function getAliasesSuggestionForType(ContainerBuilder $container, string $type): ?string { - // keep an array of all services matching this type - if (!isset($this->ambiguousServiceTypes[$type])) { - $this->ambiguousServiceTypes[$type] = array( - $this->types[$type], - ); - } - $this->ambiguousServiceTypes[$type][] = $id; - } - - /** - * @param \ReflectionClass $reflectionClass - * - * @return \ReflectionMethod[] - */ - private static function getSetters(\ReflectionClass $reflectionClass) - { - foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { - if (!$reflectionMethod->isStatic() && 1 === $reflectionMethod->getNumberOfParameters() && 0 === strpos($reflectionMethod->name, 'set')) { - yield $reflectionMethod; + $aliases = []; + foreach (class_parents($type) + class_implements($type) as $parent) { + if ($container->has($parent) && !$container->findDefinition($parent)->isAbstract()) { + $aliases[] = $parent; } } - } - private static function getResourceMetadataForMethod(\ReflectionMethod $method) - { - $methodArgumentsMetadata = array(); - foreach ($method->getParameters() as $parameter) { - try { - $class = $parameter->getClass(); - } catch (\ReflectionException $e) { - // type-hint is against a non-existent class - $class = false; + if (1 < $len = \count($aliases)) { + $message = 'Try changing the type-hint to one of its parents: '; + for ($i = 0, --$len; $i < $len; ++$i) { + $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); } + $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); + + return $message; + } - $methodArgumentsMetadata[] = array( - 'class' => $class, - 'isOptional' => $parameter->isOptional(), - 'defaultValue' => $parameter->isOptional() ? $parameter->getDefaultValue() : null, - ); + if ($aliases) { + return sprintf('Try changing the type-hint to "%s" instead.', $aliases[0]); } - return $methodArgumentsMetadata; + return null; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php new file mode 100644 index 000000000..5c255cfb6 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Contracts\Service\Attribute\Required; + +/** + * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters. + * + * @author Nicolas Grekas + */ +class AutowireRequiredMethodsPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + return $value; + } + + $alreadyCalledMethods = []; + $withers = []; + + foreach ($value->getMethodCalls() as [$method]) { + $alreadyCalledMethods[strtolower($method)] = true; + } + + foreach ($reflectionClass->getMethods() as $reflectionMethod) { + $r = $reflectionMethod; + + if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) { + continue; + } + + while (true) { + if (\PHP_VERSION_ID >= 80000 && $r->getAttributes(Required::class)) { + if ($this->isWither($r, $r->getDocComment() ?: '')) { + $withers[] = [$r->name, [], true]; + } else { + $value->addMethodCall($r->name, []); + } + break; + } + if (false !== $doc = $r->getDocComment()) { + if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { + if ($this->isWither($reflectionMethod, $doc)) { + $withers[] = [$reflectionMethod->name, [], true]; + } else { + $value->addMethodCall($reflectionMethod->name, []); + } + break; + } + if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) { + break; + } + } + try { + $r = $r->getPrototype(); + } catch (\ReflectionException $e) { + break; // method has no prototype + } + } + } + + if ($withers) { + // Prepend withers to prevent creating circular loops + $setters = $value->getMethodCalls(); + $value->setMethodCalls($withers); + foreach ($setters as $call) { + $value->addMethodCall($call[0], $call[1], $call[2] ?? false); + } + } + + return $value; + } + + private function isWither(\ReflectionMethod $reflectionMethod, string $doc): bool + { + $match = preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@return\s++(static|\$this)[\s\*]#i', $doc, $matches); + if ($match && 'static' === $matches[1]) { + return true; + } + + if ($match && '$this' === $matches[1]) { + return false; + } + + $reflectionType = $reflectionMethod->hasReturnType() ? $reflectionMethod->getReturnType() : null; + + return $reflectionType instanceof \ReflectionNamedType && 'static' === $reflectionType->getName(); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowireRequiredPropertiesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowireRequiredPropertiesPass.php new file mode 100644 index 000000000..52024b807 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/AutowireRequiredPropertiesPass.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Contracts\Service\Attribute\Required; + +/** + * Looks for definitions with autowiring enabled and registers their corresponding "@required" properties. + * + * @author Sebastien Morel (Plopix) + * @author Nicolas Grekas + */ +class AutowireRequiredPropertiesPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (\PHP_VERSION_ID < 70400) { + return $value; + } + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + return $value; + } + + $properties = $value->getProperties(); + foreach ($reflectionClass->getProperties() as $reflectionProperty) { + if (!($type = $reflectionProperty->getType()) instanceof \ReflectionNamedType) { + continue; + } + if ((\PHP_VERSION_ID < 80000 || !$reflectionProperty->getAttributes(Required::class)) + && ((false === $doc = $reflectionProperty->getDocComment()) || false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) + ) { + continue; + } + if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) { + continue; + } + + $type = $type->getName(); + $value->setProperty($name, new TypedReference($type, $type, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name)); + } + + return $value; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php new file mode 100644 index 000000000..348498db2 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Checks if arguments of methods are properly configured. + * + * @author Kévin Dunglas + * @author Nicolas Grekas + */ +class CheckArgumentsValidityPass extends AbstractRecursivePass +{ + private $throwExceptions; + + public function __construct(bool $throwExceptions = true) + { + $this->throwExceptions = $throwExceptions; + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + $i = 0; + foreach ($value->getArguments() as $k => $v) { + if ($k !== $i++) { + if (!\is_int($k)) { + $msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + + break; + } + + $msg = sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + } + } + + foreach ($value->getMethodCalls() as $methodCall) { + $i = 0; + foreach ($methodCall[1] as $k => $v) { + if ($k !== $i++) { + if (!\is_int($k)) { + $msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + + break; + } + + $msg = sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + } + } + } + + return null; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php index 156bcc0c3..55d911c4f 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; /** * Checks your services for circular references. @@ -31,16 +31,14 @@ class CheckCircularReferencesPass implements CompilerPassInterface /** * Checks the ContainerBuilder object for circular references. - * - * @param ContainerBuilder $container The ContainerBuilder instances */ public function process(ContainerBuilder $container) { $graph = $container->getCompiler()->getServiceReferenceGraph(); - $this->checkedNodes = array(); + $this->checkedNodes = []; foreach ($graph->getNodes() as $id => $node) { - $this->currentPath = array($id); + $this->currentPath = [$id]; $this->checkOutEdges($node->getOutEdges()); } @@ -51,7 +49,7 @@ public function process(ContainerBuilder $container) * * @param ServiceReferenceGraphEdge[] $edges An array of Edges * - * @throws ServiceCircularReferenceException When a circular reference is found. + * @throws ServiceCircularReferenceException when a circular reference is found */ private function checkOutEdges(array $edges) { @@ -60,14 +58,13 @@ private function checkOutEdges(array $edges) $id = $node->getId(); if (empty($this->checkedNodes[$id])) { - - // don't check circular dependencies for lazy services - if (!$node->getValue() || !$node->getValue()->isLazy()) { + // Don't check circular references for lazy edges + if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) { $searchKey = array_search($id, $this->currentPath); $this->currentPath[] = $id; if (false !== $searchKey) { - throw new ServiceCircularReferenceException($id, array_slice($this->currentPath, $searchKey)); + throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey)); } $this->checkOutEdges($node->getOutEdges()); diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php index 0d21ef284..7abac908f 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php @@ -12,7 +12,9 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\EnvParameterException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Loader\FileLoader; /** * This pass validates each definition individually only taking the information @@ -31,8 +33,6 @@ class CheckDefinitionValidityPass implements CompilerPassInterface /** * Processes the ContainerBuilder to validate the Definition. * - * @param ContainerBuilder $container - * * @throws RuntimeException When the Definition is invalid */ public function process(ContainerBuilder $container) @@ -44,18 +44,19 @@ public function process(ContainerBuilder $container) } // non-synthetic, non-abstract service has class - if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) { + if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass() && (!$definition->getFactory() || !preg_match(FileLoader::ANONYMOUS_ID_REGEXP, $id))) { if ($definition->getFactory()) { throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id)); } + if (class_exists($id) || interface_exists($id, false)) { + if (str_starts_with($id, '\\') && 1 < substr_count($id, '\\')) { + throw new RuntimeException(sprintf('The definition for "%s" has no class attribute, and appears to reference a class or interface. Please specify the class attribute explicitly or remove the leading backslash by renaming the service to "%s" to get rid of this error.', $id, substr($id, 1))); + } + + throw new RuntimeException(sprintf('The definition for "%s" has no class attribute, and appears to reference a class or interface in the global namespace. Leaving out the "class" attribute is only allowed for namespaced classes. Please specify the class attribute explicitly to get rid of this error.', $id)); + } - throw new RuntimeException(sprintf( - 'The definition for "%s" has no class. If you intend to inject ' - .'this service dynamically at runtime, please mark it as synthetic=true. ' - .'If this is an abstract definition solely used by child definitions, ' - .'please add abstract=true, otherwise specify a class to get rid of this error.', - $id - )); + throw new RuntimeException(sprintf('The definition for "%s" has no class. If you intend to inject this service dynamically at runtime, please mark it as synthetic=true. If this is an abstract definition solely used by child definitions, please add abstract=true, otherwise specify a class to get rid of this error.', $id)); } // tag attribute values must be scalars @@ -68,6 +69,22 @@ public function process(ContainerBuilder $container) } } } + + if ($definition->isPublic() && !$definition->isPrivate()) { + $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs); + if (null !== $usedEnvs) { + throw new EnvParameterException([$resolvedId], null, 'A service name ("%s") cannot contain dynamic values.'); + } + } + } + + foreach ($container->getAliases() as $id => $alias) { + if ($alias->isPublic() && !$alias->isPrivate()) { + $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs); + if (null !== $usedEnvs) { + throw new EnvParameterException([$resolvedId], null, 'An alias name ("%s") cannot contain dynamic values.'); + } + } } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php index 304f78495..fd3173831 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php @@ -11,53 +11,95 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Checks that all references are pointing to a valid service. * * @author Johannes M. Schmitt */ -class CheckExceptionOnInvalidReferenceBehaviorPass implements CompilerPassInterface +class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass { - private $container; - private $sourceId; + private $serviceLocatorContextIds = []; + /** + * {@inheritdoc} + */ public function process(ContainerBuilder $container) { - $this->container = $container; + $this->serviceLocatorContextIds = []; + foreach ($container->findTaggedServiceIds('container.service_locator_context') as $id => $tags) { + $this->serviceLocatorContextIds[$id] = $tags[0]['id']; + $container->getDefinition($id)->clearTag('container.service_locator_context'); + } - foreach ($container->getDefinitions() as $id => $definition) { - $this->sourceId = $id; - $this->processDefinition($definition); + try { + return parent::process($container); + } finally { + $this->serviceLocatorContextIds = []; } } - private function processDefinition(Definition $definition) + protected function processValue($value, bool $isRoot = false) { - $this->processReferences($definition->getArguments()); - $this->processReferences($definition->getMethodCalls()); - $this->processReferences($definition->getProperties()); + if (!$value instanceof Reference) { + return parent::processValue($value, $isRoot); + } + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $value->getInvalidBehavior() || $this->container->has($id = (string) $value)) { + return $value; + } + + $currentId = $this->currentId; + $graph = $this->container->getCompiler()->getServiceReferenceGraph(); + + if (isset($this->serviceLocatorContextIds[$currentId])) { + $currentId = $this->serviceLocatorContextIds[$currentId]; + $locator = $this->container->getDefinition($this->currentId)->getFactory()[0]; + + foreach ($locator->getArgument(0) as $k => $v) { + if ($v->getValues()[0] === $value) { + if ($k !== $id) { + $currentId = $k.'" in the container provided to "'.$currentId; + } + throw new ServiceNotFoundException($id, $currentId, null, $this->getAlternatives($id)); + } + } + } + + if ('.' === $currentId[0] && $graph->hasNode($currentId)) { + foreach ($graph->getNode($currentId)->getInEdges() as $edge) { + if (!$edge->getValue() instanceof Reference || ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $edge->getValue()->getInvalidBehavior()) { + continue; + } + $sourceId = $edge->getSourceNode()->getId(); + + if ('.' !== $sourceId[0]) { + $currentId = $sourceId; + break; + } + } + } + + throw new ServiceNotFoundException($id, $currentId, null, $this->getAlternatives($id)); } - private function processReferences(array $arguments) + private function getAlternatives(string $id): array { - foreach ($arguments as $argument) { - if (is_array($argument)) { - $this->processReferences($argument); - } elseif ($argument instanceof Definition) { - $this->processDefinition($argument); - } elseif ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) { - $destId = (string) $argument; - - if (!$this->container->has($destId)) { - throw new ServiceNotFoundException($destId, $this->sourceId); - } + $alternatives = []; + foreach ($this->container->getServiceIds() as $knownId) { + if ('' === $knownId || '.' === $knownId[0]) { + continue; + } + + $lev = levenshtein($id, $knownId); + if ($lev <= \strlen($id) / 3 || false !== strpos($knownId, $id)) { + $alternatives[] = $knownId; } } + + return $alternatives; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php index 80b81d695..0349ef761 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php @@ -12,9 +12,8 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; /** * Checks the validity of references. @@ -24,73 +23,21 @@ * * @author Johannes M. Schmitt */ -class CheckReferenceValidityPass implements CompilerPassInterface +class CheckReferenceValidityPass extends AbstractRecursivePass { - private $container; - private $currentId; - - /** - * Processes the ContainerBuilder to validate References. - * - * @param ContainerBuilder $container - */ - public function process(ContainerBuilder $container) + protected function processValue($value, bool $isRoot = false) { - $this->container = $container; - - foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isSynthetic() || $definition->isAbstract()) { - continue; - } - - $this->currentId = $id; - - $this->validateReferences($definition->getArguments()); - $this->validateReferences($definition->getMethodCalls()); - $this->validateReferences($definition->getProperties()); + if ($isRoot && $value instanceof Definition && ($value->isSynthetic() || $value->isAbstract())) { + return $value; } - } - - /** - * Validates an array of References. - * - * @param array $arguments An array of Reference objects - * - * @throws RuntimeException when there is a reference to an abstract definition. - */ - private function validateReferences(array $arguments) - { - foreach ($arguments as $argument) { - if (is_array($argument)) { - $this->validateReferences($argument); - } elseif ($argument instanceof Reference) { - $targetDefinition = $this->getDefinition((string) $argument); + if ($value instanceof Reference && $this->container->hasDefinition((string) $value)) { + $targetDefinition = $this->container->getDefinition((string) $value); - if (null !== $targetDefinition && $targetDefinition->isAbstract()) { - throw new RuntimeException(sprintf( - 'The definition "%s" has a reference to an abstract definition "%s". ' - .'Abstract definitions cannot be the target of references.', - $this->currentId, - $argument - )); - } + if ($targetDefinition->isAbstract()) { + throw new RuntimeException(sprintf('The definition "%s" has a reference to an abstract definition "%s". Abstract definitions cannot be the target of references.', $this->currentId, $value)); } } - } - - /** - * Returns the Definition given an id. - * - * @param string $id Definition identifier - * - * @return Definition - */ - private function getDefinition($id) - { - if (!$this->container->hasDefinition($id)) { - return; - } - return $this->container->getDefinition($id); + return parent::processValue($value, $isRoot); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php new file mode 100644 index 000000000..f8ec9214d --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php @@ -0,0 +1,325 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * Checks whether injected parameters are compatible with type declarations. + * + * This pass should be run after all optimization passes. + * + * It can be added either: + * * before removing passes to check all services even if they are not currently used, + * * after removing passes to check only services are used in the app. + * + * @author Nicolas Grekas + * @author Julien Maulny + */ +final class CheckTypeDeclarationsPass extends AbstractRecursivePass +{ + private const SCALAR_TYPES = [ + 'int' => true, + 'float' => true, + 'bool' => true, + 'string' => true, + ]; + + private const BUILTIN_TYPES = [ + 'array' => true, + 'bool' => true, + 'callable' => true, + 'float' => true, + 'int' => true, + 'iterable' => true, + 'object' => true, + 'string' => true, + ]; + + private $autoload; + private $skippedIds; + + private $expressionLanguage; + + /** + * @param bool $autoload Whether services who's class in not loaded should be checked or not. + * Defaults to false to save loading code during compilation. + * @param array $skippedIds An array indexed by the service ids to skip + */ + public function __construct(bool $autoload = false, array $skippedIds = []) + { + $this->autoload = $autoload; + $this->skippedIds = $skippedIds; + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (isset($this->skippedIds[$this->currentId])) { + return $value; + } + + if (!$value instanceof Definition || $value->hasErrors() || $value->isDeprecated()) { + return parent::processValue($value, $isRoot); + } + + if (!$this->autoload) { + if (!$class = $value->getClass()) { + return parent::processValue($value, $isRoot); + } + if (!class_exists($class, false) && !interface_exists($class, false)) { + return parent::processValue($value, $isRoot); + } + } + + if (ServiceLocator::class === $value->getClass()) { + return parent::processValue($value, $isRoot); + } + + if ($constructor = $this->getConstructor($value, false)) { + $this->checkTypeDeclarations($value, $constructor, $value->getArguments()); + } + + foreach ($value->getMethodCalls() as $methodCall) { + try { + $reflectionMethod = $this->getReflectionMethod($value, $methodCall[0]); + } catch (RuntimeException $e) { + if ($value->getFactory()) { + continue; + } + + throw $e; + } + + $this->checkTypeDeclarations($value, $reflectionMethod, $methodCall[1]); + } + + return parent::processValue($value, $isRoot); + } + + /** + * @throws InvalidArgumentException When not enough parameters are defined for the method + */ + private function checkTypeDeclarations(Definition $checkedDefinition, \ReflectionFunctionAbstract $reflectionFunction, array $values): void + { + $numberOfRequiredParameters = $reflectionFunction->getNumberOfRequiredParameters(); + + if (\count($values) < $numberOfRequiredParameters) { + throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": "%s::%s()" requires %d arguments, %d passed.', $this->currentId, $reflectionFunction->class, $reflectionFunction->name, $numberOfRequiredParameters, \count($values))); + } + + $reflectionParameters = $reflectionFunction->getParameters(); + $checksCount = min($reflectionFunction->getNumberOfParameters(), \count($values)); + + $envPlaceholderUniquePrefix = $this->container->getParameterBag() instanceof EnvPlaceholderParameterBag ? $this->container->getParameterBag()->getEnvPlaceholderUniquePrefix() : null; + + for ($i = 0; $i < $checksCount; ++$i) { + if (!$reflectionParameters[$i]->hasType() || $reflectionParameters[$i]->isVariadic()) { + continue; + } + + $this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i], $envPlaceholderUniquePrefix); + } + + if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) { + $variadicParameters = \array_slice($values, $lastParameter->getPosition()); + + foreach ($variadicParameters as $variadicParameter) { + $this->checkType($checkedDefinition, $variadicParameter, $lastParameter, $envPlaceholderUniquePrefix); + } + } + } + + /** + * @throws InvalidParameterTypeException When a parameter is not compatible with the declared type + */ + private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix, \ReflectionType $reflectionType = null): void + { + $reflectionType = $reflectionType ?? $parameter->getType(); + + if ($reflectionType instanceof \ReflectionUnionType) { + foreach ($reflectionType->getTypes() as $t) { + try { + $this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $t); + + return; + } catch (InvalidParameterTypeException $e) { + } + } + + throw new InvalidParameterTypeException($this->currentId, $e->getCode(), $parameter); + } + if ($reflectionType instanceof \ReflectionIntersectionType) { + foreach ($reflectionType->getTypes() as $t) { + $this->checkType($checkedDefinition, $value, $parameter, $envPlaceholderUniquePrefix, $t); + } + + return; + } + if (!$reflectionType instanceof \ReflectionNamedType) { + return; + } + + $type = $reflectionType->getName(); + + if ($value instanceof Reference) { + if (!$this->container->has($value = (string) $value)) { + return; + } + + if ('service_container' === $value && is_a($type, Container::class, true)) { + return; + } + + $value = $this->container->findDefinition($value); + } + + if ('self' === $type) { + $type = $parameter->getDeclaringClass()->getName(); + } + + if ('static' === $type) { + $type = $checkedDefinition->getClass(); + } + + $class = null; + + if ($value instanceof Definition) { + $class = $value->getClass(); + + if ($class && isset(self::BUILTIN_TYPES[strtolower($class)])) { + $class = strtolower($class); + } elseif (!$class || (!$this->autoload && !class_exists($class, false) && !interface_exists($class, false))) { + return; + } + } elseif ($value instanceof Parameter) { + $value = $this->container->getParameter($value); + } elseif ($value instanceof Expression) { + try { + $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this->container]); + } catch (\Exception $e) { + // If a service from the expression cannot be fetched from the container, we skip the validation. + return; + } + } elseif (\is_string($value)) { + if ('%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) { + $value = $this->container->getParameter(substr($value, 1, -1)); + } + + if ($envPlaceholderUniquePrefix && \is_string($value) && str_contains($value, 'env_')) { + // If the value is an env placeholder that is either mixed with a string or with another env placeholder, then its resolved value will always be a string, so we don't need to resolve it. + // We don't need to change the value because it is already a string. + if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) { + try { + $value = $this->container->resolveEnvPlaceholders($value, true); + } catch (\Exception $e) { + // If an env placeholder cannot be resolved, we skip the validation. + return; + } + } + } + } + + if (null === $value && $parameter->allowsNull()) { + return; + } + + if (null === $class) { + if ($value instanceof IteratorArgument) { + $class = RewindableGenerator::class; + } elseif ($value instanceof ServiceClosureArgument) { + $class = \Closure::class; + } elseif ($value instanceof ServiceLocatorArgument) { + $class = ServiceLocator::class; + } elseif (\is_object($value)) { + $class = \get_class($value); + } else { + $class = \gettype($value); + $class = ['integer' => 'int', 'double' => 'float', 'boolean' => 'bool'][$class] ?? $class; + } + } + + if (isset(self::SCALAR_TYPES[$type]) && isset(self::SCALAR_TYPES[$class])) { + return; + } + + if ('string' === $type && method_exists($class, '__toString')) { + return; + } + + if ('callable' === $type && (\Closure::class === $class || method_exists($class, '__invoke'))) { + return; + } + + if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition || \is_string($value[0]))) { + return; + } + + if ('iterable' === $type && (\is_array($value) || 'array' === $class || is_subclass_of($class, \Traversable::class))) { + return; + } + + if ($type === $class) { + return; + } + + if ('object' === $type && !isset(self::BUILTIN_TYPES[$class])) { + return; + } + + if ('mixed' === $type) { + return; + } + + if (is_a($class, $type, true)) { + return; + } + + if ('false' === $type) { + if (false === $value) { + return; + } + } elseif ($reflectionType->isBuiltin()) { + $checkFunction = sprintf('is_%s', $type); + if ($checkFunction($value)) { + return; + } + } + + throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? $class : get_debug_type($value), $parameter); + } + + private function getExpressionLanguage(): ExpressionLanguage + { + if (null === $this->expressionLanguage) { + $this->expressionLanguage = new ExpressionLanguage(null, $this->container->getExpressionLanguageProviders()); + } + + return $this->expressionLanguage; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/Compiler.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/Compiler.php index 3aa252639..f6566072f 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/Compiler.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/Compiler.php @@ -22,15 +22,13 @@ class Compiler { private $passConfig; - private $log = array(); - private $loggingFormatter; + private $log = []; private $serviceReferenceGraph; public function __construct() { $this->passConfig = new PassConfig(); $this->serviceReferenceGraph = new ServiceReferenceGraph(); - $this->loggingFormatter = new LoggingFormatter(); } /** @@ -53,49 +51,24 @@ public function getServiceReferenceGraph() return $this->serviceReferenceGraph; } - /** - * Returns the logging formatter which can be used by compilation passes. - * - * @return LoggingFormatter - */ - public function getLoggingFormatter() - { - return $this->loggingFormatter; - } - /** * Adds a pass to the PassConfig. - * - * @param CompilerPassInterface $pass A compiler pass - * @param string $type The type of the pass - * @param int $priority Used to sort the passes */ - public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) + public function addPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { - if (func_num_args() >= 3) { - $priority = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); - } - } - - $priority = 0; - } - $this->passConfig->addPass($pass, $type, $priority); } /** - * Adds a log message. - * - * @param string $string The log message + * @final */ - public function addLogMessage($string) + public function log(CompilerPassInterface $pass, string $message) { - $this->log[] = $string; + if (str_contains($message, "\n")) { + $message = str_replace("\n", "\n".\get_class($pass).': ', trim($message)); + } + + $this->log[] = \get_class($pass).': '.$message; } /** @@ -110,8 +83,6 @@ public function getLog() /** * Run the Compiler and process all Passes. - * - * @param ContainerBuilder $container */ public function compile(ContainerBuilder $container) { @@ -120,7 +91,7 @@ public function compile(ContainerBuilder $container) $pass->process($container); } } catch (\Exception $e) { - $usedEnvs = array(); + $usedEnvs = []; $prev = $e; do { @@ -138,6 +109,8 @@ public function compile(ContainerBuilder $container) } throw $e; + } finally { + $this->getServiceReferenceGraph()->clear(); } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php index 30cb1d5d4..308500605 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php @@ -22,8 +22,6 @@ interface CompilerPassInterface { /** * You can modify the container here before it is dumped to PHP code. - * - * @param ContainerBuilder $container */ public function process(ContainerBuilder $container); } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php index 8edb717b4..0c3f75ad8 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php @@ -11,8 +11,12 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Reference; /** * Overwrites a service but keeps the overridden one. @@ -21,22 +25,32 @@ * @author Fabien Potencier * @author Diego Saint Esteben */ -class DecoratorServicePass implements CompilerPassInterface +class DecoratorServicePass extends AbstractRecursivePass { + private $innerId = '.inner'; + + public function __construct(?string $innerId = '.inner') + { + $this->innerId = $innerId; + } + public function process(ContainerBuilder $container) { $definitions = new \SplPriorityQueue(); - $order = PHP_INT_MAX; + $order = \PHP_INT_MAX; foreach ($container->getDefinitions() as $id => $definition) { if (!$decorated = $definition->getDecoratedService()) { continue; } - $definitions->insert(array($id, $definition), array($decorated[2], --$order)); + $definitions->insert([$id, $definition], [$decorated[2], --$order]); } + $decoratingDefinitions = []; - foreach ($definitions as list($id, $definition)) { - list($inner, $renamedId) = $definition->getDecoratedService(); + foreach ($definitions as [$id, $definition]) { + $decoratedService = $definition->getDecoratedService(); + [$inner, $renamedId] = $decoratedService; + $invalidBehavior = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; $definition->setDecoratedService(null); @@ -44,24 +58,71 @@ public function process(ContainerBuilder $container) $renamedId = $id.'.inner'; } + $this->currentId = $renamedId; + $this->processValue($definition); + + $definition->innerServiceId = $renamedId; + $definition->decorationOnInvalid = $invalidBehavior; + // we create a new alias/service for the service we are replacing // to be able to reference it in the new one if ($container->hasAlias($inner)) { $alias = $container->getAlias($inner); $public = $alias->isPublic(); + $private = $alias->isPrivate(); $container->setAlias($renamedId, new Alias((string) $alias, false)); - } else { + $decoratedDefinition = $container->findDefinition($alias); + } elseif ($container->hasDefinition($inner)) { $decoratedDefinition = $container->getDefinition($inner); - $definition->setTags(array_merge($decoratedDefinition->getTags(), $definition->getTags())); - $definition->setAutowiringTypes(array_merge($decoratedDefinition->getAutowiringTypes(), $definition->getAutowiringTypes())); $public = $decoratedDefinition->isPublic(); + $private = $decoratedDefinition->isPrivate(); $decoratedDefinition->setPublic(false); - $decoratedDefinition->setTags(array()); - $decoratedDefinition->setAutowiringTypes(array()); $container->setDefinition($renamedId, $decoratedDefinition); + $decoratingDefinitions[$inner] = $decoratedDefinition; + } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { + $container->removeDefinition($id); + continue; + } elseif (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { + $public = $definition->isPublic(); + $private = $definition->isPrivate(); + $decoratedDefinition = null; + } else { + throw new ServiceNotFoundException($inner, $id); } - $container->setAlias($inner, new Alias($id, $public)); + if ($decoratedDefinition && $decoratedDefinition->isSynthetic()) { + throw new InvalidArgumentException(sprintf('A synthetic service cannot be decorated: service "%s" cannot decorate "%s".', $id, $inner)); + } + + if (isset($decoratingDefinitions[$inner])) { + $decoratingDefinition = $decoratingDefinitions[$inner]; + + $decoratingTags = $decoratingDefinition->getTags(); + $resetTags = []; + + // container.service_locator and container.service_subscriber have special logic and they must not be transferred out to decorators + foreach (['container.service_locator', 'container.service_subscriber'] as $containerTag) { + if (isset($decoratingTags[$containerTag])) { + $resetTags[$containerTag] = $decoratingTags[$containerTag]; + unset($decoratingTags[$containerTag]); + } + } + + $definition->setTags(array_merge($decoratingTags, $definition->getTags())); + $decoratingDefinition->setTags($resetTags); + $decoratingDefinitions[$inner] = $definition; + } + + $container->setAlias($inner, $id)->setPublic($public); } } + + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Reference && $this->innerId === (string) $value) { + return new Reference($this->currentId, $value->getInvalidBehavior()); + } + + return parent::processValue($value, $isRoot); + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php new file mode 100644 index 000000000..5e7ba3173 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Throws an exception for any Definitions that have errors and still exist. + * + * @author Ryan Weaver + */ +class DefinitionErrorExceptionPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (!$value instanceof Definition || !$value->hasErrors()) { + return parent::processValue($value, $isRoot); + } + + if ($isRoot && !$value->isPublic()) { + $graph = $this->container->getCompiler()->getServiceReferenceGraph(); + $runtimeException = false; + foreach ($graph->getNode($this->currentId)->getInEdges() as $edge) { + if (!$edge->getValue() instanceof Reference || ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE !== $edge->getValue()->getInvalidBehavior()) { + $runtimeException = false; + break; + } + $runtimeException = true; + } + if ($runtimeException) { + return parent::processValue($value, $isRoot); + } + } + + // only show the first error so the user can focus on it + $errors = $value->getErrors(); + $message = reset($errors); + + throw new RuntimeException($message); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php deleted file mode 100644 index 66ddd03c9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @author Guilhem N. - */ -class FactoryReturnTypePass implements CompilerPassInterface -{ - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container) - { - // works only since php 7.0 and hhvm 3.11 - if (!method_exists(\ReflectionMethod::class, 'getReturnType')) { - return; - } - - foreach ($container->getDefinitions() as $id => $definition) { - $this->updateDefinition($container, $id, $definition); - } - } - - private function updateDefinition(ContainerBuilder $container, $id, Definition $definition, array $previous = array()) - { - // circular reference - if (isset($previous[$id])) { - return; - } - - $factory = $definition->getFactory(); - if (null === $factory || null !== $definition->getClass()) { - return; - } - - $class = null; - if (is_string($factory)) { - try { - $m = new \ReflectionFunction($factory); - } catch (\ReflectionException $e) { - return; - } - } else { - if ($factory[0] instanceof Reference) { - $previous[$id] = true; - $factoryDefinition = $container->findDefinition((string) $factory[0]); - $this->updateDefinition($container, (string) $factory[0], $factoryDefinition, $previous); - $class = $factoryDefinition->getClass(); - } else { - $class = $factory[0]; - } - - try { - $m = new \ReflectionMethod($class, $factory[1]); - } catch (\ReflectionException $e) { - return; - } - } - - $returnType = $m->getReturnType(); - if (null !== $returnType && !$returnType->isBuiltin()) { - $returnType = (string) $returnType; - if (null !== $class) { - $declaringClass = $m->getDeclaringClass()->getName(); - if ('self' === $returnType) { - $returnType = $declaringClass; - } elseif ('parent' === $returnType) { - $returnType = get_parent_class($declaringClass) ?: null; - } - } - - $definition->setClass($returnType); - } - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php index a4e2e041b..fe7d29e26 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php @@ -11,107 +11,179 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Inline service definitions where this is possible. * * @author Johannes M. Schmitt */ -class InlineServiceDefinitionsPass implements RepeatablePassInterface +class InlineServiceDefinitionsPass extends AbstractRecursivePass { - private $repeatedPass; + private $analyzingPass; + private $cloningIds = []; + private $connectedIds = []; + private $notInlinedIds = []; + private $inlinedIds = []; + private $notInlinableIds = []; private $graph; - private $compiler; - private $formatter; - private $currentId; - /** - * {@inheritdoc} - */ - public function setRepeatedPass(RepeatedPass $repeatedPass) + public function __construct(AnalyzeServiceReferencesPass $analyzingPass = null) { - $this->repeatedPass = $repeatedPass; + $this->analyzingPass = $analyzingPass; } - /** - * Processes the ContainerBuilder for inline service definitions. - * - * @param ContainerBuilder $container - */ public function process(ContainerBuilder $container) { - $this->compiler = $container->getCompiler(); - $this->formatter = $this->compiler->getLoggingFormatter(); - $this->graph = $this->compiler->getServiceReferenceGraph(); + $this->container = $container; + if ($this->analyzingPass) { + $analyzedContainer = new ContainerBuilder(); + $analyzedContainer->setAliases($container->getAliases()); + $analyzedContainer->setDefinitions($container->getDefinitions()); + foreach ($container->getExpressionLanguageProviders() as $provider) { + $analyzedContainer->addExpressionLanguageProvider($provider); + } + } else { + $analyzedContainer = $container; + } + try { + $remainingInlinedIds = []; + $this->connectedIds = $this->notInlinedIds = $container->getDefinitions(); + do { + if ($this->analyzingPass) { + $analyzedContainer->setDefinitions(array_intersect_key($analyzedContainer->getDefinitions(), $this->connectedIds)); + $this->analyzingPass->process($analyzedContainer); + } + $this->graph = $analyzedContainer->getCompiler()->getServiceReferenceGraph(); + $notInlinedIds = $this->notInlinedIds; + $this->connectedIds = $this->notInlinedIds = $this->inlinedIds = []; + + foreach ($analyzedContainer->getDefinitions() as $id => $definition) { + if (!$this->graph->hasNode($id)) { + continue; + } + foreach ($this->graph->getNode($id)->getOutEdges() as $edge) { + if (isset($notInlinedIds[$edge->getSourceNode()->getId()])) { + $this->currentId = $id; + $this->processValue($definition, true); + break; + } + } + } - $container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true)); + foreach ($this->inlinedIds as $id => $isPublicOrNotShared) { + if ($isPublicOrNotShared) { + $remainingInlinedIds[$id] = $id; + } else { + $container->removeDefinition($id); + $analyzedContainer->removeDefinition($id); + } + } + } while ($this->inlinedIds && $this->analyzingPass); + + foreach ($remainingInlinedIds as $id) { + if (isset($this->notInlinableIds[$id])) { + continue; + } + + $definition = $container->getDefinition($id); + + if (!$definition->isShared() && !$definition->isPublic()) { + $container->removeDefinition($id); + } + } + } finally { + $this->container = null; + $this->connectedIds = $this->notInlinedIds = $this->inlinedIds = []; + $this->notInlinableIds = []; + $this->graph = null; + } } /** - * Processes inline arguments. - * - * @param ContainerBuilder $container The ContainerBuilder - * @param array $arguments An array of arguments - * @param bool $isRoot If we are processing the root definitions or not - * - * @return array + * {@inheritdoc} */ - private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false) + protected function processValue($value, bool $isRoot = false) { - foreach ($arguments as $k => $argument) { - if ($isRoot) { - $this->currentId = $k; + if ($value instanceof ArgumentInterface) { + // Reference found in ArgumentInterface::getValues() are not inlineable + return $value; + } + + if ($value instanceof Definition && $this->cloningIds) { + if ($value->isShared()) { + return $value; } - if (is_array($argument)) { - $arguments[$k] = $this->inlineArguments($container, $argument); - } elseif ($argument instanceof Reference) { - if (!$container->hasDefinition($id = (string) $argument)) { - continue; - } + $value = clone $value; + } - if ($this->isInlineableDefinition($id, $definition = $container->getDefinition($id))) { - $this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId)); + if (!$value instanceof Reference) { + return parent::processValue($value, $isRoot); + } elseif (!$this->container->hasDefinition($id = (string) $value)) { + return $value; + } - if ($definition->isShared()) { - $arguments[$k] = $definition; - } else { - $arguments[$k] = clone $definition; - } - } - } elseif ($argument instanceof Definition) { - $argument->setArguments($this->inlineArguments($container, $argument->getArguments())); - $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls())); - $argument->setProperties($this->inlineArguments($container, $argument->getProperties())); + $definition = $this->container->getDefinition($id); - $configurator = $this->inlineArguments($container, array($argument->getConfigurator())); - $argument->setConfigurator($configurator[0]); + if (!$this->isInlineableDefinition($id, $definition)) { + $this->notInlinableIds[$id] = true; - $factory = $this->inlineArguments($container, array($argument->getFactory())); - $argument->setFactory($factory[0]); - } + return $value; + } + + $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); + $this->inlinedIds[$id] = $definition->isPublic() || !$definition->isShared(); + $this->notInlinedIds[$this->currentId] = true; + + if ($definition->isShared()) { + return $definition; + } + + if (isset($this->cloningIds[$id])) { + $ids = array_keys($this->cloningIds); + $ids[] = $id; + + throw new ServiceCircularReferenceException($id, \array_slice($ids, array_search($id, $ids))); } - return $arguments; + $this->cloningIds[$id] = true; + try { + return $this->processValue($definition); + } finally { + unset($this->cloningIds[$id]); + } } /** * Checks if the definition is inlineable. - * - * @param string $id - * @param Definition $definition - * - * @return bool If the definition is inlineable */ - private function isInlineableDefinition($id, Definition $definition) + private function isInlineableDefinition(string $id, Definition $definition): bool { + if ($definition->hasErrors() || $definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic()) { + return false; + } + if (!$definition->isShared()) { + if (!$this->graph->hasNode($id)) { + return true; + } + + foreach ($this->graph->getNode($id)->getInEdges() as $edge) { + $srcId = $edge->getSourceNode()->getId(); + $this->connectedIds[$srcId] = true; + if ($edge->isWeak() || $edge->isLazy()) { + return false; + } + } + return true; } - if ($definition->isPublic() || $definition->isLazy()) { + if ($definition->isPublic()) { return false; } @@ -122,20 +194,32 @@ private function isInlineableDefinition($id, Definition $definition) if ($this->currentId == $id) { return false; } + $this->connectedIds[$id] = true; - $ids = array(); + $srcIds = []; + $srcCount = 0; + $isReferencedByConstructor = false; foreach ($this->graph->getNode($id)->getInEdges() as $edge) { - $ids[] = $edge->getSourceNode()->getId(); + $isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor(); + $srcId = $edge->getSourceNode()->getId(); + $this->connectedIds[$srcId] = true; + if ($edge->isWeak() || $edge->isLazy()) { + return false; + } + $srcIds[$srcId] = true; + ++$srcCount; } - if (count(array_unique($ids)) > 1) { + if (1 !== \count($srcIds)) { + $this->notInlinedIds[$id] = true; + return false; } - if (count($ids) > 1 && is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) { + if ($srcCount > 1 && \is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) { return false; } - return true; + return $this->container->getDefinition($srcId)->isShared(); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php deleted file mode 100644 index db208fa0d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/LoggingFormatter.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -/** - * Used to format logging messages during the compilation. - * - * @author Johannes M. Schmitt - */ -class LoggingFormatter -{ - public function formatRemoveService(CompilerPassInterface $pass, $id, $reason) - { - return $this->format($pass, sprintf('Removed service "%s"; reason: %s.', $id, $reason)); - } - - public function formatInlineService(CompilerPassInterface $pass, $id, $target) - { - return $this->format($pass, sprintf('Inlined service "%s" to "%s".', $id, $target)); - } - - public function formatUpdateReference(CompilerPassInterface $pass, $serviceId, $oldDestId, $newDestId) - { - return $this->format($pass, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $serviceId, $oldDestId, $newDestId)); - } - - public function formatResolveInheritance(CompilerPassInterface $pass, $childId, $parentId) - { - return $this->format($pass, sprintf('Resolving inheritance for "%s" (parent: %s).', $childId, $parentId)); - } - - public function format(CompilerPassInterface $pass, $message) - { - return sprintf('%s: %s', get_class($pass), $message); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php index f9e602416..feaad77e5 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php @@ -11,8 +11,16 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\Config\Definition\BaseNode; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; /** * Merges extension configs into the container builder. @@ -30,6 +38,7 @@ public function process(ContainerBuilder $container) $definitions = $container->getDefinitions(); $aliases = $container->getAliases(); $exprLangProviders = $container->getExpressionLanguageProviders(); + $configAvailable = class_exists(BaseNode::class); foreach ($container->getExtensions() as $extension) { if ($extension instanceof PrependExtensionInterface) { @@ -42,23 +51,174 @@ public function process(ContainerBuilder $container) // this extension was not called continue; } - $config = $container->getParameterBag()->resolveValue($config); + $resolvingBag = $container->getParameterBag(); + if ($resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension) { + // create a dedicated bag so that we can track env vars per-extension + $resolvingBag = new MergeExtensionConfigurationParameterBag($resolvingBag); + if ($configAvailable) { + BaseNode::setPlaceholderUniquePrefix($resolvingBag->getEnvPlaceholderUniquePrefix()); + } + } + $config = $resolvingBag->resolveValue($config); + + try { + $tmpContainer = new MergeExtensionConfigurationContainerBuilder($extension, $resolvingBag); + $tmpContainer->setResourceTracking($container->isTrackingResources()); + $tmpContainer->addObjectResource($extension); + if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) { + $tmpContainer->addObjectResource($configuration); + } + + foreach ($exprLangProviders as $provider) { + $tmpContainer->addExpressionLanguageProvider($provider); + } - $tmpContainer = new ContainerBuilder($container->getParameterBag()); - $tmpContainer->setResourceTracking($container->isTrackingResources()); - $tmpContainer->addObjectResource($extension); + $extension->load($config, $tmpContainer); + } catch (\Exception $e) { + if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { + $container->getParameterBag()->mergeEnvPlaceholders($resolvingBag); + } - foreach ($exprLangProviders as $provider) { - $tmpContainer->addExpressionLanguageProvider($provider); + if ($configAvailable) { + BaseNode::resetPlaceholders(); + } + + throw $e; } - $extension->load($config, $tmpContainer); + if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { + // don't keep track of env vars that are *overridden* when configs are merged + $resolvingBag->freezeAfterProcessing($extension, $tmpContainer); + } $container->merge($tmpContainer); $container->getParameterBag()->add($parameters); } + if ($configAvailable) { + BaseNode::resetPlaceholders(); + } + $container->addDefinitions($definitions); $container->addAliases($aliases); } } + +/** + * @internal + */ +class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag +{ + private $processedEnvPlaceholders; + + public function __construct(parent $parameterBag) + { + parent::__construct($parameterBag->all()); + $this->mergeEnvPlaceholders($parameterBag); + } + + public function freezeAfterProcessing(Extension $extension, ContainerBuilder $container) + { + if (!$config = $extension->getProcessedConfigs()) { + // Extension::processConfiguration() wasn't called, we cannot know how configs were merged + return; + } + $this->processedEnvPlaceholders = []; + + // serialize config and container to catch env vars nested in object graphs + $config = serialize($config).serialize($container->getDefinitions()).serialize($container->getAliases()).serialize($container->getParameterBag()->all()); + + foreach (parent::getEnvPlaceholders() as $env => $placeholders) { + foreach ($placeholders as $placeholder) { + if (false !== stripos($config, $placeholder)) { + $this->processedEnvPlaceholders[$env] = $placeholders; + break; + } + } + } + } + + /** + * {@inheritdoc} + */ + public function getEnvPlaceholders(): array + { + return $this->processedEnvPlaceholders ?? parent::getEnvPlaceholders(); + } + + public function getUnusedEnvPlaceholders(): array + { + return null === $this->processedEnvPlaceholders ? [] : array_diff_key(parent::getEnvPlaceholders(), $this->processedEnvPlaceholders); + } +} + +/** + * A container builder preventing using methods that wouldn't have any effect from extensions. + * + * @internal + */ +class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder +{ + private $extensionClass; + + public function __construct(ExtensionInterface $extension, ParameterBagInterface $parameterBag = null) + { + parent::__construct($parameterBag); + + $this->extensionClass = \get_class($extension); + } + + /** + * {@inheritdoc} + */ + public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0): self + { + throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', get_debug_type($pass), $this->extensionClass)); + } + + /** + * {@inheritdoc} + */ + public function registerExtension(ExtensionInterface $extension) + { + throw new LogicException(sprintf('You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.', get_debug_type($extension), $this->extensionClass)); + } + + /** + * {@inheritdoc} + */ + public function compile(bool $resolveEnvPlaceholders = false) + { + throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass)); + } + + /** + * {@inheritdoc} + */ + public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) + { + if (true !== $format || !\is_string($value)) { + return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); + } + + $bag = $this->getParameterBag(); + $value = $bag->resolveValue($value); + + if (!$bag instanceof EnvPlaceholderParameterBag) { + return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); + } + + foreach ($bag->getEnvPlaceholders() as $env => $placeholders) { + if (!str_contains($env, ':')) { + continue; + } + foreach ($placeholders as $placeholder) { + if (false !== stripos($value, $placeholder)) { + throw new RuntimeException(sprintf('Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.', $env, $this->extensionClass)); + } + } + } + + return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/PassConfig.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/PassConfig.php index b95a41482..961711fd2 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/PassConfig.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/PassConfig.php @@ -22,16 +22,16 @@ */ class PassConfig { - const TYPE_AFTER_REMOVING = 'afterRemoving'; - const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization'; - const TYPE_BEFORE_REMOVING = 'beforeRemoving'; - const TYPE_OPTIMIZE = 'optimization'; - const TYPE_REMOVE = 'removing'; + public const TYPE_AFTER_REMOVING = 'afterRemoving'; + public const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization'; + public const TYPE_BEFORE_REMOVING = 'beforeRemoving'; + public const TYPE_OPTIMIZE = 'optimization'; + public const TYPE_REMOVE = 'removing'; private $mergePass; - private $afterRemovingPasses = array(); - private $beforeOptimizationPasses = array(); - private $beforeRemovingPasses = array(); + private $afterRemovingPasses = []; + private $beforeOptimizationPasses = []; + private $beforeRemovingPasses = []; private $optimizationPasses; private $removingPasses; @@ -39,33 +39,58 @@ public function __construct() { $this->mergePass = new MergeExtensionConfigurationPass(); - $this->optimizationPasses = array(array( - new ExtensionCompilerPass(), - new ResolveDefinitionTemplatesPass(), + $this->beforeOptimizationPasses = [ + 100 => [ + new ResolveClassPass(), + new ResolveInstanceofConditionalsPass(), + new RegisterEnvVarProcessorsPass(), + ], + -1000 => [new ExtensionCompilerPass()], + ]; + + $this->optimizationPasses = [[ + new AutoAliasServicePass(), + new ValidateEnvPlaceholdersPass(), + new ResolveDecoratorStackPass(), + new ResolveChildDefinitionsPass(), + new RegisterServiceSubscribersPass(), + new ResolveParameterPlaceHoldersPass(false, false), + new ResolveFactoryClassPass(), + new ResolveNamedArgumentsPass(), + new AutowireRequiredMethodsPass(), + new AutowireRequiredPropertiesPass(), + new ResolveBindingsPass(), + new ServiceLocatorTagPass(), new DecoratorServicePass(), - new ResolveParameterPlaceHoldersPass(), - new FactoryReturnTypePass(), new CheckDefinitionValidityPass(), + new AutowirePass(false), + new ResolveTaggedIteratorArgumentPass(), + new ResolveServiceSubscribersPass(), new ResolveReferencesToAliasesPass(), new ResolveInvalidReferencesPass(), - new AutowirePass(), new AnalyzeServiceReferencesPass(true), new CheckCircularReferencesPass(), new CheckReferenceValidityPass(), - )); + new CheckArgumentsValidityPass(false), + ]]; - $this->removingPasses = array(array( + $this->removingPasses = [[ new RemovePrivateAliasesPass(), new ReplaceAliasByActualDefinitionPass(), new RemoveAbstractDefinitionsPass(), - new RepeatedPass(array( - new AnalyzeServiceReferencesPass(), - new InlineServiceDefinitionsPass(), - new AnalyzeServiceReferencesPass(), - new RemoveUnusedDefinitionsPass(), - )), + new RemoveUnusedDefinitionsPass(), + new AnalyzeServiceReferencesPass(), new CheckExceptionOnInvalidReferenceBehaviorPass(), - )); + new InlineServiceDefinitionsPass(new AnalyzeServiceReferencesPass()), + new AnalyzeServiceReferencesPass(), + new DefinitionErrorExceptionPass(), + ]]; + + $this->afterRemovingPasses = [[ + new ResolveHotPathPass(), + new ResolveNoPreloadPass(), + new AliasDeprecatedPublicServicesPass(), + ]]; } /** @@ -76,7 +101,7 @@ public function __construct() public function getPasses() { return array_merge( - array($this->mergePass), + [$this->mergePass], $this->getBeforeOptimizationPasses(), $this->getOptimizationPasses(), $this->getBeforeRemovingPasses(), @@ -88,27 +113,10 @@ public function getPasses() /** * Adds a pass. * - * @param CompilerPassInterface $pass A Compiler pass - * @param string $type The pass type - * @param int $priority Used to sort the passes - * * @throws InvalidArgumentException when a pass type doesn't exist */ - public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) + public function addPass(CompilerPassInterface $pass, string $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { - if (func_num_args() >= 3) { - $priority = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); - } - } - - $priority = 0; - } - $property = $type.'Passes'; if (!isset($this->$property)) { throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); @@ -117,7 +125,7 @@ public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_O $passes = &$this->$property; if (!isset($passes[$priority])) { - $passes[$priority] = array(); + $passes[$priority] = []; } $passes[$priority][] = $pass; } @@ -182,11 +190,6 @@ public function getMergePass() return $this->mergePass; } - /** - * Sets the Merge Pass. - * - * @param CompilerPassInterface $pass The merge pass - */ public function setMergePass(CompilerPassInterface $pass) { $this->mergePass = $pass; @@ -199,7 +202,7 @@ public function setMergePass(CompilerPassInterface $pass) */ public function setAfterRemovingPasses(array $passes) { - $this->afterRemovingPasses = array($passes); + $this->afterRemovingPasses = [$passes]; } /** @@ -209,7 +212,7 @@ public function setAfterRemovingPasses(array $passes) */ public function setBeforeOptimizationPasses(array $passes) { - $this->beforeOptimizationPasses = array($passes); + $this->beforeOptimizationPasses = [$passes]; } /** @@ -219,7 +222,7 @@ public function setBeforeOptimizationPasses(array $passes) */ public function setBeforeRemovingPasses(array $passes) { - $this->beforeRemovingPasses = array($passes); + $this->beforeRemovingPasses = [$passes]; } /** @@ -229,7 +232,7 @@ public function setBeforeRemovingPasses(array $passes) */ public function setOptimizationPasses(array $passes) { - $this->optimizationPasses = array($passes); + $this->optimizationPasses = [$passes]; } /** @@ -239,7 +242,7 @@ public function setOptimizationPasses(array $passes) */ public function setRemovingPasses(array $passes) { - $this->removingPasses = array($passes); + $this->removingPasses = [$passes]; } /** @@ -249,15 +252,15 @@ public function setRemovingPasses(array $passes) * * @return CompilerPassInterface[] */ - private function sortPasses(array $passes) + private function sortPasses(array $passes): array { - if (0 === count($passes)) { - return array(); + if (0 === \count($passes)) { + return []; } krsort($passes); // Flatten the array - return call_user_func_array('array_merge', $passes); + return array_merge(...$passes); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php index 123bec999..806545bee 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php @@ -11,8 +11,11 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; /** * Trait that allows a generic method to find and sort service by priority option in the tag. @@ -24,24 +27,148 @@ trait PriorityTaggedServiceTrait /** * Finds all services with the given tag name and order them by their priority. * - * @param string $tagName - * @param ContainerBuilder $container + * The order of additions must be respected for services having the same priority, + * and knowing that the \SplPriorityQueue class does not respect the FIFO method, + * we should not use that class. + * + * @see https://bugs.php.net/53710 + * @see https://bugs.php.net/60926 + * + * @param string|TaggedIteratorArgument $tagName * * @return Reference[] */ - private function findAndSortTaggedServices($tagName, ContainerBuilder $container) + private function findAndSortTaggedServices($tagName, ContainerBuilder $container): array { - $services = $container->findTaggedServiceIds($tagName); + $indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null; + + if ($tagName instanceof TaggedIteratorArgument) { + $indexAttribute = $tagName->getIndexAttribute(); + $defaultIndexMethod = $tagName->getDefaultIndexMethod(); + $needsIndexes = $tagName->needsIndexes(); + $defaultPriorityMethod = $tagName->getDefaultPriorityMethod() ?? 'getDefaultPriority'; + $tagName = $tagName->getTag(); + } + + $i = 0; + $services = []; + + foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) { + $defaultPriority = null; + $defaultIndex = null; + $class = $container->getDefinition($serviceId)->getClass(); + $class = $container->getParameterBag()->resolveValue($class) ?: null; + + foreach ($attributes as $attribute) { + $index = $priority = null; + + if (isset($attribute['priority'])) { + $priority = $attribute['priority']; + } elseif (null === $defaultPriority && $defaultPriorityMethod && $class) { + $defaultPriority = PriorityTaggedServiceUtil::getDefaultPriority($container, $serviceId, $class, $defaultPriorityMethod, $tagName); + } + $priority = $priority ?? $defaultPriority ?? $defaultPriority = 0; + + if (null === $indexAttribute && !$defaultIndexMethod && !$needsIndexes) { + $services[] = [$priority, ++$i, null, $serviceId, null]; + continue 2; + } + + if (null !== $indexAttribute && isset($attribute[$indexAttribute])) { + $index = $attribute[$indexAttribute]; + } elseif (null === $defaultIndex && $defaultPriorityMethod && $class) { + $defaultIndex = PriorityTaggedServiceUtil::getDefaultIndex($container, $serviceId, $class, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute); + } + $index = $index ?? $defaultIndex ?? $defaultIndex = $serviceId; + + $services[] = [$priority, ++$i, $index, $serviceId, $class]; + } + } - $queue = new \SplPriorityQueue(); + uasort($services, static function ($a, $b) { return $b[0] <=> $a[0] ?: $a[1] <=> $b[1]; }); - foreach ($services as $serviceId => $tags) { - foreach ($tags as $attributes) { - $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; - $queue->insert(new Reference($serviceId), $priority); + $refs = []; + foreach ($services as [, , $index, $serviceId, $class]) { + if (!$class) { + $reference = new Reference($serviceId); + } elseif ($index === $serviceId) { + $reference = new TypedReference($serviceId, $class); + } else { + $reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $index); } + + if (null === $index) { + $refs[] = $reference; + } else { + $refs[$index] = $reference; + } + } + + return $refs; + } +} + +/** + * @internal + */ +class PriorityTaggedServiceUtil +{ + /** + * Gets the index defined by the default index method. + */ + public static function getDefaultIndex(ContainerBuilder $container, string $serviceId, string $class, string $defaultIndexMethod, string $tagName, ?string $indexAttribute): ?string + { + if (!($r = $container->getReflectionClass($class)) || !$r->hasMethod($defaultIndexMethod)) { + return null; + } + + if (null !== $indexAttribute) { + $service = $class !== $serviceId ? sprintf('service "%s"', $serviceId) : 'on the corresponding service'; + $message = [sprintf('Either method "%s::%s()" should ', $class, $defaultIndexMethod), sprintf(' or tag "%s" on %s is missing attribute "%s".', $tagName, $service, $indexAttribute)]; + } else { + $message = [sprintf('Method "%s::%s()" should ', $class, $defaultIndexMethod), '.']; + } + + if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) { + throw new InvalidArgumentException(implode('be static', $message)); + } + + if (!$rm->isPublic()) { + throw new InvalidArgumentException(implode('be public', $message)); + } + + $defaultIndex = $rm->invoke(null); + + if (!\is_string($defaultIndex)) { + throw new InvalidArgumentException(implode(sprintf('return a string (got "%s")', get_debug_type($defaultIndex)), $message)); + } + + return $defaultIndex; + } + + /** + * Gets the priority defined by the default priority method. + */ + public static function getDefaultPriority(ContainerBuilder $container, string $serviceId, string $class, string $defaultPriorityMethod, string $tagName): ?int + { + if (!($r = $container->getReflectionClass($class)) || !$r->hasMethod($defaultPriorityMethod)) { + return null; + } + + if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) { + throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should be static or tag "%s" on service "%s" is missing attribute "priority".', $class, $defaultPriorityMethod, $tagName, $serviceId)); + } + + if (!$rm->isPublic()) { + throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should be public or tag "%s" on service "%s" is missing attribute "priority".', $class, $defaultPriorityMethod, $tagName, $serviceId)); + } + + $defaultPriority = $rm->invoke(null); + + if (!\is_int($defaultPriority)) { + throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer (got "%s") or tag "%s" on service "%s" is missing attribute "priority".', $class, $defaultPriorityMethod, get_debug_type($defaultPriority), $tagName, $serviceId)); } - return iterator_to_array($queue, false); + return $defaultPriority; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php new file mode 100644 index 000000000..251889ebe --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\EnvVarProcessor; +use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Creates the container.env_var_processors_locator service. + * + * @author Nicolas Grekas + */ +class RegisterEnvVarProcessorsPass implements CompilerPassInterface +{ + private const ALLOWED_TYPES = ['array', 'bool', 'float', 'int', 'string']; + + public function process(ContainerBuilder $container) + { + $bag = $container->getParameterBag(); + $types = []; + $processors = []; + foreach ($container->findTaggedServiceIds('container.env_var_processor') as $id => $tags) { + if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class)); + } + foreach ($class::getProvidedTypes() as $prefix => $type) { + $processors[$prefix] = new Reference($id); + $types[$prefix] = self::validateProvidedTypes($type, $class); + } + } + + if ($bag instanceof EnvPlaceholderParameterBag) { + foreach (EnvVarProcessor::getProvidedTypes() as $prefix => $type) { + if (!isset($types[$prefix])) { + $types[$prefix] = self::validateProvidedTypes($type, EnvVarProcessor::class); + } + } + $bag->setProvidedTypes($types); + } + + if ($processors) { + $container->setAlias('container.env_var_processors_locator', (string) ServiceLocatorTagPass::register($container, $processors)) + ->setPublic(true) + ; + } + } + + private static function validateProvidedTypes(string $types, string $class): array + { + $types = explode('|', $types); + + foreach ($types as $type) { + if (!\in_array($type, self::ALLOWED_TYPES)) { + throw new InvalidArgumentException(sprintf('Invalid type "%s" returned by "%s::getProvidedTypes()", expected one of "%s".', $type, $class, implode('", "', self::ALLOWED_TYPES))); + } + } + + return $types; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php new file mode 100644 index 000000000..571eab3ef --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +class RegisterReverseContainerPass implements CompilerPassInterface +{ + private $beforeRemoving; + private $serviceId; + private $tagName; + + public function __construct(bool $beforeRemoving, string $serviceId = 'reverse_container', string $tagName = 'container.reversible') + { + $this->beforeRemoving = $beforeRemoving; + $this->serviceId = $serviceId; + $this->tagName = $tagName; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->serviceId)) { + return; + } + + $refType = $this->beforeRemoving ? ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + $services = []; + foreach ($container->findTaggedServiceIds($this->tagName) as $id => $tags) { + $services[$id] = new Reference($id, $refType); + } + + if ($this->beforeRemoving) { + // prevent inlining of the reverse container + $services[$this->serviceId] = new Reference($this->serviceId, $refType); + } + $locator = $container->getDefinition($this->serviceId)->getArgument(1); + + if ($locator instanceof Reference) { + $locator = $container->getDefinition((string) $locator); + } + if ($locator instanceof Definition) { + foreach ($services as $id => $ref) { + $services[$id] = new ServiceClosureArgument($ref); + } + $locator->replaceArgument(0, $services); + } else { + $locator->setValues($services); + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php new file mode 100644 index 000000000..c292c23b1 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Contracts\Service\ServiceProviderInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; + +/** + * Compiler pass to register tagged services that require a service locator. + * + * @author Nicolas Grekas + */ +class RegisterServiceSubscribersPass extends AbstractRecursivePass +{ + protected function processValue($value, bool $isRoot = false) + { + if (!$value instanceof Definition || $value->isAbstract() || $value->isSynthetic() || !$value->hasTag('container.service_subscriber')) { + return parent::processValue($value, $isRoot); + } + + $serviceMap = []; + $autowire = $value->isAutowired(); + + foreach ($value->getTag('container.service_subscriber') as $attributes) { + if (!$attributes) { + $autowire = true; + continue; + } + ksort($attributes); + if ([] !== array_diff(array_keys($attributes), ['id', 'key'])) { + throw new InvalidArgumentException(sprintf('The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".', implode('", "', array_keys($attributes)), $this->currentId)); + } + if (!\array_key_exists('id', $attributes)) { + throw new InvalidArgumentException(sprintf('Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".', $attributes['key'], $this->currentId)); + } + if (!\array_key_exists('key', $attributes)) { + $attributes['key'] = $attributes['id']; + } + if (isset($serviceMap[$attributes['key']])) { + continue; + } + $serviceMap[$attributes['key']] = new Reference($attributes['id']); + } + $class = $value->getClass(); + + if (!$r = $this->container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $this->currentId)); + } + if (!$r->isSubclassOf(ServiceSubscriberInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $this->currentId, ServiceSubscriberInterface::class)); + } + $class = $r->name; + + $subscriberMap = []; + + foreach ($class::getSubscribedServices() as $key => $type) { + if (!\is_string($type) || !preg_match('/^\??[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $type)) { + throw new InvalidArgumentException(sprintf('"%s::getSubscribedServices()" must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, \is_string($type) ? $type : get_debug_type($type))); + } + if ($optionalBehavior = '?' === $type[0]) { + $type = substr($type, 1); + $optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } + if (\is_int($name = $key)) { + $key = $type; + $name = null; + } + if (!isset($serviceMap[$key])) { + if (!$autowire) { + throw new InvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by "%s::getSubscribedServices()".', $this->currentId, $key, $class)); + } + $serviceMap[$key] = new Reference($type); + } + + if ($name) { + if (false !== $i = strpos($name, '::get')) { + $name = lcfirst(substr($name, 5 + $i)); + } elseif (str_contains($name, '::')) { + $name = null; + } + } + + if (null !== $name && !$this->container->has($name) && !$this->container->has($type.' $'.$name)) { + $camelCaseName = lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $name)))); + $name = $this->container->has($type.' $'.$camelCaseName) ? $camelCaseName : $name; + } + + $subscriberMap[$key] = new TypedReference((string) $serviceMap[$key], $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name); + unset($serviceMap[$key]); + } + + if ($serviceMap = array_keys($serviceMap)) { + $message = sprintf(1 < \count($serviceMap) ? 'keys "%s" do' : 'key "%s" does', str_replace('%', '%%', implode('", "', $serviceMap))); + throw new InvalidArgumentException(sprintf('Service %s not exist in the map returned by "%s::getSubscribedServices()" for service "%s".', $message, $class, $this->currentId)); + } + + $locatorRef = ServiceLocatorTagPass::register($this->container, $subscriberMap, $this->currentId); + + $value->addTag('container.service_subscriber.locator', ['id' => (string) $locatorRef]); + + $value->setBindings([ + PsrContainerInterface::class => new BoundArgument($locatorRef, false), + ServiceProviderInterface::class => new BoundArgument($locatorRef, false), + ] + $value->getBindings()); + + return parent::processValue($value); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php index 0ef0af05b..04b6852fa 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php @@ -20,18 +20,13 @@ class RemoveAbstractDefinitionsPass implements CompilerPassInterface { /** * Removes abstract definitions from the ContainerBuilder. - * - * @param ContainerBuilder $container */ public function process(ContainerBuilder $container) { - $compiler = $container->getCompiler(); - $formatter = $compiler->getLoggingFormatter(); - foreach ($container->getDefinitions() as $id => $definition) { if ($definition->isAbstract()) { $container->removeDefinition($id); - $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'abstract')); + $container->log($this, sprintf('Removed service "%s"; reason: abstract.', $id)); } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php index 5c53a3394..75b36d227 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php @@ -24,21 +24,16 @@ class RemovePrivateAliasesPass implements CompilerPassInterface { /** * Removes private aliases from the ContainerBuilder. - * - * @param ContainerBuilder $container */ public function process(ContainerBuilder $container) { - $compiler = $container->getCompiler(); - $formatter = $compiler->getLoggingFormatter(); - foreach ($container->getAliases() as $id => $alias) { if ($alias->isPublic()) { continue; } $container->removeAlias($id); - $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'private alias')); + $container->log($this, sprintf('Removed service "%s"; reason: private alias.', $id)); } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php index 9e18a9ebd..cf1a3ddc7 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php @@ -12,73 +12,79 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; /** * Removes unused service definitions from the container. * * @author Johannes M. Schmitt + * @author Nicolas Grekas */ -class RemoveUnusedDefinitionsPass implements RepeatablePassInterface +class RemoveUnusedDefinitionsPass extends AbstractRecursivePass { - private $repeatedPass; - - /** - * {@inheritdoc} - */ - public function setRepeatedPass(RepeatedPass $repeatedPass) - { - $this->repeatedPass = $repeatedPass; - } + private $connectedIds = []; /** * Processes the ContainerBuilder to remove unused definitions. - * - * @param ContainerBuilder $container */ public function process(ContainerBuilder $container) { - $compiler = $container->getCompiler(); - $formatter = $compiler->getLoggingFormatter(); - $graph = $compiler->getServiceReferenceGraph(); + try { + $this->enableExpressionProcessing(); + $this->container = $container; + $connectedIds = []; + $aliases = $container->getAliases(); - $hasChanged = false; - foreach ($container->getDefinitions() as $id => $definition) { - if ($definition->isPublic()) { - continue; + foreach ($aliases as $id => $alias) { + if ($alias->isPublic()) { + $this->connectedIds[] = (string) $aliases[$id]; + } } - if ($graph->hasNode($id)) { - $edges = $graph->getNode($id)->getInEdges(); - $referencingAliases = array(); - $sourceIds = array(); - foreach ($edges as $edge) { - $node = $edge->getSourceNode(); - $sourceIds[] = $node->getId(); + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isPublic()) { + $connectedIds[$id] = true; + $this->processValue($definition); + } + } - if ($node->isAlias()) { - $referencingAliases[] = $node->getValue(); + while ($this->connectedIds) { + $ids = $this->connectedIds; + $this->connectedIds = []; + foreach ($ids as $id) { + if (!isset($connectedIds[$id]) && $container->hasDefinition($id)) { + $connectedIds[$id] = true; + $this->processValue($container->getDefinition($id)); } } - $isReferenced = (count(array_unique($sourceIds)) - count($referencingAliases)) > 0; - } else { - $referencingAliases = array(); - $isReferenced = false; } - if (1 === count($referencingAliases) && false === $isReferenced) { - $container->setDefinition((string) reset($referencingAliases), $definition); - $definition->setPublic(true); - $container->removeDefinition($id); - $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'replaces alias '.reset($referencingAliases))); - } elseif (0 === count($referencingAliases) && false === $isReferenced) { - $container->removeDefinition($id); - $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'unused')); - $hasChanged = true; + foreach ($container->getDefinitions() as $id => $definition) { + if (!isset($connectedIds[$id])) { + $container->removeDefinition($id); + $container->resolveEnvPlaceholders(!$definition->hasErrors() ? serialize($definition) : $definition); + $container->log($this, sprintf('Removed service "%s"; reason: unused.', $id)); + } } + } finally { + $this->container = null; + $this->connectedIds = []; } + } - if ($hasChanged) { - $this->repeatedPass->setRepeat(); + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (!$value instanceof Reference) { + return parent::processValue($value, $isRoot); + } + + if (ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior()) { + $this->connectedIds[] = (string) $value; } + + return $value; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.php deleted file mode 100644 index d60ae35bc..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -/** - * Interface that must be implemented by passes that are run as part of an - * RepeatedPass. - * - * @author Johannes M. Schmitt - */ -interface RepeatablePassInterface extends CompilerPassInterface -{ - /** - * Sets the RepeatedPass interface. - * - * @param RepeatedPass $repeatedPass - */ - public function setRepeatedPass(RepeatedPass $repeatedPass); -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/RepeatedPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/RepeatedPass.php deleted file mode 100644 index 59d4e0a76..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/RepeatedPass.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; - -/** - * A pass that might be run repeatedly. - * - * @author Johannes M. Schmitt - */ -class RepeatedPass implements CompilerPassInterface -{ - /** - * @var bool - */ - private $repeat = false; - - /** - * @var RepeatablePassInterface[] - */ - private $passes; - - /** - * @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects - * - * @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface - */ - public function __construct(array $passes) - { - foreach ($passes as $pass) { - if (!$pass instanceof RepeatablePassInterface) { - throw new InvalidArgumentException('$passes must be an array of RepeatablePassInterface.'); - } - - $pass->setRepeatedPass($this); - } - - $this->passes = $passes; - } - - /** - * Process the repeatable passes that run more than once. - * - * @param ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - do { - $this->repeat = false; - foreach ($this->passes as $pass) { - $pass->process($container); - } - } while ($this->repeat); - } - - /** - * Sets if the pass should repeat. - */ - public function setRepeat() - { - $this->repeat = true; - } - - /** - * Returns the passes. - * - * @return RepeatablePassInterface[] An array of RepeatablePassInterface objects - */ - public function getPasses() - { - return $this->passes; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php index 00fc859d3..18e69fdcb 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Reference; /** @@ -21,26 +22,20 @@ * * @author Johannes M. Schmitt */ -class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface +class ReplaceAliasByActualDefinitionPass extends AbstractRecursivePass { - private $compiler; - private $formatter; + private $replacements; /** * Process the Container to replace aliases with service definitions. * - * @param ContainerBuilder $container - * * @throws InvalidArgumentException if the service definition does not exist */ public function process(ContainerBuilder $container) { - // Setup - $this->compiler = $container->getCompiler(); - $this->formatter = $this->compiler->getLoggingFormatter(); // First collect all alias targets that need to be replaced - $seenAliasTargets = array(); - $replacements = array(); + $seenAliasTargets = []; + $replacements = []; foreach ($container->getAliases() as $definitionId => $target) { $targetId = (string) $target; // Special case: leave this target alone @@ -49,9 +44,9 @@ public function process(ContainerBuilder $container) } // Check if target needs to be replaces if (isset($replacements[$targetId])) { - $container->setAlias($definitionId, $replacements[$targetId]); + $container->setAlias($definitionId, $replacements[$targetId])->setPublic($target->isPublic()); } - // No neeed to process the same target twice + // No need to process the same target twice if (isset($seenAliasTargets[$targetId])) { continue; } @@ -59,68 +54,40 @@ public function process(ContainerBuilder $container) $seenAliasTargets[$targetId] = true; try { $definition = $container->getDefinition($targetId); - } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); + } catch (ServiceNotFoundException $e) { + if ('' !== $e->getId() && '@' === $e->getId()[0]) { + throw new ServiceNotFoundException($e->getId(), $e->getSourceId(), null, [substr($e->getId(), 1)]); + } + + throw $e; } if ($definition->isPublic()) { continue; } // Remove private definition and schedule for replacement - $definition->setPublic(true); + $definition->setPublic($target->isPublic()); $container->setDefinition($definitionId, $definition); $container->removeDefinition($targetId); $replacements[$targetId] = $definitionId; } + $this->replacements = $replacements; - // Now replace target instances in all definitions - foreach ($container->getDefinitions() as $definitionId => $definition) { - $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments())); - $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls())); - $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties())); - $definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory())); - } + parent::process($container); + $this->replacements = []; } /** - * Recursively updates references in an array. - * - * @param array $replacements Table of aliases to replace - * @param string $definitionId Identifier of this definition - * @param array $arguments Where to replace the aliases - * - * @return array + * {@inheritdoc} */ - private function updateArgumentReferences(array $replacements, $definitionId, array $arguments) + protected function processValue($value, bool $isRoot = false) { - foreach ($arguments as $k => $argument) { - // Handle recursion step - if (is_array($argument)) { - $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument); - continue; - } - // Skip arguments that don't need replacement - if (!$argument instanceof Reference) { - continue; - } - $referenceId = (string) $argument; - if (!isset($replacements[$referenceId])) { - continue; - } + if ($value instanceof Reference && isset($this->replacements[$referenceId = (string) $value])) { // Perform the replacement - $newId = $replacements[$referenceId]; - $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); - $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId)); - } - - return $arguments; - } - - private function updateFactoryReference(array $replacements, $factory) - { - if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) { - $factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior()); + $newId = $this->replacements[$referenceId]; + $value = new Reference($newId, $value->getInvalidBehavior()); + $this->container->log($this, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $this->currentId, $referenceId, $newId)); } - return $factory; + return parent::processValue($value, $isRoot); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php new file mode 100644 index 000000000..91cba0029 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php @@ -0,0 +1,239 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * @author Guilhem Niot + */ +class ResolveBindingsPass extends AbstractRecursivePass +{ + private $usedBindings = []; + private $unusedBindings = []; + private $errorMessages = []; + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->usedBindings = $container->getRemovedBindingIds(); + + try { + parent::process($container); + + foreach ($this->unusedBindings as [$key, $serviceId, $bindingType, $file]) { + $argumentType = $argumentName = $message = null; + + if (str_contains($key, ' ')) { + [$argumentType, $argumentName] = explode(' ', $key, 2); + } elseif ('$' === $key[0]) { + $argumentName = $key; + } else { + $argumentType = $key; + } + + if ($argumentType) { + $message .= sprintf('of type "%s" ', $argumentType); + } + + if ($argumentName) { + $message .= sprintf('named "%s" ', $argumentName); + } + + if (BoundArgument::DEFAULTS_BINDING === $bindingType) { + $message .= 'under "_defaults"'; + } elseif (BoundArgument::INSTANCEOF_BINDING === $bindingType) { + $message .= 'under "_instanceof"'; + } else { + $message .= sprintf('for service "%s"', $serviceId); + } + + if ($file) { + $message .= sprintf(' in file "%s"', $file); + } + + $message = sprintf('A binding is configured for an argument %s, but no corresponding argument has been found. It may be unused and should be removed, or it may have a typo.', $message); + + if ($this->errorMessages) { + $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : ''); + } + foreach ($this->errorMessages as $m) { + $message .= "\n - ".$m; + } + throw new InvalidArgumentException($message); + } + } finally { + $this->usedBindings = []; + $this->unusedBindings = []; + $this->errorMessages = []; + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof TypedReference && $value->getType() === (string) $value) { + // Already checked + $bindings = $this->container->getDefinition($this->currentId)->getBindings(); + $name = $value->getName(); + + if (isset($name, $bindings[$name = $value.' $'.$name])) { + return $this->getBindingValue($bindings[$name]); + } + + if (isset($bindings[$value->getType()])) { + return $this->getBindingValue($bindings[$value->getType()]); + } + + return parent::processValue($value, $isRoot); + } + + if (!$value instanceof Definition || !$bindings = $value->getBindings()) { + return parent::processValue($value, $isRoot); + } + + $bindingNames = []; + + foreach ($bindings as $key => $binding) { + [$bindingValue, $bindingId, $used, $bindingType, $file] = $binding->getValues(); + if ($used) { + $this->usedBindings[$bindingId] = true; + unset($this->unusedBindings[$bindingId]); + } elseif (!isset($this->usedBindings[$bindingId])) { + $this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file]; + } + + if (preg_match('/^(?:(?:array|bool|float|int|string|([^ $]++)) )\$/', $key, $m)) { + $bindingNames[substr($key, \strlen($m[0]))] = $binding; + } + + if (!isset($m[1])) { + continue; + } + + if (null !== $bindingValue && !$bindingValue instanceof Reference && !$bindingValue instanceof Definition && !$bindingValue instanceof TaggedIteratorArgument && !$bindingValue instanceof ServiceLocatorArgument) { + throw new InvalidArgumentException(sprintf('Invalid value for binding key "%s" for service "%s": expected "%s", "%s", "%s", "%s" or null, "%s" given.', $key, $this->currentId, Reference::class, Definition::class, TaggedIteratorArgument::class, ServiceLocatorArgument::class, get_debug_type($bindingValue))); + } + } + + if ($value->isAbstract()) { + return parent::processValue($value, $isRoot); + } + + $calls = $value->getMethodCalls(); + + try { + if ($constructor = $this->getConstructor($value, false)) { + $calls[] = [$constructor, $value->getArguments()]; + } + } catch (RuntimeException $e) { + $this->errorMessages[] = $e->getMessage(); + $this->container->getDefinition($this->currentId)->addError($e->getMessage()); + + return parent::processValue($value, $isRoot); + } + + foreach ($calls as $i => $call) { + [$method, $arguments] = $call; + + if ($method instanceof \ReflectionFunctionAbstract) { + $reflectionMethod = $method; + } else { + try { + $reflectionMethod = $this->getReflectionMethod($value, $method); + } catch (RuntimeException $e) { + if ($value->getFactory()) { + continue; + } + throw $e; + } + } + + foreach ($reflectionMethod->getParameters() as $key => $parameter) { + if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) { + continue; + } + + $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter); + + if ($typeHint && \array_key_exists($k = ltrim($typeHint, '\\').' $'.$parameter->name, $bindings)) { + $arguments[$key] = $this->getBindingValue($bindings[$k]); + + continue; + } + + if (\array_key_exists('$'.$parameter->name, $bindings)) { + $arguments[$key] = $this->getBindingValue($bindings['$'.$parameter->name]); + + continue; + } + + if ($typeHint && '\\' === $typeHint[0] && isset($bindings[$typeHint = substr($typeHint, 1)])) { + $arguments[$key] = $this->getBindingValue($bindings[$typeHint]); + + continue; + } + + if (isset($bindingNames[$parameter->name])) { + $bindingKey = array_search($binding, $bindings, true); + $argumentType = substr($bindingKey, 0, strpos($bindingKey, ' ')); + $this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name); + } + } + + if ($arguments !== $call[1]) { + ksort($arguments); + $calls[$i][1] = $arguments; + } + } + + if ($constructor) { + [, $arguments] = array_pop($calls); + + if ($arguments !== $value->getArguments()) { + $value->setArguments($arguments); + } + } + + if ($calls !== $value->getMethodCalls()) { + $value->setMethodCalls($calls); + } + + return parent::processValue($value, $isRoot); + } + + /** + * @return mixed + */ + private function getBindingValue(BoundArgument $binding) + { + [$bindingValue, $bindingId] = $binding->getValues(); + + $this->usedBindings[$bindingId] = true; + unset($this->unusedBindings[$bindingId]); + + return $bindingValue; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php new file mode 100644 index 000000000..a1b2c86e3 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ExceptionInterface; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; + +/** + * This replaces all ChildDefinition instances with their equivalent fully + * merged Definition instance. + * + * @author Johannes M. Schmitt + * @author Nicolas Grekas + */ +class ResolveChildDefinitionsPass extends AbstractRecursivePass +{ + private $currentPath; + + protected function processValue($value, bool $isRoot = false) + { + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + if ($isRoot) { + // yes, we are specifically fetching the definition from the + // container to ensure we are not operating on stale data + $value = $this->container->getDefinition($this->currentId); + } + if ($value instanceof ChildDefinition) { + $this->currentPath = []; + $value = $this->resolveDefinition($value); + if ($isRoot) { + $this->container->setDefinition($this->currentId, $value); + } + } + + return parent::processValue($value, $isRoot); + } + + /** + * Resolves the definition. + * + * @throws RuntimeException When the definition is invalid + */ + private function resolveDefinition(ChildDefinition $definition): Definition + { + try { + return $this->doResolveDefinition($definition); + } catch (ServiceCircularReferenceException $e) { + throw $e; + } catch (ExceptionInterface $e) { + $r = new \ReflectionProperty($e, 'message'); + $r->setAccessible(true); + $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage())); + + throw $e; + } + } + + private function doResolveDefinition(ChildDefinition $definition): Definition + { + if (!$this->container->has($parent = $definition->getParent())) { + throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent)); + } + + $searchKey = array_search($parent, $this->currentPath); + $this->currentPath[] = $parent; + + if (false !== $searchKey) { + throw new ServiceCircularReferenceException($parent, \array_slice($this->currentPath, $searchKey)); + } + + $parentDef = $this->container->findDefinition($parent); + if ($parentDef instanceof ChildDefinition) { + $id = $this->currentId; + $this->currentId = $parent; + $parentDef = $this->resolveDefinition($parentDef); + $this->container->setDefinition($parent, $parentDef); + $this->currentId = $id; + } + + $this->container->log($this, sprintf('Resolving inheritance for "%s" (parent: %s).', $this->currentId, $parent)); + $def = new Definition(); + + // merge in parent definition + // purposely ignored attributes: abstract, shared, tags, autoconfigured + $def->setClass($parentDef->getClass()); + $def->setArguments($parentDef->getArguments()); + $def->setMethodCalls($parentDef->getMethodCalls()); + $def->setProperties($parentDef->getProperties()); + if ($parentDef->isDeprecated()) { + $deprecation = $parentDef->getDeprecation('%service_id%'); + $def->setDeprecated($deprecation['package'], $deprecation['version'], $deprecation['message']); + } + $def->setFactory($parentDef->getFactory()); + $def->setConfigurator($parentDef->getConfigurator()); + $def->setFile($parentDef->getFile()); + $def->setPublic($parentDef->isPublic()); + $def->setLazy($parentDef->isLazy()); + $def->setAutowired($parentDef->isAutowired()); + $def->setChanges($parentDef->getChanges()); + + $def->setBindings($definition->getBindings() + $parentDef->getBindings()); + + // overwrite with values specified in the decorator + $changes = $definition->getChanges(); + if (isset($changes['class'])) { + $def->setClass($definition->getClass()); + } + if (isset($changes['factory'])) { + $def->setFactory($definition->getFactory()); + } + if (isset($changes['configurator'])) { + $def->setConfigurator($definition->getConfigurator()); + } + if (isset($changes['file'])) { + $def->setFile($definition->getFile()); + } + if (isset($changes['public'])) { + $def->setPublic($definition->isPublic()); + } else { + $def->setPublic($parentDef->isPublic()); + } + if (isset($changes['lazy'])) { + $def->setLazy($definition->isLazy()); + } + if (isset($changes['deprecated'])) { + if ($definition->isDeprecated()) { + $deprecation = $definition->getDeprecation('%service_id%'); + $def->setDeprecated($deprecation['package'], $deprecation['version'], $deprecation['message']); + } else { + $def->setDeprecated(false); + } + } + if (isset($changes['autowired'])) { + $def->setAutowired($definition->isAutowired()); + } + if (isset($changes['shared'])) { + $def->setShared($definition->isShared()); + } + if (isset($changes['decorated_service'])) { + $decoratedService = $definition->getDecoratedService(); + if (null === $decoratedService) { + $def->setDecoratedService($decoratedService); + } else { + $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2], $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); + } + } + + // merge arguments + foreach ($definition->getArguments() as $k => $v) { + if (is_numeric($k)) { + $def->addArgument($v); + } elseif (str_starts_with($k, 'index_')) { + $def->replaceArgument((int) substr($k, \strlen('index_')), $v); + } else { + $def->setArgument($k, $v); + } + } + + // merge properties + foreach ($definition->getProperties() as $k => $v) { + $def->setProperty($k, $v); + } + + // append method calls + if ($calls = $definition->getMethodCalls()) { + $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); + } + + $def->addError($parentDef); + $def->addError($definition); + + // these attributes are always taken from the child + $def->setAbstract($definition->isAbstract()); + $def->setTags($definition->getTags()); + // autoconfigure is never taken from parent (on purpose) + // and it's not legal on an instanceof + $def->setAutoconfigured($definition->isAutoconfigured()); + + return $def; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php new file mode 100644 index 000000000..e67a2a8ed --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + */ +class ResolveClassPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isSynthetic() || null !== $definition->getClass()) { + continue; + } + if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) { + if ($definition instanceof ChildDefinition && !class_exists($id)) { + throw new InvalidArgumentException(sprintf('Service definition "%s" has a parent but no class, and its name looks like an FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); + } + $definition->setClass($id); + } + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveDecoratorStackPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveDecoratorStackPass.php new file mode 100644 index 000000000..61202adf3 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveDecoratorStackPass.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +class ResolveDecoratorStackPass implements CompilerPassInterface +{ + private $tag; + + public function __construct(string $tag = 'container.stack') + { + $this->tag = $tag; + } + + public function process(ContainerBuilder $container) + { + $stacks = []; + + foreach ($container->findTaggedServiceIds($this->tag) as $id => $tags) { + $definition = $container->getDefinition($id); + + if (!$definition instanceof ChildDefinition) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": only definitions with a "parent" can have the "%s" tag.', $id, $this->tag)); + } + + if (!$stack = $definition->getArguments()) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": the stack of decorators is empty.', $id)); + } + + $stacks[$id] = $stack; + } + + if (!$stacks) { + return; + } + + $resolvedDefinitions = []; + + foreach ($container->getDefinitions() as $id => $definition) { + if (!isset($stacks[$id])) { + $resolvedDefinitions[$id] = $definition; + continue; + } + + foreach (array_reverse($this->resolveStack($stacks, [$id]), true) as $k => $v) { + $resolvedDefinitions[$k] = $v; + } + + $alias = $container->setAlias($id, $k); + + if ($definition->getChanges()['public'] ?? false) { + $alias->setPublic($definition->isPublic()); + } + + if ($definition->isDeprecated()) { + $alias->setDeprecated(...array_values($definition->getDeprecation('%alias_id%'))); + } + } + + $container->setDefinitions($resolvedDefinitions); + } + + private function resolveStack(array $stacks, array $path): array + { + $definitions = []; + $id = end($path); + $prefix = '.'.$id.'.'; + + if (!isset($stacks[$id])) { + return [$id => new ChildDefinition($id)]; + } + + if (key($path) !== $searchKey = array_search($id, $path)) { + throw new ServiceCircularReferenceException($id, \array_slice($path, $searchKey)); + } + + foreach ($stacks[$id] as $k => $definition) { + if ($definition instanceof ChildDefinition && isset($stacks[$definition->getParent()])) { + $path[] = $definition->getParent(); + $definition = unserialize(serialize($definition)); // deep clone + } elseif ($definition instanceof Definition) { + $definitions[$decoratedId = $prefix.$k] = $definition; + continue; + } elseif ($definition instanceof Reference || $definition instanceof Alias) { + $path[] = (string) $definition; + } else { + throw new InvalidArgumentException(sprintf('Invalid service "%s": unexpected value of type "%s" found in the stack of decorators.', $id, get_debug_type($definition))); + } + + $p = $prefix.$k; + + foreach ($this->resolveStack($stacks, $path) as $k => $v) { + $definitions[$decoratedId = $p.$k] = $definition instanceof ChildDefinition ? $definition->setParent($k) : new ChildDefinition($k); + $definition = null; + } + array_pop($path); + } + + if (1 === \count($path)) { + foreach ($definitions as $k => $definition) { + $definition->setPublic(false)->setTags([])->setDecoratedService($decoratedId); + } + $definition->setDecoratedService(null); + } + + return $definitions; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php deleted file mode 100644 index a755fd61e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php +++ /dev/null @@ -1,218 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Compiler; - -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\ExceptionInterface; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; - -/** - * This replaces all DefinitionDecorator instances with their equivalent fully - * merged Definition instance. - * - * @author Johannes M. Schmitt - * @author Nicolas Grekas - */ -class ResolveDefinitionTemplatesPass implements CompilerPassInterface -{ - private $compiler; - private $formatter; - private $currentId; - - /** - * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances. - * - * @param ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - $this->compiler = $container->getCompiler(); - $this->formatter = $this->compiler->getLoggingFormatter(); - - $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true)); - } - - /** - * Resolves definition decorator arguments. - * - * @param ContainerBuilder $container The ContainerBuilder - * @param array $arguments An array of arguments - * @param bool $isRoot If we are processing the root definitions or not - * - * @return array - */ - private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false) - { - foreach ($arguments as $k => $argument) { - if ($isRoot) { - // yes, we are specifically fetching the definition from the - // container to ensure we are not operating on stale data - $arguments[$k] = $argument = $container->getDefinition($k); - $this->currentId = $k; - } - if (is_array($argument)) { - $arguments[$k] = $this->resolveArguments($container, $argument); - } elseif ($argument instanceof Definition) { - if ($argument instanceof DefinitionDecorator) { - $arguments[$k] = $argument = $this->resolveDefinition($container, $argument); - if ($isRoot) { - $container->setDefinition($k, $argument); - } - } - $argument->setArguments($this->resolveArguments($container, $argument->getArguments())); - $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls())); - $argument->setProperties($this->resolveArguments($container, $argument->getProperties())); - - $configurator = $this->resolveArguments($container, array($argument->getConfigurator())); - $argument->setConfigurator($configurator[0]); - - $factory = $this->resolveArguments($container, array($argument->getFactory())); - $argument->setFactory($factory[0]); - } - } - - return $arguments; - } - - /** - * Resolves the definition. - * - * @param ContainerBuilder $container The ContainerBuilder - * @param DefinitionDecorator $definition - * - * @return Definition - * - * @throws \RuntimeException When the definition is invalid - */ - private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) - { - try { - return $this->doResolveDefinition($container, $definition); - } catch (ExceptionInterface $e) { - $r = new \ReflectionProperty($e, 'message'); - $r->setAccessible(true); - $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage())); - - throw $e; - } - } - - private function doResolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) - { - if (!$container->has($parent = $definition->getParent())) { - throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent)); - } - - $parentDef = $container->findDefinition($parent); - if ($parentDef instanceof DefinitionDecorator) { - $id = $this->currentId; - $this->currentId = $parent; - $parentDef = $this->resolveDefinition($container, $parentDef); - $container->setDefinition($parent, $parentDef); - $this->currentId = $id; - } - - $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent)); - $def = new Definition(); - - // merge in parent definition - // purposely ignored attributes: abstract, tags - $def->setClass($parentDef->getClass()); - $def->setArguments($parentDef->getArguments()); - $def->setMethodCalls($parentDef->getMethodCalls()); - $def->setProperties($parentDef->getProperties()); - $def->setAutowiringTypes($parentDef->getAutowiringTypes()); - if ($parentDef->isDeprecated()) { - $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); - } - $def->setFactory($parentDef->getFactory()); - $def->setConfigurator($parentDef->getConfigurator()); - $def->setFile($parentDef->getFile()); - $def->setPublic($parentDef->isPublic()); - $def->setLazy($parentDef->isLazy()); - $def->setAutowired($parentDef->isAutowired()); - - // overwrite with values specified in the decorator - $changes = $definition->getChanges(); - if (isset($changes['class'])) { - $def->setClass($definition->getClass()); - } - if (isset($changes['factory'])) { - $def->setFactory($definition->getFactory()); - } - if (isset($changes['configurator'])) { - $def->setConfigurator($definition->getConfigurator()); - } - if (isset($changes['file'])) { - $def->setFile($definition->getFile()); - } - if (isset($changes['public'])) { - $def->setPublic($definition->isPublic()); - } - if (isset($changes['lazy'])) { - $def->setLazy($definition->isLazy()); - } - if (isset($changes['deprecated'])) { - $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%')); - } - if (isset($changes['autowire'])) { - $def->setAutowired($definition->isAutowired()); - } - if (isset($changes['decorated_service'])) { - $decoratedService = $definition->getDecoratedService(); - if (null === $decoratedService) { - $def->setDecoratedService($decoratedService); - } else { - $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]); - } - } - - // merge arguments - foreach ($definition->getArguments() as $k => $v) { - if (is_numeric($k)) { - $def->addArgument($v); - continue; - } - - if (0 !== strpos($k, 'index_')) { - throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k)); - } - - $index = (int) substr($k, strlen('index_')); - $def->replaceArgument($index, $v); - } - - // merge properties - foreach ($definition->getProperties() as $k => $v) { - $def->setProperty($k, $v); - } - - // append method calls - if (count($calls = $definition->getMethodCalls()) > 0) { - $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); - } - - // merge autowiring types - foreach ($definition->getAutowiringTypes() as $autowiringType) { - $def->addAutowiringType($autowiringType); - } - - // these attributes are always taken from the child - $def->setAbstract($definition->isAbstract()); - $def->setShared($definition->isShared()); - $def->setTags($definition->getTags()); - - return $def; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php new file mode 100644 index 000000000..ea52b1459 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Replaces env var placeholders by their current values. + */ +class ResolveEnvPlaceholdersPass extends AbstractRecursivePass +{ + protected function processValue($value, bool $isRoot = false) + { + if (\is_string($value)) { + return $this->container->resolveEnvPlaceholders($value, true); + } + if ($value instanceof Definition) { + $changes = $value->getChanges(); + if (isset($changes['class'])) { + $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true)); + } + if (isset($changes['file'])) { + $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true)); + } + } + + $value = parent::processValue($value, $isRoot); + + if ($value && \is_array($value) && !$isRoot) { + $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value); + } + + return $value; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php new file mode 100644 index 000000000..23f535b71 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * @author Maxime Steinhausser + */ +class ResolveFactoryClassPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Definition && \is_array($factory = $value->getFactory()) && null === $factory[0]) { + if (null === $class = $value->getClass()) { + throw new RuntimeException(sprintf('The "%s" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class?', $this->currentId)); + } + + $factory[0] = $class; + $value->setFactory($factory); + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php new file mode 100644 index 000000000..f6942c45d --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Propagate "container.hot_path" tags to referenced services. + * + * @author Nicolas Grekas + */ +class ResolveHotPathPass extends AbstractRecursivePass +{ + private $tagName; + private $resolvedIds = []; + + public function __construct(string $tagName = 'container.hot_path') + { + $this->tagName = $tagName; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + try { + parent::process($container); + $container->getDefinition('service_container')->clearTag($this->tagName); + } finally { + $this->resolvedIds = []; + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof ArgumentInterface) { + return $value; + } + + if ($value instanceof Definition && $isRoot) { + if ($value->isDeprecated()) { + return $value->clearTag($this->tagName); + } + + $this->resolvedIds[$this->currentId] = true; + + if (!$value->hasTag($this->tagName)) { + return $value; + } + } + + if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->hasDefinition($id = (string) $value)) { + $definition = $this->container->getDefinition($id); + + if ($definition->isDeprecated() || $definition->hasTag($this->tagName)) { + return $value; + } + + $definition->addTag($this->tagName); + + if (isset($this->resolvedIds[$id])) { + parent::processValue($definition, false); + } + + return $value; + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php new file mode 100644 index 000000000..b211b84e1 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Applies instanceof conditionals to definitions. + * + * @author Nicolas Grekas + */ +class ResolveInstanceofConditionalsPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) { + if ($definition->getArguments()) { + throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface)); + } + } + + $tagsToKeep = []; + + if ($container->hasParameter('container.behavior_describing_tags')) { + $tagsToKeep = $container->getParameter('container.behavior_describing_tags'); + } + + foreach ($container->getDefinitions() as $id => $definition) { + $container->setDefinition($id, $this->processDefinition($container, $id, $definition, $tagsToKeep)); + } + + if ($container->hasParameter('container.behavior_describing_tags')) { + $container->getParameterBag()->remove('container.behavior_describing_tags'); + } + } + + private function processDefinition(ContainerBuilder $container, string $id, Definition $definition, array $tagsToKeep): Definition + { + $instanceofConditionals = $definition->getInstanceofConditionals(); + $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : []; + if (!$instanceofConditionals && !$autoconfiguredInstanceof) { + return $definition; + } + + if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) { + return $definition; + } + + $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container); + + $definition->setInstanceofConditionals([]); + $shared = null; + $instanceofTags = []; + $instanceofCalls = []; + $instanceofBindings = []; + $reflectionClass = null; + $parent = $definition instanceof ChildDefinition ? $definition->getParent() : null; + + foreach ($conditionals as $interface => $instanceofDefs) { + if ($interface !== $class && !($reflectionClass ?? $reflectionClass = $container->getReflectionClass($class, false) ?: false)) { + continue; + } + + if ($interface !== $class && !is_subclass_of($class, $interface)) { + continue; + } + + foreach ($instanceofDefs as $key => $instanceofDef) { + /** @var ChildDefinition $instanceofDef */ + $instanceofDef = clone $instanceofDef; + $instanceofDef->setAbstract(true)->setParent($parent ?: '.abstract.instanceof.'.$id); + $parent = '.instanceof.'.$interface.'.'.$key.'.'.$id; + $container->setDefinition($parent, $instanceofDef); + $instanceofTags[] = $instanceofDef->getTags(); + $instanceofBindings = $instanceofDef->getBindings() + $instanceofBindings; + + foreach ($instanceofDef->getMethodCalls() as $methodCall) { + $instanceofCalls[] = $methodCall; + } + + $instanceofDef->setTags([]); + $instanceofDef->setMethodCalls([]); + $instanceofDef->setBindings([]); + + if (isset($instanceofDef->getChanges()['shared'])) { + $shared = $instanceofDef->isShared(); + } + } + } + + if ($parent) { + $bindings = $definition->getBindings(); + $abstract = $container->setDefinition('.abstract.instanceof.'.$id, $definition); + $definition->setBindings([]); + $definition = serialize($definition); + + if (Definition::class === \get_class($abstract)) { + // cast Definition to ChildDefinition + $definition = substr_replace($definition, '53', 2, 2); + $definition = substr_replace($definition, 'Child', 44, 0); + } + /** @var ChildDefinition $definition */ + $definition = unserialize($definition); + $definition->setParent($parent); + + if (null !== $shared && !isset($definition->getChanges()['shared'])) { + $definition->setShared($shared); + } + + // Don't add tags to service decorators + $i = \count($instanceofTags); + while (0 <= --$i) { + foreach ($instanceofTags[$i] as $k => $v) { + if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) { + foreach ($v as $v) { + if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { + continue; + } + $definition->addTag($k, $v); + } + } + } + } + + $definition->setMethodCalls(array_merge($instanceofCalls, $definition->getMethodCalls())); + $definition->setBindings($bindings + $instanceofBindings); + + // reset fields with "merge" behavior + $abstract + ->setBindings([]) + ->setArguments([]) + ->setMethodCalls([]) + ->setDecoratedService(null) + ->setTags([]) + ->setAbstract(true); + } + + return $definition; + } + + private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container): array + { + // make each value an array of ChildDefinition + $conditionals = array_map(function ($childDef) { return [$childDef]; }, $autoconfiguredInstanceof); + + foreach ($instanceofConditionals as $interface => $instanceofDef) { + // make sure the interface/class exists (but don't validate automaticInstanceofConditionals) + if (!$container->getReflectionClass($interface)) { + throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface)); + } + + if (!isset($autoconfiguredInstanceof[$interface])) { + $conditionals[$interface] = []; + } + + $conditionals[$interface][] = $instanceofDef; + } + + return $conditionals; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php index 5b58fb1ec..948de421f 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php @@ -11,10 +11,15 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; /** * Emulates the invalid behavior if the reference is not found within the @@ -25,93 +30,107 @@ class ResolveInvalidReferencesPass implements CompilerPassInterface { private $container; + private $signalingException; + private $currentId; /** * Process the ContainerBuilder to resolve invalid references. - * - * @param ContainerBuilder $container */ public function process(ContainerBuilder $container) { $this->container = $container; - foreach ($container->getDefinitions() as $definition) { - if ($definition->isSynthetic() || $definition->isAbstract()) { - continue; - } - - $definition->setArguments( - $this->processArguments($definition->getArguments()) - ); - - $calls = array(); - foreach ($definition->getMethodCalls() as $call) { - try { - $calls[] = array($call[0], $this->processArguments($call[1], true)); - } catch (RuntimeException $e) { - // this call is simply removed - } - } - $definition->setMethodCalls($calls); + $this->signalingException = new RuntimeException('Invalid reference.'); - $properties = array(); - foreach ($definition->getProperties() as $name => $value) { - try { - $value = $this->processArguments(array($value), true); - $properties[$name] = reset($value); - } catch (RuntimeException $e) { - // ignore property - } + try { + foreach ($container->getDefinitions() as $this->currentId => $definition) { + $this->processValue($definition); } - $definition->setProperties($properties); + } finally { + $this->container = $this->signalingException = null; } } /** * Processes arguments to determine invalid references. * - * @param array $arguments An array of Reference objects - * @param bool $inMethodCall - * @param bool $inCollection - * - * @return array + * @return mixed * - * @throws RuntimeException When the config is invalid + * @throws RuntimeException When an invalid reference is found */ - private function processArguments(array $arguments, $inMethodCall = false, $inCollection = false) + private function processValue($value, int $rootLevel = 0, int $level = 0) { - $isNumeric = array_keys($arguments) === range(0, count($arguments) - 1); - - foreach ($arguments as $k => $argument) { - if (is_array($argument)) { - $arguments[$k] = $this->processArguments($argument, $inMethodCall, true); - } elseif ($argument instanceof Reference) { - $id = (string) $argument; - - $invalidBehavior = $argument->getInvalidBehavior(); - $exists = $this->container->has($id); - - // resolve invalid behavior - if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { - $arguments[$k] = null; - } elseif (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { - if ($inCollection) { - unset($arguments[$k]); - continue; + if ($value instanceof ServiceClosureArgument) { + $value->setValues($this->processValue($value->getValues(), 1, 1)); + } elseif ($value instanceof ArgumentInterface) { + $value->setValues($this->processValue($value->getValues(), $rootLevel, 1 + $level)); + } elseif ($value instanceof Definition) { + if ($value->isSynthetic() || $value->isAbstract()) { + return $value; + } + $value->setArguments($this->processValue($value->getArguments(), 0)); + $value->setProperties($this->processValue($value->getProperties(), 1)); + $value->setMethodCalls($this->processValue($value->getMethodCalls(), 2)); + } elseif (\is_array($value)) { + $i = 0; + + foreach ($value as $k => $v) { + try { + if (false !== $i && $k !== $i++) { + $i = false; } - if ($inMethodCall) { - throw new RuntimeException('Method shouldn\'t be called.'); + if ($v !== $processedValue = $this->processValue($v, $rootLevel, 1 + $level)) { + $value[$k] = $processedValue; + } + } catch (RuntimeException $e) { + if ($rootLevel < $level || ($rootLevel && !$level)) { + unset($value[$k]); + } elseif ($rootLevel) { + throw $e; + } else { + $value[$k] = null; } - - $arguments[$k] = null; } } - } - // Ensure numerically indexed arguments have sequential numeric keys. - if ($isNumeric) { - $arguments = array_values($arguments); + // Ensure numerically indexed arguments have sequential numeric keys. + if (false !== $i) { + $value = array_values($value); + } + } elseif ($value instanceof Reference) { + if ($this->container->has($id = (string) $value)) { + return $value; + } + + $currentDefinition = $this->container->getDefinition($this->currentId); + + // resolve decorated service behavior depending on decorator service + if ($currentDefinition->innerServiceId === $id && ContainerInterface::NULL_ON_INVALID_REFERENCE === $currentDefinition->decorationOnInvalid) { + return null; + } + + $invalidBehavior = $value->getInvalidBehavior(); + + if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior && $value instanceof TypedReference && !$this->container->has($id)) { + $e = new ServiceNotFoundException($id, $this->currentId); + + // since the error message varies by $id and $this->currentId, so should the id of the dummy errored definition + $this->container->register($id = sprintf('.errored.%s.%s', $this->currentId, $id), $value->getType()) + ->addError($e->getMessage()); + + return new TypedReference($id, $value->getType(), $value->getInvalidBehavior()); + } + + // resolve invalid behavior + if (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { + $value = null; + } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { + if (0 < $level || $rootLevel) { + throw $this->signalingException; + } + $value = null; + } } - return $arguments; + return $value; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php new file mode 100644 index 000000000..c1c5748e8 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Resolves named arguments to their corresponding numeric index. + * + * @author Kévin Dunglas + */ +class ResolveNamedArgumentsPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof AbstractArgument && $value->getText().'.' === $value->getTextWithContext()) { + $value->setContext(sprintf('A value found in service "%s"', $this->currentId)); + } + + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + $calls = $value->getMethodCalls(); + $calls[] = ['__construct', $value->getArguments()]; + + foreach ($calls as $i => $call) { + [$method, $arguments] = $call; + $parameters = null; + $resolvedArguments = []; + + foreach ($arguments as $key => $argument) { + if ($argument instanceof AbstractArgument && $argument->getText().'.' === $argument->getTextWithContext()) { + $argument->setContext(sprintf('Argument '.(\is_int($key) ? 1 + $key : '"%3$s"').' of '.('__construct' === $method ? 'service "%s"' : 'method call "%s::%s()"'), $this->currentId, $method, $key)); + } + + if (\is_int($key)) { + $resolvedArguments[$key] = $argument; + continue; + } + + if (null === $parameters) { + $r = $this->getReflectionMethod($value, $method); + $class = $r instanceof \ReflectionMethod ? $r->class : $this->currentId; + $method = $r->getName(); + $parameters = $r->getParameters(); + } + + if (isset($key[0]) && '$' !== $key[0] && !class_exists($key) && !interface_exists($key, false)) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": did you forget to add the "$" prefix to argument "%s"?', $this->currentId, $key)); + } + + if (isset($key[0]) && '$' === $key[0]) { + foreach ($parameters as $j => $p) { + if ($key === '$'.$p->name) { + if ($p->isVariadic() && \is_array($argument)) { + foreach ($argument as $variadicArgument) { + $resolvedArguments[$j++] = $variadicArgument; + } + } else { + $resolvedArguments[$j] = $argument; + } + + continue 2; + } + } + + throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); + } + + if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of "%s" or an instance of "%s", "%s" given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, get_debug_type($argument))); + } + + $typeFound = false; + foreach ($parameters as $j => $p) { + if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) { + $resolvedArguments[$j] = $argument; + $typeFound = true; + } + } + + if (!$typeFound) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); + } + } + + if ($resolvedArguments !== $call[1]) { + ksort($resolvedArguments); + $calls[$i][1] = $resolvedArguments; + } + } + + [, $arguments] = array_pop($calls); + + if ($arguments !== $value->getArguments()) { + $value->setArguments($arguments); + } + if ($calls !== $value->getMethodCalls()) { + $value->setMethodCalls($calls); + } + + foreach ($value->getProperties() as $key => $argument) { + if ($argument instanceof AbstractArgument && $argument->getText().'.' === $argument->getTextWithContext()) { + $argument->setContext(sprintf('Property "%s" of service "%s"', $key, $this->currentId)); + } + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveNoPreloadPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveNoPreloadPass.php new file mode 100644 index 000000000..50c35dff3 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveNoPreloadPass.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Propagate the "container.no_preload" tag. + * + * @author Nicolas Grekas + */ +class ResolveNoPreloadPass extends AbstractRecursivePass +{ + private const DO_PRELOAD_TAG = '.container.do_preload'; + + private $tagName; + private $resolvedIds = []; + + public function __construct(string $tagName = 'container.no_preload') + { + $this->tagName = $tagName; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + + try { + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isPublic() && !$definition->isPrivate() && !isset($this->resolvedIds[$id])) { + $this->resolvedIds[$id] = true; + $this->processValue($definition, true); + } + } + + foreach ($container->getAliases() as $alias) { + if ($alias->isPublic() && !$alias->isPrivate() && !isset($this->resolvedIds[$id = (string) $alias]) && $container->hasDefinition($id)) { + $this->resolvedIds[$id] = true; + $this->processValue($container->getDefinition($id), true); + } + } + } finally { + $this->resolvedIds = []; + $this->container = null; + } + + foreach ($container->getDefinitions() as $definition) { + if ($definition->hasTag(self::DO_PRELOAD_TAG)) { + $definition->clearTag(self::DO_PRELOAD_TAG); + } elseif (!$definition->isDeprecated() && !$definition->hasErrors()) { + $definition->addTag($this->tagName); + } + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->hasDefinition($id = (string) $value)) { + $definition = $this->container->getDefinition($id); + + if (!isset($this->resolvedIds[$id]) && (!$definition->isPublic() || $definition->isPrivate())) { + $this->resolvedIds[$id] = true; + $this->processValue($definition, true); + } + + return $value; + } + + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + if ($value->hasTag($this->tagName) || $value->isDeprecated() || $value->hasErrors()) { + return $value; + } + + if ($isRoot) { + $value->addTag(self::DO_PRELOAD_TAG); + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php index 0c5963cc1..b1c81ee3a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** @@ -19,52 +20,79 @@ * * @author Johannes M. Schmitt */ -class ResolveParameterPlaceHoldersPass implements CompilerPassInterface +class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass { + private $bag; + private $resolveArrays; + private $throwOnResolveException; + + public function __construct($resolveArrays = true, $throwOnResolveException = true) + { + $this->resolveArrays = $resolveArrays; + $this->throwOnResolveException = $throwOnResolveException; + } + /** - * Processes the ContainerBuilder to resolve parameter placeholders. - * - * @param ContainerBuilder $container + * {@inheritdoc} * * @throws ParameterNotFoundException */ public function process(ContainerBuilder $container) { - $parameterBag = $container->getParameterBag(); + $this->bag = $container->getParameterBag(); - foreach ($container->getDefinitions() as $id => $definition) { - try { - $definition->setClass($parameterBag->resolveValue($definition->getClass())); - $definition->setFile($parameterBag->resolveValue($definition->getFile())); - $definition->setArguments($parameterBag->resolveValue($definition->getArguments())); + try { + parent::process($container); - $factory = $definition->getFactory(); + $aliases = []; + foreach ($container->getAliases() as $name => $target) { + $this->currentId = $name; + $aliases[$this->bag->resolveValue($name)] = $target; + } + $container->setAliases($aliases); + } catch (ParameterNotFoundException $e) { + $e->setSourceId($this->currentId); - if (is_array($factory) && isset($factory[0])) { - $factory[0] = $parameterBag->resolveValue($factory[0]); - $definition->setFactory($factory); - } + throw $e; + } - $calls = array(); - foreach ($definition->getMethodCalls() as $name => $arguments) { - $calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments); - } - $definition->setMethodCalls($calls); + $this->bag->resolve(); + $this->bag = null; + } - $definition->setProperties($parameterBag->resolveValue($definition->getProperties())); + protected function processValue($value, bool $isRoot = false) + { + if (\is_string($value)) { + try { + $v = $this->bag->resolveValue($value); } catch (ParameterNotFoundException $e) { - $e->setSourceId($id); + if ($this->throwOnResolveException) { + throw $e; + } - throw $e; + $v = null; + $this->container->getDefinition($this->currentId)->addError($e->getMessage()); } + + return $this->resolveArrays || !$v || !\is_array($v) ? $v : $value; } + if ($value instanceof Definition) { + $value->setBindings($this->processValue($value->getBindings())); + $changes = $value->getChanges(); + if (isset($changes['class'])) { + $value->setClass($this->bag->resolveValue($value->getClass())); + } + if (isset($changes['file'])) { + $value->setFile($this->bag->resolveValue($value->getFile())); + } + } + + $value = parent::processValue($value, $isRoot); - $aliases = array(); - foreach ($container->getAliases() as $name => $target) { - $aliases[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($target); + if ($value && \is_array($value)) { + $value = array_combine($this->bag->resolveValue(array_keys($value)), $value); } - $container->setAliases($aliases); - $parameterBag->resolve(); + return $value; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php new file mode 100644 index 000000000..b63e3f5c2 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +trigger_deprecation('symfony/dependency-injection', '5.2', 'The "%s" class is deprecated.', ResolvePrivatesPass::class); + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Nicolas Grekas + * + * @deprecated since Symfony 5.2 + */ +class ResolvePrivatesPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isPrivate()) { + $definition->setPublic(false); + $definition->setPrivate(true); + } + } + + foreach ($container->getAliases() as $id => $alias) { + if ($alias->isPrivate()) { + $alias->setPublic(false); + $alias->setPrivate(true); + } + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php index 8514739a7..e59893ff7 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php @@ -11,104 +11,73 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; /** * Replaces all references to aliases with references to the actual service. * * @author Johannes M. Schmitt */ -class ResolveReferencesToAliasesPass implements CompilerPassInterface +class ResolveReferencesToAliasesPass extends AbstractRecursivePass { - private $container; - /** - * Processes the ContainerBuilder to replace references to aliases with actual service references. - * - * @param ContainerBuilder $container + * {@inheritdoc} */ public function process(ContainerBuilder $container) { - $this->container = $container; - - foreach ($container->getDefinitions() as $definition) { - if ($definition->isSynthetic() || $definition->isAbstract()) { - continue; - } - - $definition->setArguments($this->processArguments($definition->getArguments())); - $definition->setMethodCalls($this->processArguments($definition->getMethodCalls())); - $definition->setProperties($this->processArguments($definition->getProperties())); - $definition->setFactory($this->processFactory($definition->getFactory())); - } + parent::process($container); foreach ($container->getAliases() as $id => $alias) { $aliasId = (string) $alias; - if ($aliasId !== $defId = $this->getDefinitionId($aliasId)) { - $container->setAlias($id, new Alias($defId, $alias->isPublic())); + $this->currentId = $id; + + if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) { + $container->setAlias($id, $defId)->setPublic($alias->isPublic()); } } } /** - * Processes the arguments to replace aliases. - * - * @param array $arguments An array of References - * - * @return array An array of References + * {@inheritdoc} */ - private function processArguments(array $arguments) + protected function processValue($value, bool $isRoot = false) { - foreach ($arguments as $k => $argument) { - if (is_array($argument)) { - $arguments[$k] = $this->processArguments($argument); - } elseif ($argument instanceof Reference) { - $defId = $this->getDefinitionId($id = (string) $argument); - - if ($defId !== $id) { - $arguments[$k] = new Reference($defId, $argument->getInvalidBehavior()); - } - } + if (!$value instanceof Reference) { + return parent::processValue($value, $isRoot); } - return $arguments; + $defId = $this->getDefinitionId($id = (string) $value, $this->container); + + return $defId !== $id ? new Reference($defId, $value->getInvalidBehavior()) : $value; } - private function processFactory($factory) + private function getDefinitionId(string $id, ContainerBuilder $container): string { - if (null === $factory || !is_array($factory) || !$factory[0] instanceof Reference) { - return $factory; + if (!$container->hasAlias($id)) { + return $id; } - $defId = $this->getDefinitionId($id = (string) $factory[0]); + $alias = $container->getAlias($id); - if ($defId !== $id) { - $factory[0] = new Reference($defId, $factory[0]->getInvalidBehavior()); + if ($alias->isDeprecated()) { + $referencingDefinition = $container->hasDefinition($this->currentId) ? $container->getDefinition($this->currentId) : $container->getAlias($this->currentId); + if (!$referencingDefinition->isDeprecated()) { + $deprecation = $alias->getDeprecation($id); + trigger_deprecation($deprecation['package'], $deprecation['version'], rtrim($deprecation['message'], '. ').'. It is being referenced by the "%s" '.($container->hasDefinition($this->currentId) ? 'service.' : 'alias.'), $this->currentId); + } } - return $factory; - } - - /** - * Resolves an alias into a definition id. - * - * @param string $id The definition or alias id to resolve - * - * @return string The definition id with aliases resolved - */ - private function getDefinitionId($id) - { - $seen = array(); - while ($this->container->hasAlias($id)) { + $seen = []; + do { if (isset($seen[$id])) { - throw new ServiceCircularReferenceException($id, array_keys($seen)); + throw new ServiceCircularReferenceException($id, array_merge(array_keys($seen), [$id])); } + $seen[$id] = true; - $id = (string) $this->container->getAlias($id); - } + $id = (string) $container->getAlias($id); + } while ($container->hasAlias($id)); return $id; } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php new file mode 100644 index 000000000..518c03d7e --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Contracts\Service\ServiceProviderInterface; + +/** + * Compiler pass to inject their service locator to service subscribers. + * + * @author Nicolas Grekas + */ +class ResolveServiceSubscribersPass extends AbstractRecursivePass +{ + private $serviceLocator; + + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Reference && $this->serviceLocator && \in_array((string) $value, [ContainerInterface::class, ServiceProviderInterface::class], true)) { + return new Reference($this->serviceLocator); + } + + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + $serviceLocator = $this->serviceLocator; + $this->serviceLocator = null; + + if ($value->hasTag('container.service_subscriber.locator')) { + $this->serviceLocator = $value->getTag('container.service_subscriber.locator')[0]['id']; + $value->clearTag('container.service_subscriber.locator'); + } + + try { + return parent::processValue($value); + } finally { + $this->serviceLocator = $serviceLocator; + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php new file mode 100644 index 000000000..48a034a84 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; + +/** + * Resolves all TaggedIteratorArgument arguments. + * + * @author Roland Franssen + */ +class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass +{ + use PriorityTaggedServiceTrait; + + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (!$value instanceof TaggedIteratorArgument) { + return parent::processValue($value, $isRoot); + } + + $value->setValues($this->findAndSortTaggedServices($value, $this->container)); + + return $value; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php new file mode 100644 index 000000000..b872bdc6d --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; + +/** + * Applies the "container.service_locator" tag by wrapping references into ServiceClosureArgument instances. + * + * @author Nicolas Grekas + */ +final class ServiceLocatorTagPass extends AbstractRecursivePass +{ + use PriorityTaggedServiceTrait; + + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof ServiceLocatorArgument) { + if ($value->getTaggedIteratorArgument()) { + $value->setValues($this->findAndSortTaggedServices($value->getTaggedIteratorArgument(), $this->container)); + } + + return self::register($this->container, $value->getValues()); + } + + if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) { + return parent::processValue($value, $isRoot); + } + + if (!$value->getClass()) { + $value->setClass(ServiceLocator::class); + } + + $arguments = $value->getArguments(); + if (!isset($arguments[0]) || !\is_array($arguments[0])) { + throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId)); + } + + $i = 0; + + foreach ($arguments[0] as $k => $v) { + if ($v instanceof ServiceClosureArgument) { + continue; + } + if (!$v instanceof Reference) { + throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, get_debug_type($v), $k)); + } + + if ($i === $k) { + unset($arguments[0][$k]); + + $k = (string) $v; + ++$i; + } elseif (\is_int($k)) { + $i = null; + } + $arguments[0][$k] = new ServiceClosureArgument($v); + } + ksort($arguments[0]); + + $value->setArguments($arguments); + + $id = '.service_locator.'.ContainerBuilder::hash($value); + + if ($isRoot) { + if ($id !== $this->currentId) { + $this->container->setAlias($id, new Alias($this->currentId, false)); + } + + return $value; + } + + $this->container->setDefinition($id, $value->setPublic(false)); + + return new Reference($id); + } + + /** + * @param Reference[] $refMap + */ + public static function register(ContainerBuilder $container, array $refMap, string $callerId = null): Reference + { + foreach ($refMap as $id => $ref) { + if (!$ref instanceof Reference) { + throw new InvalidArgumentException(sprintf('Invalid service locator definition: only services can be referenced, "%s" found for key "%s". Inject parameter values using constructors instead.', get_debug_type($ref), $id)); + } + $refMap[$id] = new ServiceClosureArgument($ref); + } + ksort($refMap); + + $locator = (new Definition(ServiceLocator::class)) + ->addArgument($refMap) + ->addTag('container.service_locator'); + + if (null !== $callerId && $container->hasDefinition($callerId)) { + $locator->setBindings($container->getDefinition($callerId)->getBindings()); + } + + if (!$container->hasDefinition($id = '.service_locator.'.ContainerBuilder::hash($locator))) { + $container->setDefinition($id, $locator); + } + + if (null !== $callerId) { + $locatorId = $id; + // Locators are shared when they hold the exact same list of factories; + // to have them specialized per consumer service, we use a cloning factory + // to derivate customized instances from the prototype one. + $container->register($id .= '.'.$callerId, ServiceLocator::class) + ->setFactory([new Reference($locatorId), 'withContext']) + ->addTag('container.service_locator_context', ['id' => $callerId]) + ->addArgument($callerId) + ->addArgument(new Reference('service_container')); + } + + return new Reference($id); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php index dc9a1a00e..1225514c2 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; /** * This is a directed graph of your services. @@ -20,22 +21,17 @@ * it themselves which improves performance quite a lot. * * @author Johannes M. Schmitt + * + * @final */ class ServiceReferenceGraph { /** * @var ServiceReferenceGraphNode[] */ - private $nodes = array(); + private $nodes = []; - /** - * Checks if the graph has a specific node. - * - * @param string $id Id to check - * - * @return bool - */ - public function hasNode($id) + public function hasNode(string $id): bool { return isset($this->nodes[$id]); } @@ -43,13 +39,9 @@ public function hasNode($id) /** * Gets a node by identifier. * - * @param string $id The id to retrieve - * - * @return ServiceReferenceGraphNode The node matching the supplied identifier - * * @throws InvalidArgumentException if no node matches the supplied identifier */ - public function getNode($id) + public function getNode(string $id): ServiceReferenceGraphNode { if (!isset($this->nodes[$id])) { throw new InvalidArgumentException(sprintf('There is no node with id "%s".', $id)); @@ -61,9 +53,9 @@ public function getNode($id) /** * Returns all nodes. * - * @return ServiceReferenceGraphNode[] An array of all ServiceReferenceGraphNode objects + * @return ServiceReferenceGraphNode[] */ - public function getNodes() + public function getNodes(): array { return $this->nodes; } @@ -73,37 +65,30 @@ public function getNodes() */ public function clear() { - $this->nodes = array(); + foreach ($this->nodes as $node) { + $node->clear(); + } + $this->nodes = []; } /** * Connects 2 nodes together in the Graph. - * - * @param string $sourceId - * @param string $sourceValue - * @param string $destId - * @param string $destValue - * @param string $reference */ - public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null) + public function connect(?string $sourceId, $sourceValue, ?string $destId, $destValue = null, Reference $reference = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false) { + if (null === $sourceId || null === $destId) { + return; + } + $sourceNode = $this->createNode($sourceId, $sourceValue); $destNode = $this->createNode($destId, $destValue); - $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference); + $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak, $byConstructor); $sourceNode->addOutEdge($edge); $destNode->addInEdge($edge); } - /** - * Creates a graph node. - * - * @param string $id - * @param string $value - * - * @return ServiceReferenceGraphNode - */ - private function createNode($id, $value) + private function createNode(string $id, $value): ServiceReferenceGraphNode { if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) { return $this->nodes[$id]; diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php index e3c793c4f..986145606 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php @@ -23,23 +23,24 @@ class ServiceReferenceGraphEdge private $sourceNode; private $destNode; private $value; + private $lazy; + private $weak; + private $byConstructor; - /** - * @param ServiceReferenceGraphNode $sourceNode - * @param ServiceReferenceGraphNode $destNode - * @param string $value - */ - public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null) + public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, bool $lazy = false, bool $weak = false, bool $byConstructor = false) { $this->sourceNode = $sourceNode; $this->destNode = $destNode; $this->value = $value; + $this->lazy = $lazy; + $this->weak = $weak; + $this->byConstructor = $byConstructor; } /** * Returns the value of the edge. * - * @return string + * @return mixed */ public function getValue() { @@ -65,4 +66,34 @@ public function getDestNode() { return $this->destNode; } + + /** + * Returns true if the edge is lazy, meaning it's a dependency not requiring direct instantiation. + * + * @return bool + */ + public function isLazy() + { + return $this->lazy; + } + + /** + * Returns true if the edge is weak, meaning it shouldn't prevent removing the target service. + * + * @return bool + */ + public function isWeak() + { + return $this->weak; + } + + /** + * Returns true if the edge links with a constructor argument. + * + * @return bool + */ + public function isReferencedByConstructor() + { + return $this->byConstructor; + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php index e5718b2b6..fec142426 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Definition; /** * Represents a node in your service graph. @@ -24,35 +24,25 @@ class ServiceReferenceGraphNode { private $id; - private $inEdges = array(); - private $outEdges = array(); + private $inEdges = []; + private $outEdges = []; private $value; /** * @param string $id The node identifier * @param mixed $value The node value */ - public function __construct($id, $value) + public function __construct(string $id, $value) { $this->id = $id; $this->value = $value; } - /** - * Adds an in edge to this node. - * - * @param ServiceReferenceGraphEdge $edge - */ public function addInEdge(ServiceReferenceGraphEdge $edge) { $this->inEdges[] = $edge; } - /** - * Adds an out edge to this node. - * - * @param ServiceReferenceGraphEdge $edge - */ public function addOutEdge(ServiceReferenceGraphEdge $edge) { $this->outEdges[] = $edge; @@ -91,7 +81,7 @@ public function getId() /** * Returns the in edges. * - * @return array The in ServiceReferenceGraphEdge array + * @return ServiceReferenceGraphEdge[] */ public function getInEdges() { @@ -101,7 +91,7 @@ public function getInEdges() /** * Returns the out edges. * - * @return array The out ServiceReferenceGraphEdge array + * @return ServiceReferenceGraphEdge[] */ public function getOutEdges() { @@ -117,4 +107,12 @@ public function getValue() { return $this->value; } + + /** + * Clears all edges. + */ + public function clear() + { + $this->inEdges = $this->outEdges = []; + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php b/tests/integration/vendor/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php new file mode 100644 index 000000000..88c7851d6 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\Config\Definition\BaseNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +/** + * Validates environment variable placeholders used in extension configuration with dummy values. + * + * @author Roland Franssen + */ +class ValidateEnvPlaceholdersPass implements CompilerPassInterface +{ + private const TYPE_FIXTURES = ['array' => [], 'bool' => false, 'float' => 0.0, 'int' => 0, 'string' => '']; + + private $extensionConfig = []; + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->extensionConfig = []; + + if (!class_exists(BaseNode::class) || !$extensions = $container->getExtensions()) { + return; + } + + $resolvingBag = $container->getParameterBag(); + if (!$resolvingBag instanceof EnvPlaceholderParameterBag) { + return; + } + + $defaultBag = new ParameterBag($resolvingBag->all()); + $envTypes = $resolvingBag->getProvidedTypes(); + try { + foreach ($resolvingBag->getEnvPlaceholders() + $resolvingBag->getUnusedEnvPlaceholders() as $env => $placeholders) { + $values = []; + if (false === $i = strpos($env, ':')) { + $default = $defaultBag->has("env($env)") ? $defaultBag->get("env($env)") : self::TYPE_FIXTURES['string']; + $defaultType = null !== $default ? get_debug_type($default) : 'string'; + $values[$defaultType] = $default; + } else { + $prefix = substr($env, 0, $i); + foreach ($envTypes[$prefix] ?? ['string'] as $type) { + $values[$type] = self::TYPE_FIXTURES[$type] ?? null; + } + } + foreach ($placeholders as $placeholder) { + BaseNode::setPlaceholder($placeholder, $values); + } + } + + $processor = new Processor(); + + foreach ($extensions as $name => $extension) { + if (!($extension instanceof ConfigurationExtensionInterface || $extension instanceof ConfigurationInterface) + || !$config = array_filter($container->getExtensionConfig($name)) + ) { + // this extension has no semantic configuration or was not called + continue; + } + + $config = $resolvingBag->resolveValue($config); + + if ($extension instanceof ConfigurationInterface) { + $configuration = $extension; + } elseif (null === $configuration = $extension->getConfiguration($config, $container)) { + continue; + } + + $this->extensionConfig[$name] = $processor->processConfiguration($configuration, $config); + } + } finally { + BaseNode::resetPlaceholders(); + } + + $resolvingBag->clearUnusedEnvPlaceholders(); + } + + /** + * @internal + */ + public function getExtensionConfig(): array + { + try { + return $this->extensionConfig; + } finally { + $this->extensionConfig = []; + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Config/AutowireServiceResource.php b/tests/integration/vendor/symfony/dependency-injection/Config/AutowireServiceResource.php deleted file mode 100644 index 36449d8d3..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Config/AutowireServiceResource.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Config; - -use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; -use Symfony\Component\DependencyInjection\Compiler\AutowirePass; - -class AutowireServiceResource implements SelfCheckingResourceInterface, \Serializable -{ - private $class; - private $filePath; - private $autowiringMetadata = array(); - - public function __construct($class, $path, array $autowiringMetadata) - { - $this->class = $class; - $this->filePath = $path; - $this->autowiringMetadata = $autowiringMetadata; - } - - public function isFresh($timestamp) - { - if (!file_exists($this->filePath)) { - return false; - } - - // has the file *not* been modified? Definitely fresh - if (@filemtime($this->filePath) <= $timestamp) { - return true; - } - - try { - $reflectionClass = new \ReflectionClass($this->class); - } catch (\ReflectionException $e) { - // the class does not exist anymore! - return false; - } - - return (array) $this === (array) AutowirePass::createResourceForClass($reflectionClass); - } - - public function __toString() - { - return 'service.autowire.'.$this->class; - } - - public function serialize() - { - return serialize(array($this->class, $this->filePath, $this->autowiringMetadata)); - } - - public function unserialize($serialized) - { - list($this->class, $this->filePath, $this->autowiringMetadata) = unserialize($serialized); - } - - /** - * @deprecated Implemented for compatibility with Symfony 2.8 - */ - public function getResource() - { - return $this->filePath; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php b/tests/integration/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php new file mode 100644 index 000000000..52b303079 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Tracks container parameters. + * + * @author Maxime Steinhausser + * + * @final + */ +class ContainerParametersResource implements ResourceInterface +{ + private $parameters; + + /** + * @param array $parameters The container parameters to track + */ + public function __construct(array $parameters) + { + $this->parameters = $parameters; + } + + public function __toString(): string + { + return 'container_parameters_'.md5(serialize($this->parameters)); + } + + /** + * @return array Tracked parameters + */ + public function getParameters(): array + { + return $this->parameters; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php b/tests/integration/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php new file mode 100644 index 000000000..2f2affaa5 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Config\ResourceCheckerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * @author Maxime Steinhausser + */ +class ContainerParametersResourceChecker implements ResourceCheckerInterface +{ + /** @var ContainerInterface */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function supports(ResourceInterface $metadata) + { + return $metadata instanceof ContainerParametersResource; + } + + /** + * {@inheritdoc} + */ + public function isFresh(ResourceInterface $resource, int $timestamp) + { + foreach ($resource->getParameters() as $key => $value) { + if (!$this->container->hasParameter($key) || $this->container->getParameter($key) !== $value) { + return false; + } + } + + return true; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Container.php b/tests/integration/vendor/symfony/dependency-injection/Container.php index da84d18ac..d2793f1c9 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Container.php +++ b/tests/integration/vendor/symfony/dependency-injection/Container.php @@ -11,74 +11,60 @@ namespace Symfony\Component\DependencyInjection; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Contracts\Service\ResetInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(RewindableGenerator::class); +class_exists(ArgumentServiceLocator::class); /** * Container is a dependency injection container. * * It gives access to object instances (services). - * * Services and parameters are simple key/pair stores. - * - * Parameter and service keys are case insensitive. - * - * A service id can contain lowercased letters, digits, underscores, and dots. - * Underscores are used to separate words, and dots to group services - * under namespaces: - * - *
    - *
  • request
  • - *
  • mysql_session_storage
  • - *
  • symfony.mysql_session_storage
  • - *
- * - * A service can also be defined by creating a method named - * getXXXService(), where XXX is the camelized version of the id: - * - *
    - *
  • request -> getRequestService()
  • - *
  • mysql_session_storage -> getMysqlSessionStorageService()
  • - *
  • symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()
  • - *
- * - * The container can have three possible behaviors when a service does not exist: + * The container can have four possible behaviors when a service + * does not exist (or is not initialized for the last case): * * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default) * * NULL_ON_INVALID_REFERENCE: Returns null * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference * (for instance, ignore a setter if the service does not exist) + * * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references * * @author Fabien Potencier * @author Johannes M. Schmitt */ -class Container implements ResettableContainerInterface +class Container implements ContainerInterface, ResetInterface { - /** - * @var ParameterBagInterface - */ protected $parameterBag; + protected $services = []; + protected $privates = []; + protected $fileMap = []; + protected $methodMap = []; + protected $factories = []; + protected $aliases = []; + protected $loading = []; + protected $resolving = []; + protected $syntheticIds = []; + + private $envCache = []; + private $compiled = false; + private $getEnv; - protected $services = array(); - protected $methodMap = array(); - protected $privates = array(); - protected $aliases = array(); - protected $loading = array(); - - private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_'); - private $envCache = array(); - - /** - * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance - */ public function __construct(ParameterBagInterface $parameterBag = null) { - $this->parameterBag = $parameterBag ?: new EnvPlaceholderParameterBag(); + $this->parameterBag = $parameterBag ?? new EnvPlaceholderParameterBag(); } /** @@ -94,16 +80,18 @@ public function compile() $this->parameterBag->resolve(); $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + + $this->compiled = true; } /** - * Returns true if the container parameter bag are frozen. + * Returns true if the container is compiled. * - * @return bool true if the container parameter bag are frozen, false otherwise + * @return bool */ - public function isFrozen() + public function isCompiled() { - return $this->parameterBag instanceof FrozenParameterBag; + return $this->compiled; } /** @@ -119,25 +107,19 @@ public function getParameterBag() /** * Gets a parameter. * - * @param string $name The parameter name - * - * @return mixed The parameter value + * @return array|bool|string|int|float|null * * @throws InvalidArgumentException if the parameter is not defined */ - public function getParameter($name) + public function getParameter(string $name) { return $this->parameterBag->get($name); } /** - * Checks if a parameter exists. - * - * @param string $name The parameter name - * * @return bool The presence of parameter in container */ - public function hasParameter($name) + public function hasParameter(string $name) { return $this->parameterBag->has($name); } @@ -145,10 +127,10 @@ public function hasParameter($name) /** * Sets a parameter. * - * @param string $name The parameter name - * @param mixed $value The parameter value + * @param string $name The parameter name + * @param array|bool|string|int|float|null $value The parameter value */ - public function setParameter($name, $value) + public function setParameter(string $name, $value) { $this->parameterBag->set($name, $value); } @@ -156,38 +138,45 @@ public function setParameter($name, $value) /** * Sets a service. * - * Setting a service to null resets the service: has() returns false and get() + * Setting a synthetic service to null resets it: has() returns false and get() * behaves in the same way as if the service was never created. - * - * @param string $id The service identifier - * @param object $service The service instance */ - public function set($id, $service) + public function set(string $id, ?object $service) { - $id = strtolower($id); + // Runs the internal initializer; used by the dumped container to include always-needed files + if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) { + $initialize = $this->privates['service_container']; + unset($this->privates['service_container']); + $initialize(); + } if ('service_container' === $id) { throw new InvalidArgumentException('You cannot set service "service_container".'); } + if (!(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { + if (isset($this->syntheticIds[$id]) || !isset($this->getRemovedIds()[$id])) { + // no-op + } elseif (null === $service) { + throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot unset it.', $id)); + } else { + throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot replace it.', $id)); + } + } elseif (isset($this->services[$id])) { + throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); + } + if (isset($this->aliases[$id])) { unset($this->aliases[$id]); } - $this->services[$id] = $service; - if (null === $service) { unset($this->services[$id]); - } - if (isset($this->privates[$id])) { - if (null === $service) { - @trigger_error(sprintf('Unsetting the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); - unset($this->privates[$id]); - } else { - @trigger_error(sprintf('Setting the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0. A new public service will be created instead.', $id), E_USER_DEPRECATED); - } + return; } + + $this->services[$id] = $service; } /** @@ -199,49 +188,23 @@ public function set($id, $service) */ public function has($id) { - for ($i = 2;;) { - if ('service_container' === $id - || isset($this->aliases[$id]) - || isset($this->services[$id]) - ) { - return true; - } - - if (isset($this->privates[$id])) { - @trigger_error(sprintf('Checking for the existence of the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); - } - - if (isset($this->methodMap[$id])) { - return true; - } - - if (--$i && $id !== $lcId = strtolower($id)) { - $id = $lcId; - continue; - } - - // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, - // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) - if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service')) { - @trigger_error('Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED); - - return true; - } - - return false; + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + if (isset($this->services[$id])) { + return true; + } + if ('service_container' === $id) { + return true; } + + return isset($this->fileMap[$id]) || isset($this->methodMap[$id]); } /** * Gets a service. * - * If a service is defined both through a set() method and - * with a get{$id}Service() method, the former has always precedence. - * - * @param string $id The service identifier - * @param int $invalidBehavior The behavior when the service does not exist - * - * @return object The associated service + * @return object|null The associated service * * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined @@ -249,96 +212,83 @@ public function has($id) * * @see Reference */ - public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) + public function get($id, int $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1) { - // Attempt to retrieve the service by checking first aliases then - // available services. Service IDs are case insensitive, however since - // this method can be called thousands of times during a request, avoid - // calling strtolower() unless necessary. - for ($i = 2;;) { - if ('service_container' === $id) { - return $this; - } - if (isset($this->aliases[$id])) { - $id = $this->aliases[$id]; - } - // Re-use shared service instance if it exists. - if (isset($this->services[$id])) { - return $this->services[$id]; - } + return $this->services[$id] + ?? $this->services[$id = $this->aliases[$id] ?? $id] + ?? ('service_container' === $id ? $this : ($this->factories[$id] ?? [$this, 'make'])($id, $invalidBehavior)); + } - if (isset($this->loading[$id])) { - throw new ServiceCircularReferenceException($id, array_keys($this->loading)); - } + /** + * Creates a service. + * + * As a separate method to allow "get()" to use the really fast `??` operator. + */ + private function make(string $id, int $invalidBehavior) + { + if (isset($this->loading[$id])) { + throw new ServiceCircularReferenceException($id, array_merge(array_keys($this->loading), [$id])); + } - if (isset($this->methodMap[$id])) { - $method = $this->methodMap[$id]; - } elseif (--$i && $id !== $lcId = strtolower($id)) { - $id = $lcId; - continue; - } elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) { - // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, - // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) - @trigger_error('Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED); - // $method is set to the right value, proceed - } else { - if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { - if (!$id) { - throw new ServiceNotFoundException($id); - } - - $alternatives = array(); - foreach ($this->getServiceIds() as $knownId) { - $lev = levenshtein($id, $knownId); - if ($lev <= strlen($id) / 3 || false !== strpos($knownId, $id)) { - $alternatives[] = $knownId; - } - } - - throw new ServiceNotFoundException($id, null, null, $alternatives); - } + $this->loading[$id] = true; - return; - } - if (isset($this->privates[$id])) { - @trigger_error(sprintf('Requesting the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED); + try { + if (isset($this->fileMap[$id])) { + return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]); + } elseif (isset($this->methodMap[$id])) { + return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); } + } catch (\Exception $e) { + unset($this->services[$id]); - $this->loading[$id] = true; + throw $e; + } finally { + unset($this->loading[$id]); + } - try { - $service = $this->$method(); - } catch (\Exception $e) { - unset($this->services[$id]); + if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) { + if (!$id) { + throw new ServiceNotFoundException($id); + } + if (isset($this->syntheticIds[$id])) { + throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is synthetic, it needs to be set at boot time before it can be used.', $id)); + } + if (isset($this->getRemovedIds()[$id])) { + throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.', $id)); + } - throw $e; - } finally { - unset($this->loading[$id]); + $alternatives = []; + foreach ($this->getServiceIds() as $knownId) { + if ('' === $knownId || '.' === $knownId[0]) { + continue; + } + $lev = levenshtein($id, $knownId); + if ($lev <= \strlen($id) / 3 || str_contains($knownId, $id)) { + $alternatives[] = $knownId; + } } - return $service; + throw new ServiceNotFoundException($id, null, null, $alternatives); } + + return null; } /** * Returns true if the given service has actually been initialized. * - * @param string $id The service identifier - * * @return bool true if service has already been initialized, false otherwise */ - public function initialized($id) + public function initialized(string $id) { - $id = strtolower($id); + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } if ('service_container' === $id) { return false; } - if (isset($this->aliases[$id])) { - $id = $this->aliases[$id]; - } - return isset($this->services[$id]); } @@ -347,83 +297,135 @@ public function initialized($id) */ public function reset() { - $this->services = array(); + $services = $this->services + $this->privates; + $this->services = $this->factories = $this->privates = []; + + foreach ($services as $service) { + try { + if ($service instanceof ResetInterface) { + $service->reset(); + } + } catch (\Throwable $e) { + continue; + } + } } /** * Gets all service ids. * - * @return array An array of all defined service ids + * @return string[] An array of all defined service ids */ public function getServiceIds() { - $ids = array(); - - if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class) { - // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, - // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) - @trigger_error('Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED); - - foreach (get_class_methods($this) as $method) { - if (preg_match('/^get(.+)Service$/', $method, $match)) { - $ids[] = self::underscore($match[1]); - } - } - } - $ids[] = 'service_container'; + return array_map('strval', array_unique(array_merge(['service_container'], array_keys($this->fileMap), array_keys($this->methodMap), array_keys($this->aliases), array_keys($this->services)))); + } - return array_unique(array_merge($ids, array_keys($this->methodMap), array_keys($this->services))); + /** + * Gets service ids that existed at compile time. + * + * @return array + */ + public function getRemovedIds() + { + return []; } /** * Camelizes a string. * - * @param string $id A string to camelize - * * @return string The camelized string */ - public static function camelize($id) + public static function camelize(string $id) { - return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => '')); + return strtr(ucwords(strtr($id, ['_' => ' ', '.' => '_ ', '\\' => '_ '])), [' ' => '']); } /** * A string to underscore. * - * @param string $id The string to underscore - * * @return string The underscored string */ - public static function underscore($id) + public static function underscore(string $id) + { + return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], str_replace('_', '.', $id))); + } + + /** + * Creates a service by requiring its factory file. + */ + protected function load(string $file) { - return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id))); + return require $file; } /** * Fetches a variable from the environment. * - * @param string The name of the environment variable - * - * @return scalar The value to use for the provided environment variable name + * @return mixed The value to use for the provided environment variable name * * @throws EnvNotFoundException When the environment variable is not found and has no default value */ - protected function getEnv($name) + protected function getEnv(string $name) { - if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) { + if (isset($this->resolving[$envName = "env($name)"])) { + throw new ParameterCircularReferenceException(array_keys($this->resolving)); + } + if (isset($this->envCache[$name]) || \array_key_exists($name, $this->envCache)) { return $this->envCache[$name]; } - if (isset($_ENV[$name])) { - return $this->envCache[$name] = $_ENV[$name]; + if (!$this->has($id = 'container.env_var_processors_locator')) { + $this->set($id, new ServiceLocator([])); + } + if (!$this->getEnv) { + $this->getEnv = \Closure::fromCallable([$this, 'getEnv']); + } + $processors = $this->get($id); + + if (false !== $i = strpos($name, ':')) { + $prefix = substr($name, 0, $i); + $localName = substr($name, 1 + $i); + } else { + $prefix = 'string'; + $localName = $name; + } + $processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this); + + $this->resolving[$envName] = true; + try { + return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv); + } finally { + unset($this->resolving[$envName]); + } + } + + /** + * @param string|false $registry + * @param string|bool $load + * + * @return mixed + * + * @internal + */ + final protected function getService($registry, string $id, ?string $method, $load) + { + if ('service_container' === $id) { + return $this; + } + if (\is_string($load)) { + throw new RuntimeException($load); + } + if (null === $method) { + return false !== $registry ? $this->{$registry}[$id] ?? null : null; } - if (false !== $env = getenv($name)) { - return $this->envCache[$name] = $env; + if (false !== $registry) { + return $this->{$registry}[$id] ?? $this->{$registry}[$id] = $load ? $this->load($method) : $this->{$method}(); } - if (!$this->hasParameter("env($name)")) { - throw new EnvNotFoundException($name); + if (!$load) { + return $this->{$method}(); } - return $this->envCache[$name] = $this->getParameter("env($name)"); + return ($factory = $this->factories[$id] ?? $this->factories['service_container'][$id] ?? null) ? $factory() : $this->load($method); } private function __clone() diff --git a/tests/integration/vendor/symfony/dependency-injection/ContainerAwareInterface.php b/tests/integration/vendor/symfony/dependency-injection/ContainerAwareInterface.php index fe301b627..e7b9d575e 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ContainerAwareInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/ContainerAwareInterface.php @@ -20,8 +20,6 @@ interface ContainerAwareInterface { /** * Sets the container. - * - * @param ContainerInterface|null $container A ContainerInterface instance or null */ public function setContainer(ContainerInterface $container = null); } diff --git a/tests/integration/vendor/symfony/dependency-injection/ContainerAwareTrait.php b/tests/integration/vendor/symfony/dependency-injection/ContainerAwareTrait.php index ccf064f6f..ee1ea2cb3 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ContainerAwareTrait.php +++ b/tests/integration/vendor/symfony/dependency-injection/ContainerAwareTrait.php @@ -23,11 +23,6 @@ trait ContainerAwareTrait */ protected $container; - /** - * Sets the container. - * - * @param ContainerInterface|null $container A ContainerInterface instance or null - */ public function setContainer(ContainerInterface $container = null) { $this->container = $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/ContainerBuilder.php b/tests/integration/vendor/symfony/dependency-injection/ContainerBuilder.php index 45c6f2cf6..266c3bfd1 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ContainerBuilder.php +++ b/tests/integration/vendor/symfony/dependency-injection/ContainerBuilder.php @@ -11,9 +11,25 @@ namespace Symfony\Component\DependencyInjection; +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\Config\Resource\ComposerResource; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\Config\Resource\ReflectionClassResource; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Compiler\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; @@ -21,11 +37,11 @@ use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; -use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; @@ -39,36 +55,36 @@ class ContainerBuilder extends Container implements TaggedContainerInterface /** * @var ExtensionInterface[] */ - private $extensions = array(); + private $extensions = []; /** * @var ExtensionInterface[] */ - private $extensionsByNs = array(); + private $extensionsByNs = []; /** * @var Definition[] */ - private $definitions = array(); + private $definitions = []; /** * @var Alias[] */ - private $aliasDefinitions = array(); + private $aliasDefinitions = []; /** * @var ResourceInterface[] */ - private $resources = array(); + private $resources = []; - private $extensionConfigs = array(); + private $extensionConfigs = []; /** * @var Compiler */ private $compiler; - private $trackResources = true; + private $trackResources; /** * @var InstantiatorInterface|null @@ -83,40 +99,78 @@ class ContainerBuilder extends Container implements TaggedContainerInterface /** * @var ExpressionFunctionProviderInterface[] */ - private $expressionLanguageProviders = array(); + private $expressionLanguageProviders = []; /** * @var string[] with tag names used by findTaggedServiceIds */ - private $usedTags = array(); + private $usedTags = []; /** - * @var string[][] A map of env var names to their placeholders + * @var string[][] a map of env var names to their placeholders */ - private $envPlaceholders = array(); + private $envPlaceholders = []; + + /** + * @var int[] a map of env vars to their resolution counter + */ + private $envCounters = []; + + /** + * @var string[] the list of vendor directories + */ + private $vendors; + + private $autoconfiguredInstanceof = []; + + private $removedIds = []; + + private $removedBindingIds = []; + + private const INTERNAL_TYPES = [ + 'int' => true, + 'float' => true, + 'string' => true, + 'bool' => true, + 'resource' => true, + 'object' => true, + 'array' => true, + 'null' => true, + 'callable' => true, + 'iterable' => true, + 'mixed' => true, + ]; + + public function __construct(ParameterBagInterface $parameterBag = null) + { + parent::__construct($parameterBag); + + $this->trackResources = interface_exists(ResourceInterface::class); + $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true)); + $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false))->setDeprecated('symfony/dependency-injection', '5.1', $deprecationMessage = 'The "%alias_id%" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it.'); + $this->setAlias(ContainerInterface::class, new Alias('service_container', false))->setDeprecated('symfony/dependency-injection', '5.1', $deprecationMessage); + } /** - * @var int[] A map of env vars to their resolution counter. + * @var \ReflectionClass[] a list of class reflectors */ - private $envCounters = array(); + private $classReflectors; /** * Sets the track resources flag. * * If you are not using the loaders and therefore don't want * to depend on the Config component, set this flag to false. - * - * @param bool $track true if you want to track resources, false otherwise */ - public function setResourceTracking($track) + public function setResourceTracking(bool $track) { - $this->trackResources = (bool) $track; + $this->trackResources = $track; } /** * Checks if resources are tracked. * - * @return bool true if resources are tracked, false otherwise + * @return bool true If resources are tracked, false otherwise */ public function isTrackingResources() { @@ -125,19 +179,12 @@ public function isTrackingResources() /** * Sets the instantiator to be used when fetching proxies. - * - * @param InstantiatorInterface $proxyInstantiator */ public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator) { $this->proxyInstantiator = $proxyInstantiator; } - /** - * Registers an extension. - * - * @param ExtensionInterface $extension An extension instance - */ public function registerExtension(ExtensionInterface $extension) { $this->extensions[$extension->getAlias()] = $extension; @@ -150,13 +197,11 @@ public function registerExtension(ExtensionInterface $extension) /** * Returns an extension by alias or namespace. * - * @param string $name An alias or a namespace - * * @return ExtensionInterface An extension instance * * @throws LogicException if the extension is not registered */ - public function getExtension($name) + public function getExtension(string $name) { if (isset($this->extensions[$name])) { return $this->extensions[$name]; @@ -166,7 +211,7 @@ public function getExtension($name) return $this->extensionsByNs[$name]; } - throw new LogicException(sprintf('Container extension "%s" is not registered', $name)); + throw new LogicException(sprintf('Container extension "%s" is not registered.', $name)); } /** @@ -182,11 +227,9 @@ public function getExtensions() /** * Checks if we have an extension. * - * @param string $name The name of the extension - * * @return bool If the extension exists */ - public function hasExtension($name) + public function hasExtension(string $name) { return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]); } @@ -198,15 +241,11 @@ public function hasExtension($name) */ public function getResources() { - return array_unique($this->resources); + return array_values($this->resources); } /** - * Adds a resource for this configuration. - * - * @param ResourceInterface $resource A resource instance - * - * @return ContainerBuilder The current instance + * @return $this */ public function addResource(ResourceInterface $resource) { @@ -214,7 +253,11 @@ public function addResource(ResourceInterface $resource) return $this; } - $this->resources[] = $resource; + if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) { + return $this; + } + + $this->resources[(string) $resource] = $resource; return $this; } @@ -224,7 +267,7 @@ public function addResource(ResourceInterface $resource) * * @param ResourceInterface[] $resources An array of resources * - * @return ContainerBuilder The current instance + * @return $this */ public function setResources(array $resources) { @@ -240,39 +283,128 @@ public function setResources(array $resources) /** * Adds the object class hierarchy as resources. * - * @param object $object An object instance + * @param object|string $object An object instance or class name * - * @return ContainerBuilder The current instance + * @return $this */ public function addObjectResource($object) { if ($this->trackResources) { - $this->addClassResource(new \ReflectionClass($object)); + if (\is_object($object)) { + $object = \get_class($object); + } + if (!isset($this->classReflectors[$object])) { + $this->classReflectors[$object] = new \ReflectionClass($object); + } + $class = $this->classReflectors[$object]; + + foreach ($class->getInterfaceNames() as $name) { + if (null === $interface = &$this->classReflectors[$name]) { + $interface = new \ReflectionClass($name); + } + $file = $interface->getFileName(); + if (false !== $file && file_exists($file)) { + $this->fileExists($file); + } + } + do { + $file = $class->getFileName(); + if (false !== $file && file_exists($file)) { + $this->fileExists($file); + } + foreach ($class->getTraitNames() as $name) { + $this->addObjectResource($name); + } + } while ($class = $class->getParentClass()); } return $this; } /** - * Adds the given class hierarchy as resources. + * Retrieves the requested reflection class and registers it for resource tracking. * - * @param \ReflectionClass $class + * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true * - * @return ContainerBuilder The current instance + * @final */ - public function addClassResource(\ReflectionClass $class) + public function getReflectionClass(?string $class, bool $throw = true): ?\ReflectionClass { - if (!$this->trackResources) { - return $this; + if (!$class = $this->getParameterBag()->resolveValue($class)) { + return null; + } + + if (isset(self::INTERNAL_TYPES[$class])) { + return null; } - do { - if (is_file($class->getFileName())) { - $this->addResource(new FileResource($class->getFileName())); + $resource = $classReflector = null; + + try { + if (isset($this->classReflectors[$class])) { + $classReflector = $this->classReflectors[$class]; + } elseif (class_exists(ClassExistenceResource::class)) { + $resource = new ClassExistenceResource($class, false); + $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class); + } else { + $classReflector = class_exists($class) ? new \ReflectionClass($class) : false; + } + } catch (\ReflectionException $e) { + if ($throw) { + throw $e; } - } while ($class = $class->getParentClass()); + } - return $this; + if ($this->trackResources) { + if (!$classReflector) { + $this->addResource($resource ?? new ClassExistenceResource($class, false)); + } elseif (!$classReflector->isInternal()) { + $path = $classReflector->getFileName(); + + if (!$this->inVendors($path)) { + $this->addResource(new ReflectionClassResource($classReflector, $this->vendors)); + } + } + $this->classReflectors[$class] = $classReflector; + } + + return $classReflector ?: null; + } + + /** + * Checks whether the requested file or directory exists and registers the result for resource tracking. + * + * @param string $path The file or directory path for which to check the existence + * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed, + * it will be used as pattern for tracking contents of the requested directory + * + * @final + */ + public function fileExists(string $path, $trackContents = true): bool + { + $exists = file_exists($path); + + if (!$this->trackResources || $this->inVendors($path)) { + return $exists; + } + + if (!$exists) { + $this->addResource(new FileExistenceResource($path)); + + return $exists; + } + + if (is_dir($path)) { + if ($trackContents) { + $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null)); + } else { + $this->addResource(new GlobResource($path, '/*', false)); + } + } elseif ($trackContents) { + $this->addResource(new FileResource($path)); + } + + return $exists; } /** @@ -281,15 +413,19 @@ public function addClassResource(\ReflectionClass $class) * @param string $extension The extension alias or namespace * @param array $values An array of values that customizes the extension * - * @return ContainerBuilder The current instance + * @return $this * - * @throws BadMethodCallException When this ContainerBuilder is frozen - * @throws \LogicException if the container is frozen + * @throws BadMethodCallException When this ContainerBuilder is compiled + * @throws \LogicException if the extension is not registered */ - public function loadFromExtension($extension, array $values = array()) + public function loadFromExtension(string $extension, array $values = null) { - if ($this->isFrozen()) { - throw new BadMethodCallException('Cannot load from an extension on a frozen container.'); + if ($this->isCompiled()) { + throw new BadMethodCallException('Cannot load from an extension on a compiled container.'); + } + + if (\func_num_args() < 2) { + $values = []; } $namespace = $this->getExtension($extension)->getAlias(); @@ -302,27 +438,13 @@ public function loadFromExtension($extension, array $values = array()) /** * Adds a compiler pass. * - * @param CompilerPassInterface $pass A compiler pass - * @param string $type The type of compiler pass - * @param int $priority Used to sort the passes + * @param string $type The type of compiler pass + * @param int $priority Used to sort the passes * - * @return ContainerBuilder The current instance + * @return $this */ - public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/) + public function addCompilerPass(CompilerPassInterface $pass, string $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) { - if (func_num_args() >= 3) { - $priority = func_get_arg(2); - } else { - if (__CLASS__ !== get_class($this)) { - $r = new \ReflectionMethod($this, __FUNCTION__); - if (__CLASS__ !== $r->getDeclaringClass()->getName()) { - @trigger_error(sprintf('Method %s() will have a third `$priority = 0` argument in version 4.0. Not defining it is deprecated since 3.2.', get_class($this), __FUNCTION__), E_USER_DEPRECATED); - } - } - - $priority = 0; - } - $this->getCompiler()->addPass($pass, $type, $priority); $this->addObjectResource($pass); @@ -357,33 +479,29 @@ public function getCompiler() /** * Sets a service. * - * @param string $id The service identifier - * @param object $service The service instance - * - * @throws BadMethodCallException When this ContainerBuilder is frozen + * @throws BadMethodCallException When this ContainerBuilder is compiled */ - public function set($id, $service) + public function set(string $id, ?object $service) { - $id = strtolower($id); - - if ($this->isFrozen() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { - // setting a synthetic service on a frozen container is alright - throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a frozen container is not allowed.', $id)); + if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { + // setting a synthetic service on a compiled container is alright + throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id)); } - unset($this->definitions[$id], $this->aliasDefinitions[$id]); + unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]); parent::set($id, $service); } /** * Removes a service definition. - * - * @param string $id The service identifier */ - public function removeDefinition($id) + public function removeDefinition(string $id) { - unset($this->definitions[strtolower($id)]); + if (isset($this->definitions[$id])) { + unset($this->definitions[$id]); + $this->removedIds[$id] = true; + } } /** @@ -395,18 +513,13 @@ public function removeDefinition($id) */ public function has($id) { - $id = strtolower($id); + $id = (string) $id; return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); } /** - * Gets a service. - * - * @param string $id The service identifier - * @param int $invalidBehavior The behavior when the service does not exist - * - * @return object The associated service + * @return object|null The associated service * * @throws InvalidArgumentException when no definitions are available * @throws ServiceCircularReferenceException When a circular reference is detected @@ -415,37 +528,73 @@ public function has($id) * * @see Reference */ - public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + public function get($id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - $id = strtolower($id); + if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior) { + return parent::get($id); + } + + return $this->doGet($id, $invalidBehavior); + } - if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { - return $service; + private function doGet(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, bool $isConstructorArgument = false) + { + if (isset($inlineServices[$id])) { + return $inlineServices[$id]; + } + if (null === $inlineServices) { + $isConstructorArgument = true; + $inlineServices = []; + } + try { + if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { + return parent::get($id, $invalidBehavior); + } + if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { + return $service; + } + } catch (ServiceCircularReferenceException $e) { + if ($isConstructorArgument) { + throw $e; + } } if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { - return $this->get($this->aliasDefinitions[$id], $invalidBehavior); + $alias = $this->aliasDefinitions[$id]; + + if ($alias->isDeprecated()) { + $deprecation = $alias->getDeprecation($id); + trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); + } + + return $this->doGet((string) $alias, $invalidBehavior, $inlineServices, $isConstructorArgument); } try { $definition = $this->getDefinition($id); } catch (ServiceNotFoundException $e) { - if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { - return; + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $invalidBehavior) { + return null; } throw $e; } - $this->loading[$id] = true; + if ($definition->hasErrors() && $e = $definition->getErrors()) { + throw new RuntimeException(reset($e)); + } + + if ($isConstructorArgument) { + $this->loading[$id] = true; + } try { - $service = $this->createService($definition, $id); + return $this->createService($definition, $inlineServices, $isConstructorArgument, $id); } finally { - unset($this->loading[$id]); + if ($isConstructorArgument) { + unset($this->loading[$id]); + } } - - return $service; } /** @@ -457,23 +606,21 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV * the parameters passed to the container constructor to have precedence * over the loaded ones. * - * $container = new ContainerBuilder(array('foo' => 'bar')); - * $loader = new LoaderXXX($container); - * $loader->load('resource_name'); - * $container->register('foo', new stdClass()); + * $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar'])); + * $loader = new LoaderXXX($container); + * $loader->load('resource_name'); + * $container->register('foo', 'stdClass'); * * In the above example, even if the loaded resource defines a foo * parameter, the value will still be 'bar' as defined in the ContainerBuilder * constructor. * - * @param ContainerBuilder $container The ContainerBuilder instance to merge - * - * @throws BadMethodCallException When this ContainerBuilder is frozen + * @throws BadMethodCallException When this ContainerBuilder is compiled */ - public function merge(ContainerBuilder $container) + public function merge(self $container) { - if ($this->isFrozen()) { - throw new BadMethodCallException('Cannot merge on a frozen container.'); + if ($this->isCompiled()) { + throw new BadMethodCallException('Cannot merge on a compiled container.'); } $this->addDefinitions($container->getDefinitions()); @@ -488,36 +635,48 @@ public function merge(ContainerBuilder $container) foreach ($this->extensions as $name => $extension) { if (!isset($this->extensionConfigs[$name])) { - $this->extensionConfigs[$name] = array(); + $this->extensionConfigs[$name] = []; } $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name)); } if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) { + $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders(); $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag()); + } else { + $envPlaceholders = []; } foreach ($container->envCounters as $env => $count) { + if (!$count && !isset($envPlaceholders[$env])) { + continue; + } if (!isset($this->envCounters[$env])) { $this->envCounters[$env] = $count; } else { $this->envCounters[$env] += $count; } } + + foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) { + if (isset($this->autoconfiguredInstanceof[$interface])) { + throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface)); + } + + $this->autoconfiguredInstanceof[$interface] = $childDefinition; + } } /** * Returns the configuration array for the given extension. * - * @param string $name The name of the extension - * * @return array An array of configuration */ - public function getExtensionConfig($name) + public function getExtensionConfig(string $name) { if (!isset($this->extensionConfigs[$name])) { - $this->extensionConfigs[$name] = array(); + $this->extensionConfigs[$name] = []; } return $this->extensionConfigs[$name]; @@ -525,14 +684,11 @@ public function getExtensionConfig($name) /** * Prepends a config array to the configs of the given extension. - * - * @param string $name The name of the extension - * @param array $config The config to set */ - public function prependExtensionConfig($name, array $config) + public function prependExtensionConfig(string $name, array $config) { if (!isset($this->extensionConfigs[$name])) { - $this->extensionConfigs[$name] = array(); + $this->extensionConfigs[$name] = []; } array_unshift($this->extensionConfigs[$name], $config); @@ -551,8 +707,13 @@ public function prependExtensionConfig($name, array $config) * * Parameter values are resolved; * * The parameter bag is frozen; * * Extension loading is disabled. + * + * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current + * env vars or be replaced by uniquely identifiable placeholders. + * Set to "true" when you want to use the current ContainerBuilder + * directly, keep to "false" when the container is dumped instead. */ - public function compile() + public function compile(bool $resolveEnvPlaceholders = false) { $compiler = $this->getCompiler(); @@ -561,40 +722,59 @@ public function compile() $this->addObjectResource($pass); } } + $bag = $this->getParameterBag(); + + if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) { + $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000); + } $compiler->compile($this); foreach ($this->definitions as $id => $definition) { - if (!$definition->isPublic()) { - $this->privates[$id] = true; - } - if ($this->trackResources && $definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { - $this->addClassResource(new \ReflectionClass($class)); + if ($this->trackResources && $definition->isLazy()) { + $this->getReflectionClass($definition->getClass()); } } - $this->extensionConfigs = array(); - $bag = $this->getParameterBag(); + $this->extensionConfigs = []; + + if ($bag instanceof EnvPlaceholderParameterBag) { + if ($resolveEnvPlaceholders) { + $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true)); + } + + $this->envPlaceholders = $bag->getEnvPlaceholders(); + } parent::compile(); - $this->envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : array(); + foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) { + if (!$definition->isPublic() || $definition->isPrivate()) { + $this->removedIds[$id] = true; + } + } } /** - * Gets all service ids. - * - * @return array An array of all defined service ids + * {@inheritdoc} */ public function getServiceIds() { - return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())); + return array_map('strval', array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds()))); } /** - * Adds the service aliases. + * Gets removed service or alias ids. * - * @param array $aliases An array of aliases + * @return array + */ + public function getRemovedIds() + { + return $this->removedIds; + } + + /** + * Adds the service aliases. */ public function addAliases(array $aliases) { @@ -605,12 +785,10 @@ public function addAliases(array $aliases) /** * Sets the service aliases. - * - * @param array $aliases An array of aliases */ public function setAliases(array $aliases) { - $this->aliasDefinitions = array(); + $this->aliasDefinitions = []; $this->addAliases($aliases); } @@ -620,14 +798,18 @@ public function setAliases(array $aliases) * @param string $alias The alias to create * @param string|Alias $id The service to alias * + * @return Alias + * * @throws InvalidArgumentException if the id is not a string or an Alias * @throws InvalidArgumentException if the alias is for itself */ - public function setAlias($alias, $id) + public function setAlias(string $alias, $id) { - $alias = strtolower($alias); + if ('' === $alias || '\\' === $alias[-1] || \strlen($alias) !== strcspn($alias, "\0\r\n'")) { + throw new InvalidArgumentException(sprintf('Invalid alias id: "%s".', $alias)); + } - if (is_string($id)) { + if (\is_string($id)) { $id = new Alias($id); } elseif (!$id instanceof Alias) { throw new InvalidArgumentException('$id must be a string, or an Alias object.'); @@ -637,36 +819,28 @@ public function setAlias($alias, $id) throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias)); } - unset($this->definitions[$alias]); + unset($this->definitions[$alias], $this->removedIds[$alias]); - $this->aliasDefinitions[$alias] = $id; + return $this->aliasDefinitions[$alias] = $id; } - /** - * Removes an alias. - * - * @param string $alias The alias to remove - */ - public function removeAlias($alias) + public function removeAlias(string $alias) { - unset($this->aliasDefinitions[strtolower($alias)]); + if (isset($this->aliasDefinitions[$alias])) { + unset($this->aliasDefinitions[$alias]); + $this->removedIds[$alias] = true; + } } /** - * Returns true if an alias exists under the given identifier. - * - * @param string $id The service identifier - * * @return bool true if the alias exists, false otherwise */ - public function hasAlias($id) + public function hasAlias(string $id) { - return isset($this->aliasDefinitions[strtolower($id)]); + return isset($this->aliasDefinitions[$id]); } /** - * Gets all defined aliases. - * * @return Alias[] An array of aliases */ public function getAliases() @@ -675,18 +849,12 @@ public function getAliases() } /** - * Gets an alias. - * - * @param string $id The service identifier - * * @return Alias An Alias instance * * @throws InvalidArgumentException if the alias does not exist */ - public function getAlias($id) + public function getAlias(string $id) { - $id = strtolower($id); - if (!isset($this->aliasDefinitions[$id])) { throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); } @@ -700,16 +868,26 @@ public function getAlias($id) * This methods allows for simple registration of service definition * with a fluid interface. * - * @param string $id The service identifier - * @param string $class The service class - * * @return Definition A Definition instance */ - public function register($id, $class = null) + public function register(string $id, string $class = null) { return $this->setDefinition($id, new Definition($class)); } + /** + * Registers an autowired service definition. + * + * This method implements a shortcut for using setDefinition() with + * an autowired definition. + * + * @return Definition The created definition + */ + public function autowire(string $id, string $class = null) + { + return $this->setDefinition($id, (new Definition($class))->setAutowired(true)); + } + /** * Adds the service definitions. * @@ -729,7 +907,7 @@ public function addDefinitions(array $definitions) */ public function setDefinitions(array $definitions) { - $this->definitions = array(); + $this->definitions = []; $this->addDefinitions($definitions); } @@ -746,22 +924,21 @@ public function getDefinitions() /** * Sets a service definition. * - * @param string $id The service identifier - * @param Definition $definition A Definition instance - * * @return Definition the service definition * - * @throws BadMethodCallException When this ContainerBuilder is frozen + * @throws BadMethodCallException When this ContainerBuilder is compiled */ - public function setDefinition($id, Definition $definition) + public function setDefinition(string $id, Definition $definition) { - if ($this->isFrozen()) { - throw new BadMethodCallException('Adding definition to a frozen container is not allowed'); + if ($this->isCompiled()) { + throw new BadMethodCallException('Adding definition to a compiled container is not allowed.'); } - $id = strtolower($id); + if ('' === $id || '\\' === $id[-1] || \strlen($id) !== strcspn($id, "\0\r\n'")) { + throw new InvalidArgumentException(sprintf('Invalid service id: "%s".', $id)); + } - unset($this->aliasDefinitions[$id]); + unset($this->aliasDefinitions[$id], $this->removedIds[$id]); return $this->definitions[$id] = $definition; } @@ -769,28 +946,22 @@ public function setDefinition($id, Definition $definition) /** * Returns true if a service definition exists under the given identifier. * - * @param string $id The service identifier - * * @return bool true if the service definition exists, false otherwise */ - public function hasDefinition($id) + public function hasDefinition(string $id) { - return isset($this->definitions[strtolower($id)]); + return isset($this->definitions[$id]); } /** * Gets a service definition. * - * @param string $id The service identifier - * * @return Definition A Definition instance * * @throws ServiceNotFoundException if the service definition does not exist */ - public function getDefinition($id) + public function getDefinition(string $id) { - $id = strtolower($id); - if (!isset($this->definitions[$id])) { throw new ServiceNotFoundException($id); } @@ -803,18 +974,25 @@ public function getDefinition($id) * * The method "unaliases" recursively to return a Definition instance. * - * @param string $id The service identifier or alias - * * @return Definition A Definition instance * * @throws ServiceNotFoundException if the service definition does not exist */ - public function findDefinition($id) + public function findDefinition(string $id) { - $id = strtolower($id); - + $seen = []; while (isset($this->aliasDefinitions[$id])) { $id = (string) $this->aliasDefinitions[$id]; + + if (isset($seen[$id])) { + $seen = array_values($seen); + $seen = \array_slice($seen, array_search($id, $seen)); + $seen[] = $id; + + throw new ServiceCircularReferenceException($id, $seen); + } + + $seen[$id] = $id; } return $this->getDefinition($id); @@ -823,19 +1001,19 @@ public function findDefinition($id) /** * Creates a service for a service definition. * - * @param Definition $definition A service definition instance - * @param string $id The service identifier - * @param bool $tryProxy Whether to try proxying the service with a lazy proxy - * - * @return object The service described by the service definition + * @return mixed The service described by the service definition * * @throws RuntimeException When the factory definition is incomplete * @throws RuntimeException When the service is a synthetic service * @throws InvalidArgumentException When configure callable is not callable */ - private function createService(Definition $definition, $id, $tryProxy = true) + private function createService(Definition $definition, array &$inlineServices, bool $isConstructorArgument = false, string $id = null, bool $tryProxy = true) { - if ($definition instanceof DefinitionDecorator) { + if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) { + return $inlineServices[$h]; + } + + if ($definition instanceof ChildDefinition) { throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id)); } @@ -844,20 +1022,19 @@ private function createService(Definition $definition, $id, $tryProxy = true) } if ($definition->isDeprecated()) { - @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); + $deprecation = $definition->getDeprecation($id); + trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } - if ($tryProxy && $definition->isLazy()) { - $proxy = $this - ->getProxyInstantiator() - ->instantiateProxy( - $this, - $definition, - $id, function () use ($definition, $id) { - return $this->createService($definition, $id, false); - } - ); - $this->shareService($definition, $proxy, $id); + if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) { + $proxy = $proxy->instantiateProxy( + $this, + $definition, + $id, function () use ($definition, &$inlineServices, $id) { + return $this->createService($definition, $inlineServices, true, $id, false); + } + ); + $this->shareService($definition, $proxy, $id, $inlineServices); return $proxy; } @@ -868,64 +1045,82 @@ private function createService(Definition $definition, $id, $tryProxy = true) require_once $parameterBag->resolveValue($definition->getFile()); } - $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments()))); + $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices, $isConstructorArgument); if (null !== $factory = $definition->getFactory()) { - if (is_array($factory)) { - $factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]); - } elseif (!is_string($factory)) { - throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); + if (\is_array($factory)) { + $factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]]; + } elseif (!\is_string($factory)) { + throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory.', $id)); } + } - $service = call_user_func_array($factory, $arguments); + if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) { + return $this->services[$id]; + } - if (!$definition->isDeprecated() && is_array($factory) && is_string($factory[0])) { + if (null !== $factory) { + $service = $factory(...$arguments); + + if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) { $r = new \ReflectionClass($factory[0]); if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) { - @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED); + trigger_deprecation('', '', 'The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name); } } } else { $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); - $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); + $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs(array_values($arguments)); if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { - @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED); + trigger_deprecation('', '', 'The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name); } } - if ($tryProxy || !$definition->isLazy()) { - // share only if proxying failed, or if not a proxy - $this->shareService($definition, $service, $id); + $lastWitherIndex = null; + foreach ($definition->getMethodCalls() as $k => $call) { + if ($call[2] ?? false) { + $lastWitherIndex = $k; + } } - $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); + if (null === $lastWitherIndex && ($tryProxy || !$definition->isLazy())) { + // share only if proxying failed, or if not a proxy, and if no withers are found + $this->shareService($definition, $service, $id, $inlineServices); + } + + $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices); foreach ($properties as $name => $value) { $service->$name = $value; } - foreach ($definition->getMethodCalls() as $call) { - $this->callMethod($service, $call); + foreach ($definition->getMethodCalls() as $k => $call) { + $service = $this->callMethod($service, $call, $inlineServices); + + if ($lastWitherIndex === $k && ($tryProxy || !$definition->isLazy())) { + // share only if proxying failed, or if not a proxy, and this is the last wither + $this->shareService($definition, $service, $id, $inlineServices); + } } if ($callable = $definition->getConfigurator()) { - if (is_array($callable)) { + if (\is_array($callable)) { $callable[0] = $parameterBag->resolveValue($callable[0]); if ($callable[0] instanceof Reference) { - $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior()); + $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices); } elseif ($callable[0] instanceof Definition) { - $callable[0] = $this->createService($callable[0], null); + $callable[0] = $this->createService($callable[0], $inlineServices); } } - if (!is_callable($callable)) { - throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service))); + if (!\is_callable($callable)) { + throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_debug_type($service))); } - call_user_func($callable, $service); + $callable($service); } return $service; @@ -941,16 +1136,74 @@ private function createService(Definition $definition, $id, $tryProxy = true) */ public function resolveServices($value) { - if (is_array($value)) { + return $this->doResolveServices($value); + } + + private function doResolveServices($value, array &$inlineServices = [], bool $isConstructorArgument = false) + { + if (\is_array($value)) { foreach ($value as $k => $v) { - $value[$k] = $this->resolveServices($v); + $value[$k] = $this->doResolveServices($v, $inlineServices, $isConstructorArgument); } + } elseif ($value instanceof ServiceClosureArgument) { + $reference = $value->getValues()[0]; + $value = function () use ($reference) { + return $this->resolveServices($reference); + }; + } elseif ($value instanceof IteratorArgument) { + $value = new RewindableGenerator(function () use ($value, &$inlineServices) { + foreach ($value->getValues() as $k => $v) { + foreach (self::getServiceConditionals($v) as $s) { + if (!$this->has($s)) { + continue 2; + } + } + foreach (self::getInitializedConditionals($v) as $s) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { + continue 2; + } + } + + yield $k => $this->doResolveServices($v, $inlineServices); + } + }, function () use ($value): int { + $count = 0; + foreach ($value->getValues() as $v) { + foreach (self::getServiceConditionals($v) as $s) { + if (!$this->has($s)) { + continue 2; + } + } + foreach (self::getInitializedConditionals($v) as $s) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) { + continue 2; + } + } + + ++$count; + } + + return $count; + }); + } elseif ($value instanceof ServiceLocatorArgument) { + $refs = $types = []; + foreach ($value->getValues() as $k => $v) { + if ($v) { + $refs[$k] = [$v]; + $types[$k] = $v instanceof TypedReference ? $v->getType() : '?'; + } + } + $value = new ServiceLocator(\Closure::fromCallable([$this, 'resolveServices']), $refs, $types); } elseif ($value instanceof Reference) { - $value = $this->get((string) $value, $value->getInvalidBehavior()); + $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument); } elseif ($value instanceof Definition) { - $value = $this->createService($value, null); + $value = $this->createService($value, $inlineServices, $isConstructorArgument); + } elseif ($value instanceof Parameter) { + $value = $this->getParameter((string) $value); } elseif ($value instanceof Expression) { - $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this)); + $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]); + } elseif ($value instanceof AbstractArgument) { + throw new RuntimeException($value->getTextWithContext()); } return $value; @@ -961,25 +1214,26 @@ public function resolveServices($value) * * Example: * - * $container->register('foo')->addTag('my.tag', array('hello' => 'world')); + * $container->register('foo')->addTag('my.tag', ['hello' => 'world']); * - * $serviceIds = $container->findTaggedServiceIds('my.tag'); - * foreach ($serviceIds as $serviceId => $tags) { - * foreach ($tags as $tag) { - * echo $tag['hello']; + * $serviceIds = $container->findTaggedServiceIds('my.tag'); + * foreach ($serviceIds as $serviceId => $tags) { + * foreach ($tags as $tag) { + * echo $tag['hello']; + * } * } - * } - * - * @param string $name The tag name * * @return array An array of tags with the tagged service as key, holding a list of attribute arrays */ - public function findTaggedServiceIds($name) + public function findTaggedServiceIds(string $name, bool $throwOnAbstract = false) { $this->usedTags[] = $name; - $tags = array(); + $tags = []; foreach ($this->getDefinitions() as $id => $definition) { if ($definition->hasTag($name)) { + if ($throwOnAbstract && $definition->isAbstract()) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name)); + } $tags[$id] = $definition->getTag($name); } } @@ -994,7 +1248,7 @@ public function findTaggedServiceIds($name) */ public function findTags() { - $tags = array(); + $tags = []; foreach ($this->getDefinitions() as $id => $definition) { $tags = array_merge(array_keys($definition->getTags()), $tags); } @@ -1025,14 +1279,59 @@ public function getExpressionLanguageProviders() return $this->expressionLanguageProviders; } + /** + * Returns a ChildDefinition that will be used for autoconfiguring the interface/class. + * + * @return ChildDefinition + */ + public function registerForAutoconfiguration(string $interface) + { + if (!isset($this->autoconfiguredInstanceof[$interface])) { + $this->autoconfiguredInstanceof[$interface] = new ChildDefinition(''); + } + + return $this->autoconfiguredInstanceof[$interface]; + } + + /** + * Registers an autowiring alias that only binds to a specific argument name. + * + * The argument name is derived from $name if provided (from $id otherwise) + * using camel case: "foo.bar" or "foo_bar" creates an alias bound to + * "$fooBar"-named arguments with $type as type-hint. Such arguments will + * receive the service $id when autowiring is used. + */ + public function registerAliasForArgument(string $id, string $type, string $name = null): Alias + { + $name = lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $name ?? $id)))); + + if (!preg_match('/^[a-zA-Z_\x7f-\xff]/', $name)) { + throw new InvalidArgumentException(sprintf('Invalid argument name "%s" for service "%s": the first character must be a letter.', $name, $id)); + } + + return $this->setAlias($type.' $'.$name, $id); + } + + /** + * Returns an array of ChildDefinition[] keyed by interface. + * + * @return ChildDefinition[] + */ + public function getAutoconfiguredInstanceof() + { + return $this->autoconfiguredInstanceof; + } + /** * Resolves env parameter placeholders in a string or an array. * - * @param mixed $value The value to resolve - * @param string|null $format A sprintf() format to use as replacement for env placeholders or null to use the default parameter format - * @param array &$usedEnvs Env vars found while resolving are added to this array + * @param mixed $value The value to resolve + * @param string|true|null $format A sprintf() format returning the replacement for each env var name or + * null to resolve back to the original "%env(VAR)%" format or + * true to resolve to the actual values of the referenced env vars + * @param array &$usedEnvs Env vars found while resolving are added to this array * - * @return string The string with env parameters resolved + * @return mixed The value with env parameters resolved if a string or an array is passed */ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) { @@ -1040,28 +1339,53 @@ public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs $format = '%%env(%s)%%'; } - if (is_array($value)) { - $result = array(); + $bag = $this->getParameterBag(); + if (true === $format) { + $value = $bag->resolveValue($value); + } + + if ($value instanceof Definition) { + $value = (array) $value; + } + + if (\is_array($value)) { + $result = []; foreach ($value as $k => $v) { - $result[$this->resolveEnvPlaceholders($k, $format, $usedEnvs)] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs); + $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs); } return $result; } - if (!is_string($value)) { + if (!\is_string($value) || 38 > \strlen($value)) { return $value; } - - $bag = $this->getParameterBag(); $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; + $completed = false; foreach ($envPlaceholders as $env => $placeholders) { foreach ($placeholders as $placeholder) { if (false !== stripos($value, $placeholder)) { - $value = str_ireplace($placeholder, sprintf($format, $env), $value); + if (true === $format) { + $resolved = $bag->escapeValue($this->getEnv($env)); + } else { + $resolved = sprintf($format, $env); + } + if ($placeholder === $value) { + $value = $resolved; + $completed = true; + } else { + if (!\is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type "%s" inside string value "%s".', $env, get_debug_type($resolved), $this->resolveEnvPlaceholders($value))); + } + $value = str_ireplace($placeholder, $resolved, $value); + } $usedEnvs[$env] = $env; $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1; + + if ($completed) { + break 2; + } } } } @@ -1088,22 +1412,55 @@ public function getEnvCounters() return $this->envCounters; } + /** + * @final + */ + public function log(CompilerPassInterface $pass, string $message) + { + $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message)); + } + + /** + * Gets removed binding ids. + * + * @internal + */ + public function getRemovedBindingIds(): array + { + return $this->removedBindingIds; + } + + /** + * Removes bindings for a service. + * + * @internal + */ + public function removeBindings(string $id) + { + if ($this->hasDefinition($id)) { + foreach ($this->getDefinition($id)->getBindings() as $key => $binding) { + [, $bindingId] = $binding->getValues(); + $this->removedBindingIds[(int) $bindingId] = true; + } + } + } + /** * Returns the Service Conditionals. * * @param mixed $value An array of conditionals to return * - * @return array An array of Service conditionals + * @internal */ - public static function getServiceConditionals($value) + public static function getServiceConditionals($value): array { - $services = array(); + $services = []; - if (is_array($value)) { + if (\is_array($value)) { foreach ($value as $v) { $services = array_unique(array_merge($services, self::getServiceConditionals($v))); } - } elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { + } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { $services[] = (string) $value; } @@ -1111,55 +1468,133 @@ public static function getServiceConditionals($value) } /** - * Retrieves the currently set proxy instantiator or instantiates one. + * Returns the initialized conditionals. * - * @return InstantiatorInterface + * @param mixed $value An array of conditionals to return + * + * @internal */ - private function getProxyInstantiator() + public static function getInitializedConditionals($value): array { - if (!$this->proxyInstantiator) { - $this->proxyInstantiator = new RealServiceInstantiator(); + $services = []; + + if (\is_array($value)) { + foreach ($value as $v) { + $services = array_unique(array_merge($services, self::getInitializedConditionals($v))); + } + } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) { + $services[] = (string) $value; } - return $this->proxyInstantiator; + return $services; } - private function callMethod($service, $call) + /** + * Computes a reasonably unique hash of a value. + * + * @param mixed $value A serializable value + * + * @return string + */ + public static function hash($value) + { + $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7); + + return str_replace(['/', '+'], ['.', '_'], $hash); + } + + /** + * {@inheritdoc} + */ + protected function getEnv(string $name) { - $services = self::getServiceConditionals($call[1]); + $value = parent::getEnv($name); + $bag = $this->getParameterBag(); - foreach ($services as $s) { + if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) { + return $value; + } + + $envPlaceholders = $bag->getEnvPlaceholders(); + if (isset($envPlaceholders[$name][$value])) { + $bag = new ParameterBag($bag->all()); + + return $bag->unescapeValue($bag->get("env($name)")); + } + foreach ($envPlaceholders as $env => $placeholders) { + if (isset($placeholders[$value])) { + return $this->getEnv($env); + } + } + + $this->resolving["env($name)"] = true; + try { + return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true)); + } finally { + unset($this->resolving["env($name)"]); + } + } + + private function callMethod(object $service, array $call, array &$inlineServices) + { + foreach (self::getServiceConditionals($call[1]) as $s) { if (!$this->has($s)) { - return; + return $service; } } + foreach (self::getInitializedConditionals($call[1]) as $s) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { + return $service; + } + } + + $result = $service->{$call[0]}(...$this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices)); - call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])))); + return empty($call[2]) ? $service : $result; } /** * Shares a given service in the container. * - * @param Definition $definition - * @param mixed $service - * @param string $id + * @param mixed $service */ - private function shareService(Definition $definition, $service, $id) + private function shareService(Definition $definition, $service, ?string $id, array &$inlineServices) { - if ($definition->isShared()) { - $this->services[$lowerId = strtolower($id)] = $service; + $inlineServices[$id ?? spl_object_hash($definition)] = $service; + + if (null !== $id && $definition->isShared()) { + $this->services[$id] = $service; + unset($this->loading[$id]); } } - private function getExpressionLanguage() + private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { - if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { - throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + if (!class_exists(\Symfony\Component\ExpressionLanguage\ExpressionLanguage::class)) { + throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); } $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); } return $this->expressionLanguage; } + + private function inVendors(string $path): bool + { + if (null === $this->vendors) { + $this->vendors = (new ComposerResource())->getVendors(); + } + $path = realpath($path) ?: $path; + + foreach ($this->vendors as $vendor) { + if (str_starts_with($path, $vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { + $this->addResource(new FileResource($vendor.'/composer/installed.json')); + + return true; + } + } + + return false; + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/ContainerInterface.php b/tests/integration/vendor/symfony/dependency-injection/ContainerInterface.php index 7e2fbb1c8..a3b0891c1 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ContainerInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/ContainerInterface.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection; +use Psr\Container\ContainerInterface as PsrContainerInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -21,19 +22,18 @@ * @author Fabien Potencier * @author Johannes M. Schmitt */ -interface ContainerInterface +interface ContainerInterface extends PsrContainerInterface { - const EXCEPTION_ON_INVALID_REFERENCE = 1; - const NULL_ON_INVALID_REFERENCE = 2; - const IGNORE_ON_INVALID_REFERENCE = 3; + public const RUNTIME_EXCEPTION_ON_INVALID_REFERENCE = 0; + public const EXCEPTION_ON_INVALID_REFERENCE = 1; + public const NULL_ON_INVALID_REFERENCE = 2; + public const IGNORE_ON_INVALID_REFERENCE = 3; + public const IGNORE_ON_UNINITIALIZED_REFERENCE = 4; /** * Sets a service. - * - * @param string $id The service identifier - * @param object $service The service instance */ - public function set($id, $service); + public function set(string $id, ?object $service); /** * Gets a service. @@ -41,18 +41,16 @@ public function set($id, $service); * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * - * @return object The associated service + * @return object|null The associated service * * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined * * @see Reference */ - public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); + public function get($id, int $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); /** - * Returns true if the given service is defined. - * * @param string $id The service identifier * * @return bool true if the service is defined, false otherwise @@ -62,37 +60,27 @@ public function has($id); /** * Check for whether or not a service has been initialized. * - * @param string $id - * * @return bool true if the service has been initialized, false otherwise */ - public function initialized($id); + public function initialized(string $id); /** - * Gets a parameter. - * - * @param string $name The parameter name - * - * @return mixed The parameter value + * @return array|bool|string|int|float|null * * @throws InvalidArgumentException if the parameter is not defined */ - public function getParameter($name); + public function getParameter(string $name); /** - * Checks if a parameter exists. - * - * @param string $name The parameter name - * - * @return bool The presence of parameter in container + * @return bool */ - public function hasParameter($name); + public function hasParameter(string $name); /** * Sets a parameter. * - * @param string $name The parameter name - * @param mixed $value The parameter value + * @param string $name The parameter name + * @param array|bool|string|int|float|null $value The parameter value */ - public function setParameter($name, $value); + public function setParameter(string $name, $value); } diff --git a/tests/integration/vendor/symfony/dependency-injection/Definition.php b/tests/integration/vendor/symfony/dependency-injection/Definition.php index 5f40ae41a..90f8f886c 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Definition.php +++ b/tests/integration/vendor/symfony/dependency-injection/Definition.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; @@ -21,47 +22,92 @@ */ class Definition { + private const DEFAULT_DEPRECATION_TEMPLATE = 'The "%service_id%" service is deprecated. You should stop using it, as it will be removed in the future.'; + private $class; private $file; private $factory; private $shared = true; - private $deprecated = false; - private $deprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.'; - private $properties = array(); - private $calls = array(); + private $deprecation = []; + private $properties = []; + private $calls = []; + private $instanceof = []; + private $autoconfigured = false; private $configurator; - private $tags = array(); - private $public = true; + private $tags = []; + private $public = false; private $synthetic = false; private $abstract = false; private $lazy = false; private $decoratedService; private $autowired = false; - private $autowiringTypes = array(); + private $changes = []; + private $bindings = []; + private $errors = []; + + protected $arguments = []; - protected $arguments; + /** + * @internal + * + * Used to store the name of the inner id when using service decoration together with autowiring + */ + public $innerServiceId; /** - * @param string|null $class The service class - * @param array $arguments An array of arguments to pass to the service constructor + * @internal + * + * Used to store the behavior to follow when using service decoration and the decorated service is invalid */ - public function __construct($class = null, array $arguments = array()) + public $decorationOnInvalid; + + public function __construct(string $class = null, array $arguments = []) { - $this->class = $class; + if (null !== $class) { + $this->setClass($class); + } $this->arguments = $arguments; } + /** + * Returns all changes tracked for the Definition object. + * + * @return array An array of changes for this Definition + */ + public function getChanges() + { + return $this->changes; + } + + /** + * Sets the tracked changes for the Definition object. + * + * @param array $changes An array of changes for this Definition + * + * @return $this + */ + public function setChanges(array $changes) + { + $this->changes = $changes; + + return $this; + } + /** * Sets a factory. * - * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call + * @param string|array|Reference|null $factory A PHP function, reference or an array containing a class/Reference and a method to call * - * @return Definition The current instance + * @return $this */ public function setFactory($factory) { - if (is_string($factory) && strpos($factory, '::') !== false) { + $this->changes['factory'] = true; + + if (\is_string($factory) && str_contains($factory, '::')) { $factory = explode('::', $factory, 2); + } elseif ($factory instanceof Reference) { + $factory = [$factory, '__invoke']; } $this->factory = $factory; @@ -72,7 +118,7 @@ public function setFactory($factory) /** * Gets the factory. * - * @return string|array The PHP function or an array containing a class/Reference and a method to call + * @return string|array|null The PHP function or an array containing a class/Reference and a method to call */ public function getFactory() { @@ -82,33 +128,38 @@ public function getFactory() /** * Sets the service that this service is decorating. * - * @param null|string $id The decorated service id, use null to remove decoration - * @param null|string $renamedId The new decorated service id - * @param int $priority The priority of decoration + * @param string|null $id The decorated service id, use null to remove decoration + * @param string|null $renamedId The new decorated service id * - * @return Definition The current instance + * @return $this * - * @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals. + * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals */ - public function setDecoratedService($id, $renamedId = null, $priority = 0) + public function setDecoratedService(?string $id, string $renamedId = null, int $priority = 0, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - if ($renamedId && $id == $renamedId) { + if ($renamedId && $id === $renamedId) { throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id)); } + $this->changes['decorated_service'] = true; + if (null === $id) { $this->decoratedService = null; } else { - $this->decoratedService = array($id, $renamedId, (int) $priority); + $this->decoratedService = [$id, $renamedId, (int) $priority]; + + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + $this->decoratedService[] = $invalidBehavior; + } } return $this; } /** - * Gets the service that decorates this service. + * Gets the service that this service is decorating. * - * @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated + * @return array|null An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated */ public function getDecoratedService() { @@ -118,12 +169,12 @@ public function getDecoratedService() /** * Sets the service class. * - * @param string $class The service class - * - * @return Definition The current instance + * @return $this */ - public function setClass($class) + public function setClass(?string $class) { + $this->changes['class'] = true; + $this->class = $class; return $this; @@ -142,9 +193,7 @@ public function getClass() /** * Sets the arguments to pass to the service constructor/factory method. * - * @param array $arguments An array of arguments - * - * @return Definition The current instance + * @return $this */ public function setArguments(array $arguments) { @@ -153,6 +202,11 @@ public function setArguments(array $arguments) return $this; } + /** + * Sets the properties to define when creating the service. + * + * @return $this + */ public function setProperties(array $properties) { $this->properties = $properties; @@ -160,12 +214,24 @@ public function setProperties(array $properties) return $this; } + /** + * Gets the properties to define when creating the service. + * + * @return array + */ public function getProperties() { return $this->properties; } - public function setProperty($name, $value) + /** + * Sets a specific property. + * + * @param mixed $value + * + * @return $this + */ + public function setProperty(string $name, $value) { $this->properties[$name] = $value; @@ -177,7 +243,7 @@ public function setProperty($name, $value) * * @param mixed $argument An argument * - * @return Definition The current instance + * @return $this */ public function addArgument($argument) { @@ -187,19 +253,27 @@ public function addArgument($argument) } /** - * Sets a specific argument. + * Replaces a specific argument. * - * @param int $index - * @param mixed $argument + * @param int|string $index + * @param mixed $argument * - * @return Definition The current instance + * @return $this * * @throws OutOfBoundsException When the replaced argument does not exist */ public function replaceArgument($index, $argument) { - if ($index < 0 || $index > count($this->arguments) - 1) { - throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1)); + if (0 === \count($this->arguments)) { + throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.'); + } + + if (\is_int($index) && ($index < 0 || $index > \count($this->arguments) - 1)) { + throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, \count($this->arguments) - 1)); + } + + if (!\array_key_exists($index, $this->arguments)) { + throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index)); } $this->arguments[$index] = $argument; @@ -207,6 +281,21 @@ public function replaceArgument($index, $argument) return $this; } + /** + * Sets a specific argument. + * + * @param int|string $key + * @param mixed $value + * + * @return $this + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + /** * Gets the arguments to pass to the service constructor/factory method. * @@ -220,7 +309,7 @@ public function getArguments() /** * Gets an argument to pass to the service constructor/factory method. * - * @param int $index + * @param int|string $index * * @return mixed The argument value * @@ -228,8 +317,8 @@ public function getArguments() */ public function getArgument($index) { - if ($index < 0 || $index > count($this->arguments) - 1) { - throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1)); + if (!\array_key_exists($index, $this->arguments)) { + throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index)); } return $this->arguments[$index]; @@ -238,15 +327,13 @@ public function getArgument($index) /** * Sets the methods to call after service initialization. * - * @param array $calls An array of method calls - * - * @return Definition The current instance + * @return $this */ - public function setMethodCalls(array $calls = array()) + public function setMethodCalls(array $calls = []) { - $this->calls = array(); + $this->calls = []; foreach ($calls as $call) { - $this->addMethodCall($call[0], $call[1]); + $this->addMethodCall($call[0], $call[1], $call[2] ?? false); } return $this; @@ -255,19 +342,20 @@ public function setMethodCalls(array $calls = array()) /** * Adds a method to call after service initialization. * - * @param string $method The method name to call - * @param array $arguments An array of arguments to pass to the method call + * @param string $method The method name to call + * @param array $arguments An array of arguments to pass to the method call + * @param bool $returnsClone Whether the call returns the service instance or not * - * @return Definition The current instance + * @return $this * * @throws InvalidArgumentException on empty $method param */ - public function addMethodCall($method, array $arguments = array()) + public function addMethodCall(string $method, array $arguments = [], bool $returnsClone = false) { if (empty($method)) { - throw new InvalidArgumentException(sprintf('Method name cannot be empty.')); + throw new InvalidArgumentException('Method name cannot be empty.'); } - $this->calls[] = array($method, $arguments); + $this->calls[] = $returnsClone ? [$method, $arguments, true] : [$method, $arguments]; return $this; } @@ -275,16 +363,13 @@ public function addMethodCall($method, array $arguments = array()) /** * Removes a method to call after service initialization. * - * @param string $method The method name to remove - * - * @return Definition The current instance + * @return $this */ - public function removeMethodCall($method) + public function removeMethodCall(string $method) { foreach ($this->calls as $i => $call) { if ($call[0] === $method) { unset($this->calls[$i]); - break; } } @@ -294,11 +379,9 @@ public function removeMethodCall($method) /** * Check if the current definition has a given method to call after service initialization. * - * @param string $method The method name to search for - * * @return bool */ - public function hasMethodCall($method) + public function hasMethodCall(string $method) { foreach ($this->calls as $call) { if ($call[0] === $method) { @@ -320,11 +403,55 @@ public function getMethodCalls() } /** - * Sets tags for this definition. + * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. + * + * @param ChildDefinition[] $instanceof * - * @param array $tags + * @return $this + */ + public function setInstanceofConditionals(array $instanceof) + { + $this->instanceof = $instanceof; + + return $this; + } + + /** + * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. + * + * @return ChildDefinition[] + */ + public function getInstanceofConditionals() + { + return $this->instanceof; + } + + /** + * Sets whether or not instanceof conditionals should be prepended with a global set. + * + * @return $this + */ + public function setAutoconfigured(bool $autoconfigured) + { + $this->changes['autoconfigured'] = true; + + $this->autoconfigured = $autoconfigured; + + return $this; + } + + /** + * @return bool + */ + public function isAutoconfigured() + { + return $this->autoconfigured; + } + + /** + * Sets tags for this definition. * - * @return Definition the current instance + * @return $this */ public function setTags(array $tags) { @@ -346,24 +473,19 @@ public function getTags() /** * Gets a tag by name. * - * @param string $name The tag name - * * @return array An array of attributes */ - public function getTag($name) + public function getTag(string $name) { - return isset($this->tags[$name]) ? $this->tags[$name] : array(); + return $this->tags[$name] ?? []; } /** * Adds a tag for this definition. * - * @param string $name The tag name - * @param array $attributes An array of attributes - * - * @return Definition The current instance + * @return $this */ - public function addTag($name, array $attributes = array()) + public function addTag(string $name, array $attributes = []) { $this->tags[$name][] = $attributes; @@ -373,11 +495,9 @@ public function addTag($name, array $attributes = array()) /** * Whether this definition has a tag with the given name. * - * @param string $name - * * @return bool */ - public function hasTag($name) + public function hasTag(string $name) { return isset($this->tags[$name]); } @@ -385,11 +505,9 @@ public function hasTag($name) /** * Clears all tags for a given name. * - * @param string $name The tag name - * - * @return Definition + * @return $this */ - public function clearTag($name) + public function clearTag(string $name) { unset($this->tags[$name]); @@ -399,11 +517,11 @@ public function clearTag($name) /** * Clears the tags for this definition. * - * @return Definition The current instance + * @return $this */ public function clearTags() { - $this->tags = array(); + $this->tags = []; return $this; } @@ -411,12 +529,12 @@ public function clearTags() /** * Sets a file to require before creating the service. * - * @param string $file A full pathname to include - * - * @return Definition The current instance + * @return $this */ - public function setFile($file) + public function setFile(?string $file) { + $this->changes['file'] = true; + $this->file = $file; return $this; @@ -435,13 +553,13 @@ public function getFile() /** * Sets if the service must be shared or not. * - * @param bool $shared Whether the service must be shared or not - * - * @return Definition The current instance + * @return $this */ - public function setShared($shared) + public function setShared(bool $shared) { - $this->shared = (bool) $shared; + $this->changes['shared'] = true; + + $this->shared = $shared; return $this; } @@ -459,13 +577,13 @@ public function isShared() /** * Sets the visibility of this service. * - * @param bool $boolean - * - * @return Definition The current instance + * @return $this */ - public function setPublic($boolean) + public function setPublic(bool $boolean) { - $this->public = (bool) $boolean; + $this->changes['public'] = true; + + $this->public = $boolean; return $this; } @@ -481,15 +599,39 @@ public function isPublic() } /** - * Sets the lazy flag of this service. + * Sets if this service is private. + * + * @return $this + * + * @deprecated since Symfony 5.2, use setPublic() instead + */ + public function setPrivate(bool $boolean) + { + trigger_deprecation('symfony/dependency-injection', '5.2', 'The "%s()" method is deprecated, use "setPublic()" instead.', __METHOD__); + + return $this->setPublic(!$boolean); + } + + /** + * Whether this service is private. * - * @param bool $lazy + * @return bool + */ + public function isPrivate() + { + return !$this->public; + } + + /** + * Sets the lazy flag of this service. * - * @return Definition The current instance + * @return $this */ - public function setLazy($lazy) + public function setLazy(bool $lazy) { - $this->lazy = (bool) $lazy; + $this->changes['lazy'] = true; + + $this->lazy = $lazy; return $this; } @@ -508,13 +650,15 @@ public function isLazy() * Sets whether this definition is synthetic, that is not constructed by the * container, but dynamically injected. * - * @param bool $boolean - * - * @return Definition the current instance + * @return $this */ - public function setSynthetic($boolean) + public function setSynthetic(bool $boolean) { - $this->synthetic = (bool) $boolean; + $this->synthetic = $boolean; + + if (!isset($this->changes['public'])) { + $this->setPublic(true); + } return $this; } @@ -534,13 +678,11 @@ public function isSynthetic() * Whether this definition is abstract, that means it merely serves as a * template for other definitions. * - * @param bool $boolean - * - * @return Definition the current instance + * @return $this */ - public function setAbstract($boolean) + public function setAbstract(bool $boolean) { - $this->abstract = (bool) $boolean; + $this->abstract = $boolean; return $this; } @@ -560,28 +702,48 @@ public function isAbstract() * Whether this definition is deprecated, that means it should not be called * anymore. * - * @param bool $status - * @param string $template Template message to use if the definition is deprecated + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The deprecation message to use * - * @return Definition the current instance + * @return $this * - * @throws InvalidArgumentException When the message template is invalid. + * @throws InvalidArgumentException when the message template is invalid */ - public function setDeprecated($status = true, $template = null) + public function setDeprecated(/* string $package, string $version, string $message */) { - if (null !== $template) { - if (preg_match('#[\r\n]|\*/#', $template)) { + $args = \func_get_args(); + + if (\func_num_args() < 3) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + $status = $args[0] ?? true; + + if (!$status) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Passing a null message to un-deprecate a node is deprecated.'); + } + + $message = (string) ($args[1] ?? null); + $package = $version = ''; + } else { + $status = true; + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) $args[2]; + } + + if ('' !== $message) { + if (preg_match('#[\r\n]|\*/#', $message)) { throw new InvalidArgumentException('Invalid characters found in deprecation template.'); } - if (false === strpos($template, '%service_id%')) { + if (!str_contains($message, '%service_id%')) { throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.'); } - - $this->deprecationTemplate = $template; } - $this->deprecated = (bool) $status; + $this->changes['deprecated'] = true; + $this->deprecation = $status ? ['package' => $package, 'version' => $version, 'message' => $message ?: self::DEFAULT_DEPRECATION_TEMPLATE] : []; return $this; } @@ -594,32 +756,52 @@ public function setDeprecated($status = true, $template = null) */ public function isDeprecated() { - return $this->deprecated; + return (bool) $this->deprecation; } /** * Message to use if this definition is deprecated. * + * @deprecated since Symfony 5.1, use "getDeprecation()" instead. + * * @param string $id Service id relying on this definition * * @return string */ - public function getDeprecationMessage($id) + public function getDeprecationMessage(string $id) { - return str_replace('%service_id%', $id, $this->deprecationTemplate); + trigger_deprecation('symfony/dependency-injection', '5.1', 'The "%s()" method is deprecated, use "getDeprecation()" instead.', __METHOD__); + + return $this->getDeprecation($id)['message']; + } + + /** + * @param string $id Service id relying on this definition + */ + public function getDeprecation(string $id): array + { + return [ + 'package' => $this->deprecation['package'], + 'version' => $this->deprecation['version'], + 'message' => str_replace('%service_id%', $id, $this->deprecation['message']), + ]; } /** * Sets a configurator to call after the service is fully initialized. * - * @param string|array $configurator A PHP callable + * @param string|array|Reference|null $configurator A PHP function, reference or an array containing a class/Reference and a method to call * - * @return Definition The current instance + * @return $this */ public function setConfigurator($configurator) { - if (is_string($configurator) && strpos($configurator, '::') !== false) { + $this->changes['configurator'] = true; + + if (\is_string($configurator) && str_contains($configurator, '::')) { $configurator = explode('::', $configurator, 2); + } elseif ($configurator instanceof Reference) { + $configurator = [$configurator, '__invoke']; } $this->configurator = $configurator; @@ -630,31 +812,13 @@ public function setConfigurator($configurator) /** * Gets the configurator to call after the service is fully initialized. * - * @return callable|null The PHP callable to call + * @return callable|array|null */ public function getConfigurator() { return $this->configurator; } - /** - * Sets types that will default to this definition. - * - * @param string[] $types - * - * @return Definition The current instance - */ - public function setAutowiringTypes(array $types) - { - $this->autowiringTypes = array(); - - foreach ($types as $type) { - $this->autowiringTypes[$type] = true; - } - - return $this; - } - /** * Is the definition autowired? * @@ -666,66 +830,93 @@ public function isAutowired() } /** - * Sets autowired. - * - * @param bool $autowired + * Enables/disables autowiring. * - * @return Definition The current instance + * @return $this */ - public function setAutowired($autowired) + public function setAutowired(bool $autowired) { + $this->changes['autowired'] = true; + $this->autowired = $autowired; return $this; } /** - * Gets autowiring types that will default to this definition. + * Gets bindings. * - * @return string[] + * @return array|BoundArgument[] */ - public function getAutowiringTypes() + public function getBindings() { - return array_keys($this->autowiringTypes); + return $this->bindings; } /** - * Adds a type that will default to this definition. + * Sets bindings. * - * @param string $type + * Bindings map $named or FQCN arguments to values that should be + * injected in the matching parameters (of the constructor, of methods + * called and of controller actions). * - * @return Definition The current instance + * @return $this */ - public function addAutowiringType($type) + public function setBindings(array $bindings) { - $this->autowiringTypes[$type] = true; + foreach ($bindings as $key => $binding) { + if (0 < strpos($key, '$') && $key !== $k = preg_replace('/[ \t]*\$/', ' $', $key)) { + unset($bindings[$key]); + $bindings[$key = $k] = $binding; + } + if (!$binding instanceof BoundArgument) { + $bindings[$key] = new BoundArgument($binding); + } + } + + $this->bindings = $bindings; return $this; } /** - * Removes a type. + * Add an error that occurred when building this Definition. * - * @param string $type + * @param string|\Closure|self $error * - * @return Definition The current instance + * @return $this */ - public function removeAutowiringType($type) + public function addError($error) { - unset($this->autowiringTypes[$type]); + if ($error instanceof self) { + $this->errors = array_merge($this->errors, $error->errors); + } else { + $this->errors[] = $error; + } return $this; } /** - * Will this definition default for the given type? - * - * @param string $type + * Returns any errors that occurred while building this Definition. * - * @return bool + * @return array */ - public function hasAutowiringType($type) + public function getErrors() + { + foreach ($this->errors as $i => $error) { + if ($error instanceof \Closure) { + $this->errors[$i] = (string) $error(); + } elseif (!\is_string($error)) { + $this->errors[$i] = (string) $error; + } + } + + return $this->errors; + } + + public function hasErrors(): bool { - return isset($this->autowiringTypes[$type]); + return (bool) $this->errors; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/DefinitionDecorator.php b/tests/integration/vendor/symfony/dependency-injection/DefinitionDecorator.php deleted file mode 100644 index f17657a3a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/DefinitionDecorator.php +++ /dev/null @@ -1,199 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection; - -use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; - -/** - * This definition decorates another definition. - * - * @author Johannes M. Schmitt - */ -class DefinitionDecorator extends Definition -{ - private $parent; - private $changes = array(); - - /** - * @param string $parent The id of Definition instance to decorate - */ - public function __construct($parent) - { - parent::__construct(); - - $this->parent = $parent; - } - - /** - * Returns the Definition being decorated. - * - * @return string - */ - public function getParent() - { - return $this->parent; - } - - /** - * Returns all changes tracked for the Definition object. - * - * @return array An array of changes for this Definition - */ - public function getChanges() - { - return $this->changes; - } - - /** - * {@inheritdoc} - */ - public function setClass($class) - { - $this->changes['class'] = true; - - return parent::setClass($class); - } - - /** - * {@inheritdoc} - */ - public function setFactory($callable) - { - $this->changes['factory'] = true; - - return parent::setFactory($callable); - } - - /** - * {@inheritdoc} - */ - public function setConfigurator($callable) - { - $this->changes['configurator'] = true; - - return parent::setConfigurator($callable); - } - - /** - * {@inheritdoc} - */ - public function setFile($file) - { - $this->changes['file'] = true; - - return parent::setFile($file); - } - - /** - * {@inheritdoc} - */ - public function setPublic($boolean) - { - $this->changes['public'] = true; - - return parent::setPublic($boolean); - } - - /** - * {@inheritdoc} - */ - public function setLazy($boolean) - { - $this->changes['lazy'] = true; - - return parent::setLazy($boolean); - } - - /** - * {@inheritdoc} - */ - public function setDecoratedService($id, $renamedId = null, $priority = 0) - { - $this->changes['decorated_service'] = true; - - return parent::setDecoratedService($id, $renamedId, $priority); - } - - /** - * {@inheritdoc} - */ - public function setDeprecated($boolean = true, $template = null) - { - $this->changes['deprecated'] = true; - - return parent::setDeprecated($boolean, $template); - } - - /** - * {@inheritdoc} - */ - public function setAutowired($autowired) - { - $this->changes['autowire'] = true; - - return parent::setAutowired($autowired); - } - - /** - * Gets an argument to pass to the service constructor/factory method. - * - * If replaceArgument() has been used to replace an argument, this method - * will return the replacement value. - * - * @param int $index - * - * @return mixed The argument value - * - * @throws OutOfBoundsException When the argument does not exist - */ - public function getArgument($index) - { - if (array_key_exists('index_'.$index, $this->arguments)) { - return $this->arguments['index_'.$index]; - } - - $lastIndex = count(array_filter(array_keys($this->arguments), 'is_int')) - 1; - - if ($index < 0 || $index > $lastIndex) { - throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, $lastIndex)); - } - - return $this->arguments[$index]; - } - - /** - * You should always use this method when overwriting existing arguments - * of the parent definition. - * - * If you directly call setArguments() keep in mind that you must follow - * certain conventions when you want to overwrite the arguments of the - * parent definition, otherwise your arguments will only be appended. - * - * @param int $index - * @param mixed $value - * - * @return DefinitionDecorator the current instance - * - * @throws InvalidArgumentException when $index isn't an integer - */ - public function replaceArgument($index, $value) - { - if (!is_int($index)) { - throw new InvalidArgumentException('$index must be an integer.'); - } - - $this->arguments['index_'.$index] = $value; - - return $this; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/Dumper.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/Dumper.php index a39a5c744..e7407b0e2 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Dumper/Dumper.php +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/Dumper.php @@ -22,9 +22,6 @@ abstract class Dumper implements DumperInterface { protected $container; - /** - * @param ContainerBuilder $container The service container to dump - */ public function __construct(ContainerBuilder $container) { $this->container = $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/DumperInterface.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/DumperInterface.php index dd001e4ed..8abc19250 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Dumper/DumperInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/DumperInterface.php @@ -21,9 +21,7 @@ interface DumperInterface /** * Dumps the service container. * - * @param array $options An array of options - * - * @return string The representation of the service container + * @return string|array The representation of the service container */ - public function dump(array $options = array()); + public function dump(array $options = []); } diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php index 9e6b1904b..09836cd30 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php @@ -11,12 +11,13 @@ namespace Symfony\Component\DependencyInjection\Dumper; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Parameter; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; /** * GraphvizDumper dumps a service container as a graphviz file. @@ -31,14 +32,15 @@ class GraphvizDumper extends Dumper { private $nodes; private $edges; - private $options = array( - 'graph' => array('ratio' => 'compress'), - 'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'), - 'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5), - 'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'), - 'node.definition' => array('fillcolor' => '#eeeeee'), - 'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'), - ); + // All values should be strings + private $options = [ + 'graph' => ['ratio' => 'compress'], + 'node' => ['fontsize' => '11', 'fontname' => 'Arial', 'shape' => 'record'], + 'edge' => ['fontsize' => '9', 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => '0.5'], + 'node.instance' => ['fillcolor' => '#9999ff', 'style' => 'filled'], + 'node.definition' => ['fillcolor' => '#eeeeee'], + 'node.missing' => ['fillcolor' => '#ff9999', 'style' => 'filled'], + ]; /** * Dumps the service container as a graphviz graph. @@ -52,13 +54,11 @@ class GraphvizDumper extends Dumper * * node.definition: The default options for services that are defined via service definition instances * * node.missing: The default options for missing services * - * @param array $options An array of options - * * @return string The dot representation of the service container */ - public function dump(array $options = array()) + public function dump(array $options = []) { - foreach (array('graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing') as $key) { + foreach (['graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing'] as $key) { if (isset($options[$key])) { $this->options[$key] = array_merge($this->options[$key], $options[$key]); } @@ -66,7 +66,7 @@ public function dump(array $options = array()) $this->nodes = $this->findNodes(); - $this->edges = array(); + $this->edges = []; foreach ($this->container->getDefinitions() as $id => $definition) { $this->edges[$id] = array_merge( $this->findEdges($id, $definition->getArguments(), true, ''), @@ -84,12 +84,7 @@ public function dump(array $options = array()) return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__'); } - /** - * Returns all nodes. - * - * @return string A string representation of all nodes - */ - private function addNodes() + private function addNodes(): string { $code = ''; foreach ($this->nodes as $id => $node) { @@ -101,17 +96,12 @@ private function addNodes() return $code; } - /** - * Returns all edges. - * - * @return string A string representation of all edges - */ - private function addEdges() + private function addEdges(): string { $code = ''; foreach ($this->edges as $id => $edges) { foreach ($edges as $edge) { - $code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed'); + $code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"%s];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed', $edge['lazy'] ? ' color="#9999ff"' : ''); } } @@ -120,46 +110,48 @@ private function addEdges() /** * Finds all edges belonging to a specific service id. - * - * @param string $id The service id used to find edges - * @param array $arguments An array of arguments - * @param bool $required - * @param string $name - * - * @return array An array of edges */ - private function findEdges($id, array $arguments, $required, $name) + private function findEdges(string $id, array $arguments, bool $required, string $name, bool $lazy = false): array { - $edges = array(); + $edges = []; foreach ($arguments as $argument) { if ($argument instanceof Parameter) { $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null; - } elseif (is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) { + } elseif (\is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) { $argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null; } if ($argument instanceof Reference) { + $lazyEdge = $lazy; + if (!$this->container->has((string) $argument)) { - $this->nodes[(string) $argument] = array('name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']); + $this->nodes[(string) $argument] = ['name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']]; + } elseif ('service_container' !== (string) $argument) { + $lazyEdge = $lazy || $this->container->getDefinition((string) $argument)->isLazy(); } - $edges[] = array('name' => $name, 'required' => $required, 'to' => $argument); - } elseif (is_array($argument)) { - $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name)); + $edges[] = ['name' => $name, 'required' => $required, 'to' => $argument, 'lazy' => $lazyEdge]; + } elseif ($argument instanceof ArgumentInterface) { + $edges = array_merge($edges, $this->findEdges($id, $argument->getValues(), $required, $name, true)); + } elseif ($argument instanceof Definition) { + $edges = array_merge($edges, + $this->findEdges($id, $argument->getArguments(), $required, ''), + $this->findEdges($id, $argument->getProperties(), false, '') + ); + foreach ($argument->getMethodCalls() as $call) { + $edges = array_merge($edges, $this->findEdges($id, $call[1], false, $call[0].'()')); + } + } elseif (\is_array($argument)) { + $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name, $lazy)); } } return $edges; } - /** - * Finds all nodes. - * - * @return array An array of all nodes - */ - private function findNodes() + private function findNodes(): array { - $nodes = array(); + $nodes = []; $container = $this->cloneContainer(); @@ -175,25 +167,24 @@ private function findNodes() } catch (ParameterNotFoundException $e) { } - $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() ? 'filled' : 'dotted'))); + $nodes[$id] = ['class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], ['style' => $definition->isShared() ? 'filled' : 'dotted'])]; $container->setDefinition($id, new Definition('stdClass')); } foreach ($container->getServiceIds() as $id) { - if (array_key_exists($id, $container->getAliases())) { + if (\array_key_exists($id, $container->getAliases())) { continue; } if (!$container->hasDefinition($id)) { - $class = get_class('service_container' === $id ? $this->container : $container->get($id)); - $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => $this->options['node.instance']); + $nodes[$id] = ['class' => str_replace('\\', '\\\\', \get_class($container->get($id))), 'attributes' => $this->options['node.instance']]; } } return $nodes; } - private function cloneContainer() + private function cloneContainer(): ContainerBuilder { $parameterBag = new ParameterBag($this->container->getParameterBag()->all()); @@ -208,12 +199,7 @@ private function cloneContainer() return $container; } - /** - * Returns the start dot. - * - * @return string The string representation of a start dot - */ - private function startDot() + private function startDot(): string { return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n", $this->addOptions($this->options['graph']), @@ -222,26 +208,14 @@ private function startDot() ); } - /** - * Returns the end dot. - * - * @return string - */ - private function endDot() + private function endDot(): string { return "}\n"; } - /** - * Adds attributes. - * - * @param array $attributes An array of attributes - * - * @return string A comma separated list of attributes - */ - private function addAttributes(array $attributes) + private function addAttributes(array $attributes): string { - $code = array(); + $code = []; foreach ($attributes as $k => $v) { $code[] = sprintf('%s="%s"', $k, $v); } @@ -249,16 +223,9 @@ private function addAttributes(array $attributes) return $code ? ', '.implode(', ', $code) : ''; } - /** - * Adds options. - * - * @param array $options An array of options - * - * @return string A space separated list of options - */ - private function addOptions(array $options) + private function addOptions(array $options): string { - $code = array(); + $code = []; foreach ($options as $k => $v) { $code[] = sprintf('%s="%s"', $k, $v); } @@ -266,28 +233,14 @@ private function addOptions(array $options) return implode(' ', $code); } - /** - * Dotizes an identifier. - * - * @param string $id The identifier to dotize - * - * @return string A dotized string - */ - private function dotize($id) + private function dotize(string $id): string { - return strtolower(preg_replace('/\W/i', '_', $id)); + return preg_replace('/\W/i', '_', $id); } - /** - * Compiles an array of aliases for a specified service id. - * - * @param string $id A service id - * - * @return array An array of aliases - */ - private function getAliases($id) + private function getAliases(string $id): array { - $aliases = array(); + $aliases = []; foreach ($this->container->getAliases() as $alias => $origin) { if ($id == $origin) { $aliases[] = $alias; diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/PhpDumper.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/PhpDumper.php index f827ac013..da745850d 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Dumper/PhpDumper.php +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/PhpDumper.php @@ -11,20 +11,36 @@ namespace Symfony\Component\DependencyInjection\Dumper; -use Symfony\Component\DependencyInjection\Variable; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Composer\Autoload\ClassLoader; +use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphNode; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\EnvParameterException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; -use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\DependencyInjection\Loader\FileLoader; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\ErrorHandler\DebugClassLoader; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpKernel\Kernel; @@ -38,32 +54,45 @@ class PhpDumper extends Dumper { /** * Characters that might appear in the generated variable name as first character. - * - * @var string */ - const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; + public const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; /** * Characters that might appear in the generated variable name as any but the first character. - * - * @var string */ - const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; + public const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; - private $inlinedDefinitions; private $definitionVariables; private $referenceVariables; private $variableCount; - private $reservedVariables = array('instance', 'class'); + private $inlinedDefinitions; + private $serviceCalls; + private $reservedVariables = ['instance', 'class', 'this', 'container']; private $expressionLanguage; private $targetDirRegex; private $targetDirMaxMatches; private $docStar; private $serviceIdToMethodNameMap; private $usedMethodNames; + private $namespace; + private $asFiles; + private $hotPathTag; + private $preloadTags; + private $inlineFactories; + private $inlineRequires; + private $inlinedRequires = []; + private $circularReferences = []; + private $singleUsePrivateIds = []; + private $preload = []; + private $addThrow = false; + private $addGetService = false; + private $locatedIds = []; + private $serviceLocatorTag; + private $exportedVariables = []; + private $baseClass; /** - * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface + * @var ProxyDumper */ private $proxyDumper; @@ -72,15 +101,15 @@ class PhpDumper extends Dumper */ public function __construct(ContainerBuilder $container) { - parent::__construct($container); + if (!$container->isCompiled()) { + throw new LogicException('Cannot dump an uncompiled container.'); + } - $this->inlinedDefinitions = new \SplObjectStorage(); + parent::__construct($container); } /** * Sets the dumper to be used when dumping proxies in the generated container. - * - * @param ProxyDumper $proxyDumper */ public function setProxyDumper(ProxyDumper $proxyDumper) { @@ -95,78 +124,275 @@ public function setProxyDumper(ProxyDumper $proxyDumper) * * class: The class name * * base_class: The base class name * * namespace: The class namespace + * * as_files: To split the container in several files * - * @param array $options An array of options - * - * @return string A PHP class representing of the service container + * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set * * @throws EnvParameterException When an env var exists but has not been dumped */ - public function dump(array $options = array()) + public function dump(array $options = []) { + $this->locatedIds = []; $this->targetDirRegex = null; - $options = array_merge(array( + $this->inlinedRequires = []; + $this->exportedVariables = []; + $options = array_merge([ 'class' => 'ProjectServiceContainer', 'base_class' => 'Container', 'namespace' => '', + 'as_files' => false, 'debug' => true, - ), $options); + 'hot_path_tag' => 'container.hot_path', + 'preload_tags' => ['container.preload', 'container.no_preload'], + 'inline_factories_parameter' => 'container.dumper.inline_factories', + 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', + 'preload_classes' => [], + 'service_locator_tag' => 'container.service_locator', + 'build_time' => time(), + ], $options); + + $this->addThrow = $this->addGetService = false; + $this->namespace = $options['namespace']; + $this->asFiles = $options['as_files']; + $this->hotPathTag = $options['hot_path_tag']; + $this->preloadTags = $options['preload_tags']; + $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']); + $this->inlineRequires = $options['inline_class_loader_parameter'] && ($this->container->hasParameter($options['inline_class_loader_parameter']) ? $this->container->getParameter($options['inline_class_loader_parameter']) : (\PHP_VERSION_ID < 70400 || $options['debug'])); + $this->serviceLocatorTag = $options['service_locator_tag']; + + if (!str_starts_with($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { + $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); + $this->baseClass = $baseClass; + } elseif ('Container' === $baseClass) { + $this->baseClass = Container::class; + } else { + $this->baseClass = $baseClass; + } - $this->initializeMethodNamesMap($options['base_class']); + $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); + if ($this->getProxyDumper() instanceof NullDumper) { + (new AnalyzeServiceReferencesPass(true, false))->process($this->container); + try { + (new CheckCircularReferencesPass())->process($this->container); + } catch (ServiceCircularReferenceException $e) { + $path = $e->getPath(); + end($path); + $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge'; + + throw new ServiceCircularReferenceException($e->getServiceId(), $path); + } + } + + $this->analyzeReferences(); $this->docStar = $options['debug'] ? '*' : ''; - if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) { + if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) { // Build a regexp where the first root dirs are mandatory, // but every other sub-dir is optional up to the full path in $dir - // Mandate at least 2 root dirs and not more that 5 optional dirs. + // Mandate at least 1 root dir and not more than 5 optional dirs. - $dir = explode(DIRECTORY_SEPARATOR, realpath($dir)); - $i = count($dir); + $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir)); + $i = \count($dir); - if (3 <= $i) { + if (2 + (int) ('\\' === \DIRECTORY_SEPARATOR) <= $i) { $regex = ''; - $lastOptionalDir = $i > 8 ? $i - 5 : 3; + $lastOptionalDir = $i > 8 ? $i - 5 : (2 + (int) ('\\' === \DIRECTORY_SEPARATOR)); $this->targetDirMaxMatches = $i - $lastOptionalDir; while (--$i >= $lastOptionalDir) { - $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); + $regex = sprintf('(%s%s)?', preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); } do { - $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; + $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; } while (0 < --$i); - $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; + $this->targetDirRegex = '#(^|file://|[:;, \|\r\n])'.preg_quote($dir[0], '#').$regex.'#'; } } - $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']); + $proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null; - if ($this->container->isFrozen()) { - $code .= $this->addFrozenConstructor(); - $code .= $this->addFrozenCompile(); - $code .= $this->addIsFrozenMethod(); - } else { - $code .= $this->addConstructor(); + if ($options['preload_classes']) { + $this->preload = array_combine($options['preload_classes'], $options['preload_classes']); } - $code .= - $this->addServices(). - $this->addDefaultParametersMethod(). - $this->endClass(). - $this->addProxyClasses() + $code = + $this->startClass($options['class'], $baseClass). + $this->addServices($services). + $this->addDeprecatedAliases(). + $this->addDefaultParametersMethod() ; + + $proxyClasses = $proxyClasses ?? $this->generateProxyClasses(); + + if ($this->addGetService) { + $code = preg_replace( + "/(\r?\n\r?\n public function __construct.+?\\{\r?\n)/s", + "\n protected \$getService;$1 \$this->getService = \\Closure::fromCallable([\$this, 'getService']);\n", + $code, + 1 + ); + } + + if ($this->asFiles) { + $fileTemplate = <<docStar} + * @internal This class has been auto-generated by the Symfony Dependency Injection Component. + */ +class %s extends {$options['class']} +{%s} + +EOF; + $files = []; + $preloadedFiles = []; + $ids = $this->container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if ($ids = array_keys($ids)) { + sort($ids); + $c = "doExport($id)." => true,\n"; + } + $files['removed-ids.php'] = $c."];\n"; + } + + if (!$this->inlineFactories) { + foreach ($this->generateServiceFiles($services) as $file => [$c, $preload]) { + $files[$file] = sprintf($fileTemplate, substr($file, 0, -4), $c); + + if ($preload) { + $preloadedFiles[$file] = $file; + } + } + foreach ($proxyClasses as $file => $c) { + $files[$file] = "endClass(); + + if ($this->inlineFactories) { + foreach ($proxyClasses as $c) { + $code .= $c; + } + } + + $files[$options['class'].'.php'] = $code; + $preloadedFiles[$options['class'].'.php'] = $options['class'].'.php'; + $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx')); + $code = []; + + foreach ($files as $file => $c) { + $code["Container{$hash}/{$file}"] = substr_replace($c, "namespace ? "\nnamespace {$this->namespace};\n" : ''; + $time = $options['build_time']; + $id = hash('crc32', $hash.$time); + $this->asFiles = false; + + if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) { + $autoloadFile = trim($this->export($autoloadFile), '()\\'); + + $preloadedFiles = array_reverse($preloadedFiles); + $preloadedFiles = implode("';\nrequire __DIR__.'/", $preloadedFiles); + + $code[$options['class'].'.preload.php'] = <<= 7.4 when preloading is desired + +use Symfony\Component\DependencyInjection\Dumper\Preloader; + +if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) { + return; +} + +require $autoloadFile; +require __DIR__.'/$preloadedFiles'; + +\$classes = []; + +EOF; + + foreach ($this->preload as $class) { + if (!$class || str_contains($class, '$') || \in_array($class, ['int', 'float', 'string', 'bool', 'resource', 'object', 'array', 'null', 'callable', 'iterable', 'mixed', 'void'], true)) { + continue; + } + if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || (new \ReflectionClass($class))->isUserDefined()) { + $code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class); + } + } + + $code[$options['class'].'.preload.php'] .= <<<'EOF' + +Preloader::preload($classes); + +EOF; + } + + $code[$options['class'].'.php'] = << '$hash', + 'container.build_id' => '$id', + 'container.build_time' => $time, +], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); + +EOF; + } else { + $code .= $this->endClass(); + foreach ($proxyClasses as $c) { + $code .= $c; + } + } + $this->targetDirRegex = null; + $this->inlinedRequires = []; + $this->circularReferences = []; + $this->locatedIds = []; + $this->exportedVariables = []; + $this->preload = []; - $unusedEnvs = array(); + $unusedEnvs = []; foreach ($this->container->getEnvCounters() as $env => $use) { if (!$use) { $unusedEnvs[] = $env; } } if ($unusedEnvs) { - throw new EnvParameterException($unusedEnvs); + throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.'); } return $code; @@ -174,10 +400,8 @@ public function dump(array $options = array()) /** * Retrieves the currently set proxy dumper or instantiates one. - * - * @return ProxyDumper */ - private function getProxyDumper() + private function getProxyDumper(): ProxyDumper { if (!$this->proxyDumper) { $this->proxyDumper = new NullDumper(); @@ -186,263 +410,316 @@ private function getProxyDumper() return $this->proxyDumper; } - /** - * Generates Service local temp variables. - * - * @param string $cId - * @param string $definition - * - * @return string - */ - private function addServiceLocalTempVariables($cId, $definition) + private function analyzeReferences() { - static $template = " \$%s = %s;\n"; + (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); + $checkedNodes = []; + $this->circularReferences = []; + $this->singleUsePrivateIds = []; + foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + if (!$node->getValue() instanceof Definition) { + continue; + } - $localDefinitions = array_merge( - array($definition), - $this->getInlinedDefinitions($definition) - ); + if ($this->isSingleUsePrivateNode($node)) { + $this->singleUsePrivateIds[$id] = $id; + } - $calls = $behavior = array(); - foreach ($localDefinitions as $iDefinition) { - $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior); - $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior); - $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior); - $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior); - $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior); + $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes); } - $code = ''; - foreach ($calls as $id => $callCount) { - if ('service_container' === $id || $id === $cId) { + $this->container->getCompiler()->getServiceReferenceGraph()->clear(); + $this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences); + } + + private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$loops = [], array $path = [], bool $byConstructor = true): void + { + $path[$sourceId] = $byConstructor; + $checkedNodes[$sourceId] = true; + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $id = $node->getId(); + if ($sourceId === $id || !$node->getValue() instanceof Definition || $edge->isLazy() || $edge->isWeak()) { continue; } - if ($callCount > 1) { - $name = $this->getNextVariableName(); - $this->referenceVariables[$id] = new Variable($name); + if (isset($path[$id])) { + $loop = null; + $loopByConstructor = $edge->isReferencedByConstructor(); + $pathInLoop = [$id, []]; + foreach ($path as $k => $pathByConstructor) { + if (null !== $loop) { + $loop[] = $k; + $pathInLoop[1][$k] = $pathByConstructor; + $loops[$k][] = &$pathInLoop; + $loopByConstructor = $loopByConstructor && $pathByConstructor; + } elseif ($k === $id) { + $loop = []; + } + } + $this->addCircularReferences($id, $loop, $loopByConstructor); + } elseif (!isset($checkedNodes[$id])) { + $this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $loops, $path, $edge->isReferencedByConstructor()); + } elseif (isset($loops[$id])) { + // we already had detected loops for this edge + // let's check if we have a common ancestor in one of the detected loops + foreach ($loops[$id] as [$first, $loopPath]) { + if (!isset($path[$first])) { + continue; + } + // We have a common ancestor, let's fill the current path + $fillPath = null; + foreach ($loopPath as $k => $pathByConstructor) { + if (null !== $fillPath) { + $fillPath[$k] = $pathByConstructor; + } elseif ($k === $id) { + $fillPath = $path; + $fillPath[$k] = $pathByConstructor; + } + } - if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) { - $code .= sprintf($template, $name, $this->getServiceCall($id)); - } else { - $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE))); + // we can now build the loop + $loop = null; + $loopByConstructor = $edge->isReferencedByConstructor(); + foreach ($fillPath as $k => $pathByConstructor) { + if (null !== $loop) { + $loop[] = $k; + $loopByConstructor = $loopByConstructor && $pathByConstructor; + } elseif ($k === $first) { + $loop = []; + } + } + $this->addCircularReferences($first, $loop, true); + break; } } } - - if ('' !== $code) { - $code .= "\n"; - } - - return $code; + unset($path[$sourceId]); } - /** - * Generates code for the proxies to be attached after the container class. - * - * @return string - */ - private function addProxyClasses() + private function addCircularReferences(string $sourceId, array $currentPath, bool $byConstructor) { - /* @var $definitions Definition[] */ - $definitions = array_filter( - $this->container->getDefinitions(), - array($this->getProxyDumper(), 'isProxyCandidate') - ); - $code = ''; - $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); - - foreach ($definitions as $definition) { - $proxyCode = "\n".$this->getProxyDumper()->getProxyCode($definition); - if ($strip) { - $proxyCode = "circularReferences[$parentId][$currentId])) { + $this->circularReferences[$parentId][$currentId] = $byConstructor; } - $code .= $proxyCode; - } - return $code; + $currentId = $parentId; + } } - /** - * Generates the require_once statement for service includes. - * - * @param string $id The service id - * @param Definition $definition - * - * @return string - */ - private function addServiceInclude($id, $definition) + private function collectLineage(string $class, array &$lineage) { - $template = " require_once %s;\n"; - $code = ''; + if (isset($lineage[$class])) { + return; + } + if (!$r = $this->container->getReflectionClass($class, false)) { + return; + } + if (is_a($class, $this->baseClass, true)) { + return; + } + $file = $r->getFileName(); + if (str_ends_with($file, ') : eval()\'d code')) { + $file = substr($file, 0, strrpos($file, '(', -17)); + } + if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) { + return; + } - if (null !== $file = $definition->getFile()) { - $code .= sprintf($template, $this->dumpValue($file)); + $lineage[$class] = substr($exportedFile, 1, -1); + + if ($parent = $r->getParentClass()) { + $this->collectLineage($parent->name, $lineage); } - foreach ($this->getInlinedDefinitions($definition) as $definition) { - if (null !== $file = $definition->getFile()) { - $code .= sprintf($template, $this->dumpValue($file)); - } + foreach ($r->getInterfaces() as $parent) { + $this->collectLineage($parent->name, $lineage); } - if ('' !== $code) { - $code .= "\n"; + foreach ($r->getTraits() as $parent) { + $this->collectLineage($parent->name, $lineage); } - return $code; + unset($lineage[$class]); + $lineage[$class] = substr($exportedFile, 1, -1); } - /** - * Generates the inline definition of a service. - * - * @param string $id - * @param Definition $definition - * - * @return string - * - * @throws RuntimeException When the factory definition is incomplete - * @throws ServiceCircularReferenceException When a circular reference is detected - */ - private function addServiceInlinedDefinitions($id, $definition) + private function generateProxyClasses(): array { - $code = ''; - $variableMap = $this->definitionVariables; - $nbOccurrences = new \SplObjectStorage(); - $processed = new \SplObjectStorage(); - $inlinedDefinitions = $this->getInlinedDefinitions($definition); - - foreach ($inlinedDefinitions as $definition) { - if (false === $nbOccurrences->contains($definition)) { - $nbOccurrences->offsetSet($definition, 1); - } else { - $i = $nbOccurrences->offsetGet($definition); - $nbOccurrences->offsetSet($definition, $i + 1); + $proxyClasses = []; + $alreadyGenerated = []; + $definitions = $this->container->getDefinitions(); + $strip = '' === $this->docStar && method_exists(Kernel::class, 'stripComments'); + $proxyDumper = $this->getProxyDumper(); + ksort($definitions); + foreach ($definitions as $definition) { + if (!$proxyDumper->isProxyCandidate($definition)) { + continue; } - } - - foreach ($inlinedDefinitions as $sDefinition) { - if ($processed->contains($sDefinition)) { + if (isset($alreadyGenerated[$class = $definition->getClass()])) { + continue; + } + $alreadyGenerated[$class] = true; + // register class' reflector for resource tracking + $this->container->getReflectionClass($class); + if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) { continue; } - $processed->offsetSet($sDefinition); - - $class = $this->dumpValue($sDefinition->getClass()); - if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { - $name = $this->getNextVariableName(); - $variableMap->offsetSet($sDefinition, new Variable($name)); - - // a construct like: - // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); - // this is an indication for a wrong implementation, you can circumvent this problem - // by setting up your service structure like this: - // $b = new ServiceB(); - // $a = new ServiceA(ServiceB $b); - // $b->setServiceA(ServiceA $a); - if ($this->hasReference($id, $sDefinition->getArguments())) { - throw new ServiceCircularReferenceException($id, array($id)); - } - $code .= $this->addNewInstance($sDefinition, '$'.$name, ' = ', $id); + if ($this->inlineRequires) { + $lineage = []; + $this->collectLineage($class, $lineage); - if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) { - $code .= $this->addServiceProperties(null, $sDefinition, $name); - $code .= $this->addServiceMethodCalls(null, $sDefinition, $name); - $code .= $this->addServiceConfigurator(null, $sDefinition, $name); + $code = ''; + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + if ($this->inlineFactories) { + $this->inlinedRequires[$file] = true; + } + $code .= sprintf("include_once %s;\n", $file); } - $code .= "\n"; + $proxyCode = $code.$proxyCode; + } + + if ($strip) { + $proxyCode = "inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1]; + + if ($this->asFiles || $this->namespace) { + $proxyCode .= "\nif (!\\class_exists('$proxyClass', false)) {\n \\class_alias(__NAMESPACE__.'\\\\$proxyClass', '$proxyClass', false);\n}\n"; } + + $proxyClasses[$proxyClass.'.php'] = $proxyCode; } - return $code; + return $proxyClasses; } - /** - * Adds the service return statement. - * - * @param string $id Service id - * @param Definition $definition - * - * @return string - */ - private function addServiceReturn($id, $definition) + private function addServiceInclude(string $cId, Definition $definition): string { - if ($this->isSimpleInstance($id, $definition)) { - return " }\n"; + $code = ''; + + if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) { + $lineage = []; + foreach ($this->inlinedDefinitions as $def) { + if (!$def->isDeprecated()) { + foreach ($this->getClasses($def, $cId) as $class) { + $this->collectLineage($class, $lineage); + } + } + } + + foreach ($this->serviceCalls as $id => [$callCount, $behavior]) { + if ('service_container' !== $id && $id !== $cId + && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior + && $this->container->has($id) + && $this->isTrivialInstance($def = $this->container->findDefinition($id)) + ) { + foreach ($this->getClasses($def, $cId) as $class) { + $this->collectLineage($class, $lineage); + } + } + } + + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + $code .= sprintf(" include_once %s;\n", $file); + } + } + + foreach ($this->inlinedDefinitions as $def) { + if ($file = $def->getFile()) { + $file = $this->dumpValue($file); + $file = '(' === $file[0] ? substr($file, 1, -1) : $file; + $code .= sprintf(" include_once %s;\n", $file); + } + } + + if ('' !== $code) { + $code .= "\n"; } - return "\n return \$instance;\n }\n"; + return $code; } /** - * Generates the service instance. - * - * @param string $id - * @param Definition $definition - * - * @return string - * * @throws InvalidArgumentException * @throws RuntimeException */ - private function addServiceInstance($id, Definition $definition) + private function addServiceInstance(string $id, Definition $definition, bool $isSimpleInstance): string { - $class = $definition->getClass(); - - if ('\\' === substr($class, 0, 1)) { - $class = substr($class, 1); - } - - $class = $this->dumpValue($class); + $class = $this->dumpValue($definition->getClass()); - if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + if (str_starts_with($class, "'") && !str_contains($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); } - $simple = $this->isSimpleInstance($id, $definition); $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); $instantiation = ''; - if (!$isProxyCandidate && $definition->isShared()) { - $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); - } elseif (!$simple) { + $lastWitherIndex = null; + foreach ($definition->getMethodCalls() as $k => $call) { + if ($call[2] ?? false) { + $lastWitherIndex = $k; + } + } + + if (!$isProxyCandidate && $definition->isShared() && !isset($this->singleUsePrivateIds[$id]) && null === $lastWitherIndex) { + $instantiation = sprintf('$this->%s[%s] = %s', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $this->doExport($id), $isSimpleInstance ? '' : '$instance'); + } elseif (!$isSimpleInstance) { $instantiation = '$instance'; } $return = ''; - if ($simple) { + if ($isSimpleInstance) { $return = 'return '; } else { $instantiation .= ' = '; } - $code = $this->addNewInstance($definition, $return, $instantiation, $id); - - if (!$simple) { - $code .= "\n"; - } - - return $code; + return $this->addNewInstance($definition, ' '.$return.$instantiation, $id); } - /** - * Checks if the definition is a simple instance. - * - * @param string $id - * @param Definition $definition - * - * @return bool - */ - private function isSimpleInstance($id, Definition $definition) + private function isTrivialInstance(Definition $definition): bool { - foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) { - if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) { + if ($definition->hasErrors()) { + return true; + } + if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) { + return false; + } + if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < \count($definition->getArguments())) { + return false; + } + + foreach ($definition->getArguments() as $arg) { + if (!$arg || $arg instanceof Parameter) { continue; } - - if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) { + if (\is_array($arg) && 3 >= \count($arg)) { + foreach ($arg as $k => $v) { + if ($this->dumpValue($k) !== $this->dumpValue($k, false)) { + return false; + } + if (!$v || $v instanceof Parameter) { + continue; + } + if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) { + continue; + } + if (!is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) { + return false; + } + } + } elseif ($arg instanceof Reference && $this->container->has($id = (string) $arg) && $this->container->findDefinition($id)->isSynthetic()) { + continue; + } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) { return false; } } @@ -450,31 +727,38 @@ private function isSimpleInstance($id, Definition $definition) return true; } - /** - * Adds method calls to a service definition. - * - * @param string $id - * @param Definition $definition - * @param string $variableName - * - * @return string - */ - private function addServiceMethodCalls($id, Definition $definition, $variableName = 'instance') + private function addServiceMethodCalls(Definition $definition, string $variableName, ?string $sharedNonLazyId): string { + $lastWitherIndex = null; + foreach ($definition->getMethodCalls() as $k => $call) { + if ($call[2] ?? false) { + $lastWitherIndex = $k; + } + } + $calls = ''; - foreach ($definition->getMethodCalls() as $call) { - $arguments = array(); + foreach ($definition->getMethodCalls() as $k => $call) { + $arguments = []; foreach ($call[1] as $value) { $arguments[] = $this->dumpValue($value); } - $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments))); + $witherAssignation = ''; + + if ($call[2] ?? false) { + if (null !== $sharedNonLazyId && $lastWitherIndex === $k) { + $witherAssignation = sprintf('$this->%s[\'%s\'] = ', $definition->isPublic() ? 'services' : 'privates', $sharedNonLazyId); + } + $witherAssignation .= sprintf('$%s = ', $variableName); + } + + $calls .= $this->wrapServiceConditionals($call[1], sprintf(" %s\$%s->%s(%s);\n", $witherAssignation, $variableName, $call[0], implode(', ', $arguments))); } return $calls; } - private function addServiceProperties($id, Definition $definition, $variableName = 'instance') + private function addServiceProperties(Definition $definition, string $variableName = 'instance'): string { $code = ''; foreach ($definition->getProperties() as $name => $value) { @@ -484,546 +768,758 @@ private function addServiceProperties($id, Definition $definition, $variableName return $code; } - /** - * Generates the inline definition setup. - * - * @param string $id - * @param Definition $definition - * - * @return string - * - * @throws ServiceCircularReferenceException when the container contains a circular reference - */ - private function addServiceInlinedDefinitionsSetup($id, Definition $definition) + private function addServiceConfigurator(Definition $definition, string $variableName = 'instance'): string { - $this->referenceVariables[$id] = new Variable('instance'); + if (!$callable = $definition->getConfigurator()) { + return ''; + } - $code = ''; - $processed = new \SplObjectStorage(); - foreach ($this->getInlinedDefinitions($definition) as $iDefinition) { - if ($processed->contains($iDefinition)) { - continue; - } - $processed->offsetSet($iDefinition); - - if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) { - continue; - } - - // if the instance is simple, the return statement has already been generated - // so, the only possible way to get there is because of a circular reference - if ($this->isSimpleInstance($id, $definition)) { - throw new ServiceCircularReferenceException($id, array($id)); - } - - $name = (string) $this->definitionVariables->offsetGet($iDefinition); - $code .= $this->addServiceProperties(null, $iDefinition, $name); - $code .= $this->addServiceMethodCalls(null, $iDefinition, $name); - $code .= $this->addServiceConfigurator(null, $iDefinition, $name); - } - - if ('' !== $code) { - $code .= "\n"; - } - - return $code; - } - - /** - * Adds configurator definition. - * - * @param string $id - * @param Definition $definition - * @param string $variableName - * - * @return string - */ - private function addServiceConfigurator($id, Definition $definition, $variableName = 'instance') - { - if (!$callable = $definition->getConfigurator()) { - return ''; - } - - if (is_array($callable)) { + if (\is_array($callable)) { if ($callable[0] instanceof Reference - || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { + || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0])) + ) { return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } $class = $this->dumpValue($callable[0]); - // If the class is a string we can optimize call_user_func away - if (0 === strpos($class, "'") && false === strpos($class, '$')) { + // If the class is a string we can optimize away + if (str_starts_with($class, "'") && !str_contains($class, '$')) { return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName); } - if (0 === strpos($class, 'new ')) { + if (str_starts_with($class, 'new ')) { return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } - return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + return sprintf(" [%s, '%s'](\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } return sprintf(" %s(\$%s);\n", $callable, $variableName); } - /** - * Adds a service. - * - * @param string $id - * @param Definition $definition - * - * @return string - */ - private function addService($id, Definition $definition) + private function addService(string $id, Definition $definition): array { $this->definitionVariables = new \SplObjectStorage(); - $this->referenceVariables = array(); + $this->referenceVariables = []; $this->variableCount = 0; + $this->referenceVariables[$id] = new Variable('instance'); - $return = array(); + $return = []; - if ($definition->isSynthetic()) { - $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically'; - } elseif ($class = $definition->getClass()) { - $class = $this->container->resolveEnvPlaceholders($class); - $return[] = sprintf('@return %s A %s instance', 0 === strpos($class, '%') ? 'object' : '\\'.ltrim($class, '\\'), ltrim($class, '\\')); + if ($class = $definition->getClass()) { + $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class); + $return[] = sprintf(str_starts_with($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); } elseif ($definition->getFactory()) { $factory = $definition->getFactory(); - if (is_string($factory)) { + if (\is_string($factory)) { $return[] = sprintf('@return object An instance returned by %s()', $factory); - } elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { - if (is_string($factory[0]) || $factory[0] instanceof Reference) { - $return[] = sprintf('@return object An instance returned by %s::%s()', (string) $factory[0], $factory[1]); - } elseif ($factory[0] instanceof Definition) { - $return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]); - } + } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { + $class = $factory[0] instanceof Definition ? $factory[0]->getClass() : (string) $factory[0]; + $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class); + $return[] = sprintf('@return object An instance returned by %s::%s()', $class, $factory[1]); } } if ($definition->isDeprecated()) { - if ($return && 0 === strpos($return[count($return) - 1], '@return')) { + if ($return && str_starts_with($return[\count($return) - 1], '@return')) { $return[] = ''; } - $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id)); + $deprecation = $definition->getDeprecation($id); + $return[] = sprintf('@deprecated %s', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']); } $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return)); $return = $this->container->resolveEnvPlaceholders($return); - $doc = ''; - if ($definition->isShared()) { - $doc .= <<<'EOF' + $shared = $definition->isShared() ? ' shared' : ''; + $public = $definition->isPublic() ? 'public' : 'private'; + $autowired = $definition->isAutowired() ? ' autowired' : ''; + $asFile = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition); + $methodName = $this->generateMethodName($id); - * - * This service is shared. - * This method always returns the same instance of the service. -EOF; + if ($asFile || $definition->isLazy()) { + $lazyInitialization = '$lazyLoad = true'; + } else { + $lazyInitialization = ''; } - if (!$definition->isPublic()) { - $doc .= <<<'EOF' + $code = <<docStar} + * Gets the $public '$id'$shared$autowired service. * - * This service is private. - * If you want to be able to request this service from the container directly, - * make it public, otherwise you might end up with broken code. + * $return EOF; - } + $code = str_replace('*/', ' ', $code).<<isAutowired()) { - $doc = <<isLazy()) { - $lazyInitialization = '$lazyLoad = true'; - $lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *"; + if ($definition->hasErrors() && $e = $definition->getErrors()) { + $this->addThrow = true; + + $code .= sprintf(" \$this->throw(%s);\n", $this->export(reset($e))); } else { - $lazyInitialization = ''; - $lazyInitializationDoc = ''; + $this->serviceCalls = []; + $this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls); + + if ($definition->isDeprecated()) { + $deprecation = $definition->getDeprecation($id); + $code .= sprintf(" trigger_deprecation(%s, %s, %s);\n\n", $this->export($deprecation['package']), $this->export($deprecation['version']), $this->export($deprecation['message'])); + } elseif ($definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1])) { + foreach ($this->inlinedDefinitions as $def) { + foreach ($this->getClasses($def, $id) as $class) { + $this->preload[$class] = $class; + } + } + } + + if (!$definition->isShared()) { + $factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id)); + } + + if ($isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition)) { + if (!$definition->isShared()) { + $code .= sprintf(' %s = %1$s ?? ', $factory); + + if ($asFile) { + $code .= "function () {\n"; + $code .= " return self::do(\$container);\n"; + $code .= " };\n\n"; + } else { + $code .= sprintf("\\Closure::fromCallable([\$this, '%s']);\n\n", $methodName); + } + } + + $factoryCode = $asFile ? 'self::do($container, false)' : sprintf('$this->%s(false)', $methodName); + $factoryCode = $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $factoryCode); + $code .= $asFile ? preg_replace('/function \(([^)]*+)\)( {|:)/', 'function (\1) use ($container)\2', $factoryCode) : $factoryCode; + } + + $c = $this->addServiceInclude($id, $definition); + + if ('' !== $c && $isProxyCandidate && !$definition->isShared()) { + $c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c))); + $code .= " static \$include = true;\n\n"; + $code .= " if (\$include) {\n"; + $code .= $c; + $code .= " \$include = false;\n"; + $code .= " }\n\n"; + } else { + $code .= $c; + } + + $c = $this->addInlineService($id, $definition); + + if (!$isProxyCandidate && !$definition->isShared()) { + $c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c))); + $lazyloadInitialization = $definition->isLazy() ? '$lazyLoad = true' : ''; + + $c = sprintf(" %s = function (%s) {\n%s };\n\n return %1\$s();\n", $factory, $lazyloadInitialization, $c); + } + + $code .= $c; } - // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer - $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); - $visibility = $isProxyCandidate ? 'public' : 'protected'; - $methodName = $this->generateMethodName($id); - $code = <<docStar} - * Gets the '$id' service.$doc - *$lazyInitializationDoc - * $return - */ - {$visibility} function {$methodName}($lazyInitialization) + $code .= " }\n"; + + $this->definitionVariables = $this->inlinedDefinitions = null; + $this->referenceVariables = $this->serviceCalls = null; + + return [$file, $code]; + } + + private function addInlineVariables(string $id, Definition $definition, array $arguments, bool $forConstructor): string { + $code = ''; -EOF; + foreach ($arguments as $argument) { + if (\is_array($argument)) { + $code .= $this->addInlineVariables($id, $definition, $argument, $forConstructor); + } elseif ($argument instanceof Reference) { + $code .= $this->addInlineReference($id, $definition, $argument, $forConstructor); + } elseif ($argument instanceof Definition) { + $code .= $this->addInlineService($id, $definition, $argument, $forConstructor); + } + } - $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $methodName) : ''; + return $code; + } - if ($definition->isSynthetic()) { - $code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id); + private function addInlineReference(string $id, Definition $definition, string $targetId, bool $forConstructor): string + { + while ($this->container->hasAlias($targetId)) { + $targetId = (string) $this->container->getAlias($targetId); + } + + [$callCount, $behavior] = $this->serviceCalls[$targetId]; + + if ($id === $targetId) { + return $this->addInlineService($id, $definition, $definition); + } + + if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) { + return ''; + } + + if ($this->container->hasDefinition($targetId) && ($def = $this->container->getDefinition($targetId)) && !$def->isShared()) { + return ''; + } + + $hasSelfRef = isset($this->circularReferences[$id][$targetId]) && !isset($this->definitionVariables[$definition]); + + if ($hasSelfRef && !$forConstructor && !$forConstructor = !$this->circularReferences[$id][$targetId]) { + $code = $this->addInlineService($id, $definition, $definition); } else { - if ($definition->isDeprecated()) { - $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); + $code = ''; + } + + if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) { + return $code; + } + + $name = $this->getNextVariableName(); + $this->referenceVariables[$targetId] = new Variable($name); + + $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null; + $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference)); + + if (!$hasSelfRef || !$forConstructor) { + return $code; + } + + $code .= sprintf(<<<'EOTXT' + + if (isset($this->%s[%s])) { + return $this->%1$s[%2$s]; + } + +EOTXT + , + $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', + $this->doExport($id) + ); + + return $code; + } + + private function addInlineService(string $id, Definition $definition, Definition $inlineDef = null, bool $forConstructor = true): string + { + $code = ''; + + if ($isSimpleInstance = $isRootInstance = null === $inlineDef) { + foreach ($this->serviceCalls as $targetId => [$callCount, $behavior, $byConstructor]) { + if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId]) { + $code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor); + } } + } - $code .= - $this->addServiceInclude($id, $definition). - $this->addServiceLocalTempVariables($id, $definition). - $this->addServiceInlinedDefinitions($id, $definition). - $this->addServiceInstance($id, $definition). - $this->addServiceInlinedDefinitionsSetup($id, $definition). - $this->addServiceProperties($id, $definition). - $this->addServiceMethodCalls($id, $definition). - $this->addServiceConfigurator($id, $definition). - $this->addServiceReturn($id, $definition) - ; + if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) { + return $code; } - $this->definitionVariables = null; - $this->referenceVariables = null; + $arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()]; + + $code .= $this->addInlineVariables($id, $definition, $arguments, $forConstructor); + + if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) { + $isSimpleInstance = false; + } elseif ($definition !== $inlineDef && 2 > $this->inlinedDefinitions[$inlineDef]) { + return $code; + } + + if (isset($this->definitionVariables[$inlineDef])) { + $isSimpleInstance = false; + } else { + $name = $definition === $inlineDef ? 'instance' : $this->getNextVariableName(); + $this->definitionVariables[$inlineDef] = new Variable($name); + $code .= '' !== $code ? "\n" : ''; + + if ('instance' === $name) { + $code .= $this->addServiceInstance($id, $definition, $isSimpleInstance); + } else { + $code .= $this->addNewInstance($inlineDef, ' $'.$name.' = ', $id); + } + + if ('' !== $inline = $this->addInlineVariables($id, $definition, $arguments, false)) { + $code .= "\n".$inline."\n"; + } elseif ($arguments && 'instance' === $name) { + $code .= "\n"; + } + + $code .= $this->addServiceProperties($inlineDef, $name); + $code .= $this->addServiceMethodCalls($inlineDef, $name, !$this->getProxyDumper()->isProxyCandidate($inlineDef) && $inlineDef->isShared() && !isset($this->singleUsePrivateIds[$id]) ? $id : null); + $code .= $this->addServiceConfigurator($inlineDef, $name); + } + + if ($isRootInstance && !$isSimpleInstance) { + $code .= "\n return \$instance;\n"; + } return $code; } - /** - * Adds multiple services. - * - * @return string - */ - private function addServices() + private function addServices(array &$services = null): string { $publicServices = $privateServices = ''; $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { + if (!$definition->isSynthetic()) { + $services[$id] = $this->addService($id, $definition); + } elseif ($definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1])) { + $services[$id] = null; + + foreach ($this->getClasses($definition, $id) as $class) { + $this->preload[$class] = $class; + } + } + } + + foreach ($definitions as $id => $definition) { + if (!([$file, $code] = $services[$id]) || null !== $file) { + continue; + } if ($definition->isPublic()) { - $publicServices .= $this->addService($id, $definition); - } else { - $privateServices .= $this->addService($id, $definition); + $publicServices .= $code; + } elseif (!$this->isTrivialInstance($definition) || isset($this->locatedIds[$id])) { + $privateServices .= $code; } } return $publicServices.$privateServices; } - private function addNewInstance(Definition $definition, $return, $instantiation, $id) + private function generateServiceFiles(array $services): iterable { - $class = $this->dumpValue($definition->getClass()); + $definitions = $this->container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if (([$file, $code] = $services[$id]) && null !== $file && ($definition->isPublic() || !$this->isTrivialInstance($definition) || isset($this->locatedIds[$id]))) { + yield $file => [$code, $definition->hasTag($this->hotPathTag) || !$definition->hasTag($this->preloadTags[1]) && !$definition->isDeprecated() && !$definition->hasErrors()]; + } + } + } + + private function addNewInstance(Definition $definition, string $return = '', string $id = null): string + { + $tail = $return ? ";\n" : ''; + + if (BaseServiceLocator::class === $definition->getClass() && $definition->hasTag($this->serviceLocatorTag)) { + $arguments = []; + foreach ($definition->getArgument(0) as $k => $argument) { + $arguments[$k] = $argument->getValues()[0]; + } + + return $return.$this->dumpValue(new ServiceLocatorArgument($arguments)).$tail; + } - $arguments = array(); + $arguments = []; foreach ($definition->getArguments() as $value) { $arguments[] = $this->dumpValue($value); } if (null !== $definition->getFactory()) { $callable = $definition->getFactory(); - if (is_array($callable)) { + + if (\is_array($callable)) { if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) { - throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); + throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s).', $callable[1] ?: 'n/a')); } if ($callable[0] instanceof Reference || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { - return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('%s->%s(%s)', $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } $class = $this->dumpValue($callable[0]); - // If the class is a string we can optimize call_user_func away - if (0 === strpos($class, "'") && false === strpos($class, '$')) { + // If the class is a string we can optimize away + if (str_starts_with($class, "'") && !str_contains($class, '$')) { if ("''" === $class) { - throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id)); + throw new RuntimeException(sprintf('Cannot dump definition: "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id ? 'The "'.$id.'"' : 'inline')); } - return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } - if (0 === strpos($class, 'new ')) { - return sprintf(" $return{$instantiation}(%s)->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); + if (str_starts_with($class, 'new ')) { + return $return.sprintf('(%s)->%s(%s)', $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } - return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); + return $return.sprintf("[%s, '%s'](%s)", $class, $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } - return sprintf(" $return{$instantiation}%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); + return $return.sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '').$tail; } - if (false !== strpos($class, '$')) { - return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments)); + if (null === $class = $definition->getClass()) { + throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); } - return sprintf(" $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); + return $return.sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)).$tail; } - /** - * Adds the class headers. - * - * @param string $class Class name - * @param string $baseClass The name of the base class - * @param string $namespace The class namespace - * - * @return string - */ - private function startClass($class, $baseClass, $namespace) + private function startClass(string $class, string $baseClass): string { - $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; - $namespaceLine = $namespace ? "namespace $namespace;\n" : ''; + $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; - return <<docStar} - * $class. - * - * This class has been auto-generated - * by the Symfony Dependency Injection Component. + * @internal This class has been auto-generated by the Symfony Dependency Injection Component. */ class $class extends $baseClass { - private \$parameters; - private \$targetDirs = array(); - -EOF; - } + protected \$parameters = []; - /** - * Adds the constructor. - * - * @return string - */ - private function addConstructor() + public function __construct() { - $targetDirs = $this->exportTargetDirs(); - $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; - $code = <<asFiles) { + $code = str_replace('$parameters = []', "\$containerDir;\n protected \$parameters = [];\n private \$buildParameters", $code); + $code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code); + $code .= " \$this->buildParameters = \$buildParameters;\n"; + $code .= " \$this->containerDir = \$containerDir;\n"; + + if (null !== $this->targetDirRegex) { + $code = str_replace('$parameters = []', "\$targetDir;\n protected \$parameters = []", $code); + $code .= ' $this->targetDir = \\dirname($containerDir);'."\n"; + } + } - /*{$this->docStar} - * Constructor. - */ - public function __construct() - {{$targetDirs} - parent::__construct($arguments); + if (Container::class !== $this->baseClass) { + $r = $this->container->getReflectionClass($this->baseClass, false); + if (null !== $r + && (null !== $constructor = $r->getConstructor()) + && 0 === $constructor->getNumberOfRequiredParameters() + && Container::class !== $constructor->getDeclaringClass()->name + ) { + $code .= " parent::__construct();\n"; + $code .= " \$this->parameterBag = null;\n\n"; + } + } -EOF; + if ($this->container->getParameterBag()->all()) { + $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; + } + $code .= " \$this->services = \$this->privates = [];\n"; + $code .= $this->addSyntheticIds(); $code .= $this->addMethodMap(); - $code .= $this->addPrivateServices(); + $code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : ''; $code .= $this->addAliases(); + $code .= $this->addInlineRequires(); + $code .= <<addRemovedIds(); - return $code; - } + if ($this->asFiles && !$this->inlineFactories) { + $code .= <<<'EOF' - /** - * Adds the constructor for a frozen container. - * - * @return string - */ - private function addFrozenConstructor() + protected function load($file, $lazyLoad = true) { - $targetDirs = $this->exportTargetDirs(); + if (class_exists($class = __NAMESPACE__.'\\'.$file, false)) { + return $class::do($this, $lazyLoad); + } - $code = <<docStar} - * Constructor. - */ - public function __construct() - {{$targetDirs} -EOF; + $service = require $this->containerDir.\DIRECTORY_SEPARATOR.$file; - if ($this->container->getParameterBag()->all()) { - $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; + return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service; + } + +EOF; } - $code .= "\n \$this->services = array();\n"; - $code .= $this->addMethodMap(); - $code .= $this->addAliases(); + $proxyDumper = $this->getProxyDumper(); + foreach ($this->container->getDefinitions() as $definition) { + if (!$proxyDumper->isProxyCandidate($definition)) { + continue; + } + + if ($this->asFiles && !$this->inlineFactories) { + $proxyLoader = "class_exists(\$class, false) || require __DIR__.'/'.\$class.'.php';\n\n "; + } else { + $proxyLoader = ''; + } + + $code .= <<docStar} - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } + $code = ''; + $definitions = $this->container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if ($definition->isSynthetic() && 'service_container' !== $id) { + $code .= ' '.$this->doExport($id)." => true,\n"; + } + } -EOF; + return $code ? " \$this->syntheticIds = [\n{$code} ];\n" : ''; } - /** - * Adds the isFrozen method for a frozen container. - * - * @return string - */ - private function addIsFrozenMethod() + private function addRemovedIds(): string { + $ids = $this->container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if (!$ids) { + return ''; + } + if ($this->asFiles) { + $code = "require \$this->containerDir.\\DIRECTORY_SEPARATOR.'removed-ids.php'"; + } else { + $code = ''; + $ids = array_keys($ids); + sort($ids); + foreach ($ids as $id) { + if (preg_match(FileLoader::ANONYMOUS_ID_REGEXP, $id)) { + continue; + } + $code .= ' '.$this->doExport($id)." => true,\n"; + } + + $code = "[\n{$code} ]"; + } + return <<docStar} - * {@inheritdoc} - */ - public function isFrozen() + public function getRemovedIds(): array { - return true; + return {$code}; } EOF; } - /** - * Adds the methodMap property definition. - * - * @return string - */ - private function addMethodMap() + private function addMethodMap(): string { - if (!$definitions = $this->container->getDefinitions()) { - return ''; - } - - $code = " \$this->methodMap = array(\n"; + $code = ''; + $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - $code .= ' '.$this->export($id).' => '.$this->export($this->generateMethodName($id)).",\n"; + if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->inlineFactories || $this->isHotPath($definition))) { + $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; + } } - return $code." );\n"; + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $id) { + if (!$id->isDeprecated()) { + continue; + } + $code .= ' '.$this->doExport($alias).' => '.$this->doExport($this->generateMethodName($alias)).",\n"; + } + + return $code ? " \$this->methodMap = [\n{$code} ];\n" : ''; } - /** - * Adds the privates property definition. - * - * @return string - */ - private function addPrivateServices() + private function addFileMap(): string { - if (!$definitions = $this->container->getDefinitions()) { - return ''; - } - $code = ''; + $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if (!$definition->isPublic()) { - $code .= ' '.$this->export($id)." => true,\n"; + if (!$definition->isSynthetic() && $definition->isPublic() && !$this->isHotPath($definition)) { + $code .= sprintf(" %s => '%s',\n", $this->doExport($id), $this->generateMethodName($id)); } } - if (empty($code)) { - return ''; - } - - $out = " \$this->privates = array(\n"; - $out .= $code; - $out .= " );\n"; - - return $out; + return $code ? " \$this->fileMap = [\n{$code} ];\n" : ''; } - /** - * Adds the aliases property definition. - * - * @return string - */ - private function addAliases() + private function addAliases(): string { if (!$aliases = $this->container->getAliases()) { - if ($this->container->isFrozen()) { - return "\n \$this->aliases = array();\n"; - } else { - return ''; - } + return "\n \$this->aliases = [];\n"; } - $code = " \$this->aliases = array(\n"; + $code = " \$this->aliases = [\n"; ksort($aliases); foreach ($aliases as $alias => $id) { + if ($id->isDeprecated()) { + continue; + } + $id = (string) $id; while (isset($aliases[$id])) { $id = (string) $aliases[$id]; } - $code .= ' '.$this->export($alias).' => '.$this->export($id).",\n"; + $code .= ' '.$this->doExport($alias).' => '.$this->doExport($id).",\n"; + } + + return $code." ];\n"; + } + + private function addDeprecatedAliases(): string + { + $code = ''; + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $definition) { + if (!$definition->isDeprecated()) { + continue; + } + $public = $definition->isPublic() ? 'public' : 'private'; + $id = (string) $definition; + $methodNameAlias = $this->generateMethodName($alias); + $idExported = $this->export($id); + $deprecation = $definition->getDeprecation($alias); + $packageExported = $this->export($deprecation['package']); + $versionExported = $this->export($deprecation['version']); + $messageExported = $this->export($deprecation['message']); + $code .= <<docStar} + * Gets the $public '$alias' alias. + * + * @return object The "$id" service. + */ + protected function {$methodNameAlias}() + { + trigger_deprecation($packageExported, $versionExported, $messageExported); + + return \$this->get($idExported); + } + +EOF; + } + + return $code; + } + + private function addInlineRequires(): string + { + if (!$this->hotPathTag || !$this->inlineRequires) { + return ''; + } + + $lineage = []; + + foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) { + $definition = $this->container->getDefinition($id); + + if ($this->getProxyDumper()->isProxyCandidate($definition)) { + continue; + } + + $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]); + + foreach ($inlinedDefinitions as $def) { + foreach ($this->getClasses($def, $id) as $class) { + $this->collectLineage($class, $lineage); + } + } + } + + $code = ''; + + foreach ($lineage as $file) { + if (!isset($this->inlinedRequires[$file])) { + $this->inlinedRequires[$file] = true; + $code .= sprintf("\n include_once %s;", $file); + } } - return $code." );\n"; + return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : ''; } - /** - * Adds default parameters method. - * - * @return string - */ - private function addDefaultParametersMethod() + private function addDefaultParametersMethod(): string { if (!$this->container->getParameterBag()->all()) { return ''; } - $php = array(); - $dynamicPhp = array(); + $php = []; + $dynamicPhp = []; foreach ($this->container->getParameterBag()->all() as $key => $value) { if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { - throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey)); + throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: "%s".', $resolvedKey)); } - $export = $this->exportParameters(array($value)); - $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2); + $export = $this->exportParameters([$value]); + $export = explode('0 => ', substr(rtrim($export, " ]\n"), 2, -1), 2); - if (preg_match("/\\\$this->(?:getEnv\('\w++'\)|targetDirs\[\d++\])/", $export[1])) { + if (preg_match("/\\\$this->(?:getEnv\('(?:[-.\w]*+:)*+\w++'\)|targetDir\.'')/", $export[1])) { $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]); } else { $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]); } } - $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8)); + $parameters = sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', 8)); - $code = ''; - if ($this->container->isFrozen()) { - $code .= <<<'EOF' + $code = <<<'EOF' /** - * {@inheritdoc} + * @return array|bool|float|int|string|null */ - public function getParameter($name) + public function getParameter(string $name) { - $name = strtolower($name); + if (isset($this->buildParameters[$name])) { + return $this->buildParameters[$name]; + } - if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) { + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) { throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); } if (isset($this->loadedDynamicParameters[$name])) { @@ -1033,34 +1529,30 @@ public function getParameter($name) return $this->parameters[$name]; } - /** - * {@inheritdoc} - */ - public function hasParameter($name) + public function hasParameter(string $name): bool { - $name = strtolower($name); + if (isset($this->buildParameters[$name])) { + return true; + } - return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]); + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters); } - /** - * {@inheritdoc} - */ - public function setParameter($name, $value) + public function setParameter(string $name, $value): void { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } - /** - * {@inheritdoc} - */ - public function getParameterBag() + public function getParameterBag(): ParameterBagInterface { if (null === $this->parameterBag) { $parameters = $this->parameters; foreach ($this->loadedDynamicParameters as $name => $loaded) { $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); } + foreach ($this->buildParameters as $name => $value) { + $parameters[$name] = $value; + } $this->parameterBag = new FrozenParameterBag($parameters); } @@ -1068,13 +1560,13 @@ public function getParameterBag() } EOF; - if ('' === $this->docStar) { - $code = str_replace('/**', '/*', $code); - } + if (!$this->asFiles) { + $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code); + } - if ($dynamicPhp) { - $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8); - $getDynamicParameter = <<<'EOF' + if ($dynamicPhp) { + $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8); + $getDynamicParameter = <<<'EOF' switch ($name) { %s default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name)); @@ -1083,44 +1575,23 @@ public function getParameterBag() return $this->dynamicParameters[$name] = $value; EOF; - $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); - } else { - $loadedDynamicParameters = 'array()'; - $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; - } + $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); + } else { + $loadedDynamicParameters = '[]'; + $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; + } - $code .= <<docStar} - * Computes a dynamic parameter. - * - * @param string The name of the dynamic parameter to load - * - * @return mixed The value of the dynamic parameter - * - * @throws InvalidArgumentException When the dynamic parameter does not exist - */ - private function getDynamicParameter(\$name) + private function getDynamicParameter(string \$name) { {$getDynamicParameter} } -EOF; - } elseif ($dynamicPhp) { - throw new RuntimeException('You cannot dump a not-frozen container with dynamic parameters.'); - } - - $code .= <<docStar} - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() + protected function getDefaultParameters(): array { return $parameters; } @@ -1131,22 +1602,16 @@ protected function getDefaultParameters() } /** - * Exports parameters. - * - * @param array $parameters - * @param string $path - * @param int $indent - * - * @return string - * * @throws InvalidArgumentException */ - private function exportParameters(array $parameters, $path = '', $indent = 12) + private function exportParameters(array $parameters, string $path = '', int $indent = 12): string { - $php = array(); + $php = []; foreach ($parameters as $key => $value) { - if (is_array($value)) { + if (\is_array($value)) { $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); + } elseif ($value instanceof ArgumentInterface) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', get_debug_type($value), $path.'/'.$key)); } elseif ($value instanceof Variable) { throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); } elseif ($value instanceof Definition) { @@ -1162,377 +1627,380 @@ private function exportParameters(array $parameters, $path = '', $indent = 12) $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value); } - return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4)); + return sprintf("[\n%s\n%s]", implode("\n", $php), str_repeat(' ', $indent - 4)); } - /** - * Ends the class definition. - * - * @return string - */ - private function endClass() + private function endClass(): string + { + if ($this->addThrow) { + return <<<'EOF' + + protected function throw($message) { + throw new RuntimeException($message); + } +} + +EOF; + } + return <<<'EOF' } EOF; } - /** - * Wraps the service conditionals. - * - * @param string $value - * @param string $code - * - * @return string - */ - private function wrapServiceConditionals($value, $code) + private function wrapServiceConditionals($value, string $code): string { - if (!$services = ContainerBuilder::getServiceConditionals($value)) { + if (!$condition = $this->getServiceConditionals($value)) { return $code; } - $conditions = array(); - foreach ($services as $service) { - $conditions[] = sprintf("\$this->has('%s')", $service); - } - // re-indent the wrapped code $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); - return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code); + return sprintf(" if (%s) {\n%s }\n", $condition, $code); } - /** - * Builds service calls from arguments. - * - * @param array $arguments - * @param array &$calls By reference - * @param array &$behavior By reference - */ - private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior) + private function getServiceConditionals($value): string { - foreach ($arguments as $argument) { - if (is_array($argument)) { - $this->getServiceCallsFromArguments($argument, $calls, $behavior); - } elseif ($argument instanceof Reference) { - $id = (string) $argument; - - if (!isset($calls[$id])) { - $calls[$id] = 0; - } - if (!isset($behavior[$id])) { - $behavior[$id] = $argument->getInvalidBehavior(); - } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) { - $behavior[$id] = $argument->getInvalidBehavior(); - } - - ++$calls[$id]; + $conditions = []; + foreach (ContainerBuilder::getInitializedConditionals($value) as $service) { + if (!$this->container->hasDefinition($service)) { + return 'false'; } + $conditions[] = sprintf('isset($this->%s[%s])', $this->container->getDefinition($service)->isPublic() ? 'services' : 'privates', $this->doExport($service)); } - } - - /** - * Returns the inline definition. - * - * @param Definition $definition - * - * @return array - */ - private function getInlinedDefinitions(Definition $definition) - { - if (false === $this->inlinedDefinitions->contains($definition)) { - $definitions = array_merge( - $this->getDefinitionsFromArguments($definition->getArguments()), - $this->getDefinitionsFromArguments($definition->getMethodCalls()), - $this->getDefinitionsFromArguments($definition->getProperties()), - $this->getDefinitionsFromArguments(array($definition->getConfigurator())), - $this->getDefinitionsFromArguments(array($definition->getFactory())) - ); + foreach (ContainerBuilder::getServiceConditionals($value) as $service) { + if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { + continue; + } - $this->inlinedDefinitions->offsetSet($definition, $definitions); + $conditions[] = sprintf('$this->has(%s)', $this->doExport($service)); + } - return $definitions; + if (!$conditions) { + return ''; } - return $this->inlinedDefinitions->offsetGet($definition); + return implode(' && ', $conditions); } - /** - * Gets the definition from arguments. - * - * @param array $arguments - * - * @return array - */ - private function getDefinitionsFromArguments(array $arguments) + private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [], bool $byConstructor = null): \SplObjectStorage { - $definitions = array(); - foreach ($arguments as $argument) { - if (is_array($argument)) { - $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument)); - } elseif ($argument instanceof Definition) { - $definitions = array_merge( - $definitions, - $this->getInlinedDefinitions($argument), - array($argument) - ); - } + if (null === $definitions) { + $definitions = new \SplObjectStorage(); } - return $definitions; - } - - /** - * Checks if a service id has a reference. - * - * @param string $id - * @param array $arguments - * @param bool $deep - * @param array $visited - * - * @return bool - */ - private function hasReference($id, array $arguments, $deep = false, array &$visited = array()) - { foreach ($arguments as $argument) { - if (is_array($argument)) { - if ($this->hasReference($id, $argument, $deep, $visited)) { - return true; - } + if (\is_array($argument)) { + $this->getDefinitionsFromArguments($argument, $definitions, $calls, $byConstructor); } elseif ($argument instanceof Reference) { - $argumentId = (string) $argument; - if ($id === $argumentId) { - return true; - } - - if ($deep && !isset($visited[$argumentId]) && 'service_container' !== $argumentId) { - $visited[$argumentId] = true; - - $service = $this->container->getDefinition($argumentId); - - // if the proxy manager is enabled, disable searching for references in lazy services, - // as these services will be instantiated lazily and don't have direct related references. - if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) { - continue; - } + $id = (string) $argument; - $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } - if ($this->hasReference($id, $arguments, $deep, $visited)) { - return true; - } + if (!isset($calls[$id])) { + $calls[$id] = [0, $argument->getInvalidBehavior(), $byConstructor]; + } else { + $calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior()); } + + ++$calls[$id][0]; + } elseif (!$argument instanceof Definition) { + // no-op + } elseif (isset($definitions[$argument])) { + $definitions[$argument] = 1 + $definitions[$argument]; + } else { + $definitions[$argument] = 1; + $arguments = [$argument->getArguments(), $argument->getFactory()]; + $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null === $byConstructor || $byConstructor); + $arguments = [$argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()]; + $this->getDefinitionsFromArguments($arguments, $definitions, $calls, null !== $byConstructor && $byConstructor); } } - return false; + return $definitions; } /** - * Dumps values. - * - * @param mixed $value - * @param bool $interpolate - * - * @return string - * * @throws RuntimeException */ - private function dumpValue($value, $interpolate = true) + private function dumpValue($value, bool $interpolate = true): string { - if (is_array($value)) { - $code = array(); + if (\is_array($value)) { + if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) { + return $this->dumpValue("%$param%"); + } + $code = []; foreach ($value as $k => $v) { $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); } - return sprintf('array(%s)', implode(', ', $code)); - } elseif ($value instanceof Definition) { - if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { - return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate); - } - if (count($value->getMethodCalls()) > 0) { - throw new RuntimeException('Cannot dump definitions which have method calls.'); - } - if (null !== $value->getConfigurator()) { - throw new RuntimeException('Cannot dump definitions which have a configurator.'); - } + return sprintf('[%s]', implode(', ', $code)); + } elseif ($value instanceof ArgumentInterface) { + $scope = [$this->definitionVariables, $this->referenceVariables]; + $this->definitionVariables = $this->referenceVariables = null; - $arguments = array(); - foreach ($value->getArguments() as $argument) { - $arguments[] = $this->dumpValue($argument); - } + try { + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + $code = $this->dumpValue($value, $interpolate); - if (null !== $value->getFactory()) { - $factory = $value->getFactory(); + $returnedType = ''; + if ($value instanceof TypedReference) { + $returnedType = sprintf(': %s\%s', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $value->getInvalidBehavior() ? '' : '?', $value->getType()); + } + + $code = sprintf('return %s;', $code); - if (is_string($factory)) { - return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments)); + return sprintf("function ()%s {\n %s\n }", $returnedType, $code); } - if (is_array($factory)) { - if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { - throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); + if ($value instanceof IteratorArgument) { + $operands = [0]; + $code = []; + $code[] = 'new RewindableGenerator(function () {'; + + if (!$values = $value->getValues()) { + $code[] = ' return new \EmptyIterator();'; + } else { + $countCode = []; + $countCode[] = 'function () {'; + + foreach ($values as $k => $v) { + ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; + $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); + foreach (explode("\n", $v) as $v) { + if ($v) { + $code[] = ' '.$v; + } + } + } + + $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); + $countCode[] = ' }'; } - if (is_string($factory[0])) { - return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments)); - } + $code[] = sprintf(' }, %s)', \count($operands) > 1 ? implode("\n", $countCode) : $operands[0]); - if ($factory[0] instanceof Definition) { - return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); - } + return implode("\n", $code); + } - if ($factory[0] instanceof Reference) { - return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments)); + if ($value instanceof ServiceLocatorArgument) { + $serviceMap = ''; + $serviceTypes = ''; + foreach ($value->getValues() as $k => $v) { + if (!$v) { + continue; + } + $id = (string) $v; + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + $definition = $this->container->getDefinition($id); + $load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e); + $serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],", + $this->export($k), + $this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false), + $this->doExport($id), + $this->export(ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $v->getInvalidBehavior() && !\is_string($load) ? $this->generateMethodName($id) : null), + $this->export($load) + ); + $serviceTypes .= sprintf("\n %s => %s,", $this->export($k), $this->export($v instanceof TypedReference ? $v->getType() : '?')); + $this->locatedIds[$id] = true; } - } + $this->addGetService = true; - throw new RuntimeException('Cannot dump definition because of invalid factory'); + return sprintf('new \%s($this->getService, [%s%s], [%s%s])', ServiceLocator::class, $serviceMap, $serviceMap ? "\n " : '', $serviceTypes, $serviceTypes ? "\n " : ''); + } + } finally { + [$this->definitionVariables, $this->referenceVariables] = $scope; } + } elseif ($value instanceof Definition) { + if ($value->hasErrors() && $e = $value->getErrors()) { + $this->addThrow = true; - $class = $value->getClass(); - if (null === $class) { - throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); + return sprintf('$this->throw(%s)', $this->export(reset($e))); + } + if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { + return $this->dumpValue($this->definitionVariables[$value], $interpolate); + } + if ($value->getMethodCalls()) { + throw new RuntimeException('Cannot dump definitions which have method calls.'); + } + if ($value->getProperties()) { + throw new RuntimeException('Cannot dump definitions which have properties.'); + } + if (null !== $value->getConfigurator()) { + throw new RuntimeException('Cannot dump definitions which have a configurator.'); } - return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); + return $this->addNewInstance($value); } elseif ($value instanceof Variable) { return '$'.$value; } elseif ($value instanceof Reference) { - if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) { + $id = (string) $value; + + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + + if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) { return $this->dumpValue($this->referenceVariables[$id], $interpolate); } - return $this->getServiceCall((string) $value, $value); + return $this->getServiceCall($id, $value); } elseif ($value instanceof Expression) { - return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); + return $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']); } elseif ($value instanceof Parameter) { return $this->dumpParameter($value); - } elseif (true === $interpolate && is_string($value)) { + } elseif (true === $interpolate && \is_string($value)) { if (preg_match('/^%([^%]+)%$/', $value, $match)) { // we do this to deal with non string values (Boolean, integer, ...) // the preg_replace_callback converts them to strings - return $this->dumpParameter(strtolower($match[1])); + return $this->dumpParameter($match[1]); } else { $replaceParameters = function ($match) { - return "'.".$this->dumpParameter(strtolower($match[2])).".'"; + return "'.".$this->dumpParameter($match[2]).".'"; }; $code = str_replace('%%', '%', preg_replace_callback('/(?export($value))); return $code; } - } elseif (is_object($value) || is_resource($value)) { + } elseif ($value instanceof \UnitEnum) { + return sprintf('\%s::%s', \get_class($value), $value->name); + } elseif ($value instanceof AbstractArgument) { + throw new RuntimeException($value->getTextWithContext()); + } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); - } else { - return $this->export($value); } + + return $this->export($value); } /** * Dumps a string to a literal (aka PHP Code) class value. * - * @param string $class - * - * @return string - * * @throws RuntimeException */ - private function dumpLiteralClass($class) + private function dumpLiteralClass(string $class): string { - if (false !== strpos($class, '$')) { + if (str_contains($class, '$')) { return sprintf('${($_ = %s) && false ?: "_"}', $class); } - if (0 !== strpos($class, "'") || !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { - throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a')); + if (!str_starts_with($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s).', $class ?: 'n/a')); } - return '\\'.substr(str_replace('\\\\', '\\', $class), 1, -1); + $class = substr(str_replace('\\\\', '\\', $class), 1, -1); + + return str_starts_with($class, '\\') ? $class : '\\'.$class; } - /** - * Dumps a parameter. - * - * @param string $name - * - * @return string - */ - private function dumpParameter($name) + private function dumpParameter(string $name): string { - if ($this->container->isFrozen() && $this->container->hasParameter($name)) { - return $this->dumpValue($this->container->getParameter($name), false); + if ($this->container->hasParameter($name)) { + $value = $this->container->getParameter($name); + $dumpedValue = $this->dumpValue($value, false); + + if (!$value || !\is_array($value)) { + return $dumpedValue; + } + + if (!preg_match("/\\\$this->(?:getEnv\('(?:[-.\w]*+:)*+\w++'\)|targetDir\.'')/", $dumpedValue)) { + return sprintf('$this->parameters[%s]', $this->doExport($name)); + } } - return sprintf("\$this->getParameter('%s')", strtolower($name)); + return sprintf('$this->getParameter(%s)', $this->doExport($name)); } - /** - * Gets a service call. - * - * @param string $id - * @param Reference $reference - * - * @return string - */ - private function getServiceCall($id, Reference $reference = null) + private function getServiceCall(string $id, Reference $reference = null): string { + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + if ('service_container' === $id) { return '$this'; } - if ($this->container->hasDefinition($id) && !$this->container->getDefinition($id)->isPublic()) { - // The following is PHP 5.5 syntax for what could be written as "(\$this->services['$id'] ?? \$this->{$this->generateMethodName($id)}())" on PHP>=7.0 + if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) { + if ($definition->isSynthetic()) { + $code = sprintf('$this->get(%s%s)', $this->doExport($id), null !== $reference ? ', '.$reference->getInvalidBehavior() : ''); + } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + $code = 'null'; + if (!$definition->isShared()) { + return $code; + } + } elseif ($this->isTrivialInstance($definition)) { + if ($definition->hasErrors() && $e = $definition->getErrors()) { + $this->addThrow = true; + + return sprintf('$this->throw(%s)', $this->export(reset($e))); + } + $code = $this->addNewInstance($definition, '', $id); + if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { + $code = sprintf('$this->%s[%s] = %s', $definition->isPublic() ? 'services' : 'privates', $this->doExport($id), $code); + } + $code = "($code)"; + } else { + $code = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) ? "\$this->load('%s')" : '$this->%s()'; + $code = sprintf($code, $this->generateMethodName($id)); - return "\${(\$_ = isset(\$this->services['$id']) ? \$this->services['$id'] : \$this->{$this->generateMethodName($id)}()) && false ?: '_'}"; - } - if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { - return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id); - } else { - if ($this->container->hasAlias($id)) { - $id = (string) $this->container->getAlias($id); + if (!$definition->isShared()) { + $factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id)); + $code = sprintf('(isset(%s) ? %1$s() : %s)', $factory, $code); + } + } + if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { + $code = sprintf('($this->%s[%s] ?? %s)', $definition->isPublic() ? 'services' : 'privates', $this->doExport($id), $code); } - return sprintf('$this->get(\'%s\')', $id); + return $code; + } + if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + return 'null'; + } + if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE < $reference->getInvalidBehavior()) { + $code = sprintf('$this->get(%s, /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $this->doExport($id), ContainerInterface::NULL_ON_INVALID_REFERENCE); + } else { + $code = sprintf('$this->get(%s)', $this->doExport($id)); } + + return sprintf('($this->services[%s] ?? %s)', $this->doExport($id), $code); } /** * Initializes the method names map to avoid conflicts with the Container methods. - * - * @param string $class the container base class */ - private function initializeMethodNamesMap($class) + private function initializeMethodNamesMap(string $class) { - $this->serviceIdToMethodNameMap = array(); - $this->usedMethodNames = array(); + $this->serviceIdToMethodNameMap = []; + $this->usedMethodNames = []; - try { - $reflectionClass = new \ReflectionClass($class); + if ($reflectionClass = $this->container->getReflectionClass($class)) { foreach ($reflectionClass->getMethods() as $method) { $this->usedMethodNames[strtolower($method->getName())] = true; } - } catch (\ReflectionException $e) { } } /** - * Convert a service id to a valid PHP method name. - * - * @param string $id - * - * @return string - * * @throws InvalidArgumentException */ - private function generateMethodName($id) + private function generateMethodName(string $id): string { if (isset($this->serviceIdToMethodNameMap[$id])) { return $this->serviceIdToMethodNameMap[$id]; } - $name = Container::camelize($id); + $i = strrpos($id, '\\'); + $name = Container::camelize(false !== $i && isset($id[1 + $i]) ? substr($id, 1 + $i) : $id); $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name); $methodName = 'get'.$name.'Service'; $suffix = 1; @@ -1548,17 +2016,12 @@ private function generateMethodName($id) return $methodName; } - /** - * Returns the next name to use. - * - * @return string - */ - private function getNextVariableName() + private function getNextVariableName(): string { $firstChars = self::FIRST_CHARS; - $firstCharsLength = strlen($firstChars); + $firstCharsLength = \strlen($firstChars); $nonFirstChars = self::NON_FIRST_CHARS; - $nonFirstCharsLength = strlen($nonFirstChars); + $nonFirstCharsLength = \strlen($nonFirstChars); while (true) { $name = ''; @@ -1578,7 +2041,7 @@ private function getNextVariableName() ++$this->variableCount; // check that the name is not reserved - if (in_array($name, $this->reservedVariables, true)) { + if (\in_array($name, $this->reservedVariables, true)) { continue; } @@ -1586,14 +2049,22 @@ private function getNextVariableName() } } - private function getExpressionLanguage() + private function getExpressionLanguage(): ExpressionLanguage { if (null === $this->expressionLanguage) { - if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { - throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + if (!class_exists(\Symfony\Component\ExpressionLanguage\ExpressionLanguage::class)) { + throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); } $providers = $this->container->getExpressionLanguageProviders(); - $this->expressionLanguage = new ExpressionLanguage(null, $providers); + $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { + $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null; + + if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) { + return $this->getServiceCall($id); + } + + return sprintf('$this->get(%s)', $arg); + }); if ($this->container->isTrackingResources()) { foreach ($providers as $provider) { @@ -1605,27 +2076,55 @@ private function getExpressionLanguage() return $this->expressionLanguage; } - private function exportTargetDirs() + private function isHotPath(Definition $definition): bool { - return null === $this->targetDirRegex ? '' : <<hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated(); + } - \$dir = __DIR__; - for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) { - \$this->targetDirs[\$i] = \$dir = dirname(\$dir); + private function isSingleUsePrivateNode(ServiceReferenceGraphNode $node): bool + { + if ($node->getValue()->isPublic()) { + return false; } -EOF; + $ids = []; + foreach ($node->getInEdges() as $edge) { + if (!$value = $edge->getSourceNode()->getValue()) { + continue; + } + if ($edge->isLazy() || !$value instanceof Definition || !$value->isShared()) { + return false; + } + $ids[$edge->getSourceNode()->getId()] = true; + } + + return 1 === \count($ids); } + /** + * @return mixed + */ private function export($value) { - if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { - $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1])).'.' : ''; - $suffix = $matches[0][1] + strlen($matches[0][0]); - $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix)) : ''; - $dirname = '__DIR__'; + if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, \PREG_OFFSET_CAPTURE)) { + $suffix = $matches[0][1] + \strlen($matches[0][0]); + $matches[0][1] += \strlen($matches[1][0]); + $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; + + if ('\\' === \DIRECTORY_SEPARATOR && isset($value[$suffix])) { + $cookie = '\\'.random_int(100000, \PHP_INT_MAX); + $suffix = '.'.$this->doExport(str_replace('\\', $cookie, substr($value, $suffix)), true); + $suffix = str_replace('\\'.$cookie, "'.\\DIRECTORY_SEPARATOR.'", $suffix); + } else { + $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : ''; + } + + $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__'; + $offset = 2 + $this->targetDirMaxMatches - \count($matches); - if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) { - $dirname = sprintf('$this->targetDirs[%d]', $offset); + if (0 < $offset) { + $dirname = sprintf('\dirname(__DIR__, %d)', $offset + (int) $this->asFiles); + } elseif ($this->asFiles) { + $dirname = "\$this->targetDir.''"; // empty string concatenation on purpose } if ($prefix || $suffix) { @@ -1635,23 +2134,117 @@ private function export($value) return $dirname; } - return $this->doExport($value); + return $this->doExport($value, true); } - private function doExport($value) + /** + * @return mixed + */ + private function doExport($value, bool $resolveEnv = false) { - $export = var_export($value, true); + $shouldCacheValue = $resolveEnv && \is_string($value); + if ($shouldCacheValue && isset($this->exportedVariables[$value])) { + return $this->exportedVariables[$value]; + } + if (\is_string($value) && str_contains($value, "\n")) { + $cleanParts = explode("\n", $value); + $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts); + $export = implode('."\n".', $cleanParts); + } else { + $export = var_export($value, true); + } + if ($this->asFiles) { + if (false !== strpos($export, '$this')) { + $export = str_replace('$this', "$'.'this", $export); + } + if (false !== strpos($export, 'function () {')) { + $export = str_replace('function () {', "function ('.') {", $export); + } + } - if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('%s').'")) { + if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) { $export = $resolvedExport; + if (str_ends_with($export, ".''")) { + $export = substr($export, 0, -3); + if ("'" === $export[1]) { + $export = substr_replace($export, '', 18, 7); + } + } if ("'" === $export[1]) { $export = substr($export, 3); } - if (".''" === substr($export, -3)) { - $export = substr($export, 0, -3); - } + } + + if ($shouldCacheValue) { + $this->exportedVariables[$value] = $export; } return $export; } + + private function getAutoloadFile(): ?string + { + $file = null; + + foreach (spl_autoload_functions() as $autoloader) { + if (!\is_array($autoloader)) { + continue; + } + + if ($autoloader[0] instanceof DebugClassLoader || $autoloader[0] instanceof LegacyDebugClassLoader) { + $autoloader = $autoloader[0]->getClassLoader(); + } + + if (!\is_array($autoloader) || !$autoloader[0] instanceof ClassLoader || !$autoloader[0]->findFile(__CLASS__)) { + continue; + } + + foreach (get_declared_classes() as $class) { + if (str_starts_with($class, 'ComposerAutoloaderInit') && $class::getLoader() === $autoloader[0]) { + $file = \dirname((new \ReflectionClass($class))->getFileName(), 2).'/autoload.php'; + + if (null !== $this->targetDirRegex && preg_match($this->targetDirRegex.'A', $file)) { + return $file; + } + } + } + } + + return $file; + } + + private function getClasses(Definition $definition, string $id): array + { + $classes = []; + + while ($definition instanceof Definition) { + foreach ($definition->getTag($this->preloadTags[0]) as $tag) { + if (!isset($tag['class'])) { + throw new InvalidArgumentException(sprintf('Missing attribute "class" on tag "%s" for service "%s".', $this->preloadTags[0], $id)); + } + + $classes[] = trim($tag['class'], '\\'); + } + + if ($class = $definition->getClass()) { + $classes[] = trim($class, '\\'); + } + $factory = $definition->getFactory(); + + if (!\is_array($factory)) { + $factory = [$factory]; + } + + if (\is_string($factory[0])) { + if (false !== $i = strrpos($factory[0], '::')) { + $factory[0] = substr($factory[0], 0, $i); + } + $classes[] = trim($factory[0], '\\'); + } + + $definition = $factory[0]; + } + + return $classes; + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/Preloader.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/Preloader.php new file mode 100644 index 000000000..95f060d96 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/Preloader.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +/** + * @author Nicolas Grekas + */ +final class Preloader +{ + public static function append(string $file, array $list): void + { + if (!file_exists($file)) { + throw new \LogicException(sprintf('File "%s" does not exist.', $file)); + } + + $cacheDir = \dirname($file); + $classes = []; + + foreach ($list as $item) { + if (0 === strpos($item, $cacheDir)) { + file_put_contents($file, sprintf("require_once __DIR__.%s;\n", var_export(strtr(substr($item, \strlen($cacheDir)), \DIRECTORY_SEPARATOR, '/'), true)), \FILE_APPEND); + continue; + } + + $classes[] = sprintf("\$classes[] = %s;\n", var_export($item, true)); + } + + file_put_contents($file, sprintf("\n\$classes = [];\n%sPreloader::preload(\$classes);\n", implode('', $classes)), \FILE_APPEND); + } + + public static function preload(array $classes): void + { + set_error_handler(function ($t, $m, $f, $l) { + if (error_reporting() & $t) { + if (__FILE__ !== $f) { + throw new \ErrorException($m, 0, $t, $f, $l); + } + + throw new \ReflectionException($m); + } + }); + + $prev = []; + $preloaded = []; + + try { + while ($prev !== $classes) { + $prev = $classes; + foreach ($classes as $c) { + if (!isset($preloaded[$c])) { + self::doPreload($c, $preloaded); + } + } + $classes = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + } + } finally { + restore_error_handler(); + } + } + + private static function doPreload(string $class, array &$preloaded): void + { + if (isset($preloaded[$class]) || \in_array($class, ['self', 'static', 'parent'], true)) { + return; + } + + $preloaded[$class] = true; + + try { + $r = new \ReflectionClass($class); + + if ($r->isInternal()) { + return; + } + + $r->getConstants(); + $r->getDefaultProperties(); + + if (\PHP_VERSION_ID >= 70400) { + foreach ($r->getProperties(\ReflectionProperty::IS_PUBLIC) as $p) { + self::preloadType($p->getType(), $preloaded); + } + } + + foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $m) { + foreach ($m->getParameters() as $p) { + if ($p->isDefaultValueAvailable() && $p->isDefaultValueConstant()) { + $c = $p->getDefaultValueConstantName(); + + if ($i = strpos($c, '::')) { + self::doPreload(substr($c, 0, $i), $preloaded); + } + } + + self::preloadType($p->getType(), $preloaded); + } + + self::preloadType($m->getReturnType(), $preloaded); + } + } catch (\Throwable $e) { + // ignore missing classes + } + } + + private static function preloadType(?\ReflectionType $t, array &$preloaded): void + { + if (!$t) { + return; + } + + foreach (($t instanceof \ReflectionUnionType || $t instanceof \ReflectionIntersectionType) ? $t->getTypes() : [$t] as $t) { + if (!$t->isBuiltin()) { + self::doPreload($t instanceof \ReflectionNamedType ? $t->getName() : $t, $preloaded); + } + } + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/XmlDumper.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/XmlDumper.php index c3a98720a..a04f75a7f 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Dumper/XmlDumper.php +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/XmlDumper.php @@ -11,12 +11,17 @@ namespace Symfony\Component\DependencyInjection\Dumper; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Parameter; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; /** @@ -35,18 +40,16 @@ class XmlDumper extends Dumper /** * Dumps the service container as an XML string. * - * @param array $options An array of options - * * @return string An xml string representing of the service container */ - public function dump(array $options = array()) + public function dump(array $options = []) { $this->document = new \DOMDocument('1.0', 'utf-8'); $this->document->formatOutput = true; $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container'); $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); - $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd'); + $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd'); $this->addParameters($container); $this->addServices($container); @@ -58,11 +61,6 @@ public function dump(array $options = array()) return $this->container->resolveEnvPlaceholders($xml); } - /** - * Adds parameters. - * - * @param \DOMElement $parent - */ private function addParameters(\DOMElement $parent) { $data = $this->container->getParameterBag()->all(); @@ -70,7 +68,7 @@ private function addParameters(\DOMElement $parent) return; } - if ($this->container->isFrozen()) { + if ($this->container->isCompiled()) { $data = $this->escape($data); } @@ -79,32 +77,22 @@ private function addParameters(\DOMElement $parent) $this->convertParameters($data, 'parameter', $parameters); } - /** - * Adds method calls. - * - * @param array $methodcalls - * @param \DOMElement $parent - */ private function addMethodCalls(array $methodcalls, \DOMElement $parent) { foreach ($methodcalls as $methodcall) { $call = $this->document->createElement('call'); $call->setAttribute('method', $methodcall[0]); - if (count($methodcall[1])) { + if (\count($methodcall[1])) { $this->convertParameters($methodcall[1], 'argument', $call); } + if ($methodcall[2] ?? false) { + $call->setAttribute('returns-clone', 'true'); + } $parent->appendChild($call); } } - /** - * Adds a service. - * - * @param Definition $definition - * @param string $id - * @param \DOMElement $parent - */ - private function addService($definition, $id, \DOMElement $parent) + private function addService(Definition $definition, ?string $id, \DOMElement $parent) { $service = $this->document->createElement('service'); if (null !== $id) { @@ -120,8 +108,8 @@ private function addService($definition, $id, \DOMElement $parent) if (!$definition->isShared()) { $service->setAttribute('shared', 'false'); } - if (!$definition->isPublic()) { - $service->setAttribute('public', 'false'); + if ($definition->isPublic()) { + $service->setAttribute('public', 'true'); } if ($definition->isSynthetic()) { $service->setAttribute('synthetic', 'true'); @@ -129,9 +117,15 @@ private function addService($definition, $id, \DOMElement $parent) if ($definition->isLazy()) { $service->setAttribute('lazy', 'true'); } - if (null !== $decorated = $definition->getDecoratedService()) { - list($decorated, $renamedId, $priority) = $decorated; + if (null !== $decoratedService = $definition->getDecoratedService()) { + [$decorated, $renamedId, $priority] = $decoratedService; $service->setAttribute('decorates', $decorated); + + $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE], true)) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE === $decorationOnInvalid ? 'null' : 'ignore'; + $service->setAttribute('decoration-on-invalid', $invalidBehavior); + } if (null !== $renamedId) { $service->setAttribute('decoration-inner-name', $renamedId); } @@ -143,7 +137,11 @@ private function addService($definition, $id, \DOMElement $parent) foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { $tag = $this->document->createElement('tag'); - $tag->setAttribute('name', $name); + if (!\array_key_exists('name', $attributes)) { + $tag->setAttribute('name', $name); + } else { + $tag->appendChild($this->document->createTextNode($name)); + } foreach ($attributes as $key => $value) { $tag->setAttribute($key, $value); } @@ -170,11 +168,13 @@ private function addService($definition, $id, \DOMElement $parent) if ($callable = $definition->getFactory()) { $factory = $this->document->createElement('factory'); - if (is_array($callable) && $callable[0] instanceof Definition) { + if (\is_array($callable) && $callable[0] instanceof Definition) { $this->addService($callable[0], null, $factory); $factory->setAttribute('method', $callable[1]); - } elseif (is_array($callable)) { - $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + } elseif (\is_array($callable)) { + if (null !== $callable[0]) { + $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + } $factory->setAttribute('method', $callable[1]); } else { $factory->setAttribute('function', $callable); @@ -183,8 +183,11 @@ private function addService($definition, $id, \DOMElement $parent) } if ($definition->isDeprecated()) { + $deprecation = $definition->getDeprecation('%service_id%'); $deprecated = $this->document->createElement('deprecated'); - $deprecated->appendChild($this->document->createTextNode($definition->getDeprecationMessage('%service_id%'))); + $deprecated->appendChild($this->document->createTextNode($definition->getDeprecation('%service_id%')['message'])); + $deprecated->setAttribute('package', $deprecation['package']); + $deprecated->setAttribute('version', $deprecation['version']); $service->appendChild($deprecated); } @@ -193,20 +196,21 @@ private function addService($definition, $id, \DOMElement $parent) $service->setAttribute('autowire', 'true'); } - foreach ($definition->getAutowiringTypes() as $autowiringTypeValue) { - $autowiringType = $this->document->createElement('autowiring-type'); - $autowiringType->appendChild($this->document->createTextNode($autowiringTypeValue)); + if ($definition->isAutoconfigured()) { + $service->setAttribute('autoconfigure', 'true'); + } - $service->appendChild($autowiringType); + if ($definition->isAbstract()) { + $service->setAttribute('abstract', 'true'); } if ($callable = $definition->getConfigurator()) { $configurator = $this->document->createElement('configurator'); - if (is_array($callable) && $callable[0] instanceof Definition) { + if (\is_array($callable) && $callable[0] instanceof Definition) { $this->addService($callable[0], null, $configurator); $configurator->setAttribute('method', $callable[1]); - } elseif (is_array($callable)) { + } elseif (\is_array($callable)) { $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); $configurator->setAttribute('method', $callable[1]); } else { @@ -218,29 +222,28 @@ private function addService($definition, $id, \DOMElement $parent) $parent->appendChild($service); } - /** - * Adds a service alias. - * - * @param string $alias - * @param Alias $id - * @param \DOMElement $parent - */ - private function addServiceAlias($alias, Alias $id, \DOMElement $parent) + private function addServiceAlias(string $alias, Alias $id, \DOMElement $parent) { $service = $this->document->createElement('service'); $service->setAttribute('id', $alias); $service->setAttribute('alias', $id); - if (!$id->isPublic()) { - $service->setAttribute('public', 'false'); + if ($id->isPublic()) { + $service->setAttribute('public', 'true'); + } + + if ($id->isDeprecated()) { + $deprecation = $id->getDeprecation('%alias_id%'); + $deprecated = $this->document->createElement('deprecated'); + $deprecated->appendChild($this->document->createTextNode($deprecation['message'])); + $deprecated->setAttribute('package', $deprecation['package']); + $deprecated->setAttribute('version', $deprecation['version']); + + $service->appendChild($deprecated); } + $parent->appendChild($service); } - /** - * Adds services. - * - * @param \DOMElement $parent - */ private function addServices(\DOMElement $parent) { $definitions = $this->container->getDefinitions(); @@ -263,34 +266,52 @@ private function addServices(\DOMElement $parent) $parent->appendChild($services); } - /** - * Converts parameters. - * - * @param array $parameters - * @param string $type - * @param \DOMElement $parent - * @param string $keyAttribute - */ - private function convertParameters(array $parameters, $type, \DOMElement $parent, $keyAttribute = 'key') + private function convertParameters(array $parameters, string $type, \DOMElement $parent, string $keyAttribute = 'key') { - $withKeys = array_keys($parameters) !== range(0, count($parameters) - 1); + $withKeys = array_keys($parameters) !== range(0, \count($parameters) - 1); foreach ($parameters as $key => $value) { $element = $this->document->createElement($type); if ($withKeys) { $element->setAttribute($keyAttribute, $key); } - if (is_array($value)) { + if (\is_array($tag = $value)) { $element->setAttribute('type', 'collection'); $this->convertParameters($value, $type, $element, 'key'); - } elseif ($value instanceof Reference) { + } elseif ($value instanceof TaggedIteratorArgument || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument())) { + $element->setAttribute('type', $value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator'); + $element->setAttribute('tag', $tag->getTag()); + + if (null !== $tag->getIndexAttribute()) { + $element->setAttribute('index-by', $tag->getIndexAttribute()); + + if (null !== $tag->getDefaultIndexMethod()) { + $element->setAttribute('default-index-method', $tag->getDefaultIndexMethod()); + } + if (null !== $tag->getDefaultPriorityMethod()) { + $element->setAttribute('default-priority-method', $tag->getDefaultPriorityMethod()); + } + } + } elseif ($value instanceof IteratorArgument) { + $element->setAttribute('type', 'iterator'); + $this->convertParameters($value->getValues(), $type, $element, 'key'); + } elseif ($value instanceof ServiceLocatorArgument) { + $element->setAttribute('type', 'service_locator'); + $this->convertParameters($value->getValues(), $type, $element, 'key'); + } elseif ($value instanceof Reference || $value instanceof ServiceClosureArgument) { $element->setAttribute('type', 'service'); + if ($value instanceof ServiceClosureArgument) { + $element->setAttribute('type', 'service_closure'); + $value = $value->getValues()[0]; + } $element->setAttribute('id', (string) $value); - $behaviour = $value->getInvalidBehavior(); - if ($behaviour == ContainerInterface::NULL_ON_INVALID_REFERENCE) { + $behavior = $value->getInvalidBehavior(); + if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behavior) { $element->setAttribute('on-invalid', 'null'); - } elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { + } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behavior) { $element->setAttribute('on-invalid', 'ignore'); + } elseif (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE == $behavior) { + $element->setAttribute('on-invalid', 'ignore_uninitialized'); } } elseif ($value instanceof Definition) { $element->setAttribute('type', 'service'); @@ -299,10 +320,26 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent $element->setAttribute('type', 'expression'); $text = $this->document->createTextNode(self::phpToXml((string) $value)); $element->appendChild($text); + } elseif (\is_string($value) && !preg_match('/^[^\x00-\x08\x0B\x0E-\x1A\x1C-\x1F\x7F]*+$/u', $value)) { + $element->setAttribute('type', 'binary'); + $text = $this->document->createTextNode(self::phpToXml(base64_encode($value))); + $element->appendChild($text); + } elseif ($value instanceof \UnitEnum) { + $element->setAttribute('type', 'constant'); + $element->appendChild($this->document->createTextNode(self::phpToXml($value))); + } elseif ($value instanceof AbstractArgument) { + $element->setAttribute('type', 'abstract'); + $text = $this->document->createTextNode(self::phpToXml($value->getText())); + $element->appendChild($text); } else { - if (in_array($value, array('null', 'true', 'false'), true)) { + if (\in_array($value, ['null', 'true', 'false'], true)) { + $element->setAttribute('type', 'string'); + } + + if (\is_string($value) && (is_numeric($value) || preg_match('/^0b[01]*$/', $value) || preg_match('/^0x[0-9a-f]++$/i', $value))) { $element->setAttribute('type', 'string'); } + $text = $this->document->createTextNode(self::phpToXml($value)); $element->appendChild($text); } @@ -312,18 +349,14 @@ private function convertParameters(array $parameters, $type, \DOMElement $parent /** * Escapes arguments. - * - * @param array $arguments - * - * @return array */ - private function escape(array $arguments) + private function escape(array $arguments): array { - $args = array(); + $args = []; foreach ($arguments as $k => $v) { - if (is_array($v)) { + if (\is_array($v)) { $args[$k] = $this->escape($v); - } elseif (is_string($v)) { + } elseif (\is_string($v)) { $args[$k] = str_replace('%', '%%', $v); } else { $args[$k] = $v; @@ -338,11 +371,9 @@ private function escape(array $arguments) * * @param mixed $value Value to convert * - * @return string - * * @throws RuntimeException When trying to dump object or resource */ - public static function phpToXml($value) + public static function phpToXml($value): string { switch (true) { case null === $value: @@ -353,7 +384,9 @@ public static function phpToXml($value) return 'false'; case $value instanceof Parameter: return '%'.$value.'%'; - case is_object($value) || is_resource($value): + case $value instanceof \UnitEnum: + return sprintf('%s::%s', \get_class($value), $value->name); + case \is_object($value) || \is_resource($value): throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); default: return (string) $value; diff --git a/tests/integration/vendor/symfony/dependency-injection/Dumper/YamlDumper.php b/tests/integration/vendor/symfony/dependency-injection/Dumper/YamlDumper.php index d28682c71..4c9bb7572 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Dumper/YamlDumper.php +++ b/tests/integration/vendor/symfony/dependency-injection/Dumper/YamlDumper.php @@ -11,14 +11,24 @@ namespace Symfony\Component\DependencyInjection\Dumper; -use Symfony\Component\Yaml\Dumper as YmlDumper; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\Yaml\Dumper as YmlDumper; +use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Tag\TaggedValue; +use Symfony\Component\Yaml\Yaml; /** * YamlDumper dumps a service container as a YAML string. @@ -32,14 +42,12 @@ class YamlDumper extends Dumper /** * Dumps the service container as an YAML string. * - * @param array $options An array of options - * * @return string A YAML string representing of the service container */ - public function dump(array $options = array()) + public function dump(array $options = []) { - if (!class_exists('Symfony\Component\Yaml\Dumper')) { - throw new RuntimeException('Unable to dump the container as the Symfony Yaml Component is not installed.'); + if (!class_exists(\Symfony\Component\Yaml\Dumper::class)) { + throw new LogicException('Unable to dump the container as the Symfony Yaml Component is not installed.'); } if (null === $this->dumper) { @@ -49,15 +57,7 @@ public function dump(array $options = array()) return $this->container->resolveEnvPlaceholders($this->addParameters()."\n".$this->addServices()); } - /** - * Adds a service. - * - * @param string $id - * @param Definition $definition - * - * @return string - */ - private function addService($id, $definition) + private function addService(string $id, Definition $definition): string { $code = " $id:\n"; if ($class = $definition->getClass()) { @@ -68,20 +68,20 @@ private function addService($id, $definition) $code .= sprintf(" class: %s\n", $this->dumper->dump($class)); } - if (!$definition->isPublic()) { - $code .= " public: false\n"; + if (!$definition->isPrivate()) { + $code .= sprintf(" public: %s\n", $definition->isPublic() ? 'true' : 'false'); } $tagsCode = ''; foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { - $att = array(); + $att = []; foreach ($attributes as $key => $value) { $att[] = sprintf('%s: %s', $this->dumper->dump($key), $this->dumper->dump($value)); } - $att = $att ? ', '.implode(', ', $att) : ''; + $att = $att ? ': { '.implode(', ', $att).' }' : ''; - $tagsCode .= sprintf(" - { name: %s%s }\n", $this->dumper->dump($name), $att); + $tagsCode .= sprintf(" - %s%s\n", $this->dumper->dump($name), $att); } } if ($tagsCode) { @@ -93,27 +93,32 @@ private function addService($id, $definition) } if ($definition->isSynthetic()) { - $code .= sprintf(" synthetic: true\n"); + $code .= " synthetic: true\n"; } if ($definition->isDeprecated()) { - $code .= sprintf(" deprecated: %s\n", $definition->getDeprecationMessage('%service_id%')); + $code .= " deprecated:\n"; + foreach ($definition->getDeprecation('%service_id%') as $key => $value) { + if ('' !== $value) { + $code .= sprintf(" %s: %s\n", $key, $this->dumper->dump($value)); + } + } } if ($definition->isAutowired()) { $code .= " autowire: true\n"; } - $autowiringTypesCode = ''; - foreach ($definition->getAutowiringTypes() as $autowiringType) { - $autowiringTypesCode .= sprintf(" - %s\n", $this->dumper->dump($autowiringType)); + if ($definition->isAutoconfigured()) { + $code .= " autoconfigure: true\n"; } - if ($autowiringTypesCode) { - $code .= sprintf(" autowiring_types:\n%s", $autowiringTypesCode); + + if ($definition->isAbstract()) { + $code .= " abstract: true\n"; } if ($definition->isLazy()) { - $code .= sprintf(" lazy: true\n"); + $code .= " lazy: true\n"; } if ($definition->getArguments()) { @@ -132,8 +137,8 @@ private function addService($id, $definition) $code .= " shared: false\n"; } - if (null !== $decorated = $definition->getDecoratedService()) { - list($decorated, $renamedId, $priority) = $decorated; + if (null !== $decoratedService = $definition->getDecoratedService()) { + [$decorated, $renamedId, $priority] = $decoratedService; $code .= sprintf(" decorates: %s\n", $decorated); if (null !== $renamedId) { $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); @@ -141,6 +146,12 @@ private function addService($id, $definition) if (0 !== $priority) { $code .= sprintf(" decoration_priority: %s\n", $priority); } + + $decorationOnInvalid = $decoratedService[3] ?? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + if (\in_array($decorationOnInvalid, [ContainerInterface::IGNORE_ON_INVALID_REFERENCE, ContainerInterface::NULL_ON_INVALID_REFERENCE])) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE === $decorationOnInvalid ? 'null' : 'ignore'; + $code .= sprintf(" decoration_on_invalid: %s\n", $invalidBehavior); + } } if ($callable = $definition->getFactory()) { @@ -154,29 +165,32 @@ private function addService($id, $definition) return $code; } - /** - * Adds a service alias. - * - * @param string $alias - * @param Alias $id - * - * @return string - */ - private function addServiceAlias($alias, $id) + private function addServiceAlias(string $alias, Alias $id): string { - if ($id->isPublic()) { + $deprecated = ''; + + if ($id->isDeprecated()) { + $deprecated = " deprecated:\n"; + + foreach ($id->getDeprecation('%alias_id%') as $key => $value) { + if ('' !== $value) { + $deprecated .= sprintf(" %s: %s\n", $key, $value); + } + } + } + + if (!$id->isDeprecated() && $id->isPrivate()) { return sprintf(" %s: '@%s'\n", $alias, $id); } - return sprintf(" %s:\n alias: %s\n public: false", $alias, $id); + if ($id->isPublic()) { + $deprecated = " public: true\n".$deprecated; + } + + return sprintf(" %s:\n alias: %s\n%s", $alias, $id, $deprecated); } - /** - * Adds services. - * - * @return string - */ - private function addServices() + private function addServices(): string { if (!$this->container->getDefinitions()) { return ''; @@ -198,36 +212,31 @@ private function addServices() return $code; } - /** - * Adds parameters. - * - * @return string - */ - private function addParameters() + private function addParameters(): string { if (!$this->container->getParameterBag()->all()) { return ''; } - $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isFrozen()); + $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isCompiled()); - return $this->dumper->dump(array('parameters' => $parameters), 2); + return $this->dumper->dump(['parameters' => $parameters], 2); } /** * Dumps callable to YAML format. * - * @param callable $callable + * @param mixed $callable * - * @return callable + * @return mixed */ private function dumpCallable($callable) { - if (is_array($callable)) { + if (\is_array($callable)) { if ($callable[0] instanceof Reference) { - $callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]); + $callable = [$this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]]; } else { - $callable = array($callable[0], $callable[1]); + $callable = [$callable[0], $callable[1]]; } } @@ -237,16 +246,53 @@ private function dumpCallable($callable) /** * Dumps the value to YAML format. * - * @param mixed $value - * * @return mixed * * @throws RuntimeException When trying to dump object or resource */ private function dumpValue($value) { - if (is_array($value)) { - $code = array(); + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + + return new TaggedValue('service_closure', $this->getServiceCall((string) $value, $value)); + } + if ($value instanceof ArgumentInterface) { + $tag = $value; + + if ($value instanceof TaggedIteratorArgument || ($value instanceof ServiceLocatorArgument && $tag = $value->getTaggedIteratorArgument())) { + if (null === $tag->getIndexAttribute()) { + $content = $tag->getTag(); + } else { + $content = [ + 'tag' => $tag->getTag(), + 'index_by' => $tag->getIndexAttribute(), + ]; + + if (null !== $tag->getDefaultIndexMethod()) { + $content['default_index_method'] = $tag->getDefaultIndexMethod(); + } + if (null !== $tag->getDefaultPriorityMethod()) { + $content['default_priority_method'] = $tag->getDefaultPriorityMethod(); + } + } + + return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator', $content); + } + + if ($value instanceof IteratorArgument) { + $tag = 'iterator'; + } elseif ($value instanceof ServiceLocatorArgument) { + $tag = 'service_locator'; + } else { + throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', get_debug_type($value))); + } + + return new TaggedValue($tag, $this->dumpValue($value->getValues())); + } + + if (\is_array($value)) { + $code = []; foreach ($value as $k => $v) { $code[$k] = $this->dumpValue($v); } @@ -258,62 +304,50 @@ private function dumpValue($value) return $this->getParameterCall((string) $value); } elseif ($value instanceof Expression) { return $this->getExpressionCall((string) $value); - } elseif (is_object($value) || is_resource($value)) { + } elseif ($value instanceof Definition) { + return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']); + } elseif ($value instanceof \UnitEnum) { + return new TaggedValue('php/const', sprintf('%s::%s', \get_class($value), $value->name)); + } elseif ($value instanceof AbstractArgument) { + return new TaggedValue('abstract', $value->getText()); + } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } return $value; } - /** - * Gets the service call. - * - * @param string $id - * @param Reference $reference - * - * @return string - */ - private function getServiceCall($id, Reference $reference = null) + private function getServiceCall(string $id, Reference $reference = null): string { - if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { - return sprintf('@?%s', $id); + if (null !== $reference) { + switch ($reference->getInvalidBehavior()) { + case ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE: break; + case ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE: break; + case ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE: return sprintf('@!%s', $id); + default: return sprintf('@?%s', $id); + } } return sprintf('@%s', $id); } - /** - * Gets parameter call. - * - * @param string $id - * - * @return string - */ - private function getParameterCall($id) + private function getParameterCall(string $id): string { return sprintf('%%%s%%', $id); } - private function getExpressionCall($expression) + private function getExpressionCall(string $expression): string { return sprintf('@=%s', $expression); } - /** - * Prepares parameters. - * - * @param array $parameters - * @param bool $escape - * - * @return array - */ - private function prepareParameters(array $parameters, $escape = true) + private function prepareParameters(array $parameters, bool $escape = true): array { - $filtered = array(); + $filtered = []; foreach ($parameters as $key => $value) { - if (is_array($value)) { + if (\is_array($value)) { $value = $this->prepareParameters($value, $escape); - } elseif ($value instanceof Reference || is_string($value) && 0 === strpos($value, '@')) { + } elseif ($value instanceof Reference || \is_string($value) && str_starts_with($value, '@')) { $value = '@'.$value; } @@ -323,20 +357,13 @@ private function prepareParameters(array $parameters, $escape = true) return $escape ? $this->escape($filtered) : $filtered; } - /** - * Escapes arguments. - * - * @param array $arguments - * - * @return array - */ - private function escape(array $arguments) + private function escape(array $arguments): array { - $args = array(); + $args = []; foreach ($arguments as $k => $v) { - if (is_array($v)) { + if (\is_array($v)) { $args[$k] = $this->escape($v); - } elseif (is_string($v)) { + } elseif (\is_string($v)) { $args[$k] = str_replace('%', '%%', $v); } else { $args[$k] = $v; diff --git a/tests/integration/vendor/symfony/dependency-injection/EnvVarLoaderInterface.php b/tests/integration/vendor/symfony/dependency-injection/EnvVarLoaderInterface.php new file mode 100644 index 000000000..0c547f8a5 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/EnvVarLoaderInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * EnvVarLoaderInterface objects return key/value pairs that are added to the list of available env vars. + * + * @author Nicolas Grekas + */ +interface EnvVarLoaderInterface +{ + /** + * @return string[] Key/value pairs that can be accessed using the regular "%env()%" syntax + */ + public function loadEnvVars(): array; +} diff --git a/tests/integration/vendor/symfony/dependency-injection/EnvVarProcessor.php b/tests/integration/vendor/symfony/dependency-injection/EnvVarProcessor.php new file mode 100644 index 000000000..02d7adbb7 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/EnvVarProcessor.php @@ -0,0 +1,297 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * @author Nicolas Grekas + */ +class EnvVarProcessor implements EnvVarProcessorInterface +{ + private $container; + private $loaders; + private $loadedVars = []; + + /** + * @param EnvVarLoaderInterface[] $loaders + */ + public function __construct(ContainerInterface $container, \Traversable $loaders = null) + { + $this->container = $container; + $this->loaders = $loaders ?? new \ArrayIterator(); + } + + /** + * {@inheritdoc} + */ + public static function getProvidedTypes() + { + return [ + 'base64' => 'string', + 'bool' => 'bool', + 'const' => 'bool|int|float|string|array', + 'csv' => 'array', + 'file' => 'string', + 'float' => 'float', + 'int' => 'int', + 'json' => 'array', + 'key' => 'bool|int|float|string|array', + 'url' => 'array', + 'query_string' => 'array', + 'resolve' => 'string', + 'default' => 'bool|int|float|string|array', + 'string' => 'string', + 'trim' => 'string', + 'require' => 'bool|int|float|string|array', + ]; + } + + /** + * {@inheritdoc} + */ + public function getEnv(string $prefix, string $name, \Closure $getEnv) + { + $i = strpos($name, ':'); + + if ('key' === $prefix) { + if (false === $i) { + throw new RuntimeException(sprintf('Invalid env "key:%s": a key specifier should be provided.', $name)); + } + + $next = substr($name, $i + 1); + $key = substr($name, 0, $i); + $array = $getEnv($next); + + if (!\is_array($array)) { + throw new RuntimeException(sprintf('Resolved value of "%s" did not result in an array value.', $next)); + } + + if (!isset($array[$key]) && !\array_key_exists($key, $array)) { + throw new EnvNotFoundException(sprintf('Key "%s" not found in %s (resolved from "%s").', $key, json_encode($array), $next)); + } + + return $array[$key]; + } + + if ('default' === $prefix) { + if (false === $i) { + throw new RuntimeException(sprintf('Invalid env "default:%s": a fallback parameter should be provided.', $name)); + } + + $next = substr($name, $i + 1); + $default = substr($name, 0, $i); + + if ('' !== $default && !$this->container->hasParameter($default)) { + throw new RuntimeException(sprintf('Invalid env fallback in "default:%s": parameter "%s" not found.', $name, $default)); + } + + try { + $env = $getEnv($next); + + if ('' !== $env && null !== $env) { + return $env; + } + } catch (EnvNotFoundException $e) { + // no-op + } + + return '' === $default ? null : $this->container->getParameter($default); + } + + if ('file' === $prefix || 'require' === $prefix) { + if (!is_scalar($file = $getEnv($name))) { + throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.', $name)); + } + if (!is_file($file)) { + throw new EnvNotFoundException(sprintf('File "%s" not found (resolved from "%s").', $file, $name)); + } + + if ('file' === $prefix) { + return file_get_contents($file); + } else { + return require $file; + } + } + + if (false !== $i || 'string' !== $prefix) { + $env = $getEnv($name); + } elseif (isset($_ENV[$name])) { + $env = $_ENV[$name]; + } elseif (isset($_SERVER[$name]) && !str_starts_with($name, 'HTTP_')) { + $env = $_SERVER[$name]; + } elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues + foreach ($this->loadedVars as $vars) { + if (false !== $env = ($vars[$name] ?? false)) { + break; + } + } + + if (false === $env || null === $env) { + $loaders = $this->loaders; + $this->loaders = new \ArrayIterator(); + + try { + $i = 0; + $ended = true; + $count = $loaders instanceof \Countable ? $loaders->count() : 0; + foreach ($loaders as $loader) { + if (\count($this->loadedVars) > $i++) { + continue; + } + $this->loadedVars[] = $vars = $loader->loadEnvVars(); + if (false !== $env = $vars[$name] ?? false) { + $ended = false; + break; + } + } + if ($ended || $count === $i) { + $loaders = $this->loaders; + } + } catch (ParameterCircularReferenceException $e) { + // skip loaders that need an env var that is not defined + } finally { + $this->loaders = $loaders; + } + } + + if (false === $env || null === $env) { + if (!$this->container->hasParameter("env($name)")) { + throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name)); + } + + $env = $this->container->getParameter("env($name)"); + } + } + + if (null === $env) { + if (!isset($this->getProvidedTypes()[$prefix])) { + throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix)); + } + + return null; + } + + if (!is_scalar($env)) { + throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to "%s".', $name, $prefix)); + } + + if ('string' === $prefix) { + return (string) $env; + } + + if ('bool' === $prefix) { + return (bool) (filter_var($env, \FILTER_VALIDATE_BOOLEAN) ?: filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT)); + } + + if ('int' === $prefix) { + if (false === $env = filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT)) { + throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.', $name)); + } + + return (int) $env; + } + + if ('float' === $prefix) { + if (false === $env = filter_var($env, \FILTER_VALIDATE_FLOAT)) { + throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.', $name)); + } + + return (float) $env; + } + + if ('const' === $prefix) { + if (!\defined($env)) { + throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".', $name, $env)); + } + + return \constant($env); + } + + if ('base64' === $prefix) { + return base64_decode(strtr($env, '-_', '+/')); + } + + if ('json' === $prefix) { + $env = json_decode($env, true); + + if (\JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException(sprintf('Invalid JSON in env var "%s": ', $name).json_last_error_msg()); + } + + if (null !== $env && !\is_array($env)) { + throw new RuntimeException(sprintf('Invalid JSON env var "%s": array or null expected, "%s" given.', $name, get_debug_type($env))); + } + + return $env; + } + + if ('url' === $prefix) { + $parsedEnv = parse_url($env); + + if (false === $parsedEnv) { + throw new RuntimeException(sprintf('Invalid URL in env var "%s".', $name)); + } + if (!isset($parsedEnv['scheme'], $parsedEnv['host'])) { + throw new RuntimeException(sprintf('Invalid URL env var "%s": schema and host expected, "%s" given.', $name, $env)); + } + $parsedEnv += [ + 'port' => null, + 'user' => null, + 'pass' => null, + 'path' => null, + 'query' => null, + 'fragment' => null, + ]; + + if (null !== $parsedEnv['path']) { + // remove the '/' separator + $parsedEnv['path'] = '/' === $parsedEnv['path'] ? null : substr($parsedEnv['path'], 1); + } + + return $parsedEnv; + } + + if ('query_string' === $prefix) { + $queryString = parse_url($env, \PHP_URL_QUERY) ?: $env; + parse_str($queryString, $result); + + return $result; + } + + if ('resolve' === $prefix) { + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name) { + if (!isset($match[1])) { + return '%'; + } + $value = $this->container->getParameter($match[1]); + if (!is_scalar($value)) { + throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, get_debug_type($value))); + } + + return $value; + }, $env); + } + + if ('csv' === $prefix) { + return str_getcsv($env, ',', '"', \PHP_VERSION_ID >= 70400 ? '' : '\\'); + } + + if ('trim' === $prefix) { + return trim($env); + } + + throw new RuntimeException(sprintf('Unsupported env var prefix "%s" for env name "%s".', $prefix, $name)); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php b/tests/integration/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php new file mode 100644 index 000000000..d3275fe29 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * The EnvVarProcessorInterface is implemented by objects that manage environment-like variables. + * + * @author Nicolas Grekas + */ +interface EnvVarProcessorInterface +{ + /** + * Returns the value of the given variable as managed by the current instance. + * + * @param string $prefix The namespace of the variable + * @param string $name The name of the variable within the namespace + * @param \Closure $getEnv A closure that allows fetching more env vars + * + * @return mixed + * + * @throws RuntimeException on error + */ + public function getEnv(string $prefix, string $name, \Closure $getEnv); + + /** + * @return string[] The PHP-types managed by getEnv(), keyed by prefixes + */ + public static function getProvidedTypes(); +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php new file mode 100644 index 000000000..0006f5621 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Thrown when a definition cannot be autowired. + */ +class AutowiringFailedException extends RuntimeException +{ + private $serviceId; + private $messageCallback; + + public function __construct(string $serviceId, $message = '', int $code = 0, \Throwable $previous = null) + { + $this->serviceId = $serviceId; + + if ($message instanceof \Closure + && (\function_exists('xdebug_is_enabled') ? xdebug_is_enabled() : \function_exists('xdebug_info')) + ) { + $message = $message(); + } + + if (!$message instanceof \Closure) { + parent::__construct($message, $code, $previous); + + return; + } + + $this->messageCallback = $message; + parent::__construct('', $code, $previous); + + $this->message = new class($this->message, $this->messageCallback) { + private $message; + private $messageCallback; + + public function __construct(&$message, &$messageCallback) + { + $this->message = &$message; + $this->messageCallback = &$messageCallback; + } + + public function __toString(): string + { + $messageCallback = $this->messageCallback; + $this->messageCallback = null; + + try { + return $this->message = $messageCallback(); + } catch (\Throwable $e) { + return $this->message = $e->getMessage(); + } + } + }; + } + + public function getMessageCallback(): ?\Closure + { + return $this->messageCallback; + } + + public function getServiceId() + { + return $this->serviceId; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php index 577095e88..04ac84800 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php @@ -18,8 +18,4 @@ */ class EnvNotFoundException extends InvalidArgumentException { - public function __construct($name) - { - parent::__construct(sprintf('Environment variable not found: "%s".', $name)); - } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/EnvParameterException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/EnvParameterException.php index 44dbab45b..48b5e486a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/EnvParameterException.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/EnvParameterException.php @@ -18,8 +18,8 @@ */ class EnvParameterException extends InvalidArgumentException { - public function __construct(array $usedEnvs, \Exception $previous = null) + public function __construct(array $envs, \Throwable $previous = null, string $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.') { - parent::__construct(sprintf('Incompatible use of dynamic environment variables "%s" found in parameters.', implode('", "', $usedEnvs)), 0, $previous); + parent::__construct(sprintf($message, implode('", "', $envs)), 0, $previous); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php b/tests/integration/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php index f5e9099f1..6202df76e 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php @@ -11,12 +11,14 @@ namespace Symfony\Component\DependencyInjection\Exception; +use Psr\Container\ContainerExceptionInterface; + /** * Base ExceptionInterface for Dependency Injection component. * * @author Fabien Potencier * @author Bulat Shakirzyanov */ -interface ExceptionInterface +interface ExceptionInterface extends ContainerExceptionInterface, \Throwable { } diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/InvalidParameterTypeException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/InvalidParameterTypeException.php new file mode 100644 index 000000000..2a11626fe --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/InvalidParameterTypeException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Thrown when trying to inject a parameter into a constructor/method with an incompatible type. + * + * @author Nicolas Grekas + * @author Julien Maulny + */ +class InvalidParameterTypeException extends InvalidArgumentException +{ + public function __construct(string $serviceId, string $type, \ReflectionParameter $parameter) + { + $acceptedType = $parameter->getType(); + $acceptedType = $acceptedType instanceof \ReflectionNamedType ? $acceptedType->getName() : (string) $acceptedType; + $this->code = $type; + + $function = $parameter->getDeclaringFunction(); + $functionName = $function instanceof \ReflectionMethod + ? sprintf('%s::%s', $function->getDeclaringClass()->getName(), $function->getName()) + : $function->getName(); + + parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s()" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $functionName, $acceptedType, $type)); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php index 29151765d..2450ccb5c 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php @@ -20,7 +20,7 @@ class ParameterCircularReferenceException extends RuntimeException { private $parameters; - public function __construct($parameters, \Exception $previous = null) + public function __construct(array $parameters, \Throwable $previous = null) { parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]), 0, $previous); diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php index 40c01b050..5d3831014 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php @@ -11,12 +11,14 @@ namespace Symfony\Component\DependencyInjection\Exception; +use Psr\Container\NotFoundExceptionInterface; + /** * This exception is thrown when a non-existent parameter is used. * * @author Fabien Potencier */ -class ParameterNotFoundException extends InvalidArgumentException +class ParameterNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface { private $key; private $sourceId; @@ -25,14 +27,14 @@ class ParameterNotFoundException extends InvalidArgumentException private $nonNestedAlternative; /** - * @param string $key The requested parameter key - * @param string $sourceId The service id that references the non-existent parameter - * @param string $sourceKey The parameter key that references the non-existent parameter - * @param \Exception $previous The previous exception - * @param string[] $alternatives Some parameter name alternatives - * @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters + * @param string $key The requested parameter key + * @param string|null $sourceId The service id that references the non-existent parameter + * @param string|null $sourceKey The parameter key that references the non-existent parameter + * @param \Throwable|null $previous The previous exception + * @param string[] $alternatives Some parameter name alternatives + * @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters */ - public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array(), $nonNestedAlternative = null) + public function __construct(string $key, string $sourceId = null, string $sourceKey = null, \Throwable $previous = null, array $alternatives = [], string $nonNestedAlternative = null) { $this->key = $key; $this->sourceId = $sourceId; @@ -56,7 +58,7 @@ public function updateRepr() } if ($this->alternatives) { - if (1 == count($this->alternatives)) { + if (1 == \count($this->alternatives)) { $this->message .= ' Did you mean this: "'; } else { $this->message .= ' Did you mean one of these: "'; @@ -82,14 +84,14 @@ public function getSourceKey() return $this->sourceKey; } - public function setSourceId($sourceId) + public function setSourceId(?string $sourceId) { $this->sourceId = $sourceId; $this->updateRepr(); } - public function setSourceKey($sourceKey) + public function setSourceKey(?string $sourceKey) { $this->sourceKey = $sourceKey; diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php index 26e3fb34b..a38671bcf 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php @@ -21,7 +21,7 @@ class ServiceCircularReferenceException extends RuntimeException private $serviceId; private $path; - public function __construct($serviceId, array $path, \Exception $previous = null) + public function __construct(string $serviceId, array $path, \Throwable $previous = null) { parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path)), 0, $previous); diff --git a/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php b/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php index e65da506b..f91afae39 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php +++ b/tests/integration/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php @@ -11,26 +11,31 @@ namespace Symfony\Component\DependencyInjection\Exception; +use Psr\Container\NotFoundExceptionInterface; + /** * This exception is thrown when a non-existent service is requested. * * @author Johannes M. Schmitt */ -class ServiceNotFoundException extends InvalidArgumentException +class ServiceNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface { private $id; private $sourceId; + private $alternatives; - public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = array()) + public function __construct(string $id, string $sourceId = null, \Throwable $previous = null, array $alternatives = [], string $msg = null) { - if (null === $sourceId) { + if (null !== $msg) { + // no-op + } elseif (null === $sourceId) { $msg = sprintf('You have requested a non-existent service "%s".', $id); } else { $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id); } if ($alternatives) { - if (1 == count($alternatives)) { + if (1 == \count($alternatives)) { $msg .= ' Did you mean this: "'; } else { $msg .= ' Did you mean one of these: "'; @@ -42,6 +47,7 @@ public function __construct($id, $sourceId = null, \Exception $previous = null, $this->id = $id; $this->sourceId = $sourceId; + $this->alternatives = $alternatives; } public function getId() @@ -53,4 +59,9 @@ public function getSourceId() { return $this->sourceId; } + + public function getAlternatives() + { + return $this->alternatives; + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguage.php b/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguage.php index acc97bcf4..961c737e8 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguage.php +++ b/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguage.php @@ -11,8 +11,12 @@ namespace Symfony\Component\DependencyInjection; +use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; + +if (!class_exists(BaseExpressionLanguage::class)) { + return; +} /** * Adds some function to the default ExpressionLanguage. @@ -23,10 +27,13 @@ */ class ExpressionLanguage extends BaseExpressionLanguage { - public function __construct(ParserCacheInterface $cache = null, array $providers = array()) + /** + * {@inheritdoc} + */ + public function __construct(CacheItemPoolInterface $cache = null, array $providers = [], callable $serviceCompiler = null) { // prepend the default provider to let users override it easily - array_unshift($providers, new ExpressionLanguageProvider()); + array_unshift($providers, new ExpressionLanguageProvider($serviceCompiler)); parent::__construct($cache, $providers); } diff --git a/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php b/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php index ce6d69522..9198ca0a4 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php +++ b/tests/integration/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php @@ -24,10 +24,17 @@ */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface { + private $serviceCompiler; + + public function __construct(callable $serviceCompiler = null) + { + $this->serviceCompiler = $serviceCompiler; + } + public function getFunctions() { - return array( - new ExpressionFunction('service', function ($arg) { + return [ + new ExpressionFunction('service', $this->serviceCompiler ?: function ($arg) { return sprintf('$this->get(%s)', $arg); }, function (array $variables, $value) { return $variables['container']->get($value); @@ -38,6 +45,6 @@ public function getFunctions() }, function (array $variables, $value) { return $variables['container']->getParameter($value); }), - ); + ]; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php b/tests/integration/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php index 705ba38ec..c3bd8423b 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; /** * ConfigurationExtensionInterface is the interface implemented by container extension classes. @@ -24,9 +24,6 @@ interface ConfigurationExtensionInterface /** * Returns extension configuration. * - * @param array $config An array of configuration values - * @param ContainerBuilder $container A ContainerBuilder instance - * * @return ConfigurationInterface|null The configuration or null */ public function getConfiguration(array $config, ContainerBuilder $container); diff --git a/tests/integration/vendor/symfony/dependency-injection/Extension/Extension.php b/tests/integration/vendor/symfony/dependency-injection/Extension/Extension.php index ced39f728..8fcf6789b 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Extension/Extension.php +++ b/tests/integration/vendor/symfony/dependency-injection/Extension/Extension.php @@ -11,13 +11,13 @@ namespace Symfony\Component\DependencyInjection\Extension; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Processor; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Config\Definition\Processor; -use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\DependencyInjection\Exception\LogicException; /** * Provides useful features shared by many extensions. @@ -26,6 +26,8 @@ */ abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface { + private $processedConfigs = []; + /** * {@inheritdoc} */ @@ -64,8 +66,8 @@ public function getNamespace() */ public function getAlias() { - $className = get_class($this); - if (substr($className, -9) != 'Extension') { + $className = static::class; + if (!str_ends_with($className, 'Extension')) { throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); } $classBaseName = substr(strrchr($className, '\\'), 1, -9); @@ -78,38 +80,57 @@ public function getAlias() */ public function getConfiguration(array $config, ContainerBuilder $container) { - $reflected = new \ReflectionClass($this); - $namespace = $reflected->getNamespaceName(); + $class = static::class; + + if (str_contains($class, "\0")) { + return null; // ignore anonymous classes + } - $class = $namespace.'\\Configuration'; - if (class_exists($class)) { - $r = new \ReflectionClass($class); - $container->addResource(new FileResource($r->getFileName())); + $class = substr_replace($class, '\Configuration', strrpos($class, '\\')); + $class = $container->getReflectionClass($class); + + if (!$class) { + return null; + } - if (!method_exists($class, '__construct')) { - return new $class(); - } + if (!$class->implementsInterface(ConfigurationInterface::class)) { + throw new LogicException(sprintf('The extension configuration class "%s" must implement "%s".', $class->getName(), ConfigurationInterface::class)); } + + if (!($constructor = $class->getConstructor()) || !$constructor->getNumberOfRequiredParameters()) { + return $class->newInstance(); + } + + return null; } - final protected function processConfiguration(ConfigurationInterface $configuration, array $configs) + final protected function processConfiguration(ConfigurationInterface $configuration, array $configs): array { $processor = new Processor(); - return $processor->processConfiguration($configuration, $configs); + return $this->processedConfigs[] = $processor->processConfiguration($configuration, $configs); + } + + /** + * @internal + */ + final public function getProcessedConfigs(): array + { + try { + return $this->processedConfigs; + } finally { + $this->processedConfigs = []; + } } /** - * @param ContainerBuilder $container - * @param array $config - * * @return bool Whether the configuration is enabled * * @throws InvalidArgumentException When the config is not enableable */ protected function isConfigEnabled(ContainerBuilder $container, array $config) { - if (!array_key_exists('enabled', $config)) { + if (!\array_key_exists('enabled', $config)) { throw new InvalidArgumentException("The config array has no 'enabled' key."); } diff --git a/tests/integration/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php b/tests/integration/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php index 6e926fa7a..6a7a2cf02 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php @@ -23,9 +23,6 @@ interface ExtensionInterface /** * Loads a specific configuration. * - * @param array $configs An array of configuration values - * @param ContainerBuilder $container A ContainerBuilder instance - * * @throws \InvalidArgumentException When provided tag is not defined in this extension */ public function load(array $configs, ContainerBuilder $container); @@ -40,7 +37,7 @@ public function getNamespace(); /** * Returns the base path for the XSD files. * - * @return string The XSD base path + * @return string|false */ public function getXsdValidationBasePath(); diff --git a/tests/integration/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php b/tests/integration/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php index c666bdbcf..5bd18d79f 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php @@ -17,8 +17,6 @@ interface PrependExtensionInterface { /** * Allow an extension to prepend the extension configurations. - * - * @param ContainerBuilder $container */ public function prepend(ContainerBuilder $container); } diff --git a/tests/integration/vendor/symfony/dependency-injection/LICENSE b/tests/integration/vendor/symfony/dependency-injection/LICENSE index 12a74531e..9ff2d0d63 100644 --- a/tests/integration/vendor/symfony/dependency-injection/LICENSE +++ b/tests/integration/vendor/symfony/dependency-injection/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2021 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php index a8dd5252e..a9d78115d 100644 --- a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php @@ -25,13 +25,10 @@ interface InstantiatorInterface /** * Instantiates a proxy object. * - * @param ContainerInterface $container the container from which the service is being requested - * @param Definition $definition the definition of the requested service - * @param string $id identifier of the requested service - * @param callable $realInstantiator zero-argument callback that is capable of producing the real - * service instance + * @param string $id Identifier of the requested service + * @param callable $realInstantiator Zero-argument callback that is capable of producing the real service instance * * @return object */ - public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator); + public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator); } diff --git a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php index cad932003..1696e7a90 100644 --- a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php +++ b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php @@ -17,7 +17,7 @@ /** * {@inheritdoc} * - * Noop proxy instantiator - simply produces the real service instead of a proxy instance. + * Noop proxy instantiator - produces the real service instead of a proxy instance. * * @author Marco Pivetta */ @@ -26,8 +26,8 @@ class RealServiceInstantiator implements InstantiatorInterface /** * {@inheritdoc} */ - public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator) + public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator) { - return call_user_func($realInstantiator); + return $realInstantiator(); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php index ce88eba97..351560d29 100644 --- a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php @@ -23,8 +23,6 @@ interface DumperInterface /** * Inspects whether the given definitions should produce proxy instantiation logic in the dumped container. * - * @param Definition $definition - * * @return bool */ public function isProxyCandidate(Definition $definition); @@ -32,19 +30,13 @@ public function isProxyCandidate(Definition $definition); /** * Generates the code to be used to instantiate a proxy in the dumped factory code. * - * @param Definition $definition - * @param string $id service identifier - * @param string $methodName the method name to get the service, will be added to the interface in 4.0 - * * @return string */ - public function getProxyFactoryCode(Definition $definition, $id/**, $methodName = null */); + public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode); /** * Generates the code for the lazy proxy. * - * @param Definition $definition - * * @return string */ public function getProxyCode(Definition $definition); diff --git a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php index 30911d3a5..7e0f14c32 100644 --- a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php +++ b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php @@ -17,13 +17,15 @@ * Null dumper, negates any proxy code generation for any given service definition. * * @author Marco Pivetta + * + * @final */ class NullDumper implements DumperInterface { /** * {@inheritdoc} */ - public function isProxyCandidate(Definition $definition) + public function isProxyCandidate(Definition $definition): bool { return false; } @@ -31,7 +33,7 @@ public function isProxyCandidate(Definition $definition) /** * {@inheritdoc} */ - public function getProxyFactoryCode(Definition $definition, $id) + public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode): string { return ''; } @@ -39,7 +41,7 @@ public function getProxyFactoryCode(Definition $definition, $id) /** * {@inheritdoc} */ - public function getProxyCode(Definition $definition) + public function getProxyCode(Definition $definition): string { return ''; } diff --git a/tests/integration/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php new file mode 100644 index 000000000..32b94df04 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class ProxyHelper +{ + /** + * @return string|null The FQCN or builtin name of the type hint, or null when the type hint references an invalid self|parent context + */ + public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionParameter $p = null, bool $noBuiltin = false): ?string + { + if ($p instanceof \ReflectionParameter) { + $type = $p->getType(); + } else { + $type = $r->getReturnType(); + } + if (!$type) { + return null; + } + + $types = []; + $glue = '|'; + if ($type instanceof \ReflectionUnionType) { + $reflectionTypes = $type->getTypes(); + } elseif ($type instanceof \ReflectionIntersectionType) { + $reflectionTypes = $type->getTypes(); + $glue = '&'; + } elseif ($type instanceof \ReflectionNamedType) { + $reflectionTypes = [$type]; + } else { + return null; + } + + foreach ($reflectionTypes as $type) { + if ($type->isBuiltin()) { + if (!$noBuiltin) { + $types[] = $type->getName(); + } + continue; + } + + $lcName = strtolower($type->getName()); + $prefix = $noBuiltin ? '' : '\\'; + + if ('self' !== $lcName && 'parent' !== $lcName) { + $types[] = $prefix.$type->getName(); + continue; + } + if (!$r instanceof \ReflectionMethod) { + continue; + } + if ('self' === $lcName) { + $types[] = $prefix.$r->getDeclaringClass()->name; + } else { + $types[] = ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null; + } + } + + return $types ? implode($glue, $types) : null; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/ClosureLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/ClosureLoader.php index df70cdf44..57aa83d6a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/ClosureLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/ClosureLoader.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\DependencyInjection\ContainerBuilder; /** * ClosureLoader loads service definitions from a PHP closure. @@ -25,9 +25,6 @@ class ClosureLoader extends Loader { private $container; - /** - * @param ContainerBuilder $container A ContainerBuilder instance - */ public function __construct(ContainerBuilder $container) { $this->container = $container; @@ -36,15 +33,15 @@ public function __construct(ContainerBuilder $container) /** * {@inheritdoc} */ - public function load($resource, $type = null) + public function load($resource, string $type = null) { - call_user_func($resource, $this->container); + $resource($this->container); } /** * {@inheritdoc} */ - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { return $resource instanceof \Closure; } diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php new file mode 100644 index 000000000..5ac7c6086 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; + +abstract class AbstractConfigurator +{ + public const FACTORY = 'unknown'; + + /** + * @var callable(mixed, bool $allowService)|null + */ + public static $valuePreProcessor; + + /** @internal */ + protected $definition; + + public function __call(string $method, array $args) + { + if (method_exists($this, 'set'.$method)) { + return $this->{'set'.$method}(...$args); + } + + throw new \BadMethodCallException(sprintf('Call to undefined method "%s::%s()".', static::class, $method)); + } + + /** + * @return array + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + /** + * Checks that a value is valid, optionally replacing Definition and Reference configurators by their configure value. + * + * @param mixed $value + * @param bool $allowServices whether Definition and Reference are allowed; by default, only scalars and arrays are + * + * @return mixed the value, optionally cast to a Definition/Reference + */ + public static function processValue($value, $allowServices = false) + { + if (\is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = static::processValue($v, $allowServices); + } + + return self::$valuePreProcessor ? (self::$valuePreProcessor)($value, $allowServices) : $value; + } + + if (self::$valuePreProcessor) { + $value = (self::$valuePreProcessor)($value, $allowServices); + } + + if ($value instanceof ReferenceConfigurator) { + return new Reference($value->id, $value->invalidBehavior); + } + + if ($value instanceof InlineServiceConfigurator) { + $def = $value->definition; + $value->definition = null; + + return $def; + } + + if ($value instanceof self) { + throw new InvalidArgumentException(sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY)); + } + + switch (true) { + case null === $value: + case is_scalar($value): + return $value; + + case $value instanceof ArgumentInterface: + case $value instanceof Definition: + case $value instanceof Expression: + case $value instanceof Parameter: + case $value instanceof AbstractArgument: + case $value instanceof Reference: + if ($allowServices) { + return $value; + } + } + + throw new InvalidArgumentException(sprintf('Cannot use values of type "%s" in service configuration files.', get_debug_type($value))); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php new file mode 100644 index 000000000..68b3cb5e9 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +abstract class AbstractServiceConfigurator extends AbstractConfigurator +{ + protected $parent; + protected $id; + private $defaultTags = []; + + public function __construct(ServicesConfigurator $parent, Definition $definition, string $id = null, array $defaultTags = []) + { + $this->parent = $parent; + $this->definition = $definition; + $this->id = $id; + $this->defaultTags = $defaultTags; + } + + public function __destruct() + { + // default tags should be added last + foreach ($this->defaultTags as $name => $attributes) { + foreach ($attributes as $attributes) { + $this->definition->addTag($name, $attributes); + } + } + $this->defaultTags = []; + } + + /** + * Registers a service. + */ + final public function set(?string $id, string $class = null): ServiceConfigurator + { + $this->__destruct(); + + return $this->parent->set($id, $class); + } + + /** + * Creates an alias. + */ + final public function alias(string $id, string $referencedId): AliasConfigurator + { + $this->__destruct(); + + return $this->parent->alias($id, $referencedId); + } + + /** + * Registers a PSR-4 namespace using a glob pattern. + */ + final public function load(string $namespace, string $resource): PrototypeConfigurator + { + $this->__destruct(); + + return $this->parent->load($namespace, $resource); + } + + /** + * Gets an already defined service definition. + * + * @throws ServiceNotFoundException if the service definition does not exist + */ + final public function get(string $id): ServiceConfigurator + { + $this->__destruct(); + + return $this->parent->get($id); + } + + /** + * Registers a stack of decorator services. + * + * @param InlineServiceConfigurator[]|ReferenceConfigurator[] $services + */ + final public function stack(string $id, array $services): AliasConfigurator + { + $this->__destruct(); + + return $this->parent->stack($id, $services); + } + + /** + * Registers a service. + */ + final public function __invoke(string $id, string $class = null): ServiceConfigurator + { + $this->__destruct(); + + return $this->parent->set($id, $class); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php new file mode 100644 index 000000000..650a9568d --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Alias; + +/** + * @author Nicolas Grekas + */ +class AliasConfigurator extends AbstractServiceConfigurator +{ + use Traits\DeprecateTrait; + use Traits\PublicTrait; + + public const FACTORY = 'alias'; + + public function __construct(ServicesConfigurator $parent, Alias $alias) + { + $this->parent = $parent; + $this->definition = $alias; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php new file mode 100644 index 000000000..666f11fe9 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * @author Nicolas Grekas + */ +class ContainerConfigurator extends AbstractConfigurator +{ + public const FACTORY = 'container'; + + private $container; + private $loader; + private $instanceof; + private $path; + private $file; + private $anonymousCount = 0; + + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file) + { + $this->container = $container; + $this->loader = $loader; + $this->instanceof = &$instanceof; + $this->path = $path; + $this->file = $file; + } + + final public function extension(string $namespace, array $config) + { + if (!$this->container->hasExtension($namespace)) { + $extensions = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getAlias(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $this->file, $namespace, $extensions ? implode('", "', $extensions) : 'none')); + } + + $this->container->loadFromExtension($namespace, static::processValue($config)); + } + + final public function import(string $resource, string $type = null, $ignoreErrors = false) + { + $this->loader->setCurrentDir(\dirname($this->path)); + $this->loader->import($resource, $type, $ignoreErrors, $this->file); + } + + final public function parameters(): ParametersConfigurator + { + return new ParametersConfigurator($this->container); + } + + final public function services(): ServicesConfigurator + { + return new ServicesConfigurator($this->container, $this->loader, $this->instanceof, $this->path, $this->anonymousCount); + } + + /** + * @return static + */ + final public function withPath(string $path): self + { + $clone = clone $this; + $clone->path = $clone->file = $path; + $clone->loader->setCurrentDir(\dirname($path)); + + return $clone; + } +} + +/** + * Creates a parameter. + */ +function param(string $name): string +{ + return '%'.$name.'%'; +} + +/** + * Creates a service reference. + * + * @deprecated since Symfony 5.1, use service() instead. + */ +function ref(string $id): ReferenceConfigurator +{ + trigger_deprecation('symfony/dependency-injection', '5.1', '"%s()" is deprecated, use "service()" instead.', __FUNCTION__); + + return new ReferenceConfigurator($id); +} + +/** + * Creates a reference to a service. + */ +function service(string $serviceId): ReferenceConfigurator +{ + return new ReferenceConfigurator($serviceId); +} + +/** + * Creates an inline service. + * + * @deprecated since Symfony 5.1, use inline_service() instead. + */ +function inline(string $class = null): InlineServiceConfigurator +{ + trigger_deprecation('symfony/dependency-injection', '5.1', '"%s()" is deprecated, use "inline_service()" instead.', __FUNCTION__); + + return new InlineServiceConfigurator(new Definition($class)); +} + +/** + * Creates an inline service. + */ +function inline_service(string $class = null): InlineServiceConfigurator +{ + return new InlineServiceConfigurator(new Definition($class)); +} + +/** + * Creates a service locator. + * + * @param ReferenceConfigurator[] $values + */ +function service_locator(array $values): ServiceLocatorArgument +{ + return new ServiceLocatorArgument(AbstractConfigurator::processValue($values, true)); +} + +/** + * Creates a lazy iterator. + * + * @param ReferenceConfigurator[] $values + */ +function iterator(array $values): IteratorArgument +{ + return new IteratorArgument(AbstractConfigurator::processValue($values, true)); +} + +/** + * Creates a lazy iterator by tag name. + */ +function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): TaggedIteratorArgument +{ + return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod); +} + +/** + * Creates a service locator by tag name. + */ +function tagged_locator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null): ServiceLocatorArgument +{ + return new ServiceLocatorArgument(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, true)); +} + +/** + * Creates an expression. + */ +function expr(string $expression): Expression +{ + return new Expression($expression); +} + +/** + * Creates an abstract argument. + */ +function abstract_arg(string $description): AbstractArgument +{ + return new AbstractArgument($description); +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php new file mode 100644 index 000000000..49a92e5ce --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + */ +class DefaultsConfigurator extends AbstractServiceConfigurator +{ + use Traits\AutoconfigureTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\PublicTrait; + + public const FACTORY = 'defaults'; + + private $path; + + public function __construct(ServicesConfigurator $parent, Definition $definition, string $path = null) + { + parent::__construct($parent, $definition, null, []); + + $this->path = $path; + } + + /** + * Adds a tag for this definition. + * + * @return $this + * + * @throws InvalidArgumentException when an invalid tag name or attribute is provided + */ + final public function tag(string $name, array $attributes = []): self + { + if ('' === $name) { + throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.'); + } + + foreach ($attributes as $attribute => $value) { + if (null !== $value && !is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type.', $name, $attribute)); + } + } + + $this->definition->addTag($name, $attributes); + + return $this; + } + + /** + * Defines an instanceof-conditional to be applied to following service definitions. + */ + final public function instanceof(string $fqcn): InstanceofConfigurator + { + return $this->parent->instanceof($fqcn); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php new file mode 100644 index 000000000..da90a0a4c --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Nicolas Grekas + */ +class InlineServiceConfigurator extends AbstractConfigurator +{ + use Traits\ArgumentTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\CallTrait; + use Traits\ConfiguratorTrait; + use Traits\FactoryTrait; + use Traits\FileTrait; + use Traits\LazyTrait; + use Traits\ParentTrait; + use Traits\PropertyTrait; + use Traits\TagTrait; + + public const FACTORY = 'service'; + + private $id = '[inline]'; + private $allowParent = true; + private $path = null; + + public function __construct(Definition $definition) + { + $this->definition = $definition; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php new file mode 100644 index 000000000..fbba62304 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Nicolas Grekas + */ +class InstanceofConfigurator extends AbstractServiceConfigurator +{ + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\CallTrait; + use Traits\ConfiguratorTrait; + use Traits\LazyTrait; + use Traits\PropertyTrait; + use Traits\PublicTrait; + use Traits\ShareTrait; + use Traits\TagTrait; + + public const FACTORY = 'instanceof'; + + private $path; + + public function __construct(ServicesConfigurator $parent, Definition $definition, string $id, string $path = null) + { + parent::__construct($parent, $definition, $id, []); + + $this->path = $path; + } + + /** + * Defines an instanceof-conditional to be applied to following service definitions. + */ + final public function instanceof(string $fqcn): self + { + return $this->parent->instanceof($fqcn); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php new file mode 100644 index 000000000..244da04fb --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Nicolas Grekas + */ +class ParametersConfigurator extends AbstractConfigurator +{ + public const FACTORY = 'parameters'; + + private $container; + + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } + + /** + * Creates a parameter. + * + * @return $this + */ + final public function set(string $name, $value): self + { + $this->container->setParameter($name, static::processValue($value, true)); + + return $this; + } + + /** + * Creates a parameter. + * + * @return $this + */ + final public function __invoke(string $name, $value): self + { + return $this->set($name, $value); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php new file mode 100644 index 000000000..e1b3702aa --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +/** + * @author Nicolas Grekas + */ +class PrototypeConfigurator extends AbstractServiceConfigurator +{ + use Traits\AbstractTrait; + use Traits\ArgumentTrait; + use Traits\AutoconfigureTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\CallTrait; + use Traits\ConfiguratorTrait; + use Traits\DeprecateTrait; + use Traits\FactoryTrait; + use Traits\LazyTrait; + use Traits\ParentTrait; + use Traits\PropertyTrait; + use Traits\PublicTrait; + use Traits\ShareTrait; + use Traits\TagTrait; + + public const FACTORY = 'load'; + + private $loader; + private $resource; + private $excludes; + private $allowParent; + + public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, string $namespace, string $resource, bool $allowParent) + { + $definition = new Definition(); + if (!$defaults->isPublic() || !$defaults->isPrivate()) { + $definition->setPublic($defaults->isPublic()); + } + $definition->setAutowired($defaults->isAutowired()); + $definition->setAutoconfigured($defaults->isAutoconfigured()); + // deep clone, to avoid multiple process of the same instance in the passes + $definition->setBindings(unserialize(serialize($defaults->getBindings()))); + $definition->setChanges([]); + + $this->loader = $loader; + $this->resource = $resource; + $this->allowParent = $allowParent; + + parent::__construct($parent, $definition, $namespace, $defaults->getTags()); + } + + public function __destruct() + { + parent::__destruct(); + + if ($this->loader) { + $this->loader->registerClasses($this->definition, $this->id, $this->resource, $this->excludes); + } + $this->loader = null; + } + + /** + * Excludes files from registration using glob patterns. + * + * @param string[]|string $excludes + * + * @return $this + */ + final public function exclude($excludes): self + { + $this->excludes = (array) $excludes; + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php new file mode 100644 index 000000000..fa042538c --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * @author Nicolas Grekas + */ +class ReferenceConfigurator extends AbstractConfigurator +{ + /** @internal */ + protected $id; + + /** @internal */ + protected $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + + public function __construct(string $id) + { + $this->id = $id; + } + + /** + * @return $this + */ + final public function ignoreOnInvalid(): self + { + $this->invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + + return $this; + } + + /** + * @return $this + */ + final public function nullOnInvalid(): self + { + $this->invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + + return $this; + } + + /** + * @return $this + */ + final public function ignoreOnUninitialized(): self + { + $this->invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; + + return $this; + } + + /** + * @return string + */ + public function __toString() + { + return $this->id; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php new file mode 100644 index 000000000..308d4aec7 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Nicolas Grekas + */ +class ServiceConfigurator extends AbstractServiceConfigurator +{ + use Traits\AbstractTrait; + use Traits\ArgumentTrait; + use Traits\AutoconfigureTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\CallTrait; + use Traits\ClassTrait; + use Traits\ConfiguratorTrait; + use Traits\DecorateTrait; + use Traits\DeprecateTrait; + use Traits\FactoryTrait; + use Traits\FileTrait; + use Traits\LazyTrait; + use Traits\ParentTrait; + use Traits\PropertyTrait; + use Traits\PublicTrait; + use Traits\ShareTrait; + use Traits\SyntheticTrait; + use Traits\TagTrait; + + public const FACTORY = 'services'; + + private $container; + private $instanceof; + private $allowParent; + private $path; + + public function __construct(ContainerBuilder $container, array $instanceof, bool $allowParent, ServicesConfigurator $parent, Definition $definition, ?string $id, array $defaultTags, string $path = null) + { + $this->container = $container; + $this->instanceof = $instanceof; + $this->allowParent = $allowParent; + $this->path = $path; + + parent::__construct($parent, $definition, $id, $defaultTags); + } + + public function __destruct() + { + parent::__destruct(); + + $this->container->removeBindings($this->id); + $this->container->setDefinition($this->id, $this->definition->setInstanceofConditionals($this->instanceof)); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php new file mode 100644 index 000000000..38d002a48 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php @@ -0,0 +1,179 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +/** + * @author Nicolas Grekas + */ +class ServicesConfigurator extends AbstractConfigurator +{ + public const FACTORY = 'services'; + + private $defaults; + private $container; + private $loader; + private $instanceof; + private $path; + private $anonymousHash; + private $anonymousCount; + + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path = null, int &$anonymousCount = 0) + { + $this->defaults = new Definition(); + $this->container = $container; + $this->loader = $loader; + $this->instanceof = &$instanceof; + $this->path = $path; + $this->anonymousHash = ContainerBuilder::hash($path ?: mt_rand()); + $this->anonymousCount = &$anonymousCount; + $instanceof = []; + } + + /** + * Defines a set of defaults for following service definitions. + */ + final public function defaults(): DefaultsConfigurator + { + return new DefaultsConfigurator($this, $this->defaults = new Definition(), $this->path); + } + + /** + * Defines an instanceof-conditional to be applied to following service definitions. + */ + final public function instanceof(string $fqcn): InstanceofConfigurator + { + $this->instanceof[$fqcn] = $definition = new ChildDefinition(''); + + return new InstanceofConfigurator($this, $definition, $fqcn, $this->path); + } + + /** + * Registers a service. + * + * @param string|null $id The service id, or null to create an anonymous service + * @param string|null $class The class of the service, or null when $id is also the class name + */ + final public function set(?string $id, string $class = null): ServiceConfigurator + { + $defaults = $this->defaults; + $definition = new Definition(); + + if (null === $id) { + if (!$class) { + throw new \LogicException('Anonymous services must have a class name.'); + } + + $id = sprintf('.%d_%s', ++$this->anonymousCount, preg_replace('/^.*\\\\/', '', $class).'~'.$this->anonymousHash); + } elseif (!$defaults->isPublic() || !$defaults->isPrivate()) { + $definition->setPublic($defaults->isPublic() && !$defaults->isPrivate()); + } + + $definition->setAutowired($defaults->isAutowired()); + $definition->setAutoconfigured($defaults->isAutoconfigured()); + // deep clone, to avoid multiple process of the same instance in the passes + $definition->setBindings(unserialize(serialize($defaults->getBindings()))); + $definition->setChanges([]); + + $configurator = new ServiceConfigurator($this->container, $this->instanceof, true, $this, $definition, $id, $defaults->getTags(), $this->path); + + return null !== $class ? $configurator->class($class) : $configurator; + } + + /** + * Creates an alias. + */ + final public function alias(string $id, string $referencedId): AliasConfigurator + { + $ref = static::processValue($referencedId, true); + $alias = new Alias((string) $ref); + if (!$this->defaults->isPublic() || !$this->defaults->isPrivate()) { + $alias->setPublic($this->defaults->isPublic()); + } + $this->container->setAlias($id, $alias); + + return new AliasConfigurator($this, $alias); + } + + /** + * Registers a PSR-4 namespace using a glob pattern. + */ + final public function load(string $namespace, string $resource): PrototypeConfigurator + { + return new PrototypeConfigurator($this, $this->loader, $this->defaults, $namespace, $resource, true); + } + + /** + * Gets an already defined service definition. + * + * @throws ServiceNotFoundException if the service definition does not exist + */ + final public function get(string $id): ServiceConfigurator + { + $definition = $this->container->getDefinition($id); + + return new ServiceConfigurator($this->container, $definition->getInstanceofConditionals(), true, $this, $definition, $id, []); + } + + /** + * Registers a stack of decorator services. + * + * @param InlineServiceConfigurator[]|ReferenceConfigurator[] $services + */ + final public function stack(string $id, array $services): AliasConfigurator + { + foreach ($services as $i => $service) { + if ($service instanceof InlineServiceConfigurator) { + $definition = $service->definition->setInstanceofConditionals($this->instanceof); + + $changes = $definition->getChanges(); + $definition->setAutowired((isset($changes['autowired']) ? $definition : $this->defaults)->isAutowired()); + $definition->setAutoconfigured((isset($changes['autoconfigured']) ? $definition : $this->defaults)->isAutoconfigured()); + $definition->setBindings(array_merge($this->defaults->getBindings(), $definition->getBindings())); + $definition->setChanges($changes); + + $services[$i] = $definition; + } elseif (!$service instanceof ReferenceConfigurator) { + throw new InvalidArgumentException(sprintf('"%s()" expects a list of definitions as returned by "%s()" or "%s()", "%s" given at index "%s" for service "%s".', __METHOD__, InlineServiceConfigurator::FACTORY, ReferenceConfigurator::FACTORY, $service instanceof AbstractConfigurator ? $service::FACTORY.'()' : get_debug_type($service), $i, $id)); + } + } + + $alias = $this->alias($id, ''); + $alias->definition = $this->set($id) + ->parent('') + ->args($services) + ->tag('container.stack') + ->definition; + + return $alias; + } + + /** + * Registers a service. + */ + final public function __invoke(string $id, string $class = null): ServiceConfigurator + { + return $this->set($id, $class); + } + + public function __destruct() + { + $this->loader->registerAliasesForSinglyImplementedInterfaces(); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php new file mode 100644 index 000000000..82ba21d7b --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait AbstractTrait +{ + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @return $this + */ + final public function abstract(bool $abstract = true): self + { + $this->definition->setAbstract($abstract); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php new file mode 100644 index 000000000..5c9a47560 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait ArgumentTrait +{ + /** + * Sets the arguments to pass to the service constructor/factory method. + * + * @return $this + */ + final public function args(array $arguments): self + { + $this->definition->setArguments(static::processValue($arguments, true)); + + return $this; + } + + /** + * Sets one argument to pass to the service constructor/factory method. + * + * @param string|int $key + * @param mixed $value + * + * @return $this + */ + final public function arg($key, $value): self + { + $this->definition->setArgument($key, static::processValue($value, true)); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php new file mode 100644 index 000000000..9eab22cfe --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait AutoconfigureTrait +{ + /** + * Sets whether or not instanceof conditionals should be prepended with a global set. + * + * @return $this + * + * @throws InvalidArgumentException when a parent is already set + */ + final public function autoconfigure(bool $autoconfigured = true): self + { + $this->definition->setAutoconfigured($autoconfigured); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php new file mode 100644 index 000000000..2837a0201 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait AutowireTrait +{ + /** + * Enables/disables autowiring. + * + * @return $this + */ + final public function autowire(bool $autowired = true): self + { + $this->definition->setAutowired($autowired); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php new file mode 100644 index 000000000..573b6f53a --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Loader\Configurator\DefaultsConfigurator; +use Symfony\Component\DependencyInjection\Loader\Configurator\InstanceofConfigurator; +use Symfony\Component\DependencyInjection\Reference; + +trait BindTrait +{ + /** + * Sets bindings. + * + * Bindings map $named or FQCN arguments to values that should be + * injected in the matching parameters (of the constructor, of methods + * called and of controller actions). + * + * @param string $nameOrFqcn A parameter name with its "$" prefix, or an FQCN + * @param mixed $valueOrRef The value or reference to bind + * + * @return $this + */ + final public function bind(string $nameOrFqcn, $valueOrRef): self + { + $valueOrRef = static::processValue($valueOrRef, true); + if (!preg_match('/^(?:(?:array|bool|float|int|string|iterable)[ \t]*+)?\$/', $nameOrFqcn) && !$valueOrRef instanceof Reference) { + throw new InvalidArgumentException(sprintf('Invalid binding for service "%s": named arguments must start with a "$", and FQCN must map to references. Neither applies to binding "%s".', $this->id, $nameOrFqcn)); + } + $bindings = $this->definition->getBindings(); + $type = $this instanceof DefaultsConfigurator ? BoundArgument::DEFAULTS_BINDING : ($this instanceof InstanceofConfigurator ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING); + $bindings[$nameOrFqcn] = new BoundArgument($valueOrRef, true, $type, $this->path ?? null); + $this->definition->setBindings($bindings); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php new file mode 100644 index 000000000..28f92d274 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait CallTrait +{ + /** + * Adds a method to call after service initialization. + * + * @param string $method The method name to call + * @param array $arguments An array of arguments to pass to the method call + * @param bool $returnsClone Whether the call returns the service instance or not + * + * @return $this + * + * @throws InvalidArgumentException on empty $method param + */ + final public function call(string $method, array $arguments = [], bool $returnsClone = false): self + { + $this->definition->addMethodCall($method, static::processValue($arguments, true), $returnsClone); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php new file mode 100644 index 000000000..20da791aa --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait ClassTrait +{ + /** + * Sets the service class. + * + * @return $this + */ + final public function class(?string $class): self + { + $this->definition->setClass($class); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php new file mode 100644 index 000000000..25d363c9a --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait ConfiguratorTrait +{ + /** + * Sets a configurator to call after the service is fully initialized. + * + * @param string|array $configurator A PHP callable reference + * + * @return $this + */ + final public function configurator($configurator): self + { + $this->definition->setConfigurator(static::processValue($configurator, true)); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php new file mode 100644 index 000000000..b3a1ae1b5 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait DecorateTrait +{ + /** + * Sets the service that this service is decorating. + * + * @param string|null $id The decorated service id, use null to remove decoration + * + * @return $this + * + * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals + */ + final public function decorate(?string $id, string $renamedId = null, int $priority = 0, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): self + { + $this->definition->setDecoratedService($id, $renamedId, $priority, $invalidBehavior); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php new file mode 100644 index 000000000..ea77e456d --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait DeprecateTrait +{ + /** + * Whether this definition is deprecated, that means it should not be called anymore. + * + * @param string $package The name of the composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The deprecation message to use + * + * @return $this + * + * @throws InvalidArgumentException when the message template is invalid + */ + final public function deprecate(/* string $package, string $version, string $message */): self + { + $args = \func_get_args(); + $package = $version = $message = ''; + + if (\func_num_args() < 3) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'The signature of method "%s()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.', __METHOD__); + + $message = (string) ($args[0] ?? null); + } else { + $package = (string) $args[0]; + $version = (string) $args[1]; + $message = (string) $args[2]; + } + + $this->definition->setDeprecated($package, $version, $message); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php new file mode 100644 index 000000000..1286ba4c1 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator; + +trait FactoryTrait +{ + /** + * Sets a factory. + * + * @param string|array|ReferenceConfigurator $factory A PHP callable reference + * + * @return $this + */ + final public function factory($factory): self + { + if (\is_string($factory) && 1 === substr_count($factory, ':')) { + $factoryParts = explode(':', $factory); + + throw new InvalidArgumentException(sprintf('Invalid factory "%s": the "service:method" notation is not available when using PHP-based DI configuration. Use "[service(\'%s\'), \'%s\']" instead.', $factory, $factoryParts[0], $factoryParts[1])); + } + + $this->definition->setFactory(static::processValue($factory, true)); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php new file mode 100644 index 000000000..5f42aef8f --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait FileTrait +{ + /** + * Sets a file to require before creating the service. + * + * @return $this + */ + final public function file(string $file): self + { + $this->definition->setFile($file); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php new file mode 100644 index 000000000..2829defb5 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait LazyTrait +{ + /** + * Sets the lazy flag of this service. + * + * @param bool|string $lazy A FQCN to derivate the lazy proxy from or `true` to make it extend from the definition's class + * + * @return $this + */ + final public function lazy($lazy = true): self + { + $this->definition->setLazy((bool) $lazy); + if (\is_string($lazy)) { + $this->definition->addTag('proxy', ['interface' => $lazy]); + } + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php new file mode 100644 index 000000000..37194e50e --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait ParentTrait +{ + /** + * Sets the Definition to inherit from. + * + * @return $this + * + * @throws InvalidArgumentException when parent cannot be set + */ + final public function parent(string $parent): self + { + if (!$this->allowParent) { + throw new InvalidArgumentException(sprintf('A parent cannot be defined when either "_instanceof" or "_defaults" are also defined for service prototype "%s".', $this->id)); + } + + if ($this->definition instanceof ChildDefinition) { + $this->definition->setParent($parent); + } else { + // cast Definition to ChildDefinition + $definition = serialize($this->definition); + $definition = substr_replace($definition, '53', 2, 2); + $definition = substr_replace($definition, 'Child', 44, 0); + $definition = unserialize($definition); + + $this->definition = $definition->setParent($parent); + } + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php new file mode 100644 index 000000000..10fdcfb82 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait PropertyTrait +{ + /** + * Sets a specific property. + * + * @return $this + */ + final public function property(string $name, $value): self + { + $this->definition->setProperty($name, static::processValue($value, true)); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php new file mode 100644 index 000000000..f15756c1b --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait PublicTrait +{ + /** + * @return $this + */ + final public function public(): self + { + $this->definition->setPublic(true); + + return $this; + } + + /** + * @return $this + */ + final public function private(): self + { + $this->definition->setPublic(false); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php new file mode 100644 index 000000000..16fde0f29 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait ShareTrait +{ + /** + * Sets if the service must be shared or not. + * + * @return $this + */ + final public function share(bool $shared = true): self + { + $this->definition->setShared($shared); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php new file mode 100644 index 000000000..cb08b1133 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +trait SyntheticTrait +{ + /** + * Sets whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @return $this + */ + final public function synthetic(bool $synthetic = true): self + { + $this->definition->setSynthetic($synthetic); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php new file mode 100644 index 000000000..f4d5f002c --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait TagTrait +{ + /** + * Adds a tag for this definition. + * + * @return $this + */ + final public function tag(string $name, array $attributes = []): self + { + if ('' === $name) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" must be a non-empty string.', $this->id)); + } + + foreach ($attributes as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $this->id, $name, $attribute)); + } + } + + $this->definition->addTag($name, $attributes); + + return $this; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php index ffb885301..d4b454a1e 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php @@ -11,8 +11,6 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\Config\Resource\DirectoryResource; - /** * DirectoryLoader is a recursive loader to go through directories. * @@ -23,11 +21,11 @@ class DirectoryLoader extends FileLoader /** * {@inheritdoc} */ - public function load($file, $type = null) + public function load($file, string $type = null) { $file = rtrim($file, '/'); $path = $this->locator->locate($file); - $this->container->addResource(new DirectoryResource($path)); + $this->container->fileExists($path, false); foreach (scandir($path) as $dir) { if ('.' !== $dir[0]) { @@ -45,12 +43,12 @@ public function load($file, $type = null) /** * {@inheritdoc} */ - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { if ('directory' === $type) { return true; } - return null === $type && is_string($resource) && '/' === substr($resource, -1); + return null === $type && \is_string($resource) && str_ends_with($resource, '/'); } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/FileLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/FileLoader.php index 90cd6bcfa..bdb9c9a8a 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/FileLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/FileLoader.php @@ -11,9 +11,16 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Config\Exception\LoaderLoadException; use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; +use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * FileLoader is the abstract class used by all built-in loaders that are file based. @@ -22,16 +29,197 @@ */ abstract class FileLoader extends BaseFileLoader { + public const ANONYMOUS_ID_REGEXP = '/^\.\d+_[^~]*+~[._a-zA-Z\d]{7}$/'; + protected $container; + protected $isLoadingInstanceof = false; + protected $instanceof = []; + protected $interfaces = []; + protected $singlyImplemented = []; + protected $autoRegisterAliasesForSinglyImplementedInterfaces = true; - /** - * @param ContainerBuilder $container A ContainerBuilder instance - * @param FileLocatorInterface $locator A FileLocator instance - */ public function __construct(ContainerBuilder $container, FileLocatorInterface $locator) { $this->container = $container; parent::__construct($locator); } + + /** + * {@inheritdoc} + * + * @param bool|string $ignoreErrors Whether errors should be ignored; pass "not_found" to ignore only when the loaded resource is not found + */ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null, $exclude = null) + { + $args = \func_get_args(); + + if ($ignoreNotFound = 'not_found' === $ignoreErrors) { + $args[2] = false; + } elseif (!\is_bool($ignoreErrors)) { + throw new \TypeError(sprintf('Invalid argument $ignoreErrors provided to "%s::import()": boolean or "not_found" expected, "%s" given.', static::class, get_debug_type($ignoreErrors))); + } + + try { + parent::import(...$args); + } catch (LoaderLoadException $e) { + if (!$ignoreNotFound || !($prev = $e->getPrevious()) instanceof FileLocatorFileNotFoundException) { + throw $e; + } + + foreach ($prev->getTrace() as $frame) { + if ('import' === ($frame['function'] ?? null) && is_a($frame['class'] ?? '', Loader::class, true)) { + break; + } + } + + if (__FILE__ !== $frame['file']) { + throw $e; + } + } + } + + /** + * Registers a set of classes as services using PSR-4 for discovery. + * + * @param Definition $prototype A definition to use as template + * @param string $namespace The namespace prefix of classes in the scanned directory + * @param string $resource The directory to look for classes, glob-patterns allowed + * @param string|string[]|null $exclude A globbed path of files to exclude or an array of globbed paths of files to exclude + */ + public function registerClasses(Definition $prototype, $namespace, $resource, $exclude = null) + { + if (!str_ends_with($namespace, '\\')) { + throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": "%s".', $namespace)); + } + if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++$/', $namespace)) { + throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: "%s".', $namespace)); + } + + $classes = $this->findClasses($namespace, $resource, (array) $exclude); + // prepare for deep cloning + $serializedPrototype = serialize($prototype); + + foreach ($classes as $class => $errorMessage) { + if (interface_exists($class, false)) { + $this->interfaces[] = $class; + } else { + $this->setDefinition($class, $definition = unserialize($serializedPrototype)); + if (null !== $errorMessage) { + $definition->addError($errorMessage); + + continue; + } + foreach (class_implements($class, false) as $interface) { + $this->singlyImplemented[$interface] = ($this->singlyImplemented[$interface] ?? $class) !== $class ? false : $class; + } + } + } + + if ($this->autoRegisterAliasesForSinglyImplementedInterfaces) { + $this->registerAliasesForSinglyImplementedInterfaces(); + } + } + + public function registerAliasesForSinglyImplementedInterfaces() + { + foreach ($this->interfaces as $interface) { + if (!empty($this->singlyImplemented[$interface]) && !$this->container->has($interface)) { + $this->container->setAlias($interface, $this->singlyImplemented[$interface]); + } + } + + $this->interfaces = $this->singlyImplemented = []; + } + + /** + * Registers a definition in the container with its instanceof-conditionals. + * + * @param string $id + */ + protected function setDefinition($id, Definition $definition) + { + $this->container->removeBindings($id); + + if ($this->isLoadingInstanceof) { + if (!$definition instanceof ChildDefinition) { + throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, get_debug_type($definition))); + } + $this->instanceof[$id] = $definition; + } else { + $this->container->setDefinition($id, $definition->setInstanceofConditionals($this->instanceof)); + } + } + + private function findClasses(string $namespace, string $pattern, array $excludePatterns): array + { + $parameterBag = $this->container->getParameterBag(); + + $excludePaths = []; + $excludePrefix = null; + $excludePatterns = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePatterns)); + foreach ($excludePatterns as $excludePattern) { + foreach ($this->glob($excludePattern, true, $resource, true, true) as $path => $info) { + if (null === $excludePrefix) { + $excludePrefix = $resource->getPrefix(); + } + + // normalize Windows slashes and remove trailing slashes + $excludePaths[rtrim(str_replace('\\', '/', $path), '/')] = true; + } + } + + $pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern)); + $classes = []; + $extRegexp = '/\\.php$/'; + $prefixLen = null; + foreach ($this->glob($pattern, true, $resource, false, false, $excludePaths) as $path => $info) { + if (null === $prefixLen) { + $prefixLen = \strlen($resource->getPrefix()); + + if ($excludePrefix && !str_starts_with($excludePrefix, $resource->getPrefix())) { + throw new InvalidArgumentException(sprintf('Invalid "exclude" pattern when importing classes for "%s": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s).', $namespace, $excludePattern, $pattern)); + } + } + + if (isset($excludePaths[str_replace('\\', '/', $path)])) { + continue; + } + + if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) { + continue; + } + $class = $namespace.ltrim(str_replace('/', '\\', substr($path, $prefixLen, -\strlen($m[0]))), '\\'); + + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $class)) { + continue; + } + + try { + $r = $this->container->getReflectionClass($class); + } catch (\ReflectionException $e) { + $classes[$class] = $e->getMessage(); + continue; + } + // check to make sure the expected class exists + if (!$r) { + throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern)); + } + + if ($r->isInstantiable() || $r->isInterface()) { + $classes[$class] = null; + } + } + + // track only for new & removed files + if ($resource instanceof GlobResource) { + $this->container->addResource($resource); + } else { + foreach ($resource as $path) { + $this->container->fileExists($path, false); + } + } + + return $classes; + } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php new file mode 100644 index 000000000..53af9cf2b --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Nicolas Grekas + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, string $type = null) + { + foreach ($this->glob($resource, false, $globResource) as $path => $info) { + $this->import($path); + } + + $this->container->addResource($globResource); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, string $type = null) + { + return 'glob' === $type; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/IniFileLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/IniFileLoader.php index 9fea56be1..f313cbcae 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/IniFileLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/IniFileLoader.php @@ -11,7 +11,6 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -25,22 +24,22 @@ class IniFileLoader extends FileLoader /** * {@inheritdoc} */ - public function load($resource, $type = null) + public function load($resource, string $type = null) { $path = $this->locator->locate($resource); - $this->container->addResource(new FileResource($path)); + $this->container->fileExists($path); // first pass to catch parsing errors $result = parse_ini_file($path, true); - if (false === $result || array() === $result) { + if (false === $result || [] === $result) { throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource)); } // real raw parsing - $result = parse_ini_file($path, true, INI_SCANNER_RAW); + $result = parse_ini_file($path, true, \INI_SCANNER_RAW); - if (isset($result['parameters']) && is_array($result['parameters'])) { + if (isset($result['parameters']) && \is_array($result['parameters'])) { foreach ($result['parameters'] as $key => $value) { $this->container->setParameter($key, $this->phpize($value)); } @@ -50,32 +49,44 @@ public function load($resource, $type = null) /** * {@inheritdoc} */ - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { - return is_string($resource) && 'ini' === pathinfo($resource, PATHINFO_EXTENSION); + if (!\is_string($resource)) { + return false; + } + + if (null === $type && 'ini' === pathinfo($resource, \PATHINFO_EXTENSION)) { + return true; + } + + return 'ini' === $type; } /** * Note that the following features are not supported: * * strings with escaped quotes are not supported "foo\"bar"; * * string concatenation ("foo" "bar"). + * + * @return mixed */ - private function phpize($value) + private function phpize(string $value) { // trim on the right as comments removal keep whitespaces - $value = rtrim($value); + if ($value !== $v = rtrim($value)) { + $value = '""' === substr_replace($v, '', 1, -1) ? substr($v, 1, -1) : $v; + } $lowercaseValue = strtolower($value); switch (true) { - case defined($value): - return constant($value); + case \defined($value): + return \constant($value); case 'yes' === $lowercaseValue || 'on' === $lowercaseValue: return true; case 'no' === $lowercaseValue || 'off' === $lowercaseValue || 'none' === $lowercaseValue: return false; case isset($value[1]) && ( - ("'" === $value[0] && "'" === $value[strlen($value) - 1]) || - ('"' === $value[0] && '"' === $value[strlen($value) - 1]) + ("'" === $value[0] && "'" === $value[\strlen($value) - 1]) || + ('"' === $value[0] && '"' === $value[\strlen($value) - 1]) ): // quoted string return substr($value, 1, -1); diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php index 08c1d9af4..196baf622 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php @@ -11,7 +11,7 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; /** * PhpFileLoader loads service definitions from a PHP file. @@ -23,6 +23,8 @@ */ class PhpFileLoader extends FileLoader { + protected $autoRegisterAliasesForSinglyImplementedInterfaces = false; + /** * {@inheritdoc} */ @@ -33,17 +35,46 @@ public function load($resource, $type = null) $loader = $this; $path = $this->locator->locate($resource); - $this->setCurrentDir(dirname($path)); - $this->container->addResource(new FileResource($path)); + $this->setCurrentDir(\dirname($path)); + $this->container->fileExists($path); + + // the closure forbids access to the private scope in the included file + $load = \Closure::bind(function ($path) use ($container, $loader, $resource, $type) { + return include $path; + }, $this, ProtectedPhpFileLoader::class); + + try { + $callback = $load($path); - include $path; + if (\is_object($callback) && \is_callable($callback)) { + $callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this); + } + } finally { + $this->instanceof = []; + $this->registerAliasesForSinglyImplementedInterfaces(); + } } /** * {@inheritdoc} */ - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { - return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION); + if (!\is_string($resource)) { + return false; + } + + if (null === $type && 'php' === pathinfo($resource, \PATHINFO_EXTENSION)) { + return true; + } + + return 'php' === $type; } } + +/** + * @internal + */ +final class ProtectedPhpFileLoader extends PhpFileLoader +{ +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php index 01911625f..970818fdf 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php @@ -11,15 +11,22 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\XmlUtils; -use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; /** @@ -29,18 +36,22 @@ */ class XmlFileLoader extends FileLoader { - const NS = 'http://symfony.com/schema/dic/services'; + public const NS = 'http://symfony.com/schema/dic/services'; + + protected $autoRegisterAliasesForSinglyImplementedInterfaces = false; /** * {@inheritdoc} */ - public function load($resource, $type = null) + public function load($resource, string $type = null) { $path = $this->locator->locate($resource); $xml = $this->parseFileToDOM($path); - $this->container->addResource(new FileResource($path)); + $this->container->fileExists($path); + + $defaults = $this->getServiceDefaults($xml, $path); // anonymous services $this->processAnonymousServices($xml, $path); @@ -49,42 +60,44 @@ public function load($resource, $type = null) $this->parseImports($xml, $path); // parameters - $this->parseParameters($xml); + $this->parseParameters($xml, $path); // extensions $this->loadFromExtensions($xml); // services - $this->parseDefinitions($xml, $path); + try { + $this->parseDefinitions($xml, $path, $defaults); + } finally { + $this->instanceof = []; + $this->registerAliasesForSinglyImplementedInterfaces(); + } } /** * {@inheritdoc} */ - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { - return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION); + if (!\is_string($resource)) { + return false; + } + + if (null === $type && 'xml' === pathinfo($resource, \PATHINFO_EXTENSION)) { + return true; + } + + return 'xml' === $type; } - /** - * Parses parameters. - * - * @param \DOMDocument $xml - */ - private function parseParameters(\DOMDocument $xml) + private function parseParameters(\DOMDocument $xml, string $file) { if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { - $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter')); + $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter', $file)); } } - /** - * Parses imports. - * - * @param \DOMDocument $xml - * @param string $file - */ - private function parseImports(\DOMDocument $xml, $file) + private function parseImports(\DOMDocument $xml, string $file) { $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); @@ -93,67 +106,145 @@ private function parseImports(\DOMDocument $xml, $file) return; } - $defaultDirectory = dirname($file); + $defaultDirectory = \dirname($file); foreach ($imports as $import) { $this->setCurrentDir($defaultDirectory); - $this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); + $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, XmlUtils::phpize($import->getAttribute('ignore-errors')) ?: false, $file); } } - /** - * Parses multiple definitions. - * - * @param \DOMDocument $xml - * @param string $file - */ - private function parseDefinitions(\DOMDocument $xml, $file) + private function parseDefinitions(\DOMDocument $xml, string $file, Definition $defaults) { $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); - if (false === $services = $xpath->query('//container:services/container:service')) { + if (false === $services = $xpath->query('//container:services/container:service|//container:services/container:prototype|//container:services/container:stack')) { return; } + $this->setCurrentDir(\dirname($file)); + + $this->instanceof = []; + $this->isLoadingInstanceof = true; + $instanceof = $xpath->query('//container:services/container:instanceof'); + foreach ($instanceof as $service) { + $this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, new Definition())); + } + $this->isLoadingInstanceof = false; foreach ($services as $service) { - if (null !== $definition = $this->parseDefinition($service, $file)) { - $this->container->setDefinition((string) $service->getAttribute('id'), $definition); + if ('stack' === $service->tagName) { + $service->setAttribute('parent', '-'); + $definition = $this->parseDefinition($service, $file, $defaults) + ->setTags(array_merge_recursive(['container.stack' => [[]]], $defaults->getTags())) + ; + $this->setDefinition($id = (string) $service->getAttribute('id'), $definition); + $stack = []; + + foreach ($this->getChildren($service, 'service') as $k => $frame) { + $k = $frame->getAttribute('id') ?: $k; + $frame->setAttribute('id', $id.'" at index "'.$k); + + if ($alias = $frame->getAttribute('alias')) { + $this->validateAlias($frame, $file); + $stack[$k] = new Reference($alias); + } else { + $stack[$k] = $this->parseDefinition($frame, $file, $defaults) + ->setInstanceofConditionals($this->instanceof); + } + } + + $definition->setArguments($stack); + } elseif (null !== $definition = $this->parseDefinition($service, $file, $defaults)) { + if ('prototype' === $service->tagName) { + $excludes = array_column($this->getChildren($service, 'exclude'), 'nodeValue'); + if ($service->hasAttribute('exclude')) { + if (\count($excludes) > 0) { + throw new InvalidArgumentException('You cannot use both the attribute "exclude" and tags at the same time.'); + } + $excludes = [$service->getAttribute('exclude')]; + } + $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), $excludes); + } else { + $this->setDefinition((string) $service->getAttribute('id'), $definition); + } } } } + private function getServiceDefaults(\DOMDocument $xml, string $file): Definition + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (null === $defaultsNode = $xpath->query('//container:services/container:defaults')->item(0)) { + return new Definition(); + } + + $defaultsNode->setAttribute('id', ''); + + return $this->parseDefinition($defaultsNode, $file, new Definition()); + } + /** * Parses an individual Definition. - * - * @param \DOMElement $service - * @param string $file - * - * @return Definition|null */ - private function parseDefinition(\DOMElement $service, $file) + private function parseDefinition(\DOMElement $service, string $file, Definition $defaults): ?Definition { if ($alias = $service->getAttribute('alias')) { $this->validateAlias($service, $file); - $public = true; + $this->container->setAlias((string) $service->getAttribute('id'), $alias = new Alias($alias)); if ($publicAttr = $service->getAttribute('public')) { - $public = XmlUtils::phpize($publicAttr); + $alias->setPublic(XmlUtils::phpize($publicAttr)); + } elseif ($defaults->getChanges()['public'] ?? false) { + $alias->setPublic($defaults->isPublic()); } - $this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public)); - return; + if ($deprecated = $this->getChildren($service, 'deprecated')) { + $message = $deprecated[0]->nodeValue ?: ''; + $package = $deprecated[0]->getAttribute('package') ?: ''; + $version = $deprecated[0]->getAttribute('version') ?: ''; + + if (!$deprecated[0]->hasAttribute('package')) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.', $file); + } + + if (!$deprecated[0]->hasAttribute('version')) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" in "%s" is deprecated.', $file); + } + + $alias->setDeprecated($package, $version, $message); + } + + return null; } - if ($parent = $service->getAttribute('parent')) { - $definition = new DefinitionDecorator($parent); + if ($this->isLoadingInstanceof) { + $definition = new ChildDefinition(''); + } elseif ($parent = $service->getAttribute('parent')) { + $definition = new ChildDefinition($parent); } else { $definition = new Definition(); } - foreach (array('class', 'shared', 'public', 'synthetic', 'lazy', 'abstract') as $key) { + if ($defaults->getChanges()['public'] ?? false) { + $definition->setPublic($defaults->isPublic()); + } + $definition->setAutowired($defaults->isAutowired()); + $definition->setAutoconfigured($defaults->isAutoconfigured()); + $definition->setChanges([]); + + foreach (['class', 'public', 'shared', 'synthetic', 'abstract'] as $key) { if ($value = $service->getAttribute($key)) { $method = 'set'.$key; - $definition->$method(XmlUtils::phpize($value)); + $definition->$method($value = XmlUtils::phpize($value)); + } + } + + if ($value = $service->getAttribute('lazy')) { + $definition->setLazy((bool) $value = XmlUtils::phpize($value)); + if (\is_string($value)) { + $definition->addTag('proxy', ['interface' => $value]); } } @@ -161,33 +252,45 @@ private function parseDefinition(\DOMElement $service, $file) $definition->setAutowired(XmlUtils::phpize($value)); } + if ($value = $service->getAttribute('autoconfigure')) { + $definition->setAutoconfigured(XmlUtils::phpize($value)); + } + if ($files = $this->getChildren($service, 'file')) { $definition->setFile($files[0]->nodeValue); } if ($deprecated = $this->getChildren($service, 'deprecated')) { - $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); + $message = $deprecated[0]->nodeValue ?: ''; + $package = $deprecated[0]->getAttribute('package') ?: ''; + $version = $deprecated[0]->getAttribute('version') ?: ''; + + if ('' === $package) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the node "deprecated" in "%s" is deprecated.', $file); + } + + if ('' === $version) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the node "deprecated" in "%s" is deprecated.', $file); + } + + $definition->setDeprecated($package, $version, $message); } - $definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); - $definition->setProperties($this->getArgumentsAsPhp($service, 'property')); + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, $definition instanceof ChildDefinition)); + $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file)); if ($factories = $this->getChildren($service, 'factory')) { $factory = $factories[0]; if ($function = $factory->getAttribute('function')) { $definition->setFactory($function); } else { - $factoryService = $this->getChildren($factory, 'service'); - - if (isset($factoryService[0])) { - $class = $this->parseDefinition($factoryService[0], $file); - } elseif ($childService = $factory->getAttribute('service')) { - $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); + if ($childService = $factory->getAttribute('service')) { + $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); } else { - $class = $factory->getAttribute('class'); + $class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null; } - $definition->setFactory(array($class, $factory->getAttribute('method'))); + $definition->setFactory([$class, $factory->getAttribute('method') ?: '__invoke']); } } @@ -196,73 +299,91 @@ private function parseDefinition(\DOMElement $service, $file) if ($function = $configurator->getAttribute('function')) { $definition->setConfigurator($function); } else { - $configuratorService = $this->getChildren($configurator, 'service'); - - if (isset($configuratorService[0])) { - $class = $this->parseDefinition($configuratorService[0], $file); - } elseif ($childService = $configurator->getAttribute('service')) { - $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); + if ($childService = $configurator->getAttribute('service')) { + $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); } else { $class = $configurator->getAttribute('class'); } - $definition->setConfigurator(array($class, $configurator->getAttribute('method'))); + $definition->setConfigurator([$class, $configurator->getAttribute('method') ?: '__invoke']); } } foreach ($this->getChildren($service, 'call') as $call) { - $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument')); + $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument', $file), XmlUtils::phpize($call->getAttribute('returns-clone'))); } - foreach ($this->getChildren($service, 'tag') as $tag) { - $parameters = array(); + $tags = $this->getChildren($service, 'tag'); + + foreach ($tags as $tag) { + $parameters = []; + $tagName = $tag->nodeValue; foreach ($tag->attributes as $name => $node) { - if ('name' === $name) { + if ('name' === $name && '' === $tagName) { continue; } - if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { + if (str_contains($name, '-') && !str_contains($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); } // keep not normalized key $parameters[$name] = XmlUtils::phpize($node->nodeValue); } - if ('' === $tag->getAttribute('name')) { - throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', (string) $service->getAttribute('id'), $file)); + if ('' === $tagName && '' === $tagName = $tag->getAttribute('name')) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file)); } - $definition->addTag($tag->getAttribute('name'), $parameters); + $definition->addTag($tagName, $parameters); } - foreach ($this->getChildren($service, 'autowiring-type') as $type) { - $definition->addAutowiringType($type->textContent); + $definition->setTags(array_merge_recursive($definition->getTags(), $defaults->getTags())); + + $bindings = $this->getArgumentsAsPhp($service, 'bind', $file); + $bindingType = $this->isLoadingInstanceof ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING; + foreach ($bindings as $argument => $value) { + $bindings[$argument] = new BoundArgument($value, true, $bindingType, $file); + } + + // deep clone, to avoid multiple process of the same instance in the passes + $bindings = array_merge(unserialize(serialize($defaults->getBindings())), $bindings); + + if ($bindings) { + $definition->setBindings($bindings); } - if ($value = $service->getAttribute('decorates')) { + if ($decorates = $service->getAttribute('decorates')) { + $decorationOnInvalid = $service->getAttribute('decoration-on-invalid') ?: 'exception'; + if ('exception' === $decorationOnInvalid) { + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + } elseif ('ignore' === $decorationOnInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } elseif ('null' === $decorationOnInvalid) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + } else { + throw new InvalidArgumentException(sprintf('Invalid value "%s" for attribute "decoration-on-invalid" on service "%s". Did you mean "exception", "ignore" or "null" in "%s"?', $decorationOnInvalid, (string) $service->getAttribute('id'), $file)); + } + $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; - $definition->setDecoratedService($value, $renameId, $priority); + + $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior); } return $definition; } /** - * Parses a XML file to a \DOMDocument. - * - * @param string $file Path to a file - * - * @return \DOMDocument + * Parses an XML file to a \DOMDocument. * * @throws InvalidArgumentException When loading of XML file returns error */ - private function parseFileToDOM($file) + private function parseFileToDOM(string $file): \DOMDocument { try { - $dom = XmlUtils::loadFile($file, array($this, 'validateSchema')); + $dom = XmlUtils::loadFile($file, [$this, 'validateSchema']); } catch (\InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e); + throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).$e->getMessage(), $e->getCode(), $e); } $this->validateExtensions($dom, $file); @@ -272,27 +393,26 @@ private function parseFileToDOM($file) /** * Processes anonymous services. - * - * @param \DOMDocument $xml - * @param string $file */ - private function processAnonymousServices(\DOMDocument $xml, $file) + private function processAnonymousServices(\DOMDocument $xml, string $file) { - $definitions = array(); + $definitions = []; $count = 0; + $suffix = '~'.ContainerBuilder::hash($file); $xpath = new \DOMXPath($xml); $xpath->registerNamespace('container', self::NS); // anonymous services as arguments/properties - if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) { + if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]|//container:bind[not(@id)]|//container:factory[not(@service)]|//container:configurator[not(@service)]')) { foreach ($nodes as $node) { - // give it a unique name - $id = sprintf('%s_%d', hash('sha256', $file), ++$count); - $node->setAttribute('id', $id); - if ($services = $this->getChildren($node, 'service')) { - $definitions[$id] = array($services[0], $file, false); + // give it a unique name + $id = sprintf('.%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $services[0]->getAttribute('class')).$suffix); + $node->setAttribute('id', $id); + $node->setAttribute('service', $id); + + $definitions[$id] = [$services[0], $file]; $services[0]->setAttribute('id', $id); // anonymous services are always private @@ -305,51 +425,31 @@ private function processAnonymousServices(\DOMDocument $xml, $file) // anonymous services "in the wild" if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { foreach ($nodes as $node) { - // give it a unique name - $id = sprintf('%s_%d', hash('sha256', $file), ++$count); - $node->setAttribute('id', $id); - $definitions[$id] = array($node, $file, true); + throw new InvalidArgumentException(sprintf('Top-level services must have "id" attribute, none found in "%s" at line %d.', $file, $node->getLineNo())); } } // resolve definitions - krsort($definitions); - foreach ($definitions as $id => list($domElement, $file, $wild)) { - if (null !== $definition = $this->parseDefinition($domElement, $file)) { - $this->container->setDefinition($id, $definition); - } - - if (true === $wild) { - $tmpDomElement = new \DOMElement('_services', null, self::NS); - $domElement->parentNode->replaceChild($tmpDomElement, $domElement); - $tmpDomElement->setAttribute('id', $id); - } else { - $domElement->parentNode->removeChild($domElement); + uksort($definitions, 'strnatcmp'); + foreach (array_reverse($definitions) as $id => [$domElement, $file]) { + if (null !== $definition = $this->parseDefinition($domElement, $file, new Definition())) { + $this->setDefinition($id, $definition); } } } - /** - * Returns arguments as valid php types. - * - * @param \DOMElement $node - * @param string $name - * @param bool $lowercase - * - * @return mixed - */ - private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) + private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file, bool $isChildDefinition = false): array { - $arguments = array(); + $arguments = []; foreach ($this->getChildren($node, $name) as $arg) { if ($arg->hasAttribute('name')) { $arg->setAttribute('key', $arg->getAttribute('name')); } - // this is used by DefinitionDecorator to overwrite a specific + // this is used by ChildDefinition to overwrite a specific // argument of the parent definition if ($arg->hasAttribute('index')) { - $key = 'index_'.$arg->getAttribute('index'); + $key = ($isChildDefinition ? 'index_' : '').$arg->getAttribute('index'); } elseif (!$arg->hasAttribute('key')) { // Append an empty argument, then fetch its key to overwrite it later $arguments[] = null; @@ -357,42 +457,89 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) $key = array_pop($keys); } else { $key = $arg->getAttribute('key'); + } - // parameter keys are case insensitive - if ('parameter' == $name && $lowercase) { - $key = strtolower($key); - } + $onInvalid = $arg->getAttribute('on-invalid'); + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + if ('ignore' == $onInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } elseif ('ignore_uninitialized' == $onInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; + } elseif ('null' == $onInvalid) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } switch ($arg->getAttribute('type')) { case 'service': - $onInvalid = $arg->getAttribute('on-invalid'); - $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; - if ('ignore' == $onInvalid) { - $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; - } elseif ('null' == $onInvalid) { - $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; - } - - if ($strict = $arg->getAttribute('strict')) { - $strict = XmlUtils::phpize($strict); - } else { - $strict = true; + if ('' === $arg->getAttribute('id')) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".', $name, $file)); } - $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict); + $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); break; case 'expression': + if (!class_exists(Expression::class)) { + throw new \LogicException('The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'); + } + $arguments[$key] = new Expression($arg->nodeValue); break; case 'collection': - $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false); + $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file); + break; + case 'iterator': + $arg = $this->getArgumentsAsPhp($arg, $name, $file); + try { + $arguments[$key] = new IteratorArgument($arg); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file)); + } + break; + case 'service_closure': + if ('' === $arg->getAttribute('id')) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service_closure" has no or empty "id" attribute in "%s".', $name, $file)); + } + + $arguments[$key] = new ServiceClosureArgument(new Reference($arg->getAttribute('id'), $invalidBehavior)); + break; + case 'service_locator': + $arg = $this->getArgumentsAsPhp($arg, $name, $file); + try { + $arguments[$key] = new ServiceLocatorArgument($arg); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service_locator" only accepts maps of type="service" references in "%s".', $name, $file)); + } + break; + case 'tagged': + case 'tagged_iterator': + case 'tagged_locator': + $type = $arg->getAttribute('type'); + $forLocator = 'tagged_locator' === $type; + + if (!$arg->getAttribute('tag')) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="%s" has no or empty "tag" attribute in "%s".', $name, $type, $file)); + } + + $arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null, $forLocator, $arg->getAttribute('default-priority-method') ?: null); + + if ($forLocator) { + $arguments[$key] = new ServiceLocatorArgument($arguments[$key]); + } + break; + case 'binary': + if (false === $value = base64_decode($arg->nodeValue)) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="binary" is not a valid base64 encoded string.', $name)); + } + $arguments[$key] = $value; + break; + case 'abstract': + $arguments[$key] = new AbstractArgument($arg->nodeValue); break; case 'string': $arguments[$key] = $arg->nodeValue; break; case 'constant': - $arguments[$key] = constant(trim($arg->nodeValue)); + $arguments[$key] = \constant(trim($arg->nodeValue)); break; default: $arguments[$key] = XmlUtils::phpize($arg->nodeValue); @@ -405,16 +552,13 @@ private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) /** * Get child elements by name. * - * @param \DOMNode $node - * @param mixed $name - * - * @return array + * @return \DOMElement[] */ - private function getChildren(\DOMNode $node, $name) + private function getChildren(\DOMNode $node, string $name): array { - $children = array(); + $children = []; foreach ($node->childNodes as $child) { - if ($child instanceof \DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) { + if ($child instanceof \DOMElement && $child->localName === $name && self::NS === $child->namespaceURI) { $children[] = $child; } } @@ -425,28 +569,27 @@ private function getChildren(\DOMNode $node, $name) /** * Validates a documents XML schema. * - * @param \DOMDocument $dom - * * @return bool * * @throws RuntimeException When extension references a non-existent XSD file */ public function validateSchema(\DOMDocument $dom) { - $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')); + $schemaLocations = ['http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')]; if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { $items = preg_split('/\s+/', $element); - for ($i = 0, $nb = count($items); $i < $nb; $i += 2) { + for ($i = 0, $nb = \count($items); $i < $nb; $i += 2) { if (!$this->container->hasExtension($items[$i])) { continue; } if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) { - $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); + $ns = $extension->getNamespace(); + $path = str_replace([$ns, str_replace('http://', 'https://', $ns)], str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); if (!is_file($path)) { - throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path)); + throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s".', get_debug_type($extension), $path)); } $schemaLocations[$items[$i]] = $path; @@ -454,20 +597,26 @@ public function validateSchema(\DOMDocument $dom) } } - $tmpfiles = array(); + $tmpfiles = []; $imports = ''; foreach ($schemaLocations as $namespace => $location) { $parts = explode('/', $location); + $locationstart = 'file:///'; if (0 === stripos($location, 'phar://')) { - $tmpfile = tempnam(sys_get_temp_dir(), 'sf2'); + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); if ($tmpfile) { copy($location, $tmpfile); $tmpfiles[] = $tmpfile; $parts = explode('/', str_replace('\\', '/', $tmpfile)); + } else { + array_shift($parts); + $locationstart = 'phar:///'; } + } elseif ('\\' === \DIRECTORY_SEPARATOR && str_starts_with($location, '\\\\')) { + $locationstart = ''; } - $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; - $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts)); + $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; + $location = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); $imports .= sprintf(' '."\n", $namespace, $location); } @@ -485,10 +634,13 @@ public function validateSchema(\DOMDocument $dom) EOF ; - $disableEntities = libxml_disable_entity_loader(false); - $valid = @$dom->schemaValidateSource($source); - libxml_disable_entity_loader($disableEntities); - + if ($this->shouldEnableEntityLoader()) { + $disableEntities = libxml_disable_entity_loader(false); + $valid = @$dom->schemaValidateSource($source); + libxml_disable_entity_loader($disableEntities); + } else { + $valid = @$dom->schemaValidateSource($source); + } foreach ($tmpfiles as $tmpfile) { @unlink($tmpfile); } @@ -496,23 +648,50 @@ public function validateSchema(\DOMDocument $dom) return $valid; } - /** - * Validates an alias. - * - * @param \DOMElement $alias - * @param string $file - */ - private function validateAlias(\DOMElement $alias, $file) + private function shouldEnableEntityLoader(): bool + { + // Version prior to 8.0 can be enabled without deprecation + if (\PHP_VERSION_ID < 80000) { + return true; + } + + static $dom, $schema; + if (null === $dom) { + $dom = new \DOMDocument(); + $dom->loadXML(''); + + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); + register_shutdown_function(static function () use ($tmpfile) { + @unlink($tmpfile); + }); + $schema = ' + + +'; + file_put_contents($tmpfile, ' + + + +'); + } + + return !@$dom->schemaValidateSource($schema); + } + + private function validateAlias(\DOMElement $alias, string $file) { foreach ($alias->attributes as $name => $node) { - if (!in_array($name, array('alias', 'id', 'public'))) { - @trigger_error(sprintf('Using the attribute "%s" is deprecated for the service "%s" which is defined as an alias in "%s". Allowed attributes for service aliases are "alias", "id" and "public". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $name, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); + if (!\in_array($name, ['alias', 'id', 'public'])) { + throw new InvalidArgumentException(sprintf('Invalid attribute "%s" defined for alias "%s" in "%s".', $name, $alias->getAttribute('id'), $file)); } } foreach ($alias->childNodes as $child) { - if ($child instanceof \DOMElement && $child->namespaceURI === self::NS) { - @trigger_error(sprintf('Using the element "%s" is deprecated for the service "%s" which is defined as an alias in "%s". The XmlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported elements.', $child->localName, $alias->getAttribute('id'), $file), E_USER_DEPRECATED); + if (!$child instanceof \DOMElement || self::NS !== $child->namespaceURI) { + continue; + } + if (!\in_array($child->localName, ['deprecated'], true)) { + throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file)); } } } @@ -520,12 +699,9 @@ private function validateAlias(\DOMElement $alias, $file) /** * Validates an extension. * - * @param \DOMDocument $dom - * @param string $file - * * @throws InvalidArgumentException When no extension is found corresponding to a tag */ - private function validateExtensions(\DOMDocument $dom, $file) + private function validateExtensions(\DOMDocument $dom, string $file) { foreach ($dom->documentElement->childNodes as $node) { if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) { @@ -534,33 +710,25 @@ private function validateExtensions(\DOMDocument $dom, $file) // can it be handled by an extension? if (!$this->container->hasExtension($node->namespaceURI)) { - $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); - throw new InvalidArgumentException(sprintf( - 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', - $node->tagName, - $file, - $node->namespaceURI, - $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' - )); + $extensionNamespaces = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $node->tagName, $file, $node->namespaceURI, $extensionNamespaces ? implode('", "', $extensionNamespaces) : 'none')); } } } /** * Loads from an extension. - * - * @param \DOMDocument $xml */ private function loadFromExtensions(\DOMDocument $xml) { foreach ($xml->documentElement->childNodes as $node) { - if (!$node instanceof \DOMElement || $node->namespaceURI === self::NS) { + if (!$node instanceof \DOMElement || self::NS === $node->namespaceURI) { continue; } $values = static::convertDomElementToArray($node); - if (!is_array($values)) { - $values = array(); + if (!\is_array($values)) { + $values = []; } $this->container->loadFromExtension($node->namespaceURI, $values); @@ -568,7 +736,7 @@ private function loadFromExtensions(\DOMDocument $xml) } /** - * Converts a \DomElement object to a PHP array. + * Converts a \DOMElement object to a PHP array. * * The following rules applies during the conversion: * @@ -582,9 +750,9 @@ private function loadFromExtensions(\DOMDocument $xml) * * * The nested-tags are converted to keys (bar) * - * @param \DomElement $element A \DomElement instance + * @param \DOMElement $element A \DOMElement instance * - * @return array A PHP array + * @return mixed */ public static function convertDomElementToArray(\DOMElement $element) { diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php b/tests/integration/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php index 06cc506e6..b644bd0ae 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php @@ -11,29 +11,36 @@ namespace Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; -use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\Yaml\Tag\TaggedValue; use Symfony\Component\Yaml\Yaml; -use Symfony\Component\ExpressionLanguage\Expression; /** * YamlFileLoader loads YAML files service definitions. * - * The YAML format does not support anonymous services (cf. the XML loader). - * * @author Fabien Potencier */ class YamlFileLoader extends FileLoader { - private static $keywords = array( + private const SERVICE_KEYWORDS = [ 'alias' => 'alias', 'parent' => 'parent', 'class' => 'class', @@ -53,22 +60,70 @@ class YamlFileLoader extends FileLoader 'decorates' => 'decorates', 'decoration_inner_name' => 'decoration_inner_name', 'decoration_priority' => 'decoration_priority', + 'decoration_on_invalid' => 'decoration_on_invalid', + 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', + 'bind' => 'bind', + ]; + + private const PROTOTYPE_KEYWORDS = [ + 'resource' => 'resource', + 'namespace' => 'namespace', + 'exclude' => 'exclude', + 'parent' => 'parent', + 'shared' => 'shared', + 'lazy' => 'lazy', + 'public' => 'public', + 'abstract' => 'abstract', + 'deprecated' => 'deprecated', + 'factory' => 'factory', + 'arguments' => 'arguments', + 'properties' => 'properties', + 'configurator' => 'configurator', + 'calls' => 'calls', + 'tags' => 'tags', + 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', + 'bind' => 'bind', + ]; + + private const INSTANCEOF_KEYWORDS = [ + 'shared' => 'shared', + 'lazy' => 'lazy', + 'public' => 'public', + 'properties' => 'properties', + 'configurator' => 'configurator', + 'calls' => 'calls', + 'tags' => 'tags', + 'autowire' => 'autowire', + 'bind' => 'bind', + ]; + + private const DEFAULTS_KEYWORDS = [ + 'public' => 'public', + 'tags' => 'tags', 'autowire' => 'autowire', - 'autowiring_types' => 'autowiring_types', - ); + 'autoconfigure' => 'autoconfigure', + 'bind' => 'bind', + ]; private $yamlParser; + private $anonymousServicesCount; + private $anonymousServicesSuffix; + + protected $autoRegisterAliasesForSinglyImplementedInterfaces = false; + /** * {@inheritdoc} */ - public function load($resource, $type = null) + public function load($resource, string $type = null) { $path = $this->locator->locate($resource); $content = $this->loadFile($path); - $this->container->addResource(new FileResource($path)); + $this->container->fileExists($path); // empty file if (null === $content) { @@ -80,12 +135,12 @@ public function load($resource, $type = null) // parameters if (isset($content['parameters'])) { - if (!is_array($content['parameters'])) { - throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $resource)); + if (!\is_array($content['parameters'])) { + throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in "%s". Check your YAML syntax.', $path)); } foreach ($content['parameters'] as $key => $value) { - $this->container->setParameter($key, $this->resolveServices($value)); + $this->container->setParameter($key, $this->resolveServices($value, $path, true)); } } @@ -93,107 +148,306 @@ public function load($resource, $type = null) $this->loadFromExtensions($content); // services - $this->parseDefinitions($content, $resource); + $this->anonymousServicesCount = 0; + $this->anonymousServicesSuffix = '~'.ContainerBuilder::hash($path); + $this->setCurrentDir(\dirname($path)); + try { + $this->parseDefinitions($content, $path); + } finally { + $this->instanceof = []; + $this->registerAliasesForSinglyImplementedInterfaces(); + } } /** * {@inheritdoc} */ - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { - return is_string($resource) && in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yml', 'yaml'), true); + if (!\is_string($resource)) { + return false; + } + + if (null === $type && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yaml', 'yml'], true)) { + return true; + } + + return \in_array($type, ['yaml', 'yml'], true); } - /** - * Parses all imports. - * - * @param array $content - * @param string $file - */ - private function parseImports($content, $file) + private function parseImports(array $content, string $file) { if (!isset($content['imports'])) { return; } - if (!is_array($content['imports'])) { - throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file)); + if (!\is_array($content['imports'])) { + throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in "%s". Check your YAML syntax.', $file)); } - $defaultDirectory = dirname($file); + $defaultDirectory = \dirname($file); foreach ($content['imports'] as $import) { - if (!is_array($import)) { - throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $file)); + if (!\is_array($import)) { + $import = ['resource' => $import]; + } + if (!isset($import['resource'])) { + throw new InvalidArgumentException(sprintf('An import should provide a resource in "%s". Check your YAML syntax.', $file)); } $this->setCurrentDir($defaultDirectory); - $this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file); + $this->import($import['resource'], $import['type'] ?? null, $import['ignore_errors'] ?? false, $file); } } - /** - * Parses definitions. - * - * @param array $content - * @param string $file - */ - private function parseDefinitions($content, $file) + private function parseDefinitions(array $content, string $file) { if (!isset($content['services'])) { return; } - if (!is_array($content['services'])) { - throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file)); + if (!\is_array($content['services'])) { + throw new InvalidArgumentException(sprintf('The "services" key should contain an array in "%s". Check your YAML syntax.', $file)); } + if (\array_key_exists('_instanceof', $content['services'])) { + $instanceof = $content['services']['_instanceof']; + unset($content['services']['_instanceof']); + + if (!\is_array($instanceof)) { + throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', get_debug_type($instanceof), $file)); + } + $this->instanceof = []; + $this->isLoadingInstanceof = true; + foreach ($instanceof as $id => $service) { + if (!$service || !\is_array($service)) { + throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in "%s". Check your YAML syntax.', $id, $file)); + } + if (\is_string($service) && str_starts_with($service, '@')) { + throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in "%s". Check your YAML syntax.', $id, $file)); + } + $this->parseDefinition($id, $service, $file, []); + } + } + + $this->isLoadingInstanceof = false; + $defaults = $this->parseDefaults($content, $file); foreach ($content['services'] as $id => $service) { - $this->parseDefinition($id, $service, $file); + $this->parseDefinition($id, $service, $file, $defaults); } } + /** + * @throws InvalidArgumentException + */ + private function parseDefaults(array &$content, string $file): array + { + if (!\array_key_exists('_defaults', $content['services'])) { + return []; + } + $defaults = $content['services']['_defaults']; + unset($content['services']['_defaults']); + + if (!\is_array($defaults)) { + throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', get_debug_type($defaults), $file)); + } + + foreach ($defaults as $key => $default) { + if (!isset(self::DEFAULTS_KEYWORDS[$key])) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::DEFAULTS_KEYWORDS))); + } + } + + if (isset($defaults['tags'])) { + if (!\is_array($tags = $defaults['tags'])) { + throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file)); + } + + foreach ($tags as $tag) { + if (!\is_array($tag)) { + $tag = ['name' => $tag]; + } + + if (1 === \count($tag) && \is_array(current($tag))) { + $name = key($tag); + $tag = current($tag); + } else { + if (!isset($tag['name'])) { + throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in "%s".', $file)); + } + $name = $tag['name']; + unset($tag['name']); + } + + if (!\is_string($name) || '' === $name) { + throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file)); + } + + foreach ($tag as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, $attribute, $file)); + } + } + } + } + + if (isset($defaults['bind'])) { + if (!\is_array($defaults['bind'])) { + throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in "%s". Check your YAML syntax.', $file)); + } + + foreach ($this->resolveServices($defaults['bind'], $file) as $argument => $value) { + $defaults['bind'][$argument] = new BoundArgument($value, true, BoundArgument::DEFAULTS_BINDING, $file); + } + } + + return $defaults; + } + + private function isUsingShortSyntax(array $service): bool + { + foreach ($service as $key => $value) { + if (\is_string($key) && ('' === $key || ('$' !== $key[0] && !str_contains($key, '\\')))) { + return false; + } + } + + return true; + } + /** * Parses a definition. * - * @param string $id - * @param array $service - * @param string $file + * @param array|string|null $service * * @throws InvalidArgumentException When tags are invalid */ - private function parseDefinition($id, $service, $file) + private function parseDefinition(string $id, $service, string $file, array $defaults, bool $return = false) { - if (is_string($service) && 0 === strpos($service, '@')) { - $this->container->setAlias($id, substr($service, 1)); + if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) { + throw new InvalidArgumentException(sprintf('Service names that start with an underscore are reserved. Rename the "%s" service or define it in XML instead.', $id)); + } - return; + if (\is_string($service) && str_starts_with($service, '@')) { + $alias = new Alias(substr($service, 1)); + + if (isset($defaults['public'])) { + $alias->setPublic($defaults['public']); + } + + return $return ? $alias : $this->container->setAlias($id, $alias); + } + + if (\is_array($service) && $this->isUsingShortSyntax($service)) { + $service = ['arguments' => $service]; } - if (!is_array($service)) { - throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file)); + if (null === $service) { + $service = []; + } + + if (!\is_array($service)) { + throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but "%s" found for service "%s" in "%s". Check your YAML syntax.', get_debug_type($service), $id, $file)); + } + + if (isset($service['stack'])) { + if (!\is_array($service['stack'])) { + throw new InvalidArgumentException(sprintf('A stack must be an array of definitions, "%s" given for service "%s" in "%s". Check your YAML syntax.', get_debug_type($service), $id, $file)); + } + + $stack = []; + + foreach ($service['stack'] as $k => $frame) { + if (\is_array($frame) && 1 === \count($frame) && !isset(self::SERVICE_KEYWORDS[key($frame)])) { + $frame = [ + 'class' => key($frame), + 'arguments' => current($frame), + ]; + } + + if (\is_array($frame) && isset($frame['stack'])) { + throw new InvalidArgumentException(sprintf('Service stack "%s" cannot contain another stack in "%s".', $id, $file)); + } + + $definition = $this->parseDefinition($id.'" at index "'.$k, $frame, $file, $defaults, true); + + if ($definition instanceof Definition) { + $definition->setInstanceofConditionals($this->instanceof); + } + + $stack[$k] = $definition; + } + + if ($diff = array_diff(array_keys($service), ['stack', 'public', 'deprecated'])) { + throw new InvalidArgumentException(sprintf('Invalid attribute "%s"; supported ones are "public" and "deprecated" for service "%s" in "%s". Check your YAML syntax.', implode('", "', $diff), $id, $file)); + } + + $service = [ + 'parent' => '', + 'arguments' => $stack, + 'tags' => ['container.stack'], + 'public' => $service['public'] ?? null, + 'deprecated' => $service['deprecated'] ?? null, + ]; } - static::checkDefinition($id, $service, $file); + $this->checkDefinition($id, $service, $file); if (isset($service['alias'])) { - $public = !array_key_exists('public', $service) || (bool) $service['public']; - $this->container->setAlias($id, new Alias($service['alias'], $public)); + $alias = new Alias($service['alias']); + + if (isset($service['public'])) { + $alias->setPublic($service['public']); + } elseif (isset($defaults['public'])) { + $alias->setPublic($defaults['public']); + } foreach ($service as $key => $value) { - if (!in_array($key, array('alias', 'public'))) { - @trigger_error(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public". The YamlFileLoader will raise an exception in Symfony 4.0, instead of silently ignoring unsupported attributes.', $key, $id, $file), E_USER_DEPRECATED); + if (!\in_array($key, ['alias', 'public', 'deprecated'])) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias", "public" and "deprecated".', $key, $id, $file)); + } + + if ('deprecated' === $key) { + $deprecation = \is_array($value) ? $value : ['message' => $value]; + + if (!isset($deprecation['package'])) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.', $file); + } + + if (!isset($deprecation['version'])) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file); + } + + $alias->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message'] ?? ''); } } - return; + return $return ? $alias : $this->container->setAlias($id, $alias); } - if (isset($service['parent'])) { - $definition = new DefinitionDecorator($service['parent']); + if ($this->isLoadingInstanceof) { + $definition = new ChildDefinition(''); + } elseif (isset($service['parent'])) { + if ('' !== $service['parent'] && '@' === $service['parent'][0]) { + throw new InvalidArgumentException(sprintf('The value of the "parent" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['parent'], substr($service['parent'], 1))); + } + + $definition = new ChildDefinition($service['parent']); } else { $definition = new Definition(); } + if (isset($defaults['public'])) { + $definition->setPublic($defaults['public']); + } + if (isset($defaults['autowire'])) { + $definition->setAutowired($defaults['autowire']); + } + if (isset($defaults['autoconfigure'])) { + $definition->setAutoconfigured($defaults['autoconfigure']); + } + + $definition->setChanges([]); + if (isset($service['class'])) { $definition->setClass($service['class']); } @@ -207,7 +461,10 @@ private function parseDefinition($id, $service, $file) } if (isset($service['lazy'])) { - $definition->setLazy($service['lazy']); + $definition->setLazy((bool) $service['lazy']); + if (\is_string($service['lazy'])) { + $definition->addTag('proxy', ['interface' => $service['lazy']]); + } } if (isset($service['public'])) { @@ -218,8 +475,18 @@ private function parseDefinition($id, $service, $file) $definition->setAbstract($service['abstract']); } - if (array_key_exists('deprecated', $service)) { - $definition->setDeprecated(true, $service['deprecated']); + if (isset($service['deprecated'])) { + $deprecation = \is_array($service['deprecated']) ? $service['deprecated'] : ['message' => $service['deprecated']]; + + if (!isset($deprecation['package'])) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "package" of the "deprecated" option in "%s" is deprecated.', $file); + } + + if (!isset($deprecation['version'])) { + trigger_deprecation('symfony/dependency-injection', '5.1', 'Not setting the attribute "version" of the "deprecated" option in "%s" is deprecated.', $file); + } + + $definition->setDeprecated($deprecation['package'] ?? '', $deprecation['version'] ?? '', $deprecation['message'] ?? ''); } if (isset($service['factory'])) { @@ -231,11 +498,11 @@ private function parseDefinition($id, $service, $file) } if (isset($service['arguments'])) { - $definition->setArguments($this->resolveServices($service['arguments'])); + $definition->setArguments($this->resolveServices($service['arguments'], $file)); } if (isset($service['properties'])) { - $definition->setProperties($this->resolveServices($service['properties'])); + $definition->setProperties($this->resolveServices($service['properties'], $file)); } if (isset($service['configurator'])) { @@ -243,140 +510,219 @@ private function parseDefinition($id, $service, $file) } if (isset($service['calls'])) { - if (!is_array($service['calls'])) { - throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + if (!\is_array($service['calls'])) { + throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } - foreach ($service['calls'] as $call) { - if (isset($call['method'])) { + foreach ($service['calls'] as $k => $call) { + if (!\is_array($call) && (!\is_string($k) || !$call instanceof TaggedValue)) { + throw new InvalidArgumentException(sprintf('Invalid method call for service "%s": expected map or array, "%s" given in "%s".', $id, $call instanceof TaggedValue ? '!'.$call->getTag() : get_debug_type($call), $file)); + } + + if (\is_string($k)) { + throw new InvalidArgumentException(sprintf('Invalid method call for service "%s", did you forgot a leading dash before "%s: ..." in "%s"?', $id, $k, $file)); + } + + if (isset($call['method']) && \is_string($call['method'])) { $method = $call['method']; - $args = isset($call['arguments']) ? $this->resolveServices($call['arguments']) : array(); + $args = $call['arguments'] ?? []; + $returnsClone = $call['returns_clone'] ?? false; } else { - $method = $call[0]; - $args = isset($call[1]) ? $this->resolveServices($call[1]) : array(); + if (1 === \count($call) && \is_string(key($call))) { + $method = key($call); + $args = $call[$method]; + + if ($args instanceof TaggedValue) { + if ('returns_clone' !== $args->getTag()) { + throw new InvalidArgumentException(sprintf('Unsupported tag "!%s", did you mean "!returns_clone" for service "%s" in "%s"?', $args->getTag(), $id, $file)); + } + + $returnsClone = true; + $args = $args->getValue(); + } else { + $returnsClone = false; + } + } elseif (empty($call[0])) { + throw new InvalidArgumentException(sprintf('Invalid call for service "%s": the method must be defined as the first index of an array or as the only key of a map in "%s".', $id, $file)); + } else { + $method = $call[0]; + $args = $call[1] ?? []; + $returnsClone = $call[2] ?? false; + } } - $definition->addMethodCall($method, $args); + if (!\is_array($args)) { + throw new InvalidArgumentException(sprintf('The second parameter for function call "%s" must be an array of its arguments for service "%s" in "%s". Check your YAML syntax.', $method, $id, $file)); + } + + $args = $this->resolveServices($args, $file); + $definition->addMethodCall($method, $args, $returnsClone); } } - if (isset($service['tags'])) { - if (!is_array($service['tags'])) { - throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); - } + $tags = $service['tags'] ?? []; + if (!\is_array($tags)) { + throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); + } - foreach ($service['tags'] as $tag) { - if (!is_array($tag)) { - throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); - } + if (isset($defaults['tags'])) { + $tags = array_merge($tags, $defaults['tags']); + } - if (!isset($tag['name'])) { - throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file)); - } + foreach ($tags as $tag) { + if (!\is_array($tag)) { + $tag = ['name' => $tag]; + } - if (!is_string($tag['name']) || '' === $tag['name']) { - throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file)); + if (1 === \count($tag) && \is_array(current($tag))) { + $name = key($tag); + $tag = current($tag); + } else { + if (!isset($tag['name'])) { + throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in "%s".', $id, $file)); } - $name = $tag['name']; unset($tag['name']); + } - foreach ($tag as $attribute => $value) { - if (!is_scalar($value) && null !== $value) { - throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file)); - } - } + if (!\is_string($name) || '' === $name) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file)); + } - $definition->addTag($name, $tag); + foreach ($tag as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, $attribute, $file)); + } } + + $definition->addTag($name, $tag); } - if (isset($service['decorates'])) { - if ('' !== $service['decorates'] && '@' === $service['decorates'][0]) { - throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($service['decorates'], 1))); + if (null !== $decorates = $service['decorates'] ?? null) { + if ('' !== $decorates && '@' === $decorates[0]) { + throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($decorates, 1))); } - $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null; - $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0; - $definition->setDecoratedService($service['decorates'], $renameId, $priority); + $decorationOnInvalid = \array_key_exists('decoration_on_invalid', $service) ? $service['decoration_on_invalid'] : 'exception'; + if ('exception' === $decorationOnInvalid) { + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + } elseif ('ignore' === $decorationOnInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } elseif (null === $decorationOnInvalid) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + } elseif ('null' === $decorationOnInvalid) { + throw new InvalidArgumentException(sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean null (without quotes) in "%s"?', $decorationOnInvalid, $id, $file)); + } else { + throw new InvalidArgumentException(sprintf('Invalid value "%s" for attribute "decoration_on_invalid" on service "%s". Did you mean "exception", "ignore" or null in "%s"?', $decorationOnInvalid, $id, $file)); + } + + $renameId = $service['decoration_inner_name'] ?? null; + $priority = $service['decoration_priority'] ?? 0; + + $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior); } if (isset($service['autowire'])) { $definition->setAutowired($service['autowire']); } - if (isset($service['autowiring_types'])) { - if (is_string($service['autowiring_types'])) { - $definition->addAutowiringType($service['autowiring_types']); - } else { - if (!is_array($service['autowiring_types'])) { - throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + if (isset($defaults['bind']) || isset($service['bind'])) { + // deep clone, to avoid multiple process of the same instance in the passes + $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : []; + + if (isset($service['bind'])) { + if (!\is_array($service['bind'])) { + throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in "%s". Check your YAML syntax.', $id, $file)); } - foreach ($service['autowiring_types'] as $autowiringType) { - if (!is_string($autowiringType)) { - throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); + $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file)); + $bindingType = $this->isLoadingInstanceof ? BoundArgument::INSTANCEOF_BINDING : BoundArgument::SERVICE_BINDING; + foreach ($bindings as $argument => $value) { + if (!$value instanceof BoundArgument) { + $bindings[$argument] = new BoundArgument($value, true, $bindingType, $file); } - - $definition->addAutowiringType($autowiringType); } } + + $definition->setBindings($bindings); + } + + if (isset($service['autoconfigure'])) { + $definition->setAutoconfigured($service['autoconfigure']); } - $this->container->setDefinition($id, $definition); + if (\array_key_exists('namespace', $service) && !\array_key_exists('resource', $service)) { + throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in "%s". Check your YAML syntax.', $id, $file)); + } + + if ($return) { + if (\array_key_exists('resource', $service)) { + throw new InvalidArgumentException(sprintf('Invalid "resource" attribute found for service "%s" in "%s". Check your YAML syntax.', $id, $file)); + } + + return $definition; + } + + if (\array_key_exists('resource', $service)) { + if (!\is_string($service['resource'])) { + throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in "%s". Check your YAML syntax.', $id, $file)); + } + $exclude = $service['exclude'] ?? null; + $namespace = $service['namespace'] ?? $id; + $this->registerClasses($definition, $namespace, $service['resource'], $exclude); + } else { + $this->setDefinition($id, $definition); + } } /** * Parses a callable. * - * @param string|array $callable A callable - * @param string $parameter A parameter (e.g. 'factory' or 'configurator') - * @param string $id A service identifier - * @param string $file A parsed file + * @param string|array $callable A callable reference * - * @throws InvalidArgumentException When errors are occuried + * @throws InvalidArgumentException When errors occur * - * @return string|array A parsed callable + * @return string|array|Reference A parsed callable */ - private function parseCallable($callable, $parameter, $id, $file) + private function parseCallable($callable, string $parameter, string $id, string $file) { - if (is_string($callable)) { + if (\is_string($callable)) { if ('' !== $callable && '@' === $callable[0]) { - throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1))); - } - - if (false !== strpos($callable, ':') && false === strpos($callable, '::')) { - $parts = explode(':', $callable); + if (!str_contains($callable, ':')) { + return [$this->resolveServices($callable, $file), '__invoke']; + } - return array($this->resolveServices('@'.$parts[0]), $parts[1]); + throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s" in "%s").', $parameter, $id, $callable, substr($callable, 1), $file)); } return $callable; } - if (is_array($callable)) { + if (\is_array($callable)) { if (isset($callable[0]) && isset($callable[1])) { - return array($this->resolveServices($callable[0]), $callable[1]); + return [$this->resolveServices($callable[0], $file), $callable[1]]; + } + + if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) { + return $callable; } - throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in %s. Check your YAML syntax.', $parameter, $id, $file)); + throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file)); } - throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $parameter, $id, $file)); + throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in "%s". Check your YAML syntax.', $parameter, $id, $file)); } /** * Loads a YAML file. * - * @param string $file - * * @return array The file content * * @throws InvalidArgumentException when the given file is not a local file or when it does not exist */ - protected function loadFile($file) + protected function loadFile(string $file) { - if (!class_exists('Symfony\Component\Yaml\Parser')) { + if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.'); } @@ -384,8 +730,8 @@ protected function loadFile($file) throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file)); } - if (!file_exists($file)) { - throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file)); + if (!is_file($file)) { + throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file)); } if (null === $this->yamlParser) { @@ -393,9 +739,9 @@ protected function loadFile($file) } try { - $configuration = $this->yamlParser->parse(file_get_contents($file), Yaml::PARSE_CONSTANT); + $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS); } catch (ParseException $e) { - throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e); + throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $file).$e->getMessage(), 0, $e); } return $this->validate($configuration, $file); @@ -404,37 +750,26 @@ protected function loadFile($file) /** * Validates a YAML file. * - * @param mixed $content - * @param string $file - * - * @return array - * * @throws InvalidArgumentException When service file is not valid */ - private function validate($content, $file) + private function validate($content, string $file): ?array { if (null === $content) { return $content; } - if (!is_array($content)) { + if (!\is_array($content)) { throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file)); } foreach ($content as $namespace => $data) { - if (in_array($namespace, array('imports', 'parameters', 'services'))) { + if (\in_array($namespace, ['imports', 'parameters', 'services'])) { continue; } if (!$this->container->hasExtension($namespace)) { - $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions())); - throw new InvalidArgumentException(sprintf( - 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', - $namespace, - $file, - $namespace, - $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' - )); + $extensionNamespaces = array_filter(array_map(function (ExtensionInterface $ext) { return $ext->getAlias(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none')); } } @@ -444,21 +779,115 @@ private function validate($content, $file) /** * Resolves services. * - * @param string|array $value - * - * @return array|string|Reference + * @return array|string|Reference|ArgumentInterface */ - private function resolveServices($value) + private function resolveServices($value, string $file, bool $isParameter = false) { - if (is_array($value)) { - $value = array_map(array($this, 'resolveServices'), $value); - } elseif (is_string($value) && 0 === strpos($value, '@=')) { + if ($value instanceof TaggedValue) { + $argument = $value->getValue(); + if ('iterator' === $value->getTag()) { + if (!\is_array($argument)) { + throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file)); + } + $argument = $this->resolveServices($argument, $file, $isParameter); + try { + return new IteratorArgument($argument); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file)); + } + } + if ('service_closure' === $value->getTag()) { + $argument = $this->resolveServices($argument, $file, $isParameter); + + if (!$argument instanceof Reference) { + throw new InvalidArgumentException(sprintf('"!service_closure" tag only accepts service references in "%s".', $file)); + } + + return new ServiceClosureArgument($argument); + } + if ('service_locator' === $value->getTag()) { + if (!\is_array($argument)) { + throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps in "%s".', $file)); + } + + $argument = $this->resolveServices($argument, $file, $isParameter); + + try { + return new ServiceLocatorArgument($argument); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('"!service_locator" tag only accepts maps of "@service" references in "%s".', $file)); + } + } + if (\in_array($value->getTag(), ['tagged', 'tagged_iterator', 'tagged_locator'], true)) { + $forLocator = 'tagged_locator' === $value->getTag(); + + if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) { + if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method', 'default_priority_method'])) { + throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('", "', $diff))); + } + + $argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null); + } elseif (\is_string($argument) && $argument) { + $argument = new TaggedIteratorArgument($argument, null, null, $forLocator); + } else { + throw new InvalidArgumentException(sprintf('"!%s" tags only accept a non empty string or an array with a key "tag" in "%s".', $value->getTag(), $file)); + } + + if ($forLocator) { + $argument = new ServiceLocatorArgument($argument); + } + + return $argument; + } + if ('service' === $value->getTag()) { + if ($isParameter) { + throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file)); + } + + $isLoadingInstanceof = $this->isLoadingInstanceof; + $this->isLoadingInstanceof = false; + $instanceof = $this->instanceof; + $this->instanceof = []; + + $id = sprintf('.%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', $argument['class'] ?? '').$this->anonymousServicesSuffix); + $this->parseDefinition($id, $argument, $file, []); + + if (!$this->container->hasDefinition($id)) { + throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file)); + } + + $this->container->getDefinition($id); + + $this->isLoadingInstanceof = $isLoadingInstanceof; + $this->instanceof = $instanceof; + + return new Reference($id); + } + if ('abstract' === $value->getTag()) { + return new AbstractArgument($value->getValue()); + } + + throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag())); + } + + if (\is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = $this->resolveServices($v, $file, $isParameter); + } + } elseif (\is_string($value) && str_starts_with($value, '@=')) { + if (!class_exists(Expression::class)) { + throw new \LogicException('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".'); + } + return new Expression(substr($value, 2)); - } elseif (is_string($value) && 0 === strpos($value, '@')) { - if (0 === strpos($value, '@@')) { + } elseif (\is_string($value) && str_starts_with($value, '@')) { + if (str_starts_with($value, '@@')) { $value = substr($value, 1); $invalidBehavior = null; - } elseif (0 === strpos($value, '@?')) { + } elseif (str_starts_with($value, '@!')) { + $value = substr($value, 2); + $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; + } elseif (str_starts_with($value, '@?')) { $value = substr($value, 2); $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; } else { @@ -466,57 +895,42 @@ private function resolveServices($value) $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; } - if ('=' === substr($value, -1)) { - $value = substr($value, 0, -1); - $strict = false; - } else { - $strict = true; - } - if (null !== $invalidBehavior) { - $value = new Reference($value, $invalidBehavior, $strict); + $value = new Reference($value, $invalidBehavior); } } return $value; } - /** - * Loads from Extensions. - * - * @param array $content - */ - private function loadFromExtensions($content) + private function loadFromExtensions(array $content) { foreach ($content as $namespace => $values) { - if (in_array($namespace, array('imports', 'parameters', 'services'))) { + if (\in_array($namespace, ['imports', 'parameters', 'services'])) { continue; } - if (!is_array($values)) { - $values = array(); + if (!\is_array($values) && null !== $values) { + $values = []; } $this->container->loadFromExtension($namespace, $values); } } - /** - * Checks the keywords used to define a service. - * - * @param string $id The service name - * @param array $definition The service definition to check - * @param string $file The loaded YAML file - */ - private static function checkDefinition($id, array $definition, $file) + private function checkDefinition(string $id, array $definition, string $file) { + if ($this->isLoadingInstanceof) { + $keywords = self::INSTANCEOF_KEYWORDS; + } elseif (isset($definition['resource']) || isset($definition['namespace'])) { + $keywords = self::PROTOTYPE_KEYWORDS; + } else { + $keywords = self::SERVICE_KEYWORDS; + } + foreach ($definition as $key => $value) { - if (!isset(static::$keywords[$key])) { - @trigger_error(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s". The YamlFileLoader object will raise an exception instead in Symfony 4.0 when detecting an unsupported service configuration key.', $key, $id, $file, implode('", "', static::$keywords)), E_USER_DEPRECATED); - // @deprecated Uncomment the following statement in Symfony 4.0 - // and also update the corresponding unit test to make it expect - // an InvalidArgumentException exception. - //throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for service definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', static::$keywords))); + if (!isset($keywords[$key])) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords))); } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd b/tests/integration/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd index 182e09e85..3d98eafc3 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd +++ b/tests/integration/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd @@ -52,8 +52,12 @@ Enclosing element for the definition of all services ]]> - - + + + + + + @@ -75,50 +79,129 @@ ]]> - + + - + + + + + + + + + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + @@ -140,37 +223,53 @@ - - + + - - + + - + - + + + + + + + + + + + + + + - - + + + + + - - - - + + + + @@ -178,20 +277,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/integration/vendor/symfony/dependency-injection/Parameter.php b/tests/integration/vendor/symfony/dependency-injection/Parameter.php index cac6f6c4c..d484ac0f9 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Parameter.php +++ b/tests/integration/vendor/symfony/dependency-injection/Parameter.php @@ -20,10 +20,7 @@ class Parameter { private $id; - /** - * @param string $id The parameter key - */ - public function __construct($id) + public function __construct(string $id) { $this->id = $id; } @@ -33,6 +30,6 @@ public function __construct($id) */ public function __toString() { - return (string) $this->id; + return $this->id; } } diff --git a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ContainerBag.php b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ContainerBag.php new file mode 100644 index 000000000..724a94e6d --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ContainerBag.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Container; + +/** + * @author Nicolas Grekas + */ +class ContainerBag extends FrozenParameterBag implements ContainerBagInterface +{ + private $container; + + public function __construct(Container $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->container->getParameterBag()->all(); + } + + /** + * {@inheritdoc} + * + * @return array|bool|string|int|float|null + */ + public function get($name) + { + return $this->container->getParameter($name); + } + + /** + * {@inheritdoc} + * + * @return bool + */ + public function has($name) + { + return $this->container->hasParameter($name); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php new file mode 100644 index 000000000..1c1227a3e --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * ContainerBagInterface is the interface implemented by objects that manage service container parameters. + * + * @author Nicolas Grekas + */ +interface ContainerBagInterface extends ContainerInterface +{ + /** + * Gets the service container parameters. + * + * @return array An array of parameters + */ + public function all(); + + /** + * Replaces parameter placeholders (%name%) by their values. + * + * @param mixed $value A value + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + */ + public function resolveValue($value); + + /** + * Escape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function escapeValue($value); + + /** + * Unescape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function unescapeValue($value); +} diff --git a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php index d20e53531..67b8aeeb1 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php +++ b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php @@ -11,22 +11,27 @@ namespace Symfony\Component\DependencyInjection\ParameterBag; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * @author Nicolas Grekas */ class EnvPlaceholderParameterBag extends ParameterBag { - private $envPlaceholders = array(); + private $envPlaceholderUniquePrefix; + private $envPlaceholders = []; + private $unusedEnvPlaceholders = []; + private $providedTypes = []; + + private static $counter = 0; /** * {@inheritdoc} */ - public function get($name) + public function get(string $name) { - if (0 === strpos($name, 'env(') && ')' === substr($name, -1) && 'env()' !== $name) { + if (str_starts_with($name, 'env(') && str_ends_with($name, ')') && 'env()' !== $name) { $env = substr($name, 4, -1); if (isset($this->envPlaceholders[$env])) { @@ -34,20 +39,20 @@ public function get($name) return $placeholder; // return first result } } - if (preg_match('/\W/', $env)) { + if (isset($this->unusedEnvPlaceholders[$env])) { + foreach ($this->unusedEnvPlaceholders[$env] as $placeholder) { + return $placeholder; // return first result + } + } + if (!preg_match('/^(?:[-.\w]*+:)*+\w++$/', $env)) { throw new InvalidArgumentException(sprintf('Invalid %s name: only "word" characters are allowed.', $name)); } - - if ($this->has($name)) { - $defaultValue = parent::get($name); - - if (null !== $defaultValue && !is_scalar($defaultValue)) { - throw new RuntimeException(sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', gettype($defaultValue), $name)); - } + if ($this->has($name) && null !== ($defaultValue = parent::get($name)) && !\is_string($defaultValue)) { + throw new RuntimeException(sprintf('The default value of an env() parameter must be a string or null, but "%s" given to "%s".', get_debug_type($defaultValue), $name)); } - $uniqueName = md5($name.uniqid(mt_rand(), true)); - $placeholder = sprintf('env_%s_%s', $env, $uniqueName); + $uniqueName = md5($name.'_'.self::$counter++); + $placeholder = sprintf('%s_%s_%s', $this->getEnvPlaceholderUniquePrefix(), strtr($env, ':-.', '___'), $uniqueName); $this->envPlaceholders[$env][$placeholder] = $placeholder; return $placeholder; @@ -56,6 +61,20 @@ public function get($name) return parent::get($name); } + /** + * Gets the common env placeholder prefix for env vars created by this bag. + */ + public function getEnvPlaceholderUniquePrefix(): string + { + if (null === $this->envPlaceholderUniquePrefix) { + $reproducibleEntropy = unserialize(serialize($this->parameters)); + array_walk_recursive($reproducibleEntropy, function (&$v) { $v = null; }); + $this->envPlaceholderUniquePrefix = 'env_'.substr(md5(serialize($reproducibleEntropy)), -16); + } + + return $this->envPlaceholderUniquePrefix; + } + /** * Returns the map of env vars used in the resolved parameter values to their placeholders. * @@ -66,6 +85,16 @@ public function getEnvPlaceholders() return $this->envPlaceholders; } + public function getUnusedEnvPlaceholders(): array + { + return $this->unusedEnvPlaceholders; + } + + public function clearUnusedEnvPlaceholders() + { + $this->unusedEnvPlaceholders = []; + } + /** * Merges the env placeholders of another EnvPlaceholderParameterBag. */ @@ -78,6 +107,32 @@ public function mergeEnvPlaceholders(self $bag) $this->envPlaceholders[$env] += $placeholders; } } + + if ($newUnusedPlaceholders = $bag->getUnusedEnvPlaceholders()) { + $this->unusedEnvPlaceholders += $newUnusedPlaceholders; + + foreach ($newUnusedPlaceholders as $env => $placeholders) { + $this->unusedEnvPlaceholders[$env] += $placeholders; + } + } + } + + /** + * Maps env prefixes to their corresponding PHP types. + */ + public function setProvidedTypes(array $providedTypes) + { + $this->providedTypes = $providedTypes; + } + + /** + * Gets the PHP types corresponding to env() parameter prefixes. + * + * @return string[][] + */ + public function getProvidedTypes() + { + return $this->providedTypes; } /** @@ -91,13 +146,8 @@ public function resolve() parent::resolve(); foreach ($this->envPlaceholders as $env => $placeholders) { - if (!isset($this->parameters[$name = strtolower("env($env)")])) { - continue; - } - if (is_numeric($default = $this->parameters[$name])) { - $this->parameters[$name] = (string) $default; - } elseif (null !== $default && !is_scalar($default)) { - throw new RuntimeException(sprintf('The default value of env parameter "%s" must be scalar or null, %s given.', $env, gettype($default))); + if ($this->has($name = "env($env)") && null !== ($default = $this->parameters[$name]) && !\is_string($default)) { + throw new RuntimeException(sprintf('The default value of env parameter "%s" must be a string or null, "%s" given.', $env, get_debug_type($default))); } } } diff --git a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php index ad65ad960..5a4aaf8b2 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php +++ b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php @@ -28,7 +28,7 @@ class FrozenParameterBag extends ParameterBag * * @param array $parameters An array of parameters */ - public function __construct(array $parameters = array()) + public function __construct(array $parameters = []) { $this->parameters = $parameters; $this->resolved = true; @@ -53,7 +53,7 @@ public function add(array $parameters) /** * {@inheritdoc} */ - public function set($name, $value) + public function set(string $name, $value) { throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); } @@ -61,7 +61,7 @@ public function set($name, $value) /** * {@inheritdoc} */ - public function remove($name) + public function remove(string $name) { throw new LogicException('Impossible to call remove() on a frozen ParameterBag.'); } diff --git a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php index 83ba7e707..fad04fc6d 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php +++ b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\ParameterBag; -use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** @@ -22,34 +22,29 @@ */ class ParameterBag implements ParameterBagInterface { - protected $parameters = array(); + protected $parameters = []; protected $resolved = false; - /** - * @param array $parameters An array of parameters - */ - public function __construct(array $parameters = array()) + public function __construct(array $parameters = []) { $this->add($parameters); } /** - * Clears all parameters. + * {@inheritdoc} */ public function clear() { - $this->parameters = array(); + $this->parameters = []; } /** - * Adds parameters to the service container parameters. - * - * @param array $parameters An array of parameters + * {@inheritdoc} */ public function add(array $parameters) { foreach ($parameters as $key => $value) { - $this->parameters[strtolower($key)] = $value; + $this->set($key, $value); } } @@ -64,30 +59,28 @@ public function all() /** * {@inheritdoc} */ - public function get($name) + public function get(string $name) { - $name = strtolower($name); - - if (!array_key_exists($name, $this->parameters)) { + if (!\array_key_exists($name, $this->parameters)) { if (!$name) { throw new ParameterNotFoundException($name); } - $alternatives = array(); + $alternatives = []; foreach ($this->parameters as $key => $parameterValue) { $lev = levenshtein($name, $key); - if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) { + if ($lev <= \strlen($name) / 3 || str_contains($key, $name)) { $alternatives[] = $key; } } $nonNestedAlternative = null; - if (!count($alternatives) && false !== strpos($name, '.')) { + if (!\count($alternatives) && str_contains($name, '.')) { $namePartsLength = array_map('strlen', explode('.', $name)); $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength))); - while (count($namePartsLength)) { + while (\count($namePartsLength)) { if ($this->has($key)) { - if (is_array($this->get($key))) { + if (\is_array($this->get($key))) { $nonNestedAlternative = $key; } break; @@ -104,32 +97,27 @@ public function get($name) } /** - * Sets a service container parameter. - * - * @param string $name The parameter name - * @param mixed $value The parameter value + * {@inheritdoc} */ - public function set($name, $value) + public function set(string $name, $value) { - $this->parameters[strtolower($name)] = $value; + $this->parameters[$name] = $value; } /** * {@inheritdoc} */ - public function has($name) + public function has(string $name) { - return array_key_exists(strtolower($name), $this->parameters); + return \array_key_exists((string) $name, $this->parameters); } /** - * Removes a parameter. - * - * @param string $name The parameter name + * {@inheritdoc} */ - public function remove($name) + public function remove(string $name) { - unset($this->parameters[strtolower($name)]); + unset($this->parameters[$name]); } /** @@ -141,7 +129,7 @@ public function resolve() return; } - $parameters = array(); + $parameters = []; foreach ($this->parameters as $key => $value) { try { $value = $this->resolveValue($value); @@ -167,20 +155,20 @@ public function resolve() * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected - * @throws RuntimeException when a given parameter has a type problem. + * @throws RuntimeException when a given parameter has a type problem */ - public function resolveValue($value, array $resolving = array()) + public function resolveValue($value, array $resolving = []) { - if (is_array($value)) { - $args = array(); + if (\is_array($value)) { + $args = []; foreach ($value as $k => $v) { - $args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving); + $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving); } return $args; } - if (!is_string($value)) { + if (!\is_string($value) || 2 > \strlen($value)) { return $value; } @@ -190,29 +178,27 @@ public function resolveValue($value, array $resolving = array()) /** * Resolves parameters inside a string. * - * @param string $value The string to resolve - * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) + * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * - * @return string The resolved string + * @return mixed The resolved string * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected - * @throws RuntimeException when a given parameter has a type problem. + * @throws RuntimeException when a given parameter has a type problem */ - public function resolveString($value, array $resolving = array()) + public function resolveString(string $value, array $resolving = []) { // we do this to deal with non string values (Boolean, integer, ...) // as the preg_replace_callback throw an exception when trying // a non-string in a parameter value if (preg_match('/^%([^%\s]+)%$/', $value, $match)) { $key = $match[1]; - $lcKey = strtolower($key); - if (isset($resolving[$lcKey])) { + if (isset($resolving[$key])) { throw new ParameterCircularReferenceException(array_keys($resolving)); } - $resolving[$lcKey] = true; + $resolving[$key] = true; return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving); } @@ -224,19 +210,18 @@ public function resolveString($value, array $resolving = array()) } $key = $match[1]; - $lcKey = strtolower($key); - if (isset($resolving[$lcKey])) { + if (isset($resolving[$key])) { throw new ParameterCircularReferenceException(array_keys($resolving)); } $resolved = $this->get($key); - if (!is_string($resolved) && !is_numeric($resolved)) { - throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type %s inside string value "%s".', $key, gettype($resolved), $value)); + if (!\is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type "%s" inside string value "%s".', $key, get_debug_type($resolved), $value)); } $resolved = (string) $resolved; - $resolving[$lcKey] = true; + $resolving[$key] = true; return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); }, $value); @@ -252,12 +237,12 @@ public function isResolved() */ public function escapeValue($value) { - if (is_string($value)) { + if (\is_string($value)) { return str_replace('%', '%%', $value); } - if (is_array($value)) { - $result = array(); + if (\is_array($value)) { + $result = []; foreach ($value as $k => $v) { $result[$k] = $this->escapeValue($v); } @@ -273,12 +258,12 @@ public function escapeValue($value) */ public function unescapeValue($value) { - if (is_string($value)) { + if (\is_string($value)) { return str_replace('%%', '%', $value); } - if (is_array($value)) { - $result = array(); + if (\is_array($value)) { + $result = []; foreach ($value as $k => $v) { $result[$k] = $this->unescapeValue($v); } diff --git a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php index 7386df064..b532edfbe 100644 --- a/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** - * ParameterBagInterface. + * ParameterBagInterface is the interface implemented by objects that manage service container parameters. * * @author Fabien Potencier */ @@ -31,8 +31,6 @@ public function clear(); /** * Adds parameters to the service container parameters. * - * @param array $parameters An array of parameters - * * @throws LogicException if the parameter can not be added */ public function add(array $parameters); @@ -47,39 +45,32 @@ public function all(); /** * Gets a service container parameter. * - * @param string $name The parameter name - * - * @return mixed The parameter value + * @return array|bool|string|int|float|null * * @throws ParameterNotFoundException if the parameter is not defined */ - public function get($name); + public function get(string $name); /** * Removes a parameter. - * - * @param string $name The parameter name */ - public function remove($name); + public function remove(string $name); /** * Sets a service container parameter. * - * @param string $name The parameter name - * @param mixed $value The parameter value + * @param array|bool|string|int|float|null $value The parameter value * * @throws LogicException if the parameter can not be set */ - public function set($name, $value); + public function set(string $name, $value); /** * Returns true if a parameter name is defined. * - * @param string $name The parameter name - * * @return bool true if the parameter name is defined, false otherwise */ - public function has($name); + public function has(string $name); /** * Replaces parameter placeholders (%name%) by their values for all parameters. diff --git a/tests/integration/vendor/symfony/dependency-injection/README.md b/tests/integration/vendor/symfony/dependency-injection/README.md index 932647f94..fa6719a79 100644 --- a/tests/integration/vendor/symfony/dependency-injection/README.md +++ b/tests/integration/vendor/symfony/dependency-injection/README.md @@ -7,8 +7,8 @@ way objects are constructed in your application. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/dependency_injection/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/components/dependency_injection.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/tests/integration/vendor/symfony/dependency-injection/Reference.php b/tests/integration/vendor/symfony/dependency-injection/Reference.php index 3c8b314f5..c13cf6fe4 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Reference.php +++ b/tests/integration/vendor/symfony/dependency-injection/Reference.php @@ -21,15 +21,9 @@ class Reference private $id; private $invalidBehavior; - /** - * @param string $id The service identifier - * @param int $invalidBehavior The behavior when the service does not exist - * - * @see Container - */ - public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + public function __construct(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - $this->id = strtolower($id); + $this->id = $id; $this->invalidBehavior = $invalidBehavior; } diff --git a/tests/integration/vendor/symfony/dependency-injection/ResettableContainerInterface.php b/tests/integration/vendor/symfony/dependency-injection/ResettableContainerInterface.php deleted file mode 100644 index b74e67624..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/ResettableContainerInterface.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection; - -/** - * ResettableContainerInterface defines additional resetting functionality - * for containers, allowing to release shared services when the container is - * not needed anymore. - * - * @author Christophe Coevoet - */ -interface ResettableContainerInterface extends ContainerInterface -{ - /** - * Resets shared services from the container. - * - * The container is not intended to be used again after being reset in a normal workflow. This method is - * meant as a way to release references for ref-counting. - * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service. - */ - public function reset(); -} diff --git a/tests/integration/vendor/symfony/dependency-injection/ReverseContainer.php b/tests/integration/vendor/symfony/dependency-injection/ReverseContainer.php new file mode 100644 index 000000000..280e9e2dd --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/ReverseContainer.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +/** + * Turns public and "container.reversible" services back to their ids. + * + * @author Nicolas Grekas + */ +final class ReverseContainer +{ + private $serviceContainer; + private $reversibleLocator; + private $tagName; + private $getServiceId; + + public function __construct(Container $serviceContainer, ContainerInterface $reversibleLocator, string $tagName = 'container.reversible') + { + $this->serviceContainer = $serviceContainer; + $this->reversibleLocator = $reversibleLocator; + $this->tagName = $tagName; + $this->getServiceId = \Closure::bind(function (object $service): ?string { + return array_search($service, $this->services, true) ?: array_search($service, $this->privates, true) ?: null; + }, $serviceContainer, Container::class); + } + + /** + * Returns the id of the passed object when it exists as a service. + * + * To be reversible, services need to be either public or be tagged with "container.reversible". + */ + public function getId(object $service): ?string + { + if ($this->serviceContainer === $service) { + return 'service_container'; + } + + if (null === $id = ($this->getServiceId)($service)) { + return null; + } + + if ($this->serviceContainer->has($id) || $this->reversibleLocator->has($id)) { + return $id; + } + + return null; + } + + /** + * @throws ServiceNotFoundException When the service is not reversible + */ + public function getService(string $id): object + { + if ($this->serviceContainer->has($id)) { + return $this->serviceContainer->get($id); + } + + if ($this->reversibleLocator->has($id)) { + return $this->reversibleLocator->get($id); + } + + if (isset($this->serviceContainer->getRemovedIds()[$id])) { + throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is private and cannot be accessed by reference. You should either make it public, or tag it as "%s".', $id, $this->tagName)); + } + + // will throw a ServiceNotFoundException + $this->serviceContainer->get($id); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/ServiceLocator.php b/tests/integration/vendor/symfony/dependency-injection/ServiceLocator.php new file mode 100644 index 000000000..58558112a --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/ServiceLocator.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Contracts\Service\ServiceLocatorTrait; +use Symfony\Contracts\Service\ServiceProviderInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; + +/** + * @author Robin Chalas + * @author Nicolas Grekas + */ +class ServiceLocator implements ServiceProviderInterface +{ + use ServiceLocatorTrait { + get as private doGet; + } + + private $externalId; + private $container; + + /** + * {@inheritdoc} + * + * @return mixed + */ + public function get($id) + { + if (!$this->externalId) { + return $this->doGet($id); + } + + try { + return $this->doGet($id); + } catch (RuntimeException $e) { + $what = sprintf('service "%s" required by "%s"', $id, $this->externalId); + $message = preg_replace('/service "\.service_locator\.[^"]++"/', $what, $e->getMessage()); + + if ($e->getMessage() === $message) { + $message = sprintf('Cannot resolve %s: %s', $what, $message); + } + + $r = new \ReflectionProperty($e, 'message'); + $r->setAccessible(true); + $r->setValue($e, $message); + + throw $e; + } + } + + public function __invoke(string $id) + { + return isset($this->factories[$id]) ? $this->get($id) : null; + } + + /** + * @internal + * + * @return static + */ + public function withContext(string $externalId, Container $container): self + { + $locator = clone $this; + $locator->externalId = $externalId; + $locator->container = $container; + + return $locator; + } + + private function createNotFoundException(string $id): NotFoundExceptionInterface + { + if ($this->loading) { + $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives()); + + return new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], $msg); + } + + $class = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 4); + $class = isset($class[3]['object']) ? \get_class($class[3]['object']) : null; + $externalId = $this->externalId ?: $class; + + $msg = []; + $msg[] = sprintf('Service "%s" not found:', $id); + + if (!$this->container) { + $class = null; + } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) { + $msg[] = 'even though it exists in the app\'s container,'; + } else { + try { + $this->container->get($id); + $class = null; + } catch (ServiceNotFoundException $e) { + if ($e->getAlternatives()) { + $msg[] = sprintf('did you mean %s? Anyway,', $this->formatAlternatives($e->getAlternatives(), 'or')); + } else { + $class = null; + } + } + } + if ($externalId) { + $msg[] = sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives()); + } else { + $msg[] = sprintf('the current service locator %s', $this->formatAlternatives()); + } + + if (!$class) { + // no-op + } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) { + $msg[] = sprintf('Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class)); + } else { + $msg[] = 'Try using dependency injection instead.'; + } + + return new ServiceNotFoundException($id, end($this->loading) ?: null, null, [], implode(' ', $msg)); + } + + private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface + { + return new ServiceCircularReferenceException($id, $path); + } + + private function formatAlternatives(array $alternatives = null, string $separator = 'and'): string + { + $format = '"%s"%s'; + if (null === $alternatives) { + if (!$alternatives = array_keys($this->factories)) { + return 'is empty...'; + } + $format = sprintf('only knows about the %s service%s.', $format, 1 < \count($alternatives) ? 's' : ''); + } + $last = array_pop($alternatives); + + return sprintf($format, $alternatives ? implode('", "', $alternatives) : $last, $alternatives ? sprintf(' %s "%s"', $separator, $last) : ''); + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/TaggedContainerInterface.php b/tests/integration/vendor/symfony/dependency-injection/TaggedContainerInterface.php index 90b297fff..2e32cd597 100644 --- a/tests/integration/vendor/symfony/dependency-injection/TaggedContainerInterface.php +++ b/tests/integration/vendor/symfony/dependency-injection/TaggedContainerInterface.php @@ -25,5 +25,5 @@ interface TaggedContainerInterface extends ContainerInterface * * @return array An array of tags */ - public function findTaggedServiceIds($name); + public function findTaggedServiceIds(string $name); } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php deleted file mode 100644 index 04fe7c2cf..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; -use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class AnalyzeServiceReferencesPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - - $a = $container - ->register('a') - ->addArgument($ref1 = new Reference('b')) - ; - - $b = $container - ->register('b') - ->addMethodCall('setA', array($ref2 = new Reference('a'))) - ; - - $c = $container - ->register('c') - ->addArgument($ref3 = new Reference('a')) - ->addArgument($ref4 = new Reference('b')) - ; - - $d = $container - ->register('d') - ->setProperty('foo', $ref5 = new Reference('b')) - ; - - $e = $container - ->register('e') - ->setConfigurator(array($ref6 = new Reference('b'), 'methodName')) - ; - - $graph = $this->process($container); - - $this->assertCount(4, $edges = $graph->getNode('b')->getInEdges()); - - $this->assertSame($ref1, $edges[0]->getValue()); - $this->assertSame($ref4, $edges[1]->getValue()); - $this->assertSame($ref5, $edges[2]->getValue()); - $this->assertSame($ref6, $edges[3]->getValue()); - } - - public function testProcessDetectsReferencesFromInlinedDefinitions() - { - $container = new ContainerBuilder(); - - $container - ->register('a') - ; - - $container - ->register('b') - ->addArgument(new Definition(null, array($ref = new Reference('a')))) - ; - - $graph = $this->process($container); - - $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); - $this->assertSame($ref, $refs[0]->getValue()); - } - - public function testProcessDetectsReferencesFromInlinedFactoryDefinitions() - { - $container = new ContainerBuilder(); - - $container - ->register('a') - ; - - $factory = new Definition(); - $factory->setFactory(array(new Reference('a'), 'a')); - - $container - ->register('b') - ->addArgument($factory) - ; - - $graph = $this->process($container); - - $this->assertTrue($graph->hasNode('a')); - $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); - } - - public function testProcessDoesNotSaveDuplicateReferences() - { - $container = new ContainerBuilder(); - - $container - ->register('a') - ; - $container - ->register('b') - ->addArgument(new Definition(null, array($ref1 = new Reference('a')))) - ->addArgument(new Definition(null, array($ref2 = new Reference('a')))) - ; - - $graph = $this->process($container); - - $this->assertCount(2, $graph->getNode('a')->getInEdges()); - } - - public function testProcessDetectsFactoryReferences() - { - $container = new ContainerBuilder(); - - $container - ->register('foo', 'stdClass') - ->setFactory(array('stdClass', 'getInstance')); - - $container - ->register('bar', 'stdClass') - ->setFactory(array(new Reference('foo'), 'getInstance')); - - $graph = $this->process($container); - - $this->assertTrue($graph->hasNode('foo')); - $this->assertCount(1, $graph->getNode('foo')->getInEdges()); - } - - protected function process(ContainerBuilder $container) - { - $pass = new RepeatedPass(array(new AnalyzeServiceReferencesPass())); - $pass->process($container); - - return $container->getCompiler()->getServiceReferenceGraph(); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.php deleted file mode 100644 index a8d32815e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.php +++ /dev/null @@ -1,111 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\AutoAliasServicePass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException - */ - public function testProcessWithMissingParameter() - { - $container = new ContainerBuilder(); - - $container->register('example') - ->addTag('auto_alias', array('format' => '%non_existing%.example')); - - $pass = new AutoAliasServicePass(); - $pass->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testProcessWithMissingFormat() - { - $container = new ContainerBuilder(); - - $container->register('example') - ->addTag('auto_alias', array()); - $container->setParameter('existing', 'mysql'); - - $pass = new AutoAliasServicePass(); - $pass->process($container); - } - - public function testProcessWithNonExistingAlias() - { - $container = new ContainerBuilder(); - - $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') - ->addTag('auto_alias', array('format' => '%existing%.example')); - $container->setParameter('existing', 'mysql'); - - $pass = new AutoAliasServicePass(); - $pass->process($container); - - $this->assertEquals('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->getDefinition('example')->getClass()); - } - - public function testProcessWithExistingAlias() - { - $container = new ContainerBuilder(); - - $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') - ->addTag('auto_alias', array('format' => '%existing%.example')); - - $container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql'); - $container->setParameter('existing', 'mysql'); - - $pass = new AutoAliasServicePass(); - $pass->process($container); - - $this->assertTrue($container->hasAlias('example')); - $this->assertEquals('mysql.example', $container->getAlias('example')); - $this->assertSame('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql', $container->getDefinition('mysql.example')->getClass()); - } - - public function testProcessWithManualAlias() - { - $container = new ContainerBuilder(); - - $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') - ->addTag('auto_alias', array('format' => '%existing%.example')); - - $container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql'); - $container->register('mariadb.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb'); - $container->setAlias('example', 'mariadb.example'); - $container->setParameter('existing', 'mysql'); - - $pass = new AutoAliasServicePass(); - $pass->process($container); - - $this->assertTrue($container->hasAlias('example')); - $this->assertEquals('mariadb.example', $container->getAlias('example')); - $this->assertSame('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb', $container->getDefinition('mariadb.example')->getClass()); - } -} - -class ServiceClassDefault -{ -} - -class ServiceClassMysql extends ServiceClassDefault -{ -} - -class ServiceClassMariaDb extends ServiceClassMysql -{ -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php deleted file mode 100644 index a8b1be355..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php +++ /dev/null @@ -1,656 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\AutowirePass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @author Kévin Dunglas - */ -class AutowirePassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - - $container->register('foo', __NAMESPACE__.'\Foo'); - $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); - $barDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(1, $container->getDefinition('bar')->getArguments()); - $this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0)); - } - - public function testProcessAutowireParent() - { - $container = new ContainerBuilder(); - - $container->register('b', __NAMESPACE__.'\B'); - $cDefinition = $container->register('c', __NAMESPACE__.'\C'); - $cDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(1, $container->getDefinition('c')->getArguments()); - $this->assertEquals('b', (string) $container->getDefinition('c')->getArgument(0)); - } - - public function testProcessAutowireInterface() - { - $container = new ContainerBuilder(); - - $container->register('f', __NAMESPACE__.'\F'); - $gDefinition = $container->register('g', __NAMESPACE__.'\G'); - $gDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(3, $container->getDefinition('g')->getArguments()); - $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(0)); - $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(1)); - $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(2)); - } - - public function testCompleteExistingDefinition() - { - $container = new ContainerBuilder(); - - $container->register('b', __NAMESPACE__.'\B'); - $container->register('f', __NAMESPACE__.'\F'); - $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b')); - $hDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(2, $container->getDefinition('h')->getArguments()); - $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); - $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1)); - } - - public function testCompleteExistingDefinitionWithNotDefinedArguments() - { - $container = new ContainerBuilder(); - - $container->register('b', __NAMESPACE__.'\B'); - $container->register('f', __NAMESPACE__.'\F'); - $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument(''); - $hDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(2, $container->getDefinition('h')->getArguments()); - $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); - $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1)); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Multiple services exist for this interface (c1, c2, c3). - */ - public function testTypeCollision() - { - $container = new ContainerBuilder(); - - $container->register('c1', __NAMESPACE__.'\CollisionA'); - $container->register('c2', __NAMESPACE__.'\CollisionB'); - $container->register('c3', __NAMESPACE__.'\CollisionB'); - $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Multiple services exist for this class (a1, a2). - */ - public function testTypeNotGuessable() - { - $container = new ContainerBuilder(); - - $container->register('a1', __NAMESPACE__.'\Foo'); - $container->register('a2', __NAMESPACE__.'\Foo'); - $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Multiple services exist for this class (a1, a2). - */ - public function testTypeNotGuessableWithSubclass() - { - $container = new ContainerBuilder(); - - $container->register('a1', __NAMESPACE__.'\B'); - $container->register('a2', __NAMESPACE__.'\B'); - $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". No services were found matching this interface and it cannot be auto-registered. - */ - public function testTypeNotGuessableNoServicesFound() - { - $container = new ContainerBuilder(); - - $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - public function testTypeNotGuessableWithTypeSet() - { - $container = new ContainerBuilder(); - - $container->register('a1', __NAMESPACE__.'\Foo'); - $container->register('a2', __NAMESPACE__.'\Foo')->addAutowiringType(__NAMESPACE__.'\Foo'); - $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(1, $container->getDefinition('a')->getArguments()); - $this->assertEquals('a2', (string) $container->getDefinition('a')->getArgument(0)); - } - - public function testWithTypeSet() - { - $container = new ContainerBuilder(); - - $container->register('c1', __NAMESPACE__.'\CollisionA'); - $container->register('c2', __NAMESPACE__.'\CollisionB')->addAutowiringType(__NAMESPACE__.'\CollisionInterface'); - $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(1, $container->getDefinition('a')->getArguments()); - $this->assertEquals('c2', (string) $container->getDefinition('a')->getArgument(0)); - } - - public function testCreateDefinition() - { - $container = new ContainerBuilder(); - - $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls'); - $coopTilleulsDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(1, $container->getDefinition('coop_tilleuls')->getArguments()); - $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0)); - - $dunglasDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas'); - $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass()); - $this->assertFalse($dunglasDefinition->isPublic()); - $this->assertCount(1, $dunglasDefinition->getArguments()); - $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\lille', $dunglasDefinition->getArgument(0)); - - $lilleDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\lille'); - $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); - } - - public function testResolveParameter() - { - $container = new ContainerBuilder(); - - $container->setParameter('class_name', __NAMESPACE__.'\Foo'); - $container->register('foo', '%class_name%'); - $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); - $barDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0)); - } - - public function testOptionalParameter() - { - $container = new ContainerBuilder(); - - $container->register('a', __NAMESPACE__.'\A'); - $container->register('foo', __NAMESPACE__.'\Foo'); - $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter'); - $optDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $definition = $container->getDefinition('opt'); - $this->assertNull($definition->getArgument(0)); - $this->assertEquals('a', $definition->getArgument(1)); - $this->assertEquals('foo', $definition->getArgument(2)); - } - - public function testDontTriggerAutowiring() - { - $container = new ContainerBuilder(); - - $container->register('foo', __NAMESPACE__.'\Foo'); - $container->register('bar', __NAMESPACE__.'\Bar'); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertCount(0, $container->getDefinition('bar')->getArguments()); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Cannot autowire argument 2 for Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument because the type-hinted class does not exist (Class Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass does not exist). - */ - public function testClassNotFoundThrowsException() - { - $container = new ContainerBuilder(); - - $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Cannot autowire argument 2 for Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument because the type-hinted class does not exist (Class Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass does not exist). - */ - public function testParentClassNotFoundThrowsException() - { - $container = new ContainerBuilder(); - - $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument'); - $aDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - public function testDontUseAbstractServices() - { - $container = new ContainerBuilder(); - - $container->register('abstract_foo', __NAMESPACE__.'\Foo')->setAbstract(true); - $container->register('foo', __NAMESPACE__.'\Foo'); - $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $arguments = $container->getDefinition('bar')->getArguments(); - $this->assertSame('foo', (string) $arguments[0]); - } - - public function testSomeSpecificArgumentsAreSet() - { - $container = new ContainerBuilder(); - - $container->register('foo', __NAMESPACE__.'\Foo'); - $container->register('a', __NAMESPACE__.'\A'); - $container->register('dunglas', __NAMESPACE__.'\Dunglas'); - $container->register('multiple', __NAMESPACE__.'\MultipleArguments') - ->setAutowired(true) - // set the 2nd (index 1) argument only: autowire the first and third - // args are: A, Foo, Dunglas - ->setArguments(array( - 1 => new Reference('foo'), - )); - - $pass = new AutowirePass(); - $pass->process($container); - - $definition = $container->getDefinition('multiple'); - $this->assertEquals( - array( - new Reference('a'), - new Reference('foo'), - new Reference('dunglas'), - ), - $definition->getArguments() - ); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument index 1 ($foo) for the service "arg_no_type_hint". If this is an object, give it a type-hint. Otherwise, specify this argument's value explicitly. - */ - public function testScalarArgsCannotBeAutowired() - { - $container = new ContainerBuilder(); - - $container->register('a', __NAMESPACE__.'\A'); - $container->register('dunglas', __NAMESPACE__.'\Dunglas'); - $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments') - ->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $container->getDefinition('arg_no_type_hint'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Unable to autowire argument index 1 ($foo) for the service "not_really_optional_scalar". If this is an object, give it a type-hint. Otherwise, specify this argument's value explicitly. - */ - public function testOptionalScalarNotReallyOptionalThrowException() - { - $container = new ContainerBuilder(); - - $container->register('a', __NAMESPACE__.'\A'); - $container->register('lille', __NAMESPACE__.'\Lille'); - $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional') - ->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - } - - public function testOptionalScalarArgsDontMessUpOrder() - { - $container = new ContainerBuilder(); - - $container->register('a', __NAMESPACE__.'\A'); - $container->register('lille', __NAMESPACE__.'\Lille'); - $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar') - ->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $definition = $container->getDefinition('with_optional_scalar'); - $this->assertEquals( - array( - new Reference('a'), - // use the default value - 'default_val', - new Reference('lille'), - ), - $definition->getArguments() - ); - } - - public function testOptionalScalarArgsNotPassedIfLast() - { - $container = new ContainerBuilder(); - - $container->register('a', __NAMESPACE__.'\A'); - $container->register('lille', __NAMESPACE__.'\Lille'); - $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast') - ->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $definition = $container->getDefinition('with_optional_scalar_last'); - $this->assertEquals( - array( - new Reference('a'), - new Reference('lille'), - // third arg shouldn't *need* to be passed - // but that's hard to "pull of" with autowiring, so - // this assumes passing the default val is ok - 'some_val', - ), - $definition->getArguments() - ); - } - - /** - * @dataProvider getCreateResourceTests - */ - public function testCreateResourceForClass($className, $isEqual) - { - $startingResource = AutowirePass::createResourceForClass( - new \ReflectionClass(__NAMESPACE__.'\ClassForResource') - ); - $newResource = AutowirePass::createResourceForClass( - new \ReflectionClass(__NAMESPACE__.'\\'.$className) - ); - - // hack so the objects don't differ by the class name - $startingReflObject = new \ReflectionObject($startingResource); - $reflProp = $startingReflObject->getProperty('class'); - $reflProp->setAccessible(true); - $reflProp->setValue($startingResource, __NAMESPACE__.'\\'.$className); - - if ($isEqual) { - $this->assertEquals($startingResource, $newResource); - } else { - $this->assertNotEquals($startingResource, $newResource); - } - } - - public function getCreateResourceTests() - { - return array( - array('IdenticalClassResource', true), - array('ClassChangedConstructorArgs', false), - ); - } - - public function testIgnoreServiceWithClassNotExisting() - { - $container = new ContainerBuilder(); - - $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass'); - - $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); - $barDefinition->setAutowired(true); - - $pass = new AutowirePass(); - $pass->process($container); - - $this->assertTrue($container->hasDefinition('bar')); - } -} - -class Foo -{ -} - -class Bar -{ - public function __construct(Foo $foo) - { - } -} - -class A -{ -} - -class B extends A -{ -} - -class C -{ - public function __construct(A $a) - { - } -} - -interface DInterface -{ -} - -interface EInterface extends DInterface -{ -} - -interface IInterface -{ -} - -class I implements IInterface -{ -} - -class F extends I implements EInterface -{ -} - -class G -{ - public function __construct(DInterface $d, EInterface $e, IInterface $i) - { - } -} - -class H -{ - public function __construct(B $b, DInterface $d) - { - } -} - -interface CollisionInterface -{ -} - -class CollisionA implements CollisionInterface -{ -} - -class CollisionB implements CollisionInterface -{ -} - -class CannotBeAutowired -{ - public function __construct(CollisionInterface $collision) - { - } -} - -class Lille -{ -} - -class Dunglas -{ - public function __construct(Lille $l) - { - } -} - -class LesTilleuls -{ - public function __construct(Dunglas $k) - { - } -} - -class OptionalParameter -{ - public function __construct(CollisionInterface $c = null, A $a, Foo $f = null) - { - } -} - -class BadTypeHintedArgument -{ - public function __construct(Dunglas $k, NotARealClass $r) - { - } -} -class BadParentTypeHintedArgument -{ - public function __construct(Dunglas $k, OptionalServiceClass $r) - { - } -} -class NotGuessableArgument -{ - public function __construct(Foo $k) - { - } -} -class NotGuessableArgumentForSubclass -{ - public function __construct(A $k) - { - } -} -class MultipleArguments -{ - public function __construct(A $k, $foo, Dunglas $dunglas) - { - } -} - -class MultipleArgumentsOptionalScalar -{ - public function __construct(A $a, $foo = 'default_val', Lille $lille = null) - { - } -} -class MultipleArgumentsOptionalScalarLast -{ - public function __construct(A $a, Lille $lille, $foo = 'some_val') - { - } -} -class MultipleArgumentsOptionalScalarNotReallyOptional -{ - public function __construct(A $a, $foo = 'default_val', Lille $lille) - { - } -} - -/* - * Classes used for testing createResourceForClass - */ -class ClassForResource -{ - public function __construct($foo, Bar $bar = null) - { - } - - public function setBar(Bar $bar) - { - } -} -class IdenticalClassResource extends ClassForResource -{ -} -class ClassChangedConstructorArgs extends ClassForResource -{ - public function __construct($foo, Bar $bar, $baz) - { - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php deleted file mode 100644 index 55351e551..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php +++ /dev/null @@ -1,130 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; -use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; -use Symfony\Component\DependencyInjection\Compiler\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class CheckCircularReferencesPassTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testProcess() - { - $container = new ContainerBuilder(); - $container->register('a')->addArgument(new Reference('b')); - $container->register('b')->addArgument(new Reference('a')); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testProcessWithAliases() - { - $container = new ContainerBuilder(); - $container->register('a')->addArgument(new Reference('b')); - $container->setAlias('b', 'c'); - $container->setAlias('c', 'a'); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testProcessWithFactory() - { - $container = new ContainerBuilder(); - - $container - ->register('a', 'stdClass') - ->setFactory(array(new Reference('b'), 'getInstance')); - - $container - ->register('b', 'stdClass') - ->setFactory(array(new Reference('a'), 'getInstance')); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testProcessDetectsIndirectCircularReference() - { - $container = new ContainerBuilder(); - $container->register('a')->addArgument(new Reference('b')); - $container->register('b')->addArgument(new Reference('c')); - $container->register('c')->addArgument(new Reference('a')); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testProcessDetectsIndirectCircularReferenceWithFactory() - { - $container = new ContainerBuilder(); - - $container->register('a')->addArgument(new Reference('b')); - - $container - ->register('b', 'stdClass') - ->setFactory(array(new Reference('c'), 'getInstance')); - - $container->register('c')->addArgument(new Reference('a')); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testDeepCircularReference() - { - $container = new ContainerBuilder(); - $container->register('a')->addArgument(new Reference('b')); - $container->register('b')->addArgument(new Reference('c')); - $container->register('c')->addArgument(new Reference('b')); - - $this->process($container); - } - - public function testProcessIgnoresMethodCalls() - { - $container = new ContainerBuilder(); - $container->register('a')->addArgument(new Reference('b')); - $container->register('b')->addMethodCall('setA', array(new Reference('a'))); - - $this->process($container); - } - - protected function process(ContainerBuilder $container) - { - $compiler = new Compiler(); - $passConfig = $compiler->getPassConfig(); - $passConfig->setOptimizationPasses(array( - new AnalyzeServiceReferencesPass(true), - new CheckCircularReferencesPass(), - )); - $passConfig->setRemovingPasses(array()); - - $compiler->compile($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.php deleted file mode 100644 index b44df71b7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\CheckDefinitionValidityPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - */ - public function testProcessDetectsSyntheticNonPublicDefinitions() - { - $container = new ContainerBuilder(); - $container->register('a')->setSynthetic(true)->setPublic(false); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - */ - public function testProcessDetectsNonSyntheticNonAbstractDefinitionWithoutClass() - { - $container = new ContainerBuilder(); - $container->register('a')->setSynthetic(false)->setAbstract(false); - - $this->process($container); - } - - public function testProcess() - { - $container = new ContainerBuilder(); - $container->register('a', 'class'); - $container->register('b', 'class')->setSynthetic(true)->setPublic(true); - $container->register('c', 'class')->setAbstract(true); - $container->register('d', 'class')->setSynthetic(true); - - $this->process($container); - } - - public function testValidTags() - { - $container = new ContainerBuilder(); - $container->register('a', 'class')->addTag('foo', array('bar' => 'baz')); - $container->register('b', 'class')->addTag('foo', array('bar' => null)); - $container->register('c', 'class')->addTag('foo', array('bar' => 1)); - $container->register('d', 'class')->addTag('foo', array('bar' => 1.1)); - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - */ - public function testInvalidTags() - { - $container = new ContainerBuilder(); - $container->register('a', 'class')->addTag('foo', array('bar' => array('baz' => 'baz'))); - - $this->process($container); - } - - protected function process(ContainerBuilder $container) - { - $pass = new CheckDefinitionValidityPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php deleted file mode 100644 index 18b605b46..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class CheckExceptionOnInvalidReferenceBehaviorPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - - $container - ->register('a', '\stdClass') - ->addArgument(new Reference('b')) - ; - $container->register('b', '\stdClass'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException - */ - public function testProcessThrowsExceptionOnInvalidReference() - { - $container = new ContainerBuilder(); - - $container - ->register('a', '\stdClass') - ->addArgument(new Reference('b')) - ; - - $this->process($container); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException - */ - public function testProcessThrowsExceptionOnInvalidReferenceFromInlinedDefinition() - { - $container = new ContainerBuilder(); - - $def = new Definition(); - $def->addArgument(new Reference('b')); - - $container - ->register('a', '\stdClass') - ->addArgument($def) - ; - - $this->process($container); - } - - private function process(ContainerBuilder $container) - { - $pass = new CheckExceptionOnInvalidReferenceBehaviorPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.php deleted file mode 100644 index 207306ca7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\CheckReferenceValidityPass; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \RuntimeException - */ - public function testProcessDetectsReferenceToAbstractDefinition() - { - $container = new ContainerBuilder(); - - $container->register('a')->setAbstract(true); - $container->register('b')->addArgument(new Reference('a')); - - $this->process($container); - } - - public function testProcess() - { - $container = new ContainerBuilder(); - $container->register('a')->addArgument(new Reference('b')); - $container->register('b'); - - $this->process($container); - } - - protected function process(ContainerBuilder $container) - { - $pass = new CheckReferenceValidityPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.php deleted file mode 100644 index fbdd3af37..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.php +++ /dev/null @@ -1,172 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Alias; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass; - -class DecoratorServicePassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcessWithoutAlias() - { - $container = new ContainerBuilder(); - $fooDefinition = $container - ->register('foo') - ->setPublic(false) - ; - $fooExtendedDefinition = $container - ->register('foo.extended') - ->setPublic(true) - ->setDecoratedService('foo') - ; - $barDefinition = $container - ->register('bar') - ->setPublic(true) - ; - $barExtendedDefinition = $container - ->register('bar.extended') - ->setPublic(true) - ->setDecoratedService('bar', 'bar.yoo') - ; - - $this->process($container); - - $this->assertEquals('foo.extended', $container->getAlias('foo')); - $this->assertFalse($container->getAlias('foo')->isPublic()); - - $this->assertEquals('bar.extended', $container->getAlias('bar')); - $this->assertTrue($container->getAlias('bar')->isPublic()); - - $this->assertSame($fooDefinition, $container->getDefinition('foo.extended.inner')); - $this->assertFalse($container->getDefinition('foo.extended.inner')->isPublic()); - - $this->assertSame($barDefinition, $container->getDefinition('bar.yoo')); - $this->assertFalse($container->getDefinition('bar.yoo')->isPublic()); - - $this->assertNull($fooExtendedDefinition->getDecoratedService()); - $this->assertNull($barExtendedDefinition->getDecoratedService()); - } - - public function testProcessWithAlias() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(true) - ; - $container->setAlias('foo.alias', new Alias('foo', false)); - $fooExtendedDefinition = $container - ->register('foo.extended') - ->setPublic(true) - ->setDecoratedService('foo.alias') - ; - - $this->process($container); - - $this->assertEquals('foo.extended', $container->getAlias('foo.alias')); - $this->assertFalse($container->getAlias('foo.alias')->isPublic()); - - $this->assertEquals('foo', $container->getAlias('foo.extended.inner')); - $this->assertFalse($container->getAlias('foo.extended.inner')->isPublic()); - - $this->assertNull($fooExtendedDefinition->getDecoratedService()); - } - - public function testProcessWithPriority() - { - $container = new ContainerBuilder(); - $fooDefinition = $container - ->register('foo') - ->setPublic(false) - ; - $barDefinition = $container - ->register('bar') - ->setPublic(true) - ->setDecoratedService('foo') - ; - $bazDefinition = $container - ->register('baz') - ->setPublic(true) - ->setDecoratedService('foo', null, 5) - ; - $quxDefinition = $container - ->register('qux') - ->setPublic(true) - ->setDecoratedService('foo', null, 3) - ; - - $this->process($container); - - $this->assertEquals('bar', $container->getAlias('foo')); - $this->assertFalse($container->getAlias('foo')->isPublic()); - - $this->assertSame($fooDefinition, $container->getDefinition('baz.inner')); - $this->assertFalse($container->getDefinition('baz.inner')->isPublic()); - - $this->assertEquals('qux', $container->getAlias('bar.inner')); - $this->assertFalse($container->getAlias('bar.inner')->isPublic()); - - $this->assertEquals('baz', $container->getAlias('qux.inner')); - $this->assertFalse($container->getAlias('qux.inner')->isPublic()); - - $this->assertNull($barDefinition->getDecoratedService()); - $this->assertNull($bazDefinition->getDecoratedService()); - $this->assertNull($quxDefinition->getDecoratedService()); - } - - public function testProcessMovesTagsFromDecoratedDefinitionToDecoratingDefinition() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setTags(array('bar' => array('attr' => 'baz'))) - ; - $container - ->register('baz') - ->setTags(array('foobar' => array('attr' => 'bar'))) - ->setDecoratedService('foo') - ; - - $this->process($container); - - $this->assertEmpty($container->getDefinition('baz.inner')->getTags()); - $this->assertEquals(array('bar' => array('attr' => 'baz'), 'foobar' => array('attr' => 'bar')), $container->getDefinition('baz')->getTags()); - } - - public function testProcessMergesAutowiringTypesInDecoratingDefinitionAndRemoveThemFromDecoratedDefinition() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addAutowiringType('Bar') - ; - - $container - ->register('child') - ->setDecoratedService('parent') - ->addAutowiringType('Foo') - ; - - $this->process($container); - - $this->assertEquals(array('Bar', 'Foo'), $container->getDefinition('child')->getAutowiringTypes()); - $this->assertEmpty($container->getDefinition('child.inner')->getAutowiringTypes()); - } - - protected function process(ContainerBuilder $container) - { - $repeatedPass = new DecoratorServicePass(); - $repeatedPass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.php deleted file mode 100644 index b786db95f..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\ExtensionCompilerPass; - -/** - * @author Wouter J - */ -class ExtensionCompilerPassTest extends \PHPUnit_Framework_TestCase -{ - private $container; - private $pass; - - protected function setUp() - { - $this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); - $this->pass = new ExtensionCompilerPass(); - } - - public function testProcess() - { - $extension1 = $this->createExtensionMock(true); - $extension1->expects($this->once())->method('process'); - $extension2 = $this->createExtensionMock(false); - $extension3 = $this->createExtensionMock(false); - $extension4 = $this->createExtensionMock(true); - $extension4->expects($this->once())->method('process'); - - $this->container->expects($this->any()) - ->method('getExtensions') - ->will($this->returnValue(array($extension1, $extension2, $extension3, $extension4))) - ; - - $this->pass->process($this->container); - } - - private function createExtensionMock($hasInlineCompile) - { - return $this->getMock('Symfony\Component\DependencyInjection\\'.( - $hasInlineCompile - ? 'Compiler\CompilerPassInterface' - : 'Extension\ExtensionInterface' - )); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/FactoryReturnTypePassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/FactoryReturnTypePassTest.php deleted file mode 100644 index ecee63799..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/FactoryReturnTypePassTest.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\FactoryReturnTypePass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Tests\Fixtures\factoryFunction; -use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryDummy; -use Symfony\Component\DependencyInjection\Tests\Fixtures\FactoryParent; - -/** - * @author Guilhem N. - */ -class FactoryReturnTypePassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - - $factory = $container->register('factory'); - $factory->setFactory(array(FactoryDummy::class, 'createFactory')); - - $container->setAlias('alias_factory', 'factory'); - - $foo = $container->register('foo'); - $foo->setFactory(array(new Reference('alias_factory'), 'create')); - - $bar = $container->register('bar', __CLASS__); - $bar->setFactory(array(new Reference('factory'), 'create')); - - $pass = new FactoryReturnTypePass(); - $pass->process($container); - - if (method_exists(\ReflectionMethod::class, 'getReturnType')) { - $this->assertEquals(FactoryDummy::class, $factory->getClass()); - $this->assertEquals(\stdClass::class, $foo->getClass()); - } else { - $this->assertNull($factory->getClass()); - $this->assertNull($foo->getClass()); - } - $this->assertEquals(__CLASS__, $bar->getClass()); - } - - /** - * @dataProvider returnTypesProvider - */ - public function testReturnTypes($factory, $returnType, $hhvmSupport = true) - { - if (!$hhvmSupport && defined('HHVM_VERSION')) { - $this->markTestSkipped('Scalar typehints not supported by hhvm.'); - } - - $container = new ContainerBuilder(); - - $service = $container->register('service'); - $service->setFactory($factory); - - $pass = new FactoryReturnTypePass(); - $pass->process($container); - - if (method_exists(\ReflectionMethod::class, 'getReturnType')) { - $this->assertEquals($returnType, $service->getClass()); - } else { - $this->assertNull($service->getClass()); - } - } - - public function returnTypesProvider() - { - return array( - // must be loaded before the function as they are in the same file - array(array(FactoryDummy::class, 'createBuiltin'), null, false), - array(array(FactoryDummy::class, 'createParent'), FactoryParent::class), - array(array(FactoryDummy::class, 'createSelf'), FactoryDummy::class), - array(factoryFunction::class, FactoryDummy::class), - ); - } - - public function testCircularReference() - { - $container = new ContainerBuilder(); - - $factory = $container->register('factory'); - $factory->setFactory(array(new Reference('factory2'), 'createSelf')); - - $factory2 = $container->register('factory2'); - $factory2->setFactory(array(new Reference('factory'), 'create')); - - $pass = new FactoryReturnTypePass(); - $pass->process($container); - - $this->assertNull($factory->getClass()); - $this->assertNull($factory2->getClass()); - } - - public function testCompile() - { - $container = new ContainerBuilder(); - - $factory = $container->register('factory'); - $factory->setFactory(array(FactoryDummy::class, 'createFactory')); - - if (!method_exists(\ReflectionMethod::class, 'getReturnType')) { - $this->setExpectedException(\RuntimeException::class, 'Please add the class to service "factory" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.'); - } - - $container->compile(); - - $this->assertEquals(FactoryDummy::class, $container->getDefinition('factory')->getClass()); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.php deleted file mode 100644 index 342a6baf8..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ /dev/null @@ -1,230 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; -use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; -use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $container - ->register('inlinable.service') - ->setPublic(false) - ; - - $container - ->register('service') - ->setArguments(array(new Reference('inlinable.service'))) - ; - - $this->process($container); - - $arguments = $container->getDefinition('service')->getArguments(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $arguments[0]); - $this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]); - } - - public function testProcessDoesNotInlinesWhenAliasedServiceIsShared() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(false) - ; - $container->setAlias('moo', 'foo'); - - $container - ->register('service') - ->setArguments(array($ref = new Reference('foo'))) - ; - - $this->process($container); - - $arguments = $container->getDefinition('service')->getArguments(); - $this->assertSame($ref, $arguments[0]); - } - - public function testProcessDoesInlineNonSharedService() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setShared(false) - ; - $container - ->register('bar') - ->setPublic(false) - ->setShared(false) - ; - $container->setAlias('moo', 'bar'); - - $container - ->register('service') - ->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar'))) - ; - - $this->process($container); - - $arguments = $container->getDefinition('service')->getArguments(); - $this->assertEquals($container->getDefinition('foo'), $arguments[0]); - $this->assertNotSame($container->getDefinition('foo'), $arguments[0]); - $this->assertSame($ref, $arguments[1]); - $this->assertEquals($container->getDefinition('bar'), $arguments[2]); - $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); - } - - public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() - { - $container = new ContainerBuilder(); - - $a = $container->register('a')->setPublic(false); - $b = $container - ->register('b') - ->addArgument(new Reference('a')) - ->addArgument(new Definition(null, array(new Reference('a')))) - ; - - $this->process($container); - - $arguments = $b->getArguments(); - $this->assertSame($a, $arguments[0]); - - $inlinedArguments = $arguments[1]->getArguments(); - $this->assertSame($a, $inlinedArguments[0]); - } - - public function testProcessInlinesPrivateFactoryReference() - { - $container = new ContainerBuilder(); - - $container->register('a')->setPublic(false); - $b = $container - ->register('b') - ->setPublic(false) - ->setFactory(array(new Reference('a'), 'a')) - ; - - $container - ->register('foo') - ->setArguments(array( - $ref = new Reference('b'), - )); - - $this->process($container); - - $inlinedArguments = $container->getDefinition('foo')->getArguments(); - $this->assertSame($b, $inlinedArguments[0]); - } - - public function testProcessDoesNotInlinePrivateFactoryIfReferencedMultipleTimesWithinTheSameDefinition() - { - $container = new ContainerBuilder(); - $container - ->register('a') - ; - $container - ->register('b') - ->setPublic(false) - ->setFactory(array(new Reference('a'), 'a')) - ; - - $container - ->register('foo') - ->setArguments(array( - $ref1 = new Reference('b'), - $ref2 = new Reference('b'), - )) - ; - $this->process($container); - - $args = $container->getDefinition('foo')->getArguments(); - $this->assertSame($ref1, $args[0]); - $this->assertSame($ref2, $args[1]); - } - - public function testProcessDoesNotInlineReferenceWhenUsedByInlineFactory() - { - $container = new ContainerBuilder(); - $container - ->register('a') - ; - $container - ->register('b') - ->setPublic(false) - ->setFactory(array(new Reference('a'), 'a')) - ; - - $inlineFactory = new Definition(); - $inlineFactory->setPublic(false); - $inlineFactory->setFactory(array(new Reference('b'), 'b')); - - $container - ->register('foo') - ->setArguments(array( - $ref = new Reference('b'), - $inlineFactory, - )) - ; - $this->process($container); - - $args = $container->getDefinition('foo')->getArguments(); - $this->assertSame($ref, $args[0]); - } - - public function testProcessDoesNotInlineWhenServiceIsPrivateButLazy() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(false) - ->setLazy(true) - ; - - $container - ->register('service') - ->setArguments(array($ref = new Reference('foo'))) - ; - - $this->process($container); - - $arguments = $container->getDefinition('service')->getArguments(); - $this->assertSame($ref, $arguments[0]); - } - - public function testProcessDoesNotInlineWhenServiceReferencesItself() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(false) - ->addMethodCall('foo', array($ref = new Reference('foo'))) - ; - - $this->process($container); - - $calls = $container->getDefinition('foo')->getMethodCalls(); - $this->assertSame($ref, $calls[0][1][0]); - } - - protected function process(ContainerBuilder $container) - { - $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass())); - $repeatedPass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php deleted file mode 100644 index c4479403a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php +++ /dev/null @@ -1,116 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Alias; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * This class tests the integration of the different compiler passes. - */ -class IntegrationTest extends \PHPUnit_Framework_TestCase -{ - /** - * This tests that dependencies are correctly processed. - * - * We're checking that: - * - * * A is public, B/C are private - * * A -> C - * * B -> C - */ - public function testProcessRemovesAndInlinesRecursively() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - - $a = $container - ->register('a', '\stdClass') - ->addArgument(new Reference('c')) - ; - - $b = $container - ->register('b', '\stdClass') - ->addArgument(new Reference('c')) - ->setPublic(false) - ; - - $c = $container - ->register('c', '\stdClass') - ->setPublic(false) - ; - - $container->compile(); - - $this->assertTrue($container->hasDefinition('a')); - $arguments = $a->getArguments(); - $this->assertSame($c, $arguments[0]); - $this->assertFalse($container->hasDefinition('b')); - $this->assertFalse($container->hasDefinition('c')); - } - - public function testProcessInlinesReferencesToAliases() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - - $a = $container - ->register('a', '\stdClass') - ->addArgument(new Reference('b')) - ; - - $container->setAlias('b', new Alias('c', false)); - - $c = $container - ->register('c', '\stdClass') - ->setPublic(false) - ; - - $container->compile(); - - $this->assertTrue($container->hasDefinition('a')); - $arguments = $a->getArguments(); - $this->assertSame($c, $arguments[0]); - $this->assertFalse($container->hasAlias('b')); - $this->assertFalse($container->hasDefinition('c')); - } - - public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDefinition() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - - $container - ->register('a', '\stdClass') - ->addArgument(new Reference('b')) - ->addMethodCall('setC', array(new Reference('c'))) - ; - - $container - ->register('b', '\stdClass') - ->addArgument(new Reference('c')) - ->setPublic(false) - ; - - $container - ->register('c', '\stdClass') - ->setPublic(false) - ; - - $container->compile(); - - $this->assertTrue($container->hasDefinition('a')); - $this->assertFalse($container->hasDefinition('b')); - $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php deleted file mode 100644 index aed8cdfe1..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; - -class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase -{ - public function testExpressionLanguageProviderForwarding() - { - $tmpProviders = array(); - - $extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface'); - $extension->expects($this->any()) - ->method('getXsdValidationBasePath') - ->will($this->returnValue(false)); - $extension->expects($this->any()) - ->method('getNamespace') - ->will($this->returnValue('http://example.org/schema/dic/foo')); - $extension->expects($this->any()) - ->method('getAlias') - ->will($this->returnValue('foo')); - $extension->expects($this->once()) - ->method('load') - ->will($this->returnCallback(function (array $config, ContainerBuilder $container) use (&$tmpProviders) { - $tmpProviders = $container->getExpressionLanguageProviders(); - })); - - $provider = $this->getMock('Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface'); - $container = new ContainerBuilder(new ParameterBag()); - $container->registerExtension($extension); - $container->prependExtensionConfig('foo', array('bar' => true)); - $container->addExpressionLanguageProvider($provider); - - $pass = new MergeExtensionConfigurationPass(); - $pass->process($container); - - $this->assertEquals(array($provider), $tmpProviders); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.php deleted file mode 100644 index 7e9238f22..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Bug\NotExistClass; - -class OptionalServiceClass extends NotExistClass -{ -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.php deleted file mode 100644 index 90659f205..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\PassConfig; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; - -/** - * @author Guilhem N - */ -class PassConfigTest extends \PHPUnit_Framework_TestCase -{ - public function testPassOrdering() - { - $config = new PassConfig(); - - $pass1 = $this->getMock(CompilerPassInterface::class); - $config->addPass($pass1, PassConfig::TYPE_BEFORE_OPTIMIZATION, 10); - - $pass2 = $this->getMock(CompilerPassInterface::class); - $config->addPass($pass2, PassConfig::TYPE_BEFORE_OPTIMIZATION, 30); - - $this->assertSame(array($pass2, $pass1), $config->getBeforeOptimizationPasses()); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.php deleted file mode 100644 index 415072050..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -class PriorityTaggedServiceTraitTest extends \PHPUnit_Framework_TestCase -{ - public function testThatCacheWarmersAreProcessedInPriorityOrder() - { - $services = array( - 'my_service1' => array('my_custom_tag' => array('priority' => 100)), - 'my_service2' => array('my_custom_tag' => array('priority' => 200)), - 'my_service3' => array('my_custom_tag' => array('priority' => -501)), - 'my_service4' => array('my_custom_tag' => array()), - 'my_service5' => array('my_custom_tag' => array('priority' => -1)), - 'my_service6' => array('my_custom_tag' => array('priority' => -500)), - 'my_service7' => array('my_custom_tag' => array('priority' => -499)), - 'my_service8' => array('my_custom_tag' => array('priority' => 1)), - 'my_service9' => array('my_custom_tag' => array('priority' => -2)), - 'my_service10' => array('my_custom_tag' => array('priority' => -1000)), - 'my_service11' => array('my_custom_tag' => array('priority' => -1001)), - 'my_service12' => array('my_custom_tag' => array('priority' => -1002)), - 'my_service13' => array('my_custom_tag' => array('priority' => -1003)), - ); - - $container = new ContainerBuilder(); - - foreach ($services as $id => $tags) { - $definition = $container->register($id); - - foreach ($tags as $name => $attributes) { - $definition->addTag($name, $attributes); - } - } - - $expected = array( - new Reference('my_service2'), - new Reference('my_service1'), - new Reference('my_service8'), - new Reference('my_service4'), - new Reference('my_service5'), - new Reference('my_service9'), - new Reference('my_service7'), - new Reference('my_service6'), - new Reference('my_service3'), - new Reference('my_service10'), - new Reference('my_service11'), - new Reference('my_service12'), - new Reference('my_service13'), - ); - - $priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation(); - - $this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test('my_custom_tag', $container)); - } -} - -class PriorityTaggedServiceTraitImplementation -{ - use PriorityTaggedServiceTrait; - - public function test($tagName, ContainerBuilder $container) - { - return $this->findAndSortTaggedServices($tagName, $container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php deleted file mode 100644 index 82149ebdb..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php +++ /dev/null @@ -1,113 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; -use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; -use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class RemoveUnusedDefinitionsPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(false) - ; - $container - ->register('bar') - ->setPublic(false) - ; - $container - ->register('moo') - ->setArguments(array(new Reference('bar'))) - ; - - $this->process($container); - - $this->assertFalse($container->hasDefinition('foo')); - $this->assertTrue($container->hasDefinition('bar')); - $this->assertTrue($container->hasDefinition('moo')); - } - - public function testProcessRemovesUnusedDefinitionsRecursively() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(false) - ; - $container - ->register('bar') - ->setArguments(array(new Reference('foo'))) - ->setPublic(false) - ; - - $this->process($container); - - $this->assertFalse($container->hasDefinition('foo')); - $this->assertFalse($container->hasDefinition('bar')); - } - - public function testProcessWorksWithInlinedDefinitions() - { - $container = new ContainerBuilder(); - $container - ->register('foo') - ->setPublic(false) - ; - $container - ->register('bar') - ->setArguments(array(new Definition(null, array(new Reference('foo'))))) - ; - - $this->process($container); - - $this->assertTrue($container->hasDefinition('foo')); - $this->assertTrue($container->hasDefinition('bar')); - } - - public function testProcessWontRemovePrivateFactory() - { - $container = new ContainerBuilder(); - - $container - ->register('foo', 'stdClass') - ->setFactory(array('stdClass', 'getInstance')) - ->setPublic(false); - - $container - ->register('bar', 'stdClass') - ->setFactory(array(new Reference('foo'), 'getInstance')) - ->setPublic(false); - - $container - ->register('foobar') - ->addArgument(new Reference('bar')); - - $this->process($container); - - $this->assertTrue($container->hasDefinition('foo')); - $this->assertTrue($container->hasDefinition('bar')); - $this->assertTrue($container->hasDefinition('foobar')); - } - - protected function process(ContainerBuilder $container) - { - $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass())); - $repeatedPass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php deleted file mode 100644 index 596592cb5..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -require_once __DIR__.'/../Fixtures/includes/foo.php'; - -class ReplaceAliasByActualDefinitionPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - - $aDefinition = $container->register('a', '\stdClass'); - $aDefinition->setFactory(array(new Reference('b'), 'createA')); - - $bDefinition = new Definition('\stdClass'); - $bDefinition->setPublic(false); - $container->setDefinition('b', $bDefinition); - - $container->setAlias('a_alias', 'a'); - $container->setAlias('b_alias', 'b'); - - $container->setAlias('container', 'service_container'); - - $this->process($container); - - $this->assertTrue($container->has('a'), '->process() does nothing to public definitions.'); - $this->assertTrue($container->hasAlias('a_alias')); - $this->assertFalse($container->has('b'), '->process() removes non-public definitions.'); - $this->assertTrue( - $container->has('b_alias') && !$container->hasAlias('b_alias'), - '->process() replaces alias to actual.' - ); - - $this->assertTrue($container->has('container')); - - $resolvedFactory = $aDefinition->getFactory(); - $this->assertSame('b_alias', (string) $resolvedFactory[0]); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testProcessWithInvalidAlias() - { - $container = new ContainerBuilder(); - $container->setAlias('a_alias', 'a'); - $this->process($container); - } - - protected function process(ContainerBuilder $container) - { - $pass = new ReplaceAliasByActualDefinitionPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php deleted file mode 100644 index 3ca0df4a7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ /dev/null @@ -1,366 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $container->register('parent', 'foo')->setArguments(array('moo', 'b'))->setProperty('foo', 'moo'); - $container->setDefinition('child', new DefinitionDecorator('parent')) - ->replaceArgument(0, 'a') - ->setProperty('foo', 'bar') - ->setClass('bar') - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertNotInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $def); - $this->assertEquals('bar', $def->getClass()); - $this->assertEquals(array('a', 'b'), $def->getArguments()); - $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); - } - - public function testProcessAppendsMethodCallsAlways() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addMethodCall('foo', array('bar')) - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ->addMethodCall('bar', array('foo')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertEquals(array( - array('foo', array('bar')), - array('bar', array('foo')), - ), $def->getMethodCalls()); - } - - public function testProcessDoesNotCopyAbstract() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->setAbstract(true) - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertFalse($def->isAbstract()); - } - - public function testProcessDoesNotCopyShared() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->setShared(false) - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertTrue($def->isShared()); - } - - public function testProcessDoesNotCopyTags() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addTag('foo') - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertEquals(array(), $def->getTags()); - } - - public function testProcessDoesNotCopyDecoratedService() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->setDecoratedService('foo') - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertNull($def->getDecoratedService()); - } - - public function testProcessDoesNotDropShared() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ->setShared(false) - ; - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertFalse($def->isShared()); - } - - public function testProcessHandlesMultipleInheritance() - { - $container = new ContainerBuilder(); - - $container - ->register('parent', 'foo') - ->setArguments(array('foo', 'bar', 'c')) - ; - - $container - ->setDefinition('child2', new DefinitionDecorator('child1')) - ->replaceArgument(1, 'b') - ; - - $container - ->setDefinition('child1', new DefinitionDecorator('parent')) - ->replaceArgument(0, 'a') - ; - - $this->process($container); - - $def = $container->getDefinition('child2'); - $this->assertEquals(array('a', 'b', 'c'), $def->getArguments()); - $this->assertEquals('foo', $def->getClass()); - } - - public function testSetLazyOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass'); - - $container->setDefinition('child1', new DefinitionDecorator('parent')) - ->setLazy(true) - ; - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isLazy()); - } - - public function testSetLazyOnServiceIsParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setLazy(true) - ; - - $container->setDefinition('child1', new DefinitionDecorator('parent')); - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isLazy()); - } - - public function testSetAutowiredOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass'); - - $container->setDefinition('child1', new DefinitionDecorator('parent')) - ->setAutowired(true) - ; - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isAutowired()); - } - - public function testSetAutowiredOnServiceIsParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass') - ->setAutowired(true) - ; - - $container->setDefinition('child1', new DefinitionDecorator('parent')); - - $this->process($container); - - $this->assertTrue($container->getDefinition('child1')->isAutowired()); - } - - public function testDeepDefinitionsResolving() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'parentClass'); - $container->register('sibling', 'siblingClass') - ->setConfigurator(new DefinitionDecorator('parent'), 'foo') - ->setFactory(array(new DefinitionDecorator('parent'), 'foo')) - ->addArgument(new DefinitionDecorator('parent')) - ->setProperty('prop', new DefinitionDecorator('parent')) - ->addMethodCall('meth', array(new DefinitionDecorator('parent'))) - ; - - $this->process($container); - - $configurator = $container->getDefinition('sibling')->getConfigurator(); - $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($configurator)); - $this->assertSame('parentClass', $configurator->getClass()); - - $factory = $container->getDefinition('sibling')->getFactory(); - $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($factory[0])); - $this->assertSame('parentClass', $factory[0]->getClass()); - - $argument = $container->getDefinition('sibling')->getArgument(0); - $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($argument)); - $this->assertSame('parentClass', $argument->getClass()); - - $properties = $container->getDefinition('sibling')->getProperties(); - $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($properties['prop'])); - $this->assertSame('parentClass', $properties['prop']->getClass()); - - $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); - $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($methodCalls[0][1][0])); - $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); - } - - public function testSetDecoratedServiceOnServiceHasParent() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'stdClass'); - - $container->setDefinition('child1', new DefinitionDecorator('parent')) - ->setDecoratedService('foo', 'foo_inner', 5) - ; - - $this->process($container); - - $this->assertEquals(array('foo', 'foo_inner', 5), $container->getDefinition('child1')->getDecoratedService()); - } - - public function testDecoratedServiceCopiesDeprecatedStatusFromParent() - { - $container = new ContainerBuilder(); - $container->register('deprecated_parent') - ->setDeprecated(true) - ; - - $container->setDefinition('decorated_deprecated_parent', new DefinitionDecorator('deprecated_parent')); - - $this->process($container); - - $this->assertTrue($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); - } - - public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() - { - $container = new ContainerBuilder(); - $container->register('deprecated_parent') - ->setDeprecated(true) - ; - - $container->setDefinition('decorated_deprecated_parent', new DefinitionDecorator('deprecated_parent')) - ->setDeprecated(false) - ; - - $this->process($container); - - $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); - } - - public function testProcessMergeAutowiringTypes() - { - $container = new ContainerBuilder(); - - $container - ->register('parent') - ->addAutowiringType('Foo') - ; - - $container - ->setDefinition('child', new DefinitionDecorator('parent')) - ->addAutowiringType('Bar') - ; - - $this->process($container); - - $childDef = $container->getDefinition('child'); - $this->assertEquals(array('Foo', 'Bar'), $childDef->getAutowiringTypes()); - - $parentDef = $container->getDefinition('parent'); - $this->assertSame(array('Foo'), $parentDef->getAutowiringTypes()); - } - - public function testProcessResolvesAliases() - { - $container = new ContainerBuilder(); - - $container->register('parent', 'ParentClass'); - $container->setAlias('parent_alias', 'parent'); - $container->setDefinition('child', new DefinitionDecorator('parent_alias')); - - $this->process($container); - - $def = $container->getDefinition('child'); - $this->assertSame('ParentClass', $def->getClass()); - } - - protected function process(ContainerBuilder $container) - { - $pass = new ResolveDefinitionTemplatesPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php deleted file mode 100644 index ecc5bee8e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class ResolveInvalidReferencesPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $def = $container - ->register('foo') - ->setArguments(array( - new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE), - new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), - )) - ->addMethodCall('foo', array(new Reference('moo', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) - ; - - $this->process($container); - - $arguments = $def->getArguments(); - $this->assertSame(array(null, null), $arguments); - $this->assertCount(0, $def->getMethodCalls()); - } - - public function testProcessIgnoreInvalidArgumentInCollectionArgument() - { - $container = new ContainerBuilder(); - $container->register('baz'); - $def = $container - ->register('foo') - ->setArguments(array( - array( - new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), - $baz = new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), - new Reference('moo', ContainerInterface::NULL_ON_INVALID_REFERENCE), - ), - )) - ; - - $this->process($container); - - $arguments = $def->getArguments(); - $this->assertSame(array($baz, null), $arguments[0]); - } - - public function testProcessKeepMethodCallOnInvalidArgumentInCollectionArgument() - { - $container = new ContainerBuilder(); - $container->register('baz'); - $def = $container - ->register('foo') - ->addMethodCall('foo', array( - array( - new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), - $baz = new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), - new Reference('moo', ContainerInterface::NULL_ON_INVALID_REFERENCE), - ), - )) - ; - - $this->process($container); - - $calls = $def->getMethodCalls(); - $this->assertCount(1, $def->getMethodCalls()); - $this->assertSame(array($baz, null), $calls[0][1][0]); - } - - public function testProcessIgnoreNonExistentServices() - { - $container = new ContainerBuilder(); - $def = $container - ->register('foo') - ->setArguments(array(new Reference('bar'))) - ; - - $this->process($container); - - $arguments = $def->getArguments(); - $this->assertEquals('bar', (string) $arguments[0]); - } - - public function testProcessRemovesPropertiesOnInvalid() - { - $container = new ContainerBuilder(); - $def = $container - ->register('foo') - ->setProperty('foo', new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)) - ; - - $this->process($container); - - $this->assertEquals(array(), $def->getProperties()); - } - - protected function process(ContainerBuilder $container) - { - $pass = new ResolveInvalidReferencesPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php deleted file mode 100644 index 1f604c228..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php +++ /dev/null @@ -1,91 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class ResolveParameterPlaceHoldersPassTest extends \PHPUnit_Framework_TestCase -{ - private $compilerPass; - private $container; - private $fooDefinition; - - protected function setUp() - { - $this->compilerPass = new ResolveParameterPlaceHoldersPass(); - $this->container = $this->createContainerBuilder(); - $this->compilerPass->process($this->container); - $this->fooDefinition = $this->container->getDefinition('foo'); - } - - public function testClassParametersShouldBeResolved() - { - $this->assertSame('Foo', $this->fooDefinition->getClass()); - } - - public function testFactoryParametersShouldBeResolved() - { - $this->assertSame(array('FooFactory', 'getFoo'), $this->fooDefinition->getFactory()); - } - - public function testArgumentParametersShouldBeResolved() - { - $this->assertSame(array('bar', 'baz'), $this->fooDefinition->getArguments()); - } - - public function testMethodCallParametersShouldBeResolved() - { - $this->assertSame(array(array('foobar', array('bar', 'baz'))), $this->fooDefinition->getMethodCalls()); - } - - public function testPropertyParametersShouldBeResolved() - { - $this->assertSame(array('bar' => 'baz'), $this->fooDefinition->getProperties()); - } - - public function testFileParametersShouldBeResolved() - { - $this->assertSame('foo.php', $this->fooDefinition->getFile()); - } - - public function testAliasParametersShouldBeResolved() - { - $this->assertSame('foo', $this->container->getAlias('bar')->__toString()); - } - - private function createContainerBuilder() - { - $containerBuilder = new ContainerBuilder(); - - $containerBuilder->setParameter('foo.class', 'Foo'); - $containerBuilder->setParameter('foo.factory.class', 'FooFactory'); - $containerBuilder->setParameter('foo.arg1', 'bar'); - $containerBuilder->setParameter('foo.arg2', 'baz'); - $containerBuilder->setParameter('foo.method', 'foobar'); - $containerBuilder->setParameter('foo.property.name', 'bar'); - $containerBuilder->setParameter('foo.property.value', 'baz'); - $containerBuilder->setParameter('foo.file', 'foo.php'); - $containerBuilder->setParameter('alias.id', 'bar'); - - $fooDefinition = $containerBuilder->register('foo', '%foo.class%'); - $fooDefinition->setFactory(array('%foo.factory.class%', 'getFoo')); - $fooDefinition->setArguments(array('%foo.arg1%', '%foo.arg2%')); - $fooDefinition->addMethodCall('%foo.method%', array('%foo.arg1%', '%foo.arg2%')); - $fooDefinition->setProperty('%foo.property.name%', '%foo.property.value%'); - $fooDefinition->setFile('%foo.file%'); - - $containerBuilder->setAlias('%alias.id%', 'foo'); - - return $containerBuilder; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php deleted file mode 100644 index 0fe83960b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Compiler; - -use Symfony\Component\DependencyInjection\Alias; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Compiler\ResolveReferencesToAliasesPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -class ResolveReferencesToAliasesPassTest extends \PHPUnit_Framework_TestCase -{ - public function testProcess() - { - $container = new ContainerBuilder(); - $container->setAlias('bar', 'foo'); - $def = $container - ->register('moo') - ->setArguments(array(new Reference('bar'))) - ; - - $this->process($container); - - $arguments = $def->getArguments(); - $this->assertEquals('foo', (string) $arguments[0]); - } - - public function testProcessRecursively() - { - $container = new ContainerBuilder(); - $container->setAlias('bar', 'foo'); - $container->setAlias('moo', 'bar'); - $def = $container - ->register('foobar') - ->setArguments(array(new Reference('moo'))) - ; - - $this->process($container); - - $arguments = $def->getArguments(); - $this->assertEquals('foo', (string) $arguments[0]); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testAliasCircularReference() - { - $container = new ContainerBuilder(); - $container->setAlias('bar', 'foo'); - $container->setAlias('foo', 'bar'); - $this->process($container); - } - - public function testResolveFactory() - { - $container = new ContainerBuilder(); - $container->register('factory', 'Factory'); - $container->setAlias('factory_alias', new Alias('factory')); - $foo = new Definition(); - $foo->setFactory(array(new Reference('factory_alias'), 'createFoo')); - $container->setDefinition('foo', $foo); - $bar = new Definition(); - $bar->setFactory(array('Factory', 'createFoo')); - $container->setDefinition('bar', $bar); - - $this->process($container); - - $resolvedFooFactory = $container->getDefinition('foo')->getFactory(); - $resolvedBarFactory = $container->getDefinition('bar')->getFactory(); - - $this->assertSame('factory', (string) $resolvedFooFactory[0]); - $this->assertSame('Factory', (string) $resolvedBarFactory[0]); - } - - protected function process(ContainerBuilder $container) - { - $pass = new ResolveReferencesToAliasesPass(); - $pass->process($container); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Config/AutowireServiceResourceTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Config/AutowireServiceResourceTest.php deleted file mode 100644 index 5c2704db2..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Config/AutowireServiceResourceTest.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Config; - -use Symfony\Component\DependencyInjection\Compiler\AutowirePass; -use Symfony\Component\DependencyInjection\Config\AutowireServiceResource; - -class AutowireServiceResourceTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var AutowireServiceResource - */ - private $resource; - private $file; - private $class; - private $time; - - protected function setUp() - { - $this->file = realpath(sys_get_temp_dir()).'/tmp.php'; - $this->time = time(); - touch($this->file, $this->time); - - $this->class = __NAMESPACE__.'\Foo'; - $this->resource = new AutowireServiceResource( - $this->class, - $this->file, - array() - ); - } - - public function testToString() - { - $this->assertSame('service.autowire.'.$this->class, (string) $this->resource); - } - - public function testSerializeUnserialize() - { - $unserialized = unserialize(serialize($this->resource)); - - $this->assertEquals($this->resource, $unserialized); - } - - public function testIsFresh() - { - $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); - $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); - $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); - } - - public function testIsFreshForDeletedResources() - { - unlink($this->file); - - $this->assertFalse($this->resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the resource does not exist'); - } - - public function testIsNotFreshChangedResource() - { - $oldResource = new AutowireServiceResource( - $this->class, - $this->file, - array('will_be_different') - ); - - // test with a stale file *and* a resource that *will* be different than the actual - $this->assertFalse($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed'); - } - - public function testIsFreshSameConstructorArgs() - { - $oldResource = AutowirePass::createResourceForClass( - new \ReflectionClass(__NAMESPACE__.'\Foo') - ); - - // test with a stale file *but* the resource will not be changed - $this->assertTrue($oldResource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the constructor arguments have changed'); - } - - public function testNotFreshIfClassNotFound() - { - $resource = new AutowireServiceResource( - 'Some\Non\Existent\Class', - $this->file, - array() - ); - - $this->assertFalse($resource->isFresh($this->getStaleFileTime()), '->isFresh() returns false if the class no longer exists'); - } - - protected function tearDown() - { - if (!file_exists($this->file)) { - return; - } - - unlink($this->file); - } - - private function getStaleFileTime() - { - return $this->time - 10; - } -} - -class Foo -{ - public function __construct($foo) - { - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php deleted file mode 100644 index 9ba5a36c4..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php +++ /dev/null @@ -1,840 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -require_once __DIR__.'/Fixtures/includes/classes.php'; -require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; - -use Symfony\Component\Config\Resource\ResourceInterface; -use Symfony\Component\DependencyInjection\Alias; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; -use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; -use Symfony\Component\DependencyInjection\Loader\ClosureLoader; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; -use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; -use Symfony\Component\ExpressionLanguage\Expression; - -class ContainerBuilderTest extends \PHPUnit_Framework_TestCase -{ - public function testDefinitions() - { - $builder = new ContainerBuilder(); - $definitions = array( - 'foo' => new Definition('Bar\FooClass'), - 'bar' => new Definition('BarClass'), - ); - $builder->setDefinitions($definitions); - $this->assertEquals($definitions, $builder->getDefinitions(), '->setDefinitions() sets the service definitions'); - $this->assertTrue($builder->hasDefinition('foo'), '->hasDefinition() returns true if a service definition exists'); - $this->assertFalse($builder->hasDefinition('foobar'), '->hasDefinition() returns false if a service definition does not exist'); - - $builder->setDefinition('foobar', $foo = new Definition('FooBarClass')); - $this->assertEquals($foo, $builder->getDefinition('foobar'), '->getDefinition() returns a service definition if defined'); - $this->assertTrue($builder->setDefinition('foobar', $foo = new Definition('FooBarClass')) === $foo, '->setDefinition() implements a fluid interface by returning the service reference'); - - $builder->addDefinitions($defs = array('foobar' => new Definition('FooBarClass'))); - $this->assertEquals(array_merge($definitions, $defs), $builder->getDefinitions(), '->addDefinitions() adds the service definitions'); - - try { - $builder->getDefinition('baz'); - $this->fail('->getDefinition() throws a ServiceNotFoundException if the service definition does not exist'); - } catch (ServiceNotFoundException $e) { - $this->assertEquals('You have requested a non-existent service "baz".', $e->getMessage(), '->getDefinition() throws a ServiceNotFoundException if the service definition does not exist'); - } - } - - /** - * @group legacy - * @expectedDeprecation The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed. - */ - public function testCreateDeprecatedService() - { - $definition = new Definition('stdClass'); - $definition->setDeprecated(true); - - $builder = new ContainerBuilder(); - $builder->setDefinition('deprecated_foo', $definition); - $builder->get('deprecated_foo'); - } - - public function testRegister() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'Bar\FooClass'); - $this->assertTrue($builder->hasDefinition('foo'), '->register() registers a new service definition'); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $builder->getDefinition('foo'), '->register() returns the newly created Definition instance'); - } - - public function testHas() - { - $builder = new ContainerBuilder(); - $this->assertFalse($builder->has('foo'), '->has() returns false if the service does not exist'); - $builder->register('foo', 'Bar\FooClass'); - $this->assertTrue($builder->has('foo'), '->has() returns true if a service definition exists'); - $builder->set('bar', new \stdClass()); - $this->assertTrue($builder->has('bar'), '->has() returns true if a service exists'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException - * @expectedExceptionMessage You have requested a non-existent service "foo". - */ - public function testGetThrowsExceptionIfServiceDoesNotExist() - { - $builder = new ContainerBuilder(); - $builder->get('foo'); - } - - public function testGetReturnsNullIfServiceDoesNotExistAndInvalidReferenceIsUsed() - { - $builder = new ContainerBuilder(); - - $this->assertNull($builder->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service does not exist and NULL_ON_INVALID_REFERENCE is passed as a second argument'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testGetThrowsCircularReferenceExceptionIfServiceHasReferenceToItself() - { - $builder = new ContainerBuilder(); - $builder->register('baz', 'stdClass')->setArguments(array(new Reference('baz'))); - $builder->get('baz'); - } - - public function testGetReturnsSameInstanceWhenServiceIsShared() - { - $builder = new ContainerBuilder(); - $builder->register('bar', 'stdClass'); - - $this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared'); - } - - public function testGetCreatesServiceBasedOnDefinition() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'stdClass'); - - $this->assertInternalType('object', $builder->get('foo'), '->get() returns the service definition associated with the id'); - } - - public function testGetReturnsRegisteredService() - { - $builder = new ContainerBuilder(); - $builder->set('bar', $bar = new \stdClass()); - - $this->assertSame($bar, $builder->get('bar'), '->get() returns the service associated with the id'); - } - - public function testRegisterDoesNotOverrideExistingService() - { - $builder = new ContainerBuilder(); - $builder->set('bar', $bar = new \stdClass()); - $builder->register('bar', 'stdClass'); - - $this->assertSame($bar, $builder->get('bar'), '->get() returns the service associated with the id even if a definition has been defined'); - } - - public function testNonSharedServicesReturnsDifferentInstances() - { - $builder = new ContainerBuilder(); - $builder->register('bar', 'stdClass')->setShared(false); - - $this->assertNotSame($builder->get('bar'), $builder->get('bar')); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage You have requested a synthetic service ("foo"). The DIC does not know how to construct this service. - */ - public function testGetUnsetLoadingServiceWhenCreateServiceThrowsAnException() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'stdClass')->setSynthetic(true); - - // we expect a RuntimeException here as foo is synthetic - try { - $builder->get('foo'); - } catch (RuntimeException $e) { - } - - // we must also have the same RuntimeException here - $builder->get('foo'); - } - - public function testGetServiceIds() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'stdClass'); - $builder->bar = $bar = new \stdClass(); - $builder->register('bar', 'stdClass'); - $this->assertEquals(array('foo', 'bar', 'service_container'), $builder->getServiceIds(), '->getServiceIds() returns all defined service ids'); - } - - public function testAliases() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'stdClass'); - $builder->setAlias('bar', 'foo'); - $this->assertTrue($builder->hasAlias('bar'), '->hasAlias() returns true if the alias exists'); - $this->assertFalse($builder->hasAlias('foobar'), '->hasAlias() returns false if the alias does not exist'); - $this->assertEquals('foo', (string) $builder->getAlias('bar'), '->getAlias() returns the aliased service'); - $this->assertTrue($builder->has('bar'), '->setAlias() defines a new service'); - $this->assertTrue($builder->get('bar') === $builder->get('foo'), '->setAlias() creates a service that is an alias to another one'); - - try { - $builder->setAlias('foobar', 'foobar'); - $this->fail('->setAlias() throws an InvalidArgumentException if the alias references itself'); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('An alias can not reference itself, got a circular reference on "foobar".', $e->getMessage(), '->setAlias() throws an InvalidArgumentException if the alias references itself'); - } - - try { - $builder->getAlias('foobar'); - $this->fail('->getAlias() throws an InvalidArgumentException if the alias does not exist'); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('The service alias "foobar" does not exist.', $e->getMessage(), '->getAlias() throws an InvalidArgumentException if the alias does not exist'); - } - } - - public function testGetAliases() - { - $builder = new ContainerBuilder(); - $builder->setAlias('bar', 'foo'); - $builder->setAlias('foobar', 'foo'); - $builder->setAlias('moo', new Alias('foo', false)); - - $aliases = $builder->getAliases(); - $this->assertEquals('foo', (string) $aliases['bar']); - $this->assertTrue($aliases['bar']->isPublic()); - $this->assertEquals('foo', (string) $aliases['foobar']); - $this->assertEquals('foo', (string) $aliases['moo']); - $this->assertFalse($aliases['moo']->isPublic()); - - $builder->register('bar', 'stdClass'); - $this->assertFalse($builder->hasAlias('bar')); - - $builder->set('foobar', 'stdClass'); - $builder->set('moo', 'stdClass'); - $this->assertCount(0, $builder->getAliases(), '->getAliases() does not return aliased services that have been overridden'); - } - - public function testSetAliases() - { - $builder = new ContainerBuilder(); - $builder->setAliases(array('bar' => 'foo', 'foobar' => 'foo')); - - $aliases = $builder->getAliases(); - $this->assertTrue(isset($aliases['bar'])); - $this->assertTrue(isset($aliases['foobar'])); - } - - public function testAddAliases() - { - $builder = new ContainerBuilder(); - $builder->setAliases(array('bar' => 'foo')); - $builder->addAliases(array('foobar' => 'foo')); - - $aliases = $builder->getAliases(); - $this->assertTrue(isset($aliases['bar'])); - $this->assertTrue(isset($aliases['foobar'])); - } - - public function testSetReplacesAlias() - { - $builder = new ContainerBuilder(); - $builder->setAlias('alias', 'aliased'); - $builder->set('aliased', new \stdClass()); - - $builder->set('alias', $foo = new \stdClass()); - $this->assertSame($foo, $builder->get('alias'), '->set() replaces an existing alias'); - } - - public function testAliasesKeepInvalidBehavior() - { - $builder = new ContainerBuilder(); - - $aliased = new Definition('stdClass'); - $aliased->addMethodCall('setBar', array(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); - $builder->setDefinition('aliased', $aliased); - $builder->setAlias('alias', 'aliased'); - - $this->assertEquals(new \stdClass(), $builder->get('alias')); - } - - public function testAddGetCompilerPass() - { - $builder = new ContainerBuilder(); - $builder->setResourceTracking(false); - $defaultPasses = $builder->getCompiler()->getPassConfig()->getPasses(); - $builder->addCompilerPass($pass1 = $this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'), PassConfig::TYPE_BEFORE_OPTIMIZATION, -5); - $builder->addCompilerPass($pass2 = $this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface'), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10); - - $passes = $builder->getCompiler()->getPassConfig()->getPasses(); - $this->assertCount(count($passes) - 2, $defaultPasses); - // Pass 1 is executed later - $this->assertTrue(array_search($pass1, $passes, true) > array_search($pass2, $passes, true)); - } - - public function testCreateService() - { - $builder = new ContainerBuilder(); - $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); - $builder->register('foo2', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/%file%.php'); - $builder->setParameter('file', 'foo'); - $this->assertInstanceOf('\Bar\FooClass', $builder->get('foo1'), '->createService() requires the file defined by the service definition'); - $this->assertInstanceOf('\Bar\FooClass', $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition'); - } - - public function testCreateProxyWithRealServiceInstantiator() - { - $builder = new ContainerBuilder(); - - $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); - $builder->getDefinition('foo1')->setLazy(true); - - $foo1 = $builder->get('foo1'); - - $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); - $this->assertSame('Bar\FooClass', get_class($foo1)); - } - - public function testCreateServiceClass() - { - $builder = new ContainerBuilder(); - $builder->register('foo1', '%class%'); - $builder->setParameter('class', 'stdClass'); - $this->assertInstanceOf('\stdClass', $builder->get('foo1'), '->createService() replaces parameters in the class provided by the service definition'); - } - - public function testCreateServiceArguments() - { - $builder = new ContainerBuilder(); - $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'Bar\FooClass')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'), '%%unescape_it%%')); - $builder->setParameter('value', 'bar'); - $this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->arguments, '->createService() replaces parameters and service references in the arguments provided by the service definition'); - } - - public function testCreateServiceFactory() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'Bar\FooClass')->setFactory('Bar\FooClass::getInstance'); - $builder->register('qux', 'Bar\FooClass')->setFactory(array('Bar\FooClass', 'getInstance')); - $builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')); - $builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance')); - - $this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance'); - $this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance'); - $this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory'); - $this->assertTrue($builder->get('baz')->called, '->createService() uses another service as factory'); - } - - public function testCreateServiceMethodCalls() - { - $builder = new ContainerBuilder(); - $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%value%', new Reference('bar')))); - $builder->setParameter('value', 'bar'); - $this->assertEquals(array('bar', $builder->get('bar')), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); - } - - public function testCreateServiceMethodCallsWithEscapedParam() - { - $builder = new ContainerBuilder(); - $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%'))); - $builder->setParameter('value', 'bar'); - $this->assertEquals(array('%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); - } - - public function testCreateServiceProperties() - { - $builder = new ContainerBuilder(); - $builder->register('bar', 'stdClass'); - $builder->register('foo1', 'Bar\FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%')); - $builder->setParameter('value', 'bar'); - $this->assertEquals(array('bar', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the properties'); - } - - public function testCreateServiceConfigurator() - { - $builder = new ContainerBuilder(); - $builder->register('foo1', 'Bar\FooClass')->setConfigurator('sc_configure'); - $builder->register('foo2', 'Bar\FooClass')->setConfigurator(array('%class%', 'configureStatic')); - $builder->setParameter('class', 'BazClass'); - $builder->register('baz', 'BazClass'); - $builder->register('foo3', 'Bar\FooClass')->setConfigurator(array(new Reference('baz'), 'configure')); - $builder->register('foo4', 'Bar\FooClass')->setConfigurator(array($builder->getDefinition('baz'), 'configure')); - $builder->register('foo5', 'Bar\FooClass')->setConfigurator('foo'); - - $this->assertTrue($builder->get('foo1')->configured, '->createService() calls the configurator'); - $this->assertTrue($builder->get('foo2')->configured, '->createService() calls the configurator'); - $this->assertTrue($builder->get('foo3')->configured, '->createService() calls the configurator'); - $this->assertTrue($builder->get('foo4')->configured, '->createService() calls the configurator'); - - try { - $builder->get('foo5'); - $this->fail('->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); - } catch (\InvalidArgumentException $e) { - $this->assertEquals('The configure callable for class "Bar\FooClass" is not a callable.', $e->getMessage(), '->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); - } - } - - /** - * @expectedException \RuntimeException - */ - public function testCreateSyntheticService() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'Bar\FooClass')->setSynthetic(true); - $builder->get('foo'); - } - - public function testCreateServiceWithExpression() - { - $builder = new ContainerBuilder(); - $builder->setParameter('bar', 'bar'); - $builder->register('bar', 'BarClass'); - $builder->register('foo', 'Bar\FooClass')->addArgument(array('foo' => new Expression('service("bar").foo ~ parameter("bar")'))); - $this->assertEquals('foobar', $builder->get('foo')->arguments['foo']); - } - - public function testResolveServices() - { - $builder = new ContainerBuilder(); - $builder->register('foo', 'Bar\FooClass'); - $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Reference('foo')), '->resolveServices() resolves service references to service instances'); - $this->assertEquals(array('foo' => array('foo', $builder->get('foo'))), $builder->resolveServices(array('foo' => array('foo', new Reference('foo')))), '->resolveServices() resolves service references to service instances in nested arrays'); - $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Constructing service "foo" from a parent definition is not supported at build time. - */ - public function testResolveServicesWithDecoratedDefinition() - { - $builder = new ContainerBuilder(); - $builder->setDefinition('grandpa', new Definition('stdClass')); - $builder->setDefinition('parent', new DefinitionDecorator('grandpa')); - $builder->setDefinition('foo', new DefinitionDecorator('parent')); - - $builder->get('foo'); - } - - public function testResolveServicesWithCustomDefinitionClass() - { - $builder = new ContainerBuilder(); - $builder->setDefinition('foo', new CustomDefinition('stdClass')); - - $this->assertInstanceOf('stdClass', $builder->get('foo')); - } - - public function testMerge() - { - $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); - $container->setResourceTracking(false); - $config = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); - $container->merge($config); - $this->assertEquals(array('bar' => 'foo', 'foo' => 'bar'), $container->getParameterBag()->all(), '->merge() merges current parameters with the loaded ones'); - - $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); - $container->setResourceTracking(false); - $config = new ContainerBuilder(new ParameterBag(array('foo' => '%bar%'))); - $container->merge($config); - $container->compile(); - $this->assertEquals(array('bar' => 'foo', 'foo' => 'foo'), $container->getParameterBag()->all(), '->merge() evaluates the values of the parameters towards already defined ones'); - - $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); - $container->setResourceTracking(false); - $config = new ContainerBuilder(new ParameterBag(array('foo' => '%bar%', 'baz' => '%foo%'))); - $container->merge($config); - $container->compile(); - $this->assertEquals(array('bar' => 'foo', 'foo' => 'foo', 'baz' => 'foo'), $container->getParameterBag()->all(), '->merge() evaluates the values of the parameters towards already defined ones'); - - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->register('foo', 'Bar\FooClass'); - $container->register('bar', 'BarClass'); - $config = new ContainerBuilder(); - $config->setDefinition('baz', new Definition('BazClass')); - $config->setAlias('alias_for_foo', 'foo'); - $container->merge($config); - $this->assertEquals(array('foo', 'bar', 'baz'), array_keys($container->getDefinitions()), '->merge() merges definitions already defined ones'); - - $aliases = $container->getAliases(); - $this->assertTrue(isset($aliases['alias_for_foo'])); - $this->assertEquals('foo', (string) $aliases['alias_for_foo']); - - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->register('foo', 'Bar\FooClass'); - $config->setDefinition('foo', new Definition('BazClass')); - $container->merge($config); - $this->assertEquals('BazClass', $container->getDefinition('foo')->getClass(), '->merge() overrides already defined services'); - - $container = new ContainerBuilder(); - $bag = new EnvPlaceholderParameterBag(); - $bag->get('env(Foo)'); - $config = new ContainerBuilder($bag); - $this->assertSame(array('%env(Bar)%'), $config->resolveEnvPlaceholders(array($bag->get('env(Bar)')))); - $container->merge($config); - $this->assertEquals(array('Foo' => 0, 'Bar' => 1), $container->getEnvCounters()); - } - - /** - * @expectedException \LogicException - */ - public function testMergeLogicException() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->compile(); - $container->merge(new ContainerBuilder()); - } - - public function testfindTaggedServiceIds() - { - $builder = new ContainerBuilder(); - $builder - ->register('foo', 'Bar\FooClass') - ->addTag('foo', array('foo' => 'foo')) - ->addTag('bar', array('bar' => 'bar')) - ->addTag('foo', array('foofoo' => 'foofoo')) - ; - $this->assertEquals($builder->findTaggedServiceIds('foo'), array( - 'foo' => array( - array('foo' => 'foo'), - array('foofoo' => 'foofoo'), - ), - ), '->findTaggedServiceIds() returns an array of service ids and its tag attributes'); - $this->assertEquals(array(), $builder->findTaggedServiceIds('foobar'), '->findTaggedServiceIds() returns an empty array if there is annotated services'); - } - - public function testFindUnusedTags() - { - $builder = new ContainerBuilder(); - $builder - ->register('foo', 'Bar\FooClass') - ->addTag('kernel.event_listener', array('foo' => 'foo')) - ->addTag('kenrel.event_listener', array('bar' => 'bar')) - ; - $builder->findTaggedServiceIds('kernel.event_listener'); - $this->assertEquals(array('kenrel.event_listener'), $builder->findUnusedTags(), '->findUnusedTags() returns an array with unused tags'); - } - - public function testFindDefinition() - { - $container = new ContainerBuilder(); - $container->setDefinition('foo', $definition = new Definition('Bar\FooClass')); - $container->setAlias('bar', 'foo'); - $container->setAlias('foobar', 'bar'); - $this->assertEquals($definition, $container->findDefinition('foobar'), '->findDefinition() returns a Definition'); - } - - public function testAddObjectResource() - { - $container = new ContainerBuilder(); - - $container->setResourceTracking(false); - $container->addObjectResource(new \BarClass()); - - $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); - - $container->setResourceTracking(true); - $container->addObjectResource(new \BarClass()); - - $resources = $container->getResources(); - - $this->assertCount(1, $resources, '1 resource was registered'); - - /* @var $resource \Symfony\Component\Config\Resource\FileResource */ - $resource = end($resources); - - $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); - $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); - } - - public function testAddClassResource() - { - $container = new ContainerBuilder(); - - $container->setResourceTracking(false); - $container->addClassResource(new \ReflectionClass('BarClass')); - - $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); - - $container->setResourceTracking(true); - $container->addClassResource(new \ReflectionClass('BarClass')); - - $resources = $container->getResources(); - - $this->assertCount(1, $resources, '1 resource was registered'); - - /* @var $resource \Symfony\Component\Config\Resource\FileResource */ - $resource = end($resources); - - $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); - $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); - } - - public function testCompilesClassDefinitionsOfLazyServices() - { - $container = new ContainerBuilder(); - - $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); - - $container->register('foo', 'BarClass'); - $container->getDefinition('foo')->setLazy(true); - - $container->compile(); - - $classesPath = realpath(__DIR__.'/Fixtures/includes/classes.php'); - $matchingResources = array_filter( - $container->getResources(), - function (ResourceInterface $resource) use ($classesPath) { - return $resource instanceof FileResource && $classesPath === realpath($resource->getResource()); - } - ); - - $this->assertNotEmpty($matchingResources); - } - - public function testResources() - { - $container = new ContainerBuilder(); - $container->addResource($a = new FileResource(__DIR__.'/Fixtures/xml/services1.xml')); - $container->addResource($b = new FileResource(__DIR__.'/Fixtures/xml/services2.xml')); - $resources = array(); - foreach ($container->getResources() as $resource) { - if (false === strpos($resource, '.php')) { - $resources[] = $resource; - } - } - $this->assertEquals(array($a, $b), $resources, '->getResources() returns an array of resources read for the current configuration'); - $this->assertSame($container, $container->setResources(array())); - $this->assertEquals(array(), $container->getResources()); - } - - public function testExtension() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - - $container->registerExtension($extension = new \ProjectExtension()); - $this->assertTrue($container->getExtension('project') === $extension, '->registerExtension() registers an extension'); - - $this->setExpectedException('LogicException'); - $container->getExtension('no_registered'); - } - - public function testRegisteredButNotLoadedExtension() - { - $extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface'); - $extension->expects($this->once())->method('getAlias')->will($this->returnValue('project')); - $extension->expects($this->never())->method('load'); - - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->registerExtension($extension); - $container->compile(); - } - - public function testRegisteredAndLoadedExtension() - { - $extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface'); - $extension->expects($this->exactly(2))->method('getAlias')->will($this->returnValue('project')); - $extension->expects($this->once())->method('load')->with(array(array('foo' => 'bar'))); - - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->registerExtension($extension); - $container->loadFromExtension('project', array('foo' => 'bar')); - $container->compile(); - } - - public function testPrivateServiceUser() - { - $fooDefinition = new Definition('BarClass'); - $fooUserDefinition = new Definition('BarUserClass', array(new Reference('bar'))); - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - - $fooDefinition->setPublic(false); - - $container->addDefinitions(array( - 'bar' => $fooDefinition, - 'bar_user' => $fooUserDefinition, - )); - - $container->compile(); - $this->assertInstanceOf('BarClass', $container->get('bar_user')->bar); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testThrowsExceptionWhenSetServiceOnAFrozenContainer() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->setDefinition('a', new Definition('stdClass')); - $container->compile(); - $container->set('a', new \stdClass()); - } - - public function testThrowsExceptionWhenAddServiceOnAFrozenContainer() - { - $container = new ContainerBuilder(); - $container->compile(); - $container->set('a', $foo = new \stdClass()); - $this->assertSame($foo, $container->get('a')); - } - - public function testNoExceptionWhenSetSyntheticServiceOnAFrozenContainer() - { - $container = new ContainerBuilder(); - $def = new Definition('stdClass'); - $def->setSynthetic(true); - $container->setDefinition('a', $def); - $container->compile(); - $container->set('a', $a = new \stdClass()); - $this->assertEquals($a, $container->get('a')); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testThrowsExceptionWhenSetDefinitionOnAFrozenContainer() - { - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->compile(); - $container->setDefinition('a', new Definition()); - } - - public function testExtensionConfig() - { - $container = new ContainerBuilder(); - - $configs = $container->getExtensionConfig('foo'); - $this->assertEmpty($configs); - - $first = array('foo' => 'bar'); - $container->prependExtensionConfig('foo', $first); - $configs = $container->getExtensionConfig('foo'); - $this->assertEquals(array($first), $configs); - - $second = array('ding' => 'dong'); - $container->prependExtensionConfig('foo', $second); - $configs = $container->getExtensionConfig('foo'); - $this->assertEquals(array($second, $first), $configs); - } - - public function testAbstractAlias() - { - $container = new ContainerBuilder(); - - $abstract = new Definition('AbstractClass'); - $abstract->setAbstract(true); - - $container->setDefinition('abstract_service', $abstract); - $container->setAlias('abstract_alias', 'abstract_service'); - - $container->compile(); - - $this->assertSame('abstract_service', (string) $container->getAlias('abstract_alias')); - } - - public function testLazyLoadedService() - { - $loader = new ClosureLoader($container = new ContainerBuilder()); - $loader->load(function (ContainerBuilder $container) { - $container->set('a', new \BazClass()); - $definition = new Definition('BazClass'); - $definition->setLazy(true); - $container->setDefinition('a', $definition); - }); - - $container->setResourceTracking(true); - - $container->compile(); - - $class = new \BazClass(); - $reflectionClass = new \ReflectionClass($class); - - $r = new \ReflectionProperty($container, 'resources'); - $r->setAccessible(true); - $resources = $r->getValue($container); - - $classInList = false; - foreach ($resources as $resource) { - if ($resource->getResource() === $reflectionClass->getFileName()) { - $classInList = true; - break; - } - } - - $this->assertTrue($classInList); - } - - public function testInitializePropertiesBeforeMethodCalls() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass'); - $container->register('bar', 'MethodCallClass') - ->setProperty('simple', 'bar') - ->setProperty('complex', new Reference('foo')) - ->addMethodCall('callMe'); - - $container->compile(); - - $this->assertTrue($container->get('bar')->callPassed(), '->compile() initializes properties before method calls'); - } - - public function testAutowiring() - { - $container = new ContainerBuilder(); - - $container->register('a', __NAMESPACE__.'\A'); - $bDefinition = $container->register('b', __NAMESPACE__.'\B'); - $bDefinition->setAutowired(true); - - $container->compile(); - - $this->assertEquals('a', (string) $container->getDefinition('b')->getArgument(0)); - } -} - -class FooClass -{ -} - -class A -{ -} - -class B -{ - public function __construct(A $a) - { - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ContainerTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ContainerTest.php deleted file mode 100644 index 31e9e97c6..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ContainerTest.php +++ /dev/null @@ -1,544 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; - -class ContainerTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $sc = new Container(); - $this->assertSame($sc, $sc->get('service_container'), '__construct() automatically registers itself as a service'); - - $sc = new Container(new ParameterBag(array('foo' => 'bar'))); - $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '__construct() takes an array of parameters as its first argument'); - } - - /** - * @dataProvider dataForTestCamelize - */ - public function testCamelize($id, $expected) - { - $this->assertEquals($expected, Container::camelize($id), sprintf('Container::camelize("%s")', $id)); - } - - public function dataForTestCamelize() - { - return array( - array('foo_bar', 'FooBar'), - array('foo.bar', 'Foo_Bar'), - array('foo.bar_baz', 'Foo_BarBaz'), - array('foo._bar', 'Foo_Bar'), - array('foo_.bar', 'Foo_Bar'), - array('_foo', 'Foo'), - array('.foo', '_Foo'), - array('foo_', 'Foo'), - array('foo.', 'Foo_'), - array('foo\bar', 'Foo_Bar'), - ); - } - - /** - * @dataProvider dataForTestUnderscore - */ - public function testUnderscore($id, $expected) - { - $this->assertEquals($expected, Container::underscore($id), sprintf('Container::underscore("%s")', $id)); - } - - public function dataForTestUnderscore() - { - return array( - array('FooBar', 'foo_bar'), - array('Foo_Bar', 'foo.bar'), - array('Foo_BarBaz', 'foo.bar_baz'), - array('FooBar_BazQux', 'foo_bar.baz_qux'), - array('_Foo', '.foo'), - array('Foo_', 'foo.'), - ); - } - - public function testCompile() - { - $sc = new Container(new ParameterBag(array('foo' => 'bar'))); - $this->assertFalse($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag'); - $sc->compile(); - $this->assertTrue($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag'); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag', $sc->getParameterBag(), '->compile() changes the parameter bag to a FrozenParameterBag instance'); - $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag'); - } - - public function testIsFrozen() - { - $sc = new Container(new ParameterBag(array('foo' => 'bar'))); - $this->assertFalse($sc->isFrozen(), '->isFrozen() returns false if the parameters are not frozen'); - $sc->compile(); - $this->assertTrue($sc->isFrozen(), '->isFrozen() returns true if the parameters are frozen'); - } - - public function testGetParameterBag() - { - $sc = new Container(); - $this->assertEquals(array(), $sc->getParameterBag()->all(), '->getParameterBag() returns an empty array if no parameter has been defined'); - } - - public function testGetSetParameter() - { - $sc = new Container(new ParameterBag(array('foo' => 'bar'))); - $sc->setParameter('bar', 'foo'); - $this->assertEquals('foo', $sc->getParameter('bar'), '->setParameter() sets the value of a new parameter'); - - $sc->setParameter('foo', 'baz'); - $this->assertEquals('baz', $sc->getParameter('foo'), '->setParameter() overrides previously set parameter'); - - $sc->setParameter('Foo', 'baz1'); - $this->assertEquals('baz1', $sc->getParameter('foo'), '->setParameter() converts the key to lowercase'); - $this->assertEquals('baz1', $sc->getParameter('FOO'), '->getParameter() converts the key to lowercase'); - - try { - $sc->getParameter('baba'); - $this->fail('->getParameter() thrown an \InvalidArgumentException if the key does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->getParameter() thrown an \InvalidArgumentException if the key does not exist'); - $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->getParameter() thrown an \InvalidArgumentException if the key does not exist'); - } - } - - public function testGetServiceIds() - { - $sc = new Container(); - $sc->set('foo', $obj = new \stdClass()); - $sc->set('bar', $obj = new \stdClass()); - $this->assertEquals(array('service_container', 'foo', 'bar'), $sc->getServiceIds(), '->getServiceIds() returns all defined service ids'); - - $sc = new ProjectServiceContainer(); - $sc->set('foo', $obj = new \stdClass()); - $this->assertEquals(array('service_container', 'internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()'); - } - - /** - * @group legacy - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - */ - public function testGetLegacyServiceIds() - { - $sc = new LegacyProjectServiceContainer(); - $sc->set('foo', $obj = new \stdClass()); - - $this->assertEquals(array('internal', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods, followed by service ids defined by set()'); - } - - public function testSet() - { - $sc = new Container(); - $sc->set('foo', $foo = new \stdClass()); - $this->assertEquals($foo, $sc->get('foo'), '->set() sets a service'); - } - - public function testSetWithNullResetTheService() - { - $sc = new Container(); - $sc->set('foo', null); - $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); - } - - public function testSetReplacesAlias() - { - $c = new ProjectServiceContainer(); - - $c->set('alias', $foo = new \stdClass()); - $this->assertSame($foo, $c->get('alias'), '->set() replaces an existing alias'); - } - - public function testGet() - { - $sc = new ProjectServiceContainer(); - $sc->set('foo', $foo = new \stdClass()); - $this->assertEquals($foo, $sc->get('foo'), '->get() returns the service for the given id'); - $this->assertEquals($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase'); - $this->assertEquals($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id'); - $this->assertEquals($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined'); - $this->assertEquals($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined'); - - $sc->set('bar', $bar = new \stdClass()); - $this->assertEquals($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()'); - - try { - $sc->get(''); - $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty'); - } - $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty'); - } - - /** - * @group legacy - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - */ - public function testLegacyGet() - { - $sc = new LegacyProjectServiceContainer(); - $sc->set('foo', $foo = new \stdClass()); - - $this->assertEquals($foo, $sc->get('foo'), '->get() returns the service for the given id'); - $this->assertEquals($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase'); - $this->assertEquals($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id'); - $this->assertEquals($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined'); - $this->assertEquals($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined'); - $this->assertEquals($sc->__foo_baz, $sc->get('foo\\baz'), '->get() returns the service if a get*Method() is defined'); - - $sc->set('bar', $bar = new \stdClass()); - $this->assertEquals($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()'); - - try { - $sc->get(''); - $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty'); - } - $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty'); - } - - public function testGetThrowServiceNotFoundException() - { - $sc = new ProjectServiceContainer(); - $sc->set('foo', $foo = new \stdClass()); - $sc->set('baz', $foo = new \stdClass()); - - try { - $sc->get('foo1'); - $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); - $this->assertEquals('You have requested a non-existent service "foo1". Did you mean this: "foo"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices'); - } - - try { - $sc->get('bag'); - $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); - $this->assertEquals('You have requested a non-existent service "bag". Did you mean one of these: "bar", "baz"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices'); - } - } - - public function testGetCircularReference() - { - $sc = new ProjectServiceContainer(); - try { - $sc->get('circular'); - $this->fail('->get() throws a ServiceCircularReferenceException if it contains circular reference'); - } catch (\Exception $e) { - $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException', $e, '->get() throws a ServiceCircularReferenceException if it contains circular reference'); - $this->assertStringStartsWith('Circular reference detected for service "circular"', $e->getMessage(), '->get() throws a \LogicException if it contains circular reference'); - } - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage You have requested a synthetic service ("request"). The DIC does not know how to construct this service. - */ - public function testGetSyntheticServiceAlwaysThrows() - { - require_once __DIR__.'/Fixtures/php/services9.php'; - - $container = new \ProjectServiceContainer(); - $container->get('request', ContainerInterface::NULL_ON_INVALID_REFERENCE); - } - - public function testHas() - { - $sc = new ProjectServiceContainer(); - $sc->set('foo', new \stdClass()); - $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist'); - $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists'); - $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined'); - } - - /** - * @group legacy - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - * @expectedDeprecation Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map. - */ - public function testLegacyHas() - { - $sc = new LegacyProjectServiceContainer(); - $sc->set('foo', new \stdClass()); - - $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist'); - $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists'); - $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined'); - $this->assertTrue($sc->has('foo\\baz'), '->has() returns true if a get*Method() is defined'); - } - - public function testInitialized() - { - $sc = new ProjectServiceContainer(); - $sc->set('foo', new \stdClass()); - $this->assertTrue($sc->initialized('foo'), '->initialized() returns true if service is loaded'); - $this->assertFalse($sc->initialized('foo1'), '->initialized() returns false if service is not loaded'); - $this->assertFalse($sc->initialized('bar'), '->initialized() returns false if a service is defined, but not currently loaded'); - $this->assertFalse($sc->initialized('alias'), '->initialized() returns false if an aliased service is not initialized'); - - $sc->set('bar', new \stdClass()); - $this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized'); - } - - public function testReset() - { - $c = new Container(); - $c->set('bar', new \stdClass()); - - $c->reset(); - - $this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Something went terribly wrong! - */ - public function testGetThrowsException() - { - $c = new ProjectServiceContainer(); - - try { - $c->get('throw_exception'); - } catch (\Exception $e) { - // Do nothing. - } - - // Retry, to make sure that get*Service() will be called. - $c->get('throw_exception'); - } - - public function testGetThrowsExceptionOnServiceConfiguration() - { - $c = new ProjectServiceContainer(); - - try { - $c->get('throws_exception_on_service_configuration'); - } catch (\Exception $e) { - // Do nothing. - } - - $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); - - // Retry, to make sure that get*Service() will be called. - try { - $c->get('throws_exception_on_service_configuration'); - } catch (\Exception $e) { - // Do nothing. - } - $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); - } - - protected function getField($obj, $field) - { - $reflection = new \ReflectionProperty($obj, $field); - $reflection->setAccessible(true); - - return $reflection->getValue($obj); - } - - public function testAlias() - { - $c = new ProjectServiceContainer(); - - $this->assertTrue($c->has('alias')); - $this->assertSame($c->get('alias'), $c->get('bar')); - } - - public function testThatCloningIsNotSupported() - { - $class = new \ReflectionClass('Symfony\Component\DependencyInjection\Container'); - $clone = $class->getMethod('__clone'); - $this->assertFalse($class->isCloneable()); - $this->assertTrue($clone->isPrivate()); - } - - /** - * @group legacy - * @expectedDeprecation Unsetting the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0. - */ - public function testUnsetInternalPrivateServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->set('internal', null); - } - - /** - * @group legacy - * @expectedDeprecation Setting the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0. A new public service will be created instead. - */ - public function testChangeInternalPrivateServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->set('internal', new \stdClass()); - } - - /** - * @group legacy - * @expectedDeprecation Checking for the existence of the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0. - */ - public function testCheckExistenceOfAnInternalPrivateServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->has('internal'); - } - - /** - * @group legacy - * @expectedDeprecation Requesting the "internal" private service is deprecated since Symfony 3.2 and won't be supported anymore in Symfony 4.0. - */ - public function testRequestAnInternalSharedPrivateServiceIsDeprecated() - { - $c = new ProjectServiceContainer(); - $c->get('internal'); - } -} - -class ProjectServiceContainer extends Container -{ - public $__bar; - public $__foo_bar; - public $__foo_baz; - public $__internal; - protected $methodMap = array( - 'internal' => 'getInternalService', - 'bar' => 'getBarService', - 'foo_bar' => 'getFooBarService', - 'foo.baz' => 'getFoo_BazService', - 'circular' => 'getCircularService', - 'throw_exception' => 'getThrowExceptionService', - 'throws_exception_on_service_configuration' => 'getThrowsExceptionOnServiceConfigurationService', - ); - - public function __construct() - { - parent::__construct(); - - $this->__bar = new \stdClass(); - $this->__foo_bar = new \stdClass(); - $this->__foo_baz = new \stdClass(); - $this->__internal = new \stdClass(); - $this->privates = array('internal' => true); - $this->aliases = array('alias' => 'bar'); - } - - protected function getInternalService() - { - return $this->__internal; - } - - protected function getBarService() - { - return $this->__bar; - } - - protected function getFooBarService() - { - return $this->__foo_bar; - } - - protected function getFoo_BazService() - { - return $this->__foo_baz; - } - - protected function getCircularService() - { - return $this->get('circular'); - } - - protected function getThrowExceptionService() - { - throw new \Exception('Something went terribly wrong!'); - } - - protected function getThrowsExceptionOnServiceConfigurationService() - { - $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass(); - - throw new \Exception('Something was terribly wrong while trying to configure the service!'); - } -} - -class LegacyProjectServiceContainer extends Container -{ - public $__bar; - public $__foo_bar; - public $__foo_baz; - public $__internal; - - public function __construct() - { - parent::__construct(); - - $this->__bar = new \stdClass(); - $this->__foo_bar = new \stdClass(); - $this->__foo_baz = new \stdClass(); - $this->__internal = new \stdClass(); - $this->privates = array('internal' => true); - $this->aliases = array('alias' => 'bar'); - } - - protected function getInternalService() - { - return $this->__internal; - } - - protected function getBarService() - { - return $this->__bar; - } - - protected function getFooBarService() - { - return $this->__foo_bar; - } - - protected function getFoo_BazService() - { - return $this->__foo_baz; - } - - protected function getCircularService() - { - return $this->get('circular'); - } - - protected function getThrowExceptionService() - { - throw new \Exception('Something went terribly wrong!'); - } - - protected function getThrowsExceptionOnServiceConfigurationService() - { - $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass(); - - throw new \Exception('Something was terribly wrong while trying to configure the service!'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/CrossCheckTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/CrossCheckTest.php deleted file mode 100644 index 423c5db2e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/CrossCheckTest.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Config\FileLocator; - -class CrossCheckTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = __DIR__.'/Fixtures/'; - - require_once self::$fixturesPath.'/includes/classes.php'; - require_once self::$fixturesPath.'/includes/foo.php'; - } - - /** - * @dataProvider crossCheckLoadersDumpers - */ - public function testCrossCheck($fixture, $type) - { - $loaderClass = 'Symfony\\Component\\DependencyInjection\\Loader\\'.ucfirst($type).'FileLoader'; - $dumperClass = 'Symfony\\Component\\DependencyInjection\\Dumper\\'.ucfirst($type).'Dumper'; - - $tmp = tempnam(sys_get_temp_dir(), 'sf'); - - file_put_contents($tmp, file_get_contents(self::$fixturesPath.'/'.$type.'/'.$fixture)); - - $container1 = new ContainerBuilder(); - $loader1 = new $loaderClass($container1, new FileLocator()); - $loader1->load($tmp); - - $dumper = new $dumperClass($container1); - file_put_contents($tmp, $dumper->dump()); - - $container2 = new ContainerBuilder(); - $loader2 = new $loaderClass($container2, new FileLocator()); - $loader2->load($tmp); - - unlink($tmp); - - $this->assertEquals($container2->getAliases(), $container1->getAliases(), 'loading a dump from a previously loaded container returns the same container'); - $this->assertEquals($container2->getDefinitions(), $container1->getDefinitions(), 'loading a dump from a previously loaded container returns the same container'); - $this->assertEquals($container2->getParameterBag()->all(), $container1->getParameterBag()->all(), '->getParameterBag() returns the same value for both containers'); - - $this->assertEquals(serialize($container2), serialize($container1), 'loading a dump from a previously loaded container returns the same container'); - - $services1 = array(); - foreach ($container1 as $id => $service) { - $services1[$id] = serialize($service); - } - $services2 = array(); - foreach ($container2 as $id => $service) { - $services2[$id] = serialize($service); - } - - unset($services1['service_container'], $services2['service_container']); - - $this->assertEquals($services2, $services1, 'Iterator on the containers returns the same services'); - } - - public function crossCheckLoadersDumpers() - { - return array( - array('services1.xml', 'xml'), - array('services2.xml', 'xml'), - array('services6.xml', 'xml'), - array('services8.xml', 'xml'), - array('services9.xml', 'xml'), - array('services1.yml', 'yaml'), - array('services2.yml', 'yaml'), - array('services6.yml', 'yaml'), - array('services8.yml', 'yaml'), - array('services9.yml', 'yaml'), - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/DefinitionDecoratorTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/DefinitionDecoratorTest.php deleted file mode 100644 index c42cf2350..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/DefinitionDecoratorTest.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use Symfony\Component\DependencyInjection\DefinitionDecorator; - -class DefinitionDecoratorTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $def = new DefinitionDecorator('foo'); - - $this->assertEquals('foo', $def->getParent()); - $this->assertEquals(array(), $def->getChanges()); - } - - /** - * @dataProvider getPropertyTests - */ - public function testSetProperty($property, $changeKey) - { - $def = new DefinitionDecorator('foo'); - - $getter = 'get'.ucfirst($property); - $setter = 'set'.ucfirst($property); - - $this->assertNull($def->$getter()); - $this->assertSame($def, $def->$setter('foo')); - $this->assertEquals('foo', $def->$getter()); - $this->assertEquals(array($changeKey => true), $def->getChanges()); - } - - public function getPropertyTests() - { - return array( - array('class', 'class'), - array('factory', 'factory'), - array('configurator', 'configurator'), - array('file', 'file'), - ); - } - - public function testSetPublic() - { - $def = new DefinitionDecorator('foo'); - - $this->assertTrue($def->isPublic()); - $this->assertSame($def, $def->setPublic(false)); - $this->assertFalse($def->isPublic()); - $this->assertEquals(array('public' => true), $def->getChanges()); - } - - public function testSetLazy() - { - $def = new DefinitionDecorator('foo'); - - $this->assertFalse($def->isLazy()); - $this->assertSame($def, $def->setLazy(false)); - $this->assertFalse($def->isLazy()); - $this->assertEquals(array('lazy' => true), $def->getChanges()); - } - - public function testSetAutowired() - { - $def = new DefinitionDecorator('foo'); - - $this->assertFalse($def->isAutowired()); - $this->assertSame($def, $def->setAutowired(false)); - $this->assertFalse($def->isAutowired()); - $this->assertEquals(array('autowire' => true), $def->getChanges()); - } - - public function testSetArgument() - { - $def = new DefinitionDecorator('foo'); - - $this->assertEquals(array(), $def->getArguments()); - $this->assertSame($def, $def->replaceArgument(0, 'foo')); - $this->assertEquals(array('index_0' => 'foo'), $def->getArguments()); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testReplaceArgumentShouldRequireIntegerIndex() - { - $def = new DefinitionDecorator('foo'); - - $def->replaceArgument('0', 'foo'); - } - - public function testReplaceArgument() - { - $def = new DefinitionDecorator('foo'); - - $def->setArguments(array(0 => 'foo', 1 => 'bar')); - $this->assertEquals('foo', $def->getArgument(0)); - $this->assertEquals('bar', $def->getArgument(1)); - - $this->assertSame($def, $def->replaceArgument(1, 'baz')); - $this->assertEquals('foo', $def->getArgument(0)); - $this->assertEquals('baz', $def->getArgument(1)); - - $this->assertEquals(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz'), $def->getArguments()); - } - - /** - * @expectedException \OutOfBoundsException - */ - public function testGetArgumentShouldCheckBounds() - { - $def = new DefinitionDecorator('foo'); - - $def->setArguments(array(0 => 'foo')); - $def->replaceArgument(0, 'foo'); - - $def->getArgument(1); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/DefinitionTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/DefinitionTest.php deleted file mode 100644 index 6a85f8044..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/DefinitionTest.php +++ /dev/null @@ -1,307 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use Symfony\Component\DependencyInjection\Definition; - -class DefinitionTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $def = new Definition('stdClass'); - $this->assertEquals('stdClass', $def->getClass(), '__construct() takes the class name as its first argument'); - - $def = new Definition('stdClass', array('foo')); - $this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument'); - } - - public function testSetGetFactory() - { - $def = new Definition('stdClass'); - - $this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface'); - $this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory'); - - $def->setFactory('Foo::bar'); - $this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array'); - } - - public function testSetGetClass() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->setClass('foo'), '->setClass() implements a fluent interface'); - $this->assertEquals('foo', $def->getClass(), '->getClass() returns the class name'); - } - - public function testSetGetDecoratedService() - { - $def = new Definition('stdClass'); - $this->assertNull($def->getDecoratedService()); - $def->setDecoratedService('foo', 'foo.renamed', 5); - $this->assertEquals(array('foo', 'foo.renamed', 5), $def->getDecoratedService()); - $def->setDecoratedService(null); - $this->assertNull($def->getDecoratedService()); - - $def = new Definition('stdClass'); - $this->assertNull($def->getDecoratedService()); - $def->setDecoratedService('foo', 'foo.renamed'); - $this->assertEquals(array('foo', 'foo.renamed', 0), $def->getDecoratedService()); - $def->setDecoratedService(null); - $this->assertNull($def->getDecoratedService()); - - $def = new Definition('stdClass'); - $def->setDecoratedService('foo'); - $this->assertEquals(array('foo', null, 0), $def->getDecoratedService()); - $def->setDecoratedService(null); - $this->assertNull($def->getDecoratedService()); - - $def = new Definition('stdClass'); - $this->setExpectedException('InvalidArgumentException', 'The decorated service inner name for "foo" must be different than the service name itself.'); - $def->setDecoratedService('foo', 'foo'); - } - - public function testArguments() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->setArguments(array('foo')), '->setArguments() implements a fluent interface'); - $this->assertEquals(array('foo'), $def->getArguments(), '->getArguments() returns the arguments'); - $this->assertSame($def, $def->addArgument('bar'), '->addArgument() implements a fluent interface'); - $this->assertEquals(array('foo', 'bar'), $def->getArguments(), '->addArgument() adds an argument'); - } - - public function testMethodCalls() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->setMethodCalls(array(array('foo', array('foo')))), '->setMethodCalls() implements a fluent interface'); - $this->assertEquals(array(array('foo', array('foo'))), $def->getMethodCalls(), '->getMethodCalls() returns the methods to call'); - $this->assertSame($def, $def->addMethodCall('bar', array('bar')), '->addMethodCall() implements a fluent interface'); - $this->assertEquals(array(array('foo', array('foo')), array('bar', array('bar'))), $def->getMethodCalls(), '->addMethodCall() adds a method to call'); - $this->assertTrue($def->hasMethodCall('bar'), '->hasMethodCall() returns true if first argument is a method to call registered'); - $this->assertFalse($def->hasMethodCall('no_registered'), '->hasMethodCall() returns false if first argument is not a method to call registered'); - $this->assertSame($def, $def->removeMethodCall('bar'), '->removeMethodCall() implements a fluent interface'); - $this->assertEquals(array(array('foo', array('foo'))), $def->getMethodCalls(), '->removeMethodCall() removes a method to call'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage Method name cannot be empty. - */ - public function testExceptionOnEmptyMethodCall() - { - $def = new Definition('stdClass'); - $def->addMethodCall(''); - } - - public function testSetGetFile() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->setFile('foo'), '->setFile() implements a fluent interface'); - $this->assertEquals('foo', $def->getFile(), '->getFile() returns the file to include'); - } - - public function testSetIsShared() - { - $def = new Definition('stdClass'); - $this->assertTrue($def->isShared(), '->isShared() returns true by default'); - $this->assertSame($def, $def->setShared(false), '->setShared() implements a fluent interface'); - $this->assertFalse($def->isShared(), '->isShared() returns false if the instance must not be shared'); - } - - public function testSetIsPublic() - { - $def = new Definition('stdClass'); - $this->assertTrue($def->isPublic(), '->isPublic() returns true by default'); - $this->assertSame($def, $def->setPublic(false), '->setPublic() implements a fluent interface'); - $this->assertFalse($def->isPublic(), '->isPublic() returns false if the instance must not be public.'); - } - - public function testSetIsSynthetic() - { - $def = new Definition('stdClass'); - $this->assertFalse($def->isSynthetic(), '->isSynthetic() returns false by default'); - $this->assertSame($def, $def->setSynthetic(true), '->setSynthetic() implements a fluent interface'); - $this->assertTrue($def->isSynthetic(), '->isSynthetic() returns true if the service is synthetic.'); - } - - public function testSetIsLazy() - { - $def = new Definition('stdClass'); - $this->assertFalse($def->isLazy(), '->isLazy() returns false by default'); - $this->assertSame($def, $def->setLazy(true), '->setLazy() implements a fluent interface'); - $this->assertTrue($def->isLazy(), '->isLazy() returns true if the service is lazy.'); - } - - public function testSetIsAbstract() - { - $def = new Definition('stdClass'); - $this->assertFalse($def->isAbstract(), '->isAbstract() returns false by default'); - $this->assertSame($def, $def->setAbstract(true), '->setAbstract() implements a fluent interface'); - $this->assertTrue($def->isAbstract(), '->isAbstract() returns true if the instance must not be public.'); - } - - public function testSetIsDeprecated() - { - $def = new Definition('stdClass'); - $this->assertFalse($def->isDeprecated(), '->isDeprecated() returns false by default'); - $this->assertSame($def, $def->setDeprecated(true), '->setDeprecated() implements a fluent interface'); - $this->assertTrue($def->isDeprecated(), '->isDeprecated() returns true if the instance should not be used anymore.'); - $this->assertSame('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', $def->getDeprecationMessage('deprecated_service'), '->getDeprecationMessage() should return a formatted message template'); - } - - /** - * @dataProvider invalidDeprecationMessageProvider - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testSetDeprecatedWithInvalidDeprecationTemplate($message) - { - $def = new Definition('stdClass'); - $def->setDeprecated(false, $message); - } - - public function invalidDeprecationMessageProvider() - { - return array( - "With \rs" => array("invalid \r message %service_id%"), - "With \ns" => array("invalid \n message %service_id%"), - 'With */s' => array('invalid */ message %service_id%'), - 'message not containing require %service_id% variable' => array('this is deprecated'), - ); - } - - public function testSetGetConfigurator() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->setConfigurator('foo'), '->setConfigurator() implements a fluent interface'); - $this->assertEquals('foo', $def->getConfigurator(), '->getConfigurator() returns the configurator'); - } - - public function testClearTags() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->clearTags(), '->clearTags() implements a fluent interface'); - $def->addTag('foo', array('foo' => 'bar')); - $def->clearTags(); - $this->assertEquals(array(), $def->getTags(), '->clearTags() removes all current tags'); - } - - public function testClearTag() - { - $def = new Definition('stdClass'); - $this->assertSame($def, $def->clearTags(), '->clearTags() implements a fluent interface'); - $def->addTag('1foo1', array('foo1' => 'bar1')); - $def->addTag('2foo2', array('foo2' => 'bar2')); - $def->addTag('3foo3', array('foo3' => 'bar3')); - $def->clearTag('2foo2'); - $this->assertTrue($def->hasTag('1foo1')); - $this->assertFalse($def->hasTag('2foo2')); - $this->assertTrue($def->hasTag('3foo3')); - $def->clearTag('1foo1'); - $this->assertFalse($def->hasTag('1foo1')); - $this->assertTrue($def->hasTag('3foo3')); - } - - public function testTags() - { - $def = new Definition('stdClass'); - $this->assertEquals(array(), $def->getTag('foo'), '->getTag() returns an empty array if the tag is not defined'); - $this->assertFalse($def->hasTag('foo')); - $this->assertSame($def, $def->addTag('foo'), '->addTag() implements a fluent interface'); - $this->assertTrue($def->hasTag('foo')); - $this->assertEquals(array(array()), $def->getTag('foo'), '->getTag() returns attributes for a tag name'); - $def->addTag('foo', array('foo' => 'bar')); - $this->assertEquals(array(array(), array('foo' => 'bar')), $def->getTag('foo'), '->addTag() can adds the same tag several times'); - $def->addTag('bar', array('bar' => 'bar')); - $this->assertEquals($def->getTags(), array( - 'foo' => array(array(), array('foo' => 'bar')), - 'bar' => array(array('bar' => 'bar')), - ), '->getTags() returns all tags'); - } - - public function testSetArgument() - { - $def = new Definition('stdClass'); - - $def->addArgument('foo'); - $this->assertSame(array('foo'), $def->getArguments()); - - $this->assertSame($def, $def->replaceArgument(0, 'moo')); - $this->assertSame(array('moo'), $def->getArguments()); - - $def->addArgument('moo'); - $def - ->replaceArgument(0, 'foo') - ->replaceArgument(1, 'bar') - ; - $this->assertSame(array('foo', 'bar'), $def->getArguments()); - } - - /** - * @expectedException \OutOfBoundsException - */ - public function testGetArgumentShouldCheckBounds() - { - $def = new Definition('stdClass'); - - $def->addArgument('foo'); - $def->getArgument(1); - } - - /** - * @expectedException \OutOfBoundsException - */ - public function testReplaceArgumentShouldCheckBounds() - { - $def = new Definition('stdClass'); - - $def->addArgument('foo'); - $def->replaceArgument(1, 'bar'); - } - - public function testSetGetProperties() - { - $def = new Definition('stdClass'); - - $this->assertEquals(array(), $def->getProperties()); - $this->assertSame($def, $def->setProperties(array('foo' => 'bar'))); - $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); - } - - public function testSetProperty() - { - $def = new Definition('stdClass'); - - $this->assertEquals(array(), $def->getProperties()); - $this->assertSame($def, $def->setProperty('foo', 'bar')); - $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); - } - - public function testAutowired() - { - $def = new Definition('stdClass'); - $this->assertFalse($def->isAutowired()); - $def->setAutowired(true); - $this->assertTrue($def->isAutowired()); - } - - public function testTypes() - { - $def = new Definition('stdClass'); - - $this->assertEquals(array(), $def->getAutowiringTypes()); - $this->assertSame($def, $def->setAutowiringTypes(array('Foo'))); - $this->assertEquals(array('Foo'), $def->getAutowiringTypes()); - $this->assertSame($def, $def->addAutowiringType('Bar')); - $this->assertTrue($def->hasAutowiringType('Bar')); - $this->assertSame($def, $def->removeAutowiringType('Foo')); - $this->assertEquals(array('Bar'), $def->getAutowiringTypes()); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.php deleted file mode 100644 index 99c6c7134..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Dumper; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Dumper\GraphvizDumper; - -class GraphvizDumperTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = __DIR__.'/../Fixtures/'; - } - - public function testDump() - { - $dumper = new GraphvizDumper($container = new ContainerBuilder()); - - $this->assertStringEqualsFile(self::$fixturesPath.'/graphviz/services1.dot', $dumper->dump(), '->dump() dumps an empty container as an empty dot file'); - - $container = include self::$fixturesPath.'/containers/container9.php'; - $dumper = new GraphvizDumper($container); - $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services9.dot')), $dumper->dump(), '->dump() dumps services'); - - $container = include self::$fixturesPath.'/containers/container10.php'; - $dumper = new GraphvizDumper($container); - $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services10.dot')), $dumper->dump(), '->dump() dumps services'); - - $container = include self::$fixturesPath.'/containers/container10.php'; - $dumper = new GraphvizDumper($container); - $this->assertEquals($dumper->dump(array( - 'graph' => array('ratio' => 'normal'), - 'node' => array('fontsize' => 13, 'fontname' => 'Verdana', 'shape' => 'square'), - 'edge' => array('fontsize' => 12, 'fontname' => 'Verdana', 'color' => 'white', 'arrowhead' => 'closed', 'arrowsize' => 1), - 'node.instance' => array('fillcolor' => 'green', 'style' => 'empty'), - 'node.definition' => array('fillcolor' => 'grey'), - 'node.missing' => array('fillcolor' => 'red', 'style' => 'empty'), - )), str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services10-1.dot')), '->dump() dumps services'); - } - - public function testDumpWithFrozenContainer() - { - $container = include self::$fixturesPath.'/containers/container13.php'; - $dumper = new GraphvizDumper($container); - $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services13.dot')), $dumper->dump(), '->dump() dumps services'); - } - - public function testDumpWithFrozenCustomClassContainer() - { - $container = include self::$fixturesPath.'/containers/container14.php'; - $dumper = new GraphvizDumper($container); - $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services14.dot')), $dumper->dump(), '->dump() dumps services'); - } - - public function testDumpWithUnresolvedParameter() - { - $container = include self::$fixturesPath.'/containers/container17.php'; - $dumper = new GraphvizDumper($container); - - $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services17.dot')), $dumper->dump(), '->dump() dumps services'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php deleted file mode 100644 index 57469b9be..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php +++ /dev/null @@ -1,394 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Dumper; - -use DummyProxyDumper; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Dumper\PhpDumper; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\DependencyInjection\Variable; -use Symfony\Component\ExpressionLanguage\Expression; - -require_once __DIR__.'/../Fixtures/includes/classes.php'; - -class PhpDumperTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); - } - - public function testDump() - { - $dumper = new PhpDumper($container = new ContainerBuilder()); - - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1-1.php', $dumper->dump(array('class' => 'Container', 'base_class' => 'AbstractContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Dump')), '->dump() takes a class and a base_class options'); - - $container = new ContainerBuilder(); - new PhpDumper($container); - } - - public function testDumpOptimizationString() - { - $definition = new Definition(); - $definition->setClass('stdClass'); - $definition->addArgument(array( - 'only dot' => '.', - 'concatenation as value' => '.\'\'.', - 'concatenation from the start value' => '\'\'.', - '.' => 'dot as a key', - '.\'\'.' => 'concatenation as a key', - '\'\'.' => 'concatenation from the start key', - 'optimize concatenation' => 'string1%some_string%string2', - 'optimize concatenation with empty string' => 'string1%empty_value%string2', - 'optimize concatenation from the start' => '%empty_value%start', - 'optimize concatenation at the end' => 'end%empty_value%', - )); - - $container = new ContainerBuilder(); - $container->setResourceTracking(false); - $container->setDefinition('test', $definition); - $container->setParameter('empty_value', ''); - $container->setParameter('some_string', '-'); - $container->compile(); - - $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services10.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); - } - - public function testDumpRelativeDir() - { - $definition = new Definition(); - $definition->setClass('stdClass'); - $definition->addArgument('%foo%'); - $definition->addArgument(array('%foo%' => '%buz%/')); - - $container = new ContainerBuilder(); - $container->setDefinition('test', $definition); - $container->setParameter('foo', 'wiz'.dirname(__DIR__)); - $container->setParameter('bar', __DIR__); - $container->setParameter('baz', '%bar%/PhpDumperTest.php'); - $container->setParameter('buz', dirname(dirname(__DIR__))); - $container->compile(); - - $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings'); - } - - /** - * @dataProvider provideInvalidParameters - * @expectedException \InvalidArgumentException - */ - public function testExportParameters($parameters) - { - $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag($parameters))); - $dumper->dump(); - } - - public function provideInvalidParameters() - { - return array( - array(array('foo' => new Definition('stdClass'))), - array(array('foo' => new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))), - array(array('foo' => new Reference('foo'))), - array(array('foo' => new Variable('foo'))), - ); - } - - public function testAddParameters() - { - $container = include self::$fixturesPath.'/containers/container8.php'; - $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services8.php', $dumper->dump(), '->dump() dumps parameters'); - } - - public function testAddService() - { - // without compilation - $container = include self::$fixturesPath.'/containers/container9.php'; - $dumper = new PhpDumper($container); - $this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9.php')), $dumper->dump(), '->dump() dumps services'); - - // with compilation - $container = include self::$fixturesPath.'/containers/container9.php'; - $container->compile(); - $dumper = new PhpDumper($container); - $this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9_compiled.php')), $dumper->dump(), '->dump() dumps services'); - - $dumper = new PhpDumper($container = new ContainerBuilder()); - $container->register('foo', 'FooClass')->addArgument(new \stdClass()); - try { - $dumper->dump(); - $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - } catch (\Exception $e) { - $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - } - } - - public function testServicesWithAnonymousFactories() - { - $container = include self::$fixturesPath.'/containers/container19.php'; - $dumper = new PhpDumper($container); - - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories'); - } - - public function testAddServiceIdWithUnsupportedCharacters() - { - $class = 'Symfony_DI_PhpDumper_Test_Unsupported_Characters'; - $container = new ContainerBuilder(); - $container->register('bar$', 'FooClass'); - $container->register('bar$!', 'FooClass'); - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array('class' => $class))); - - $this->assertTrue(method_exists($class, 'getBarService')); - $this->assertTrue(method_exists($class, 'getBar2Service')); - } - - public function testConflictingServiceIds() - { - $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Service_Ids'; - $container = new ContainerBuilder(); - $container->register('foo_bar', 'FooClass'); - $container->register('foobar', 'FooClass'); - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array('class' => $class))); - - $this->assertTrue(method_exists($class, 'getFooBarService')); - $this->assertTrue(method_exists($class, 'getFoobar2Service')); - } - - public function testConflictingMethodsWithParent() - { - $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Method_With_Parent'; - $container = new ContainerBuilder(); - $container->register('bar', 'FooClass'); - $container->register('foo_bar', 'FooClass'); - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array( - 'class' => $class, - 'base_class' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\containers\CustomContainer', - ))); - - $this->assertTrue(method_exists($class, 'getBar2Service')); - $this->assertTrue(method_exists($class, 'getFoobar2Service')); - } - - /** - * @dataProvider provideInvalidFactories - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Cannot dump definition - */ - public function testInvalidFactories($factory) - { - $container = new ContainerBuilder(); - $def = new Definition('stdClass'); - $def->setFactory($factory); - $container->setDefinition('bar', $def); - $dumper = new PhpDumper($container); - $dumper->dump(); - } - - public function provideInvalidFactories() - { - return array( - array(array('', 'method')), - array(array('class', '')), - array(array('...', 'method')), - array(array('class', '...')), - ); - } - - public function testAliases() - { - $container = include self::$fixturesPath.'/containers/container9.php'; - $container->compile(); - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Aliases'))); - - $container = new \Symfony_DI_PhpDumper_Test_Aliases(); - $container->set('foo', $foo = new \stdClass()); - $this->assertSame($foo, $container->get('foo')); - $this->assertSame($foo, $container->get('alias_for_foo')); - $this->assertSame($foo, $container->get('alias_for_alias')); - } - - public function testFrozenContainerWithoutAliases() - { - $container = new ContainerBuilder(); - $container->compile(); - - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Frozen_No_Aliases'))); - - $container = new \Symfony_DI_PhpDumper_Test_Frozen_No_Aliases(); - $this->assertFalse($container->has('foo')); - } - - public function testOverrideServiceWhenUsingADumpedContainer() - { - require_once self::$fixturesPath.'/php/services9.php'; - require_once self::$fixturesPath.'/includes/foo.php'; - - $container = new \ProjectServiceContainer(); - $container->set('bar', $bar = new \stdClass()); - $container->setParameter('foo_bar', 'foo_bar'); - - $this->assertEquals($bar, $container->get('bar'), '->set() overrides an already defined service'); - } - - public function testOverrideServiceWhenUsingADumpedContainerAndServiceIsUsedFromAnotherOne() - { - require_once self::$fixturesPath.'/php/services9.php'; - require_once self::$fixturesPath.'/includes/foo.php'; - require_once self::$fixturesPath.'/includes/classes.php'; - - $container = new \ProjectServiceContainer(); - $container->set('bar', $bar = new \stdClass()); - - $this->assertSame($bar, $container->get('foo')->bar, '->set() overrides an already defined service'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException - */ - public function testCircularReference() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->addArgument(new Reference('bar')); - $container->register('bar', 'stdClass')->setPublic(false)->addMethodCall('setA', array(new Reference('baz'))); - $container->register('baz', 'stdClass')->addMethodCall('setA', array(new Reference('foo'))); - $container->compile(); - - $dumper = new PhpDumper($container); - $dumper->dump(); - } - - public function testDumpAutowireData() - { - $container = include self::$fixturesPath.'/containers/container24.php'; - $dumper = new PhpDumper($container); - - $this->assertEquals(file_get_contents(self::$fixturesPath.'/php/services24.php'), $dumper->dump()); - } - - public function testEnvParameter() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services26.yml'); - $container->compile(); - $dumper = new PhpDumper($container); - - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException - * @expectedExceptionMessage Incompatible use of dynamic environment variables "FOO" found in parameters. - */ - public function testUnusedEnvParameter() - { - $container = new ContainerBuilder(); - $container->getParameter('env(FOO)'); - $container->compile(); - $dumper = new PhpDumper($container); - $dumper->dump(); - } - - public function testInlinedDefinitionReferencingServiceContainer() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->addMethodCall('add', array(new Reference('service_container')))->setPublic(false); - $container->register('bar', 'stdClass')->addArgument(new Reference('foo')); - $container->compile(); - - $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container'); - } - - public function testInitializePropertiesBeforeMethodCalls() - { - require_once self::$fixturesPath.'/includes/classes.php'; - - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass'); - $container->register('bar', 'MethodCallClass') - ->setProperty('simple', 'bar') - ->setProperty('complex', new Reference('foo')) - ->addMethodCall('callMe'); - $container->compile(); - - $dumper = new PhpDumper($container); - eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls'))); - - $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls(); - $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls'); - } - - public function testCircularReferenceAllowanceForLazyServices() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->addArgument(new Reference('bar')); - $container->register('bar', 'stdClass')->setLazy(true)->addArgument(new Reference('foo')); - $container->compile(); - - $dumper = new PhpDumper($container); - $dumper->dump(); - } - - public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices() - { - /* - * test graph: - * [connection] -> [event_manager] --> [entity_manager](lazy) - * | - * --(call)- addEventListener ("@lazy_service") - * - * [lazy_service](lazy) -> [entity_manager](lazy) - * - */ - - $container = new ContainerBuilder(); - - $eventManagerDefinition = new Definition('stdClass'); - - $connectionDefinition = $container->register('connection', 'stdClass'); - $connectionDefinition->addArgument($eventManagerDefinition); - - $container->register('entity_manager', 'stdClass') - ->setLazy(true) - ->addArgument(new Reference('connection')); - - $lazyServiceDefinition = $container->register('lazy_service', 'stdClass'); - $lazyServiceDefinition->setLazy(true); - $lazyServiceDefinition->addArgument(new Reference('entity_manager')); - - $eventManagerDefinition->addMethodCall('addEventListener', array(new Reference('lazy_service'))); - - $container->compile(); - - $dumper = new PhpDumper($container); - - $dumper->setProxyDumper(new DummyProxyDumper()); - $dumper->dump(); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.php deleted file mode 100644 index ead2fc179..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.php +++ /dev/null @@ -1,171 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Dumper; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Dumper\XmlDumper; - -class XmlDumperTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); - } - - public function testDump() - { - $dumper = new XmlDumper(new ContainerBuilder()); - - $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services1.xml', $dumper->dump(), '->dump() dumps an empty container as an empty XML file'); - } - - public function testExportParameters() - { - $container = include self::$fixturesPath.'//containers/container8.php'; - $dumper = new XmlDumper($container); - $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services8.xml', $dumper->dump(), '->dump() dumps parameters'); - } - - public function testAddParameters() - { - $container = include self::$fixturesPath.'//containers/container8.php'; - $dumper = new XmlDumper($container); - $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services8.xml', $dumper->dump(), '->dump() dumps parameters'); - } - - public function testAddService() - { - $container = include self::$fixturesPath.'/containers/container9.php'; - $dumper = new XmlDumper($container); - - $this->assertEquals(str_replace('%path%', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/xml/services9.xml')), $dumper->dump(), '->dump() dumps services'); - - $dumper = new XmlDumper($container = new ContainerBuilder()); - $container->register('foo', 'FooClass')->addArgument(new \stdClass()); - try { - $dumper->dump(); - $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - } catch (\Exception $e) { - $this->assertInstanceOf('\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - } - } - - public function testDumpAnonymousServices() - { - $container = include self::$fixturesPath.'/containers/container11.php'; - $dumper = new XmlDumper($container); - $this->assertEquals(' - - - - - - - - - - - - - -', $dumper->dump()); - } - - public function testDumpEntities() - { - $container = include self::$fixturesPath.'/containers/container12.php'; - $dumper = new XmlDumper($container); - $this->assertEquals(" - - - - - foo<>&bar - - - -", $dumper->dump()); - } - - /** - * @dataProvider provideDecoratedServicesData - */ - public function testDumpDecoratedServices($expectedXmlDump, $container) - { - $dumper = new XmlDumper($container); - $this->assertEquals($expectedXmlDump, $dumper->dump()); - } - - public function provideDecoratedServicesData() - { - $fixturesPath = realpath(__DIR__.'/../Fixtures/'); - - return array( - array(" - - - - - -", include $fixturesPath.'/containers/container15.php'), - array(" - - - - - -", include $fixturesPath.'/containers/container16.php'), - ); - } - - /** - * @dataProvider provideCompiledContainerData - */ - public function testCompiledContainerCanBeDumped($containerFile) - { - $fixturesPath = __DIR__.'/../Fixtures'; - $container = require $fixturesPath.'/containers/'.$containerFile.'.php'; - $container->compile(); - $dumper = new XmlDumper($container); - $dumper->dump(); - } - - public function provideCompiledContainerData() - { - return array( - array('container8'), - array('container9'), - array('container11'), - array('container12'), - array('container14'), - ); - } - - public function testDumpInlinedServices() - { - $container = include self::$fixturesPath.'/containers/container21.php'; - $dumper = new XmlDumper($container); - - $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services21.xml'), $dumper->dump()); - } - - public function testDumpAutowireData() - { - $container = include self::$fixturesPath.'/containers/container24.php'; - $dumper = new XmlDumper($container); - - $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services24.xml'), $dumper->dump()); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.php deleted file mode 100644 index cd403c6d4..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Dumper; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Dumper\YamlDumper; -use Symfony\Component\Yaml\Yaml; - -class YamlDumperTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); - } - - public function testDump() - { - $dumper = new YamlDumper($container = new ContainerBuilder()); - - $this->assertEqualYamlStructure(file_get_contents(self::$fixturesPath.'/yaml/services1.yml'), $dumper->dump(), '->dump() dumps an empty container as an empty YAML file'); - } - - public function testAddParameters() - { - $container = include self::$fixturesPath.'/containers/container8.php'; - $dumper = new YamlDumper($container); - $this->assertEqualYamlStructure(file_get_contents(self::$fixturesPath.'/yaml/services8.yml'), $dumper->dump(), '->dump() dumps parameters'); - } - - public function testAddService() - { - $container = include self::$fixturesPath.'/containers/container9.php'; - $dumper = new YamlDumper($container); - $this->assertEqualYamlStructure(str_replace('%path%', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/yaml/services9.yml')), $dumper->dump(), '->dump() dumps services'); - - $dumper = new YamlDumper($container = new ContainerBuilder()); - $container->register('foo', 'FooClass')->addArgument(new \stdClass()); - try { - $dumper->dump(); - $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - } catch (\Exception $e) { - $this->assertInstanceOf('\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); - } - } - - public function testDumpAutowireData() - { - $container = include self::$fixturesPath.'/containers/container24.php'; - $dumper = new YamlDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump()); - } - - private function assertEqualYamlStructure($yaml, $expected, $message = '') - { - $this->assertEquals(Yaml::parse($expected), Yaml::parse($yaml), $message); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.php deleted file mode 100644 index e7f19a6e9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Extension; - -class ExtensionTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getResolvedEnabledFixtures - */ - public function testIsConfigEnabledReturnsTheResolvedValue($enabled) - { - $pb = $this->getMockBuilder('Symfony\Component\DependencyInjection\ParameterBag\ParameterBag') - ->setMethods(array('resolveValue')) - ->getMock() - ; - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder') - ->setMethods(array('getParameterBag')) - ->getMock() - ; - - $pb->expects($this->once()) - ->method('resolveValue') - ->with($this->equalTo($enabled)) - ->will($this->returnValue($enabled)) - ; - - $container->expects($this->once()) - ->method('getParameterBag') - ->will($this->returnValue($pb)) - ; - - $extension = $this->getMockBuilder('Symfony\Component\DependencyInjection\Extension\Extension') - ->setMethods(array()) - ->getMockForAbstractClass() - ; - - $r = new \ReflectionMethod('Symfony\Component\DependencyInjection\Extension\Extension', 'isConfigEnabled'); - $r->setAccessible(true); - - $r->invoke($extension, $container, array('enabled' => $enabled)); - } - - public function getResolvedEnabledFixtures() - { - return array( - array(true), - array(false), - ); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage The config array has no 'enabled' key. - */ - public function testIsConfigEnabledOnNonEnableableConfig() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder') - ->getMock() - ; - - $extension = $this->getMockBuilder('Symfony\Component\DependencyInjection\Extension\Extension') - ->setMethods(array()) - ->getMockForAbstractClass() - ; - - $r = new \ReflectionMethod('Symfony\Component\DependencyInjection\Extension\Extension', 'isConfigEnabled'); - $r->setAccessible(true); - - $r->invoke($extension, $container, array()); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/CustomDefinition.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/CustomDefinition.php deleted file mode 100644 index 65eea2106..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/CustomDefinition.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Fixtures; - -use Symfony\Component\DependencyInjection\Definition; - -class CustomDefinition extends Definition -{ -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.php deleted file mode 100644 index da984b562..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Fixtures; - -class FactoryDummy extends FactoryParent -{ - public static function createFactory(): FactoryDummy - { - } - - public function create(): \stdClass - { - } - - // Not supported by hhvm - public function createBuiltin(): int - { - } - - public static function createSelf(): self - { - } - - public static function createParent(): parent - { - } -} - -class FactoryParent -{ -} - -function factoryFunction(): FactoryDummy -{ -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php deleted file mode 100644 index 225143532..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php +++ /dev/null @@ -1,17 +0,0 @@ - - register('foo', 'FooClass')-> - addArgument(new Reference('bar')) -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php deleted file mode 100644 index 3e6cafca2..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php +++ /dev/null @@ -1,12 +0,0 @@ - - register('foo', 'FooClass')-> - addArgument(new Definition('BarClass', array(new Definition('BazClass')))) -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php deleted file mode 100644 index 73c5b4ef1..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php +++ /dev/null @@ -1,12 +0,0 @@ - - register('foo', 'FooClass\\Foo')-> - addArgument('foo<>&bar')-> - addTag('foo"bar\\bar', array('foo' => 'foo"barřž€')) -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php deleted file mode 100644 index cc716c78f..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php +++ /dev/null @@ -1,16 +0,0 @@ - - register('foo', 'FooClass')-> - addArgument(new Reference('bar')) -; -$container-> - register('bar', 'BarClass') -; -$container->compile(); - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php deleted file mode 100644 index 191afb8cd..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php +++ /dev/null @@ -1,17 +0,0 @@ -register('foo', 'FooClass\\Foo') - ->setDecoratedService('bar', 'bar.woozy') -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php deleted file mode 100644 index 67b4d353d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php +++ /dev/null @@ -1,11 +0,0 @@ -register('foo', 'FooClass\\Foo') - ->setDecoratedService('bar') -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php deleted file mode 100644 index d902ec2a3..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php +++ /dev/null @@ -1,10 +0,0 @@ -register('foo', '%foo.class%') -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php deleted file mode 100644 index 64b8f066d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php +++ /dev/null @@ -1,22 +0,0 @@ -register('service_from_anonymous_factory', 'Bar\FooClass') - ->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')) -; - -$anonymousServiceWithFactory = new Definition('Bar\FooClass'); -$anonymousServiceWithFactory->setFactory('Bar\FooClass::getInstance'); -$container - ->register('service_with_method_call_and_factory', 'Bar\FooClass') - ->addMethodCall('setBar', array($anonymousServiceWithFactory)) -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php deleted file mode 100644 index d04673861..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php +++ /dev/null @@ -1,20 +0,0 @@ -setConfigurator(array(new Definition('Baz'), 'configureBar')); - -$fooFactory = new Definition('FooFactory'); -$fooFactory->setFactory(array(new Definition('Foobar'), 'createFooFactory')); - -$container - ->register('foo', 'Foo') - ->setFactory(array($fooFactory, 'createFoo')) - ->setConfigurator(array($bar, 'configureFoo')) -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php deleted file mode 100644 index 3e033059a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php +++ /dev/null @@ -1,14 +0,0 @@ -register('foo', 'Foo') - ->setAutowired(true) - ->addAutowiringType('A') - ->addAutowiringType('B') -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php deleted file mode 100644 index f0b4ca933..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php +++ /dev/null @@ -1,14 +0,0 @@ - '%baz%', - 'baz' => 'bar', - 'bar' => 'foo is %%foo bar', - 'escape' => '@escapeme', - 'values' => array(true, false, null, 0, 1000.3, 'true', 'false', 'null'), -))); - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php deleted file mode 100644 index ba25dc3c9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php +++ /dev/null @@ -1,133 +0,0 @@ -register('foo', '\Bar\FooClass') - ->addTag('foo', array('foo' => 'foo')) - ->addTag('foo', array('bar' => 'bar', 'baz' => 'baz')) - ->setFactory(array('Bar\\FooClass', 'getInstance')) - ->setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container'))) - ->setProperties(array('foo' => 'bar', 'moo' => new Reference('foo.baz'), 'qux' => array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'))) - ->addMethodCall('setBar', array(new Reference('bar'))) - ->addMethodCall('initialize') - ->setConfigurator('sc_configure') -; -$container - ->register('foo.baz', '%baz_class%') - ->setFactory(array('%baz_class%', 'getInstance')) - ->setConfigurator(array('%baz_class%', 'configureStatic1')) -; -$container - ->register('bar', 'Bar\FooClass') - ->setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar'))) - ->setConfigurator(array(new Reference('foo.baz'), 'configure')) -; -$container - ->register('foo_bar', '%foo_class%') - ->setShared(false) -; -$container->getParameterBag()->clear(); -$container->getParameterBag()->add(array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\FooClass', - 'foo' => 'bar', -)); -$container->setAlias('alias_for_foo', 'foo'); -$container->setAlias('alias_for_alias', 'alias_for_foo'); -$container - ->register('method_call1', 'Bar\FooClass') - ->setFile(realpath(__DIR__.'/../includes/foo.php')) - ->addMethodCall('setBar', array(new Reference('foo'))) - ->addMethodCall('setBar', array(new Reference('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE))) - ->addMethodCall('setBar', array(new Reference('foo3', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) - ->addMethodCall('setBar', array(new Reference('foobaz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) - ->addMethodCall('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))) -; -$container - ->register('foo_with_inline', 'Foo') - ->addMethodCall('setBar', array(new Reference('inlined'))) -; -$container - ->register('inlined', 'Bar') - ->setProperty('pub', 'pub') - ->addMethodCall('setBaz', array(new Reference('baz'))) - ->setPublic(false) -; -$container - ->register('baz', 'Baz') - ->addMethodCall('setFoo', array(new Reference('foo_with_inline'))) -; -$container - ->register('request', 'Request') - ->setSynthetic(true) -; -$container - ->register('configurator_service', 'ConfClass') - ->setPublic(false) - ->addMethodCall('setFoo', array(new Reference('baz'))) -; -$container - ->register('configured_service', 'stdClass') - ->setConfigurator(array(new Reference('configurator_service'), 'configureStdClass')) -; -$container - ->register('configurator_service_simple', 'ConfClass') - ->addArgument('bar') - ->setPublic(false) -; -$container - ->register('configured_service_simple', 'stdClass') - ->setConfigurator(array(new Reference('configurator_service_simple'), 'configureStdClass')) -; -$container - ->register('decorated', 'stdClass') -; -$container - ->register('decorator_service', 'stdClass') - ->setDecoratedService('decorated') -; -$container - ->register('decorator_service_with_name', 'stdClass') - ->setDecoratedService('decorated', 'decorated.pif-pouf') -; -$container - ->register('deprecated_service', 'stdClass') - ->setDeprecated(true) -; -$container - ->register('new_factory', 'FactoryClass') - ->setProperty('foo', 'bar') - ->setPublic(false) -; -$container - ->register('factory_service', 'Bar') - ->setFactory(array(new Reference('foo.baz'), 'getInstance')) -; -$container - ->register('new_factory_service', 'FooBarBaz') - ->setProperty('foo', 'bar') - ->setFactory(array(new Reference('new_factory'), 'getInstance')) -; -$container - ->register('service_from_static_method', 'Bar\FooClass') - ->setFactory(array('Bar\FooClass', 'getInstance')) -; -$container - ->register('factory_simple', 'SimpleFactoryClass') - ->addArgument('foo') - ->setPublic(false) -; -$container - ->register('factory_service_simple', 'Bar') - ->setFactory(array(new Reference('factory_simple'), 'getInstance')) -; - -return $container; diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml deleted file mode 100644 index 35ec4a042..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: ../recurse/ } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini deleted file mode 100644 index 0984cdac7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini +++ /dev/null @@ -1,2 +0,0 @@ -[parameters] - ini = ini diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml deleted file mode 100644 index f98ef12ea..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml +++ /dev/null @@ -1,2 +0,0 @@ -parameters: - yaml: yaml diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php deleted file mode 100644 index 4750324ad..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php +++ /dev/null @@ -1,3 +0,0 @@ -setParameter('php', 'php'); diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot deleted file mode 100644 index 1bb7c30b8..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot +++ /dev/null @@ -1,7 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot deleted file mode 100644 index 0e578b161..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot +++ /dev/null @@ -1,10 +0,0 @@ -digraph sc { - ratio="normal" - node [fontsize="13" fontname="Verdana" shape="square"]; - edge [fontsize="12" fontname="Verdana" color="white" arrowhead="closed" arrowsize="1"]; - - node_foo [label="foo\nFooClass\n", shape=square, fillcolor="grey", style="filled"]; - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=square, fillcolor="green", style="empty"]; - node_bar [label="bar\n\n", shape=square, fillcolor="red", style="empty"]; - node_foo -> node_bar [label="" style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot deleted file mode 100644 index f17857fe4..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot +++ /dev/null @@ -1,10 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; - node_bar [label="bar\n\n", shape=record, fillcolor="#ff9999", style="filled"]; - node_foo -> node_bar [label="" style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot deleted file mode 100644 index bc7f81317..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot +++ /dev/null @@ -1,10 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_bar [label="bar\nBarClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; - node_foo -> node_bar [label="" style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot deleted file mode 100644 index d07dc389e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot +++ /dev/null @@ -1,7 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_service_container [label="service_container\nContainer14\\ProjectServiceContainer\n", shape=record, fillcolor="#9999ff", style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot deleted file mode 100644 index a6d04bf5a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot +++ /dev/null @@ -1,8 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_foo [label="foo\n%foo.class%\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot deleted file mode 100644 index 4fbcceef3..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot +++ /dev/null @@ -1,8 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot deleted file mode 100644 index 3b24ef8ff..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot +++ /dev/null @@ -1,46 +0,0 @@ -digraph sc { - ratio="compress" - node [fontsize="11" fontname="Arial" shape="record"]; - edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; - - node_foo [label="foo (alias_for_foo)\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_foo_baz [label="foo.baz\nBazClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_bar [label="bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_foo_bar [label="foo_bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"]; - node_method_call1 [label="method_call1\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_foo_with_inline [label="foo_with_inline\nFoo\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_inlined [label="inlined\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_baz [label="baz\nBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_request [label="request\nRequest\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_configurator_service [label="configurator_service\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_configured_service [label="configured_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_configurator_service_simple [label="configurator_service_simple\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_configured_service_simple [label="configured_service_simple\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_deprecated_service [label="deprecated_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_factory_simple [label="factory_simple\nSimpleFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_factory_service_simple [label="factory_service_simple\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; - node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; - node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; - node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"]; - node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"]; - node_foo -> node_foo_baz [label="" style="filled"]; - node_foo -> node_service_container [label="" style="filled"]; - node_foo -> node_foo_baz [label="" style="dashed"]; - node_foo -> node_bar [label="setBar()" style="dashed"]; - node_bar -> node_foo_baz [label="" style="filled"]; - node_method_call1 -> node_foo [label="setBar()" style="dashed"]; - node_method_call1 -> node_foo2 [label="setBar()" style="dashed"]; - node_method_call1 -> node_foo3 [label="setBar()" style="dashed"]; - node_method_call1 -> node_foobaz [label="setBar()" style="dashed"]; - node_foo_with_inline -> node_inlined [label="setBar()" style="dashed"]; - node_inlined -> node_baz [label="setBaz()" style="dashed"]; - node_baz -> node_foo_with_inline [label="setFoo()" style="dashed"]; - node_configurator_service -> node_baz [label="setFoo()" style="dashed"]; -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectExtension.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectExtension.php deleted file mode 100644 index c9f801026..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectExtension.php +++ /dev/null @@ -1,40 +0,0 @@ -setDefinition('project.service.bar', new Definition('FooClass')); - $configuration->setParameter('project.parameter.bar', isset($config['foo']) ? $config['foo'] : 'foobar'); - - $configuration->setDefinition('project.service.foo', new Definition('FooClass')); - $configuration->setParameter('project.parameter.foo', isset($config['foo']) ? $config['foo'] : 'foobar'); - - return $configuration; - } - - public function getXsdValidationBasePath() - { - return false; - } - - public function getNamespace() - { - return 'http://www.example.com/schema/project'; - } - - public function getAlias() - { - return 'project'; - } - - public function getConfiguration(array $config, ContainerBuilder $container) - { - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php deleted file mode 100644 index 2ee2f12dc..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php +++ /dev/null @@ -1,19 +0,0 @@ -configure(); -} - -class BarClass -{ - protected $baz; - public $foo = 'foo'; - - public function setBaz(BazClass $baz) - { - $this->baz = $baz; - } - - public function getBaz() - { - return $this->baz; - } -} - -class BazClass -{ - protected $foo; - - public function setFoo(Foo $foo) - { - $this->foo = $foo; - } - - public function configure($instance) - { - $instance->configure(); - } - - public static function getInstance() - { - return new self(); - } - - public static function configureStatic($instance) - { - $instance->configure(); - } - - public static function configureStatic1() - { - } -} - -class BarUserClass -{ - public $bar; - - public function __construct(BarClass $bar) - { - $this->bar = $bar; - } -} - -class MethodCallClass -{ - public $simple; - public $complex; - private $callPassed = false; - - public function callMe() - { - $this->callPassed = is_scalar($this->simple) && is_object($this->complex); - } - - public function callPassed() - { - return $this->callPassed; - } -} - -class DummyProxyDumper implements ProxyDumper -{ - public function isProxyCandidate(Definition $definition) - { - return false; - } - - public function getProxyFactoryCode(Definition $definition, $id) - { - return ''; - } - - public function getProxyCode(Definition $definition) - { - return ''; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php deleted file mode 100644 index c675478fd..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php +++ /dev/null @@ -1,47 +0,0 @@ -addFromString('ProjectWithXsdExtensionInPhar.php', <<<'EOT' -addFromString('schema/project-1.0.xsd', <<<'EOT' - - - - - - - - - - -EOT -); -$phar->setStub(''); diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php deleted file mode 100644 index bcb4e20a9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php +++ /dev/null @@ -1,43 +0,0 @@ -arguments = $arguments; - } - - public static function getInstance($arguments = array()) - { - $obj = new self($arguments); - $obj->called = true; - - return $obj; - } - - public function initialize() - { - $this->initialized = true; - } - - public function configure() - { - $this->configured = true; - } - - public function setBar($value = null) - { - $this->bar = $value; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd deleted file mode 100644 index 282884e4e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini deleted file mode 100644 index 9fdef8558..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini +++ /dev/null @@ -1,2 +0,0 @@ -foo = ' -bar = bar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini deleted file mode 100644 index 9f84a6087..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini +++ /dev/null @@ -1,2 +0,0 @@ -{NOT AN INI FILE} -{JUST A PLAIN TEXT FILE} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini deleted file mode 100644 index df92f7527..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini +++ /dev/null @@ -1,3 +0,0 @@ -[parameters] - foo = bar - bar = %foo% diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini deleted file mode 100644 index e50f7222b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini +++ /dev/null @@ -1,3 +0,0 @@ -[parameters] - FOO = foo - baz = baz diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini deleted file mode 100644 index 75fbac628..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini +++ /dev/null @@ -1,2 +0,0 @@ -[parameters] - imported_from_ini = true diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini deleted file mode 100644 index 19cc5b3b3..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini +++ /dev/null @@ -1,27 +0,0 @@ -[parameters] - true = true - true_comment = true ; comment - false = false - null = null - on = on - off = off - yes = yes - no = no - none = none - constant = PHP_VERSION - 12 = 12 - 12_string = '12' - 12_comment = 12 ; comment - 12_string_comment = '12' ; comment - 12_string_comment_again = "12" ; comment - -12 = -12 - 0 = 0 - 1 = 1 - 0b0110 = 0b0110 - 11112222333344445555 = 1111,2222,3333,4444,5555 - 0777 = 0777 - 255 = 0xFF - 100.0 = 1e2 - -120.0 = -1.2E2 - -10100.1 = -10100.1 - -10,100.1 = -10,100.1 diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php deleted file mode 100644 index 0415d702a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php +++ /dev/null @@ -1,29 +0,0 @@ -parameters = $this->getDefaultParameters(); - - $this->services = array(); - $this->methodMap = array( - 'test' => 'getTestService', - ); - - $this->aliases = array(); - } - - /** - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } - - /** - * {@inheritdoc} - */ - public function isFrozen() - { - return true; - } - - /** - * Gets the 'test' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getTestService() - { - return $this->services['test'] = new \stdClass(array('only dot' => '.', 'concatenation as value' => '.\'\'.', 'concatenation from the start value' => '\'\'.', '.' => 'dot as a key', '.\'\'.' => 'concatenation as a key', '\'\'.' => 'concatenation from the start key', 'optimize concatenation' => 'string1-string2', 'optimize concatenation with empty string' => 'string1string2', 'optimize concatenation from the start' => 'start', 'optimize concatenation at the end' => 'end')); - } - - /** - * {@inheritdoc} - */ - public function getParameter($name) - { - $name = strtolower($name); - - if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } - if (isset($this->loadedDynamicParameters[$name])) { - return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - - return $this->parameters[$name]; - } - - /** - * {@inheritdoc} - */ - public function hasParameter($name) - { - $name = strtolower($name); - - return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]); - } - - /** - * {@inheritdoc} - */ - public function setParameter($name, $value) - { - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); - } - - /** - * {@inheritdoc} - */ - public function getParameterBag() - { - if (null === $this->parameterBag) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - $this->parameterBag = new FrozenParameterBag($parameters); - } - - return $this->parameterBag; - } - - private $loadedDynamicParameters = array(); - private $dynamicParameters = array(); - - /** - * Computes a dynamic parameter. - * - * @param string The name of the dynamic parameter to load - * - * @return mixed The value of the dynamic parameter - * - * @throws InvalidArgumentException When the dynamic parameter does not exist - */ - private function getDynamicParameter($name) - { - throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'empty_value' => '', - 'some_string' => '-', - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php deleted file mode 100644 index 5c299f55d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php +++ /dev/null @@ -1,159 +0,0 @@ -targetDirs[$i] = $dir = dirname($dir); - } - $this->parameters = $this->getDefaultParameters(); - - $this->services = array(); - $this->methodMap = array( - 'test' => 'getTestService', - ); - - $this->aliases = array(); - } - - /** - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } - - /** - * {@inheritdoc} - */ - public function isFrozen() - { - return true; - } - - /** - * Gets the 'test' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getTestService() - { - return $this->services['test'] = new \stdClass(('wiz'.$this->targetDirs[1]), array(('wiz'.$this->targetDirs[1]) => ($this->targetDirs[2].'/'))); - } - - /** - * {@inheritdoc} - */ - public function getParameter($name) - { - $name = strtolower($name); - - if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } - if (isset($this->loadedDynamicParameters[$name])) { - return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - - return $this->parameters[$name]; - } - - /** - * {@inheritdoc} - */ - public function hasParameter($name) - { - $name = strtolower($name); - - return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]); - } - - /** - * {@inheritdoc} - */ - public function setParameter($name, $value) - { - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); - } - - /** - * {@inheritdoc} - */ - public function getParameterBag() - { - if (null === $this->parameterBag) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - $this->parameterBag = new FrozenParameterBag($parameters); - } - - return $this->parameterBag; - } - - private $loadedDynamicParameters = array( - 'foo' => false, - 'buz' => false, - ); - private $dynamicParameters = array(); - - /** - * Computes a dynamic parameter. - * - * @param string The name of the dynamic parameter to load - * - * @return mixed The value of the dynamic parameter - * - * @throws InvalidArgumentException When the dynamic parameter does not exist - */ - private function getDynamicParameter($name) - { - switch ($name) { - case 'foo': $value = ('wiz'.$this->targetDirs[1]); break; - case 'buz': $value = $this->targetDirs[2]; break; - default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); - } - $this->loadedDynamicParameters[$name] = true; - - return $this->dynamicParameters[$name] = $value; - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'bar' => __DIR__, - 'baz' => (__DIR__.'/PhpDumperTest.php'), - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php deleted file mode 100644 index 580529667..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php +++ /dev/null @@ -1,65 +0,0 @@ -services = array(); - $this->methodMap = array( - 'bar' => 'getBarService', - ); - - $this->aliases = array(); - } - - /** - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } - - /** - * {@inheritdoc} - */ - public function isFrozen() - { - return true; - } - - /** - * Gets the 'bar' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getBarService() - { - $a = new \stdClass(); - $a->add($this); - - return $this->services['bar'] = new \stdClass($a); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php deleted file mode 100644 index e89c3c8f8..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php +++ /dev/null @@ -1,62 +0,0 @@ -methodMap = array( - 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', - 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService', - ); - } - - /** - * Gets the 'service_from_anonymous_factory' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getServiceFromAnonymousFactoryService() - { - return $this->services['service_from_anonymous_factory'] = (new \Bar\FooClass())->getInstance(); - } - - /** - * Gets the 'service_with_method_call_and_factory' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getServiceWithMethodCallAndFactoryService() - { - $this->services['service_with_method_call_and_factory'] = $instance = new \Bar\FooClass(); - - $instance->setBar(\Bar\FooClass::getInstance()); - - return $instance; - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php deleted file mode 100644 index a9724152f..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php +++ /dev/null @@ -1,43 +0,0 @@ -methodMap = array( - 'foo' => 'getFooService', - ); - } - - /** - * Gets the 'foo' service. - * - * This service is autowired. - * - * @return \Foo A Foo instance - */ - protected function getFooService() - { - return $this->services['foo'] = new \Foo(); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php deleted file mode 100644 index d834ae286..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php +++ /dev/null @@ -1,154 +0,0 @@ -parameters = $this->getDefaultParameters(); - - $this->services = array(); - $this->methodMap = array( - 'test' => 'getTestService', - ); - - $this->aliases = array(); - } - - /** - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } - - /** - * {@inheritdoc} - */ - public function isFrozen() - { - return true; - } - - /** - * Gets the 'test' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return object A %env(FOO)% instance - */ - protected function getTestService() - { - $class = $this->getEnv('FOO'); - - return $this->services['test'] = new $class($this->getEnv('Bar'), 'foo'.$this->getEnv('FOO').'baz'); - } - - /** - * {@inheritdoc} - */ - public function getParameter($name) - { - $name = strtolower($name); - - if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } - if (isset($this->loadedDynamicParameters[$name])) { - return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - - return $this->parameters[$name]; - } - - /** - * {@inheritdoc} - */ - public function hasParameter($name) - { - $name = strtolower($name); - - return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]); - } - - /** - * {@inheritdoc} - */ - public function setParameter($name, $value) - { - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); - } - - /** - * {@inheritdoc} - */ - public function getParameterBag() - { - if (null === $this->parameterBag) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - $this->parameterBag = new FrozenParameterBag($parameters); - } - - return $this->parameterBag; - } - - private $loadedDynamicParameters = array( - 'bar' => false, - ); - private $dynamicParameters = array(); - - /** - * Computes a dynamic parameter. - * - * @param string The name of the dynamic parameter to load - * - * @return mixed The value of the dynamic parameter - * - * @throws InvalidArgumentException When the dynamic parameter does not exist - */ - private function getDynamicParameter($name) - { - switch ($name) { - case 'bar': $value = $this->getEnv('FOO'); break; - default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); - } - $this->loadedDynamicParameters[$name] = true; - - return $this->dynamicParameters[$name] = $value; - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'env(foo)' => 'foo', - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php deleted file mode 100644 index 252a35d03..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php +++ /dev/null @@ -1,53 +0,0 @@ -getDefaultParameters())); - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'foo' => '%baz%', - 'baz' => 'bar', - 'bar' => 'foo is %%foo bar', - 'escape' => '@escapeme', - 'values' => array( - 0 => true, - 1 => false, - 2 => NULL, - 3 => 0, - 4 => 1000.3, - 5 => 'true', - 6 => 'false', - 7 => 'null', - ), - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9.php deleted file mode 100644 index 306375d1b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9.php +++ /dev/null @@ -1,468 +0,0 @@ -getDefaultParameters())); - $this->methodMap = array( - 'bar' => 'getBarService', - 'baz' => 'getBazService', - 'configurator_service' => 'getConfiguratorServiceService', - 'configurator_service_simple' => 'getConfiguratorServiceSimpleService', - 'configured_service' => 'getConfiguredServiceService', - 'configured_service_simple' => 'getConfiguredServiceSimpleService', - 'decorated' => 'getDecoratedService', - 'decorator_service' => 'getDecoratorServiceService', - 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', - 'deprecated_service' => 'getDeprecatedServiceService', - 'factory_service' => 'getFactoryServiceService', - 'factory_service_simple' => 'getFactoryServiceSimpleService', - 'factory_simple' => 'getFactorySimpleService', - 'foo' => 'getFooService', - 'foo.baz' => 'getFoo_BazService', - 'foo_bar' => 'getFooBarService', - 'foo_with_inline' => 'getFooWithInlineService', - 'inlined' => 'getInlinedService', - 'method_call1' => 'getMethodCall1Service', - 'new_factory' => 'getNewFactoryService', - 'new_factory_service' => 'getNewFactoryServiceService', - 'request' => 'getRequestService', - 'service_from_static_method' => 'getServiceFromStaticMethodService', - ); - $this->privates = array( - 'configurator_service' => true, - 'configurator_service_simple' => true, - 'factory_simple' => true, - 'inlined' => true, - 'new_factory' => true, - ); - $this->aliases = array( - 'alias_for_alias' => 'foo', - 'alias_for_foo' => 'foo', - ); - } - - /** - * Gets the 'bar' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getBarService() - { - $a = $this->get('foo.baz'); - - $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); - - $a->configure($instance); - - return $instance; - } - - /** - * Gets the 'baz' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Baz A Baz instance - */ - protected function getBazService() - { - $this->services['baz'] = $instance = new \Baz(); - - $instance->setFoo($this->get('foo_with_inline')); - - return $instance; - } - - /** - * Gets the 'configured_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getConfiguredServiceService() - { - $this->services['configured_service'] = $instance = new \stdClass(); - - ${($_ = isset($this->services['configurator_service']) ? $this->services['configurator_service'] : $this->getConfiguratorServiceService()) && false ?: '_'}->configureStdClass($instance); - - return $instance; - } - - /** - * Gets the 'configured_service_simple' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getConfiguredServiceSimpleService() - { - $this->services['configured_service_simple'] = $instance = new \stdClass(); - - ${($_ = isset($this->services['configurator_service_simple']) ? $this->services['configurator_service_simple'] : $this->getConfiguratorServiceSimpleService()) && false ?: '_'}->configureStdClass($instance); - - return $instance; - } - - /** - * Gets the 'decorated' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getDecoratedService() - { - return $this->services['decorated'] = new \stdClass(); - } - - /** - * Gets the 'decorator_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getDecoratorServiceService() - { - return $this->services['decorator_service'] = new \stdClass(); - } - - /** - * Gets the 'decorator_service_with_name' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getDecoratorServiceWithNameService() - { - return $this->services['decorator_service_with_name'] = new \stdClass(); - } - - /** - * Gets the 'deprecated_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - * - * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. - */ - protected function getDeprecatedServiceService() - { - @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); - - return $this->services['deprecated_service'] = new \stdClass(); - } - - /** - * Gets the 'factory_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar A Bar instance - */ - protected function getFactoryServiceService() - { - return $this->services['factory_service'] = $this->get('foo.baz')->getInstance(); - } - - /** - * Gets the 'factory_service_simple' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar A Bar instance - */ - protected function getFactoryServiceSimpleService() - { - return $this->services['factory_service_simple'] = ${($_ = isset($this->services['factory_simple']) ? $this->services['factory_simple'] : $this->getFactorySimpleService()) && false ?: '_'}->getInstance(); - } - - /** - * Gets the 'foo' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getFooService() - { - $a = $this->get('foo.baz'); - - $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')), true, $this); - - $instance->foo = 'bar'; - $instance->moo = $a; - $instance->qux = array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')); - $instance->setBar($this->get('bar')); - $instance->initialize(); - sc_configure($instance); - - return $instance; - } - - /** - * Gets the 'foo.baz' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return object A %baz_class% instance - */ - protected function getFoo_BazService() - { - $this->services['foo.baz'] = $instance = call_user_func(array($this->getParameter('baz_class'), 'getInstance')); - - call_user_func(array($this->getParameter('baz_class'), 'configureStatic1'), $instance); - - return $instance; - } - - /** - * Gets the 'foo_bar' service. - * - * @return object A %foo_class% instance - */ - protected function getFooBarService() - { - $class = $this->getParameter('foo_class'); - - return new $class(); - } - - /** - * Gets the 'foo_with_inline' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Foo A Foo instance - */ - protected function getFooWithInlineService() - { - $this->services['foo_with_inline'] = $instance = new \Foo(); - - $instance->setBar(${($_ = isset($this->services['inlined']) ? $this->services['inlined'] : $this->getInlinedService()) && false ?: '_'}); - - return $instance; - } - - /** - * Gets the 'method_call1' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getMethodCall1Service() - { - require_once '%path%foo.php'; - - $this->services['method_call1'] = $instance = new \Bar\FooClass(); - - $instance->setBar($this->get('foo')); - $instance->setBar($this->get('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE)); - if ($this->has('foo3')) { - $instance->setBar($this->get('foo3', ContainerInterface::NULL_ON_INVALID_REFERENCE)); - } - if ($this->has('foobaz')) { - $instance->setBar($this->get('foobaz', ContainerInterface::NULL_ON_INVALID_REFERENCE)); - } - $instance->setBar(($this->get("foo")->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); - - return $instance; - } - - /** - * Gets the 'new_factory_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \FooBarBaz A FooBarBaz instance - */ - protected function getNewFactoryServiceService() - { - $this->services['new_factory_service'] = $instance = ${($_ = isset($this->services['new_factory']) ? $this->services['new_factory'] : $this->getNewFactoryService()) && false ?: '_'}->getInstance(); - - $instance->foo = 'bar'; - - return $instance; - } - - /** - * Gets the 'request' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @throws RuntimeException always since this service is expected to be injected dynamically - */ - protected function getRequestService() - { - throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); - } - - /** - * Gets the 'service_from_static_method' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getServiceFromStaticMethodService() - { - return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); - } - - /** - * Gets the 'configurator_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * This service is private. - * If you want to be able to request this service from the container directly, - * make it public, otherwise you might end up with broken code. - * - * @return \ConfClass A ConfClass instance - */ - protected function getConfiguratorServiceService() - { - $this->services['configurator_service'] = $instance = new \ConfClass(); - - $instance->setFoo($this->get('baz')); - - return $instance; - } - - /** - * Gets the 'configurator_service_simple' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * This service is private. - * If you want to be able to request this service from the container directly, - * make it public, otherwise you might end up with broken code. - * - * @return \ConfClass A ConfClass instance - */ - protected function getConfiguratorServiceSimpleService() - { - return $this->services['configurator_service_simple'] = new \ConfClass('bar'); - } - - /** - * Gets the 'factory_simple' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * This service is private. - * If you want to be able to request this service from the container directly, - * make it public, otherwise you might end up with broken code. - * - * @return \SimpleFactoryClass A SimpleFactoryClass instance - */ - protected function getFactorySimpleService() - { - return $this->services['factory_simple'] = new \SimpleFactoryClass('foo'); - } - - /** - * Gets the 'inlined' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * This service is private. - * If you want to be able to request this service from the container directly, - * make it public, otherwise you might end up with broken code. - * - * @return \Bar A Bar instance - */ - protected function getInlinedService() - { - $this->services['inlined'] = $instance = new \Bar(); - - $instance->pub = 'pub'; - $instance->setBaz($this->get('baz')); - - return $instance; - } - - /** - * Gets the 'new_factory' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * This service is private. - * If you want to be able to request this service from the container directly, - * make it public, otherwise you might end up with broken code. - * - * @return \FactoryClass A FactoryClass instance - */ - protected function getNewFactoryService() - { - $this->services['new_factory'] = $instance = new \FactoryClass(); - - $instance->foo = 'bar'; - - return $instance; - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php deleted file mode 100644 index f8b263c2a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php +++ /dev/null @@ -1,434 +0,0 @@ -parameters = $this->getDefaultParameters(); - - $this->services = array(); - $this->methodMap = array( - 'bar' => 'getBarService', - 'baz' => 'getBazService', - 'configured_service' => 'getConfiguredServiceService', - 'configured_service_simple' => 'getConfiguredServiceSimpleService', - 'decorator_service' => 'getDecoratorServiceService', - 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', - 'deprecated_service' => 'getDeprecatedServiceService', - 'factory_service' => 'getFactoryServiceService', - 'factory_service_simple' => 'getFactoryServiceSimpleService', - 'foo' => 'getFooService', - 'foo.baz' => 'getFoo_BazService', - 'foo_bar' => 'getFooBarService', - 'foo_with_inline' => 'getFooWithInlineService', - 'method_call1' => 'getMethodCall1Service', - 'new_factory_service' => 'getNewFactoryServiceService', - 'request' => 'getRequestService', - 'service_from_static_method' => 'getServiceFromStaticMethodService', - ); - $this->aliases = array( - 'alias_for_alias' => 'foo', - 'alias_for_foo' => 'foo', - 'decorated' => 'decorator_service_with_name', - ); - } - - /** - * {@inheritdoc} - */ - public function compile() - { - throw new LogicException('You cannot compile a dumped frozen container.'); - } - - /** - * {@inheritdoc} - */ - public function isFrozen() - { - return true; - } - - /** - * Gets the 'bar' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getBarService() - { - $a = $this->get('foo.baz'); - - $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); - - $a->configure($instance); - - return $instance; - } - - /** - * Gets the 'baz' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Baz A Baz instance - */ - protected function getBazService() - { - $this->services['baz'] = $instance = new \Baz(); - - $instance->setFoo($this->get('foo_with_inline')); - - return $instance; - } - - /** - * Gets the 'configured_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getConfiguredServiceService() - { - $a = new \ConfClass(); - $a->setFoo($this->get('baz')); - - $this->services['configured_service'] = $instance = new \stdClass(); - - $a->configureStdClass($instance); - - return $instance; - } - - /** - * Gets the 'configured_service_simple' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getConfiguredServiceSimpleService() - { - $this->services['configured_service_simple'] = $instance = new \stdClass(); - - (new \ConfClass('bar'))->configureStdClass($instance); - - return $instance; - } - - /** - * Gets the 'decorator_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getDecoratorServiceService() - { - return $this->services['decorator_service'] = new \stdClass(); - } - - /** - * Gets the 'decorator_service_with_name' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - */ - protected function getDecoratorServiceWithNameService() - { - return $this->services['decorator_service_with_name'] = new \stdClass(); - } - - /** - * Gets the 'deprecated_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \stdClass A stdClass instance - * - * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. - */ - protected function getDeprecatedServiceService() - { - @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); - - return $this->services['deprecated_service'] = new \stdClass(); - } - - /** - * Gets the 'factory_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar A Bar instance - */ - protected function getFactoryServiceService() - { - return $this->services['factory_service'] = $this->get('foo.baz')->getInstance(); - } - - /** - * Gets the 'factory_service_simple' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar A Bar instance - */ - protected function getFactoryServiceSimpleService() - { - return $this->services['factory_service_simple'] = (new \SimpleFactoryClass('foo'))->getInstance(); - } - - /** - * Gets the 'foo' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getFooService() - { - $a = $this->get('foo.baz'); - - $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); - - $instance->foo = 'bar'; - $instance->moo = $a; - $instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); - $instance->setBar($this->get('bar')); - $instance->initialize(); - sc_configure($instance); - - return $instance; - } - - /** - * Gets the 'foo.baz' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \BazClass A BazClass instance - */ - protected function getFoo_BazService() - { - $this->services['foo.baz'] = $instance = \BazClass::getInstance(); - - \BazClass::configureStatic1($instance); - - return $instance; - } - - /** - * Gets the 'foo_bar' service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getFooBarService() - { - return new \Bar\FooClass(); - } - - /** - * Gets the 'foo_with_inline' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Foo A Foo instance - */ - protected function getFooWithInlineService() - { - $a = new \Bar(); - - $this->services['foo_with_inline'] = $instance = new \Foo(); - - $a->pub = 'pub'; - $a->setBaz($this->get('baz')); - - $instance->setBar($a); - - return $instance; - } - - /** - * Gets the 'method_call1' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getMethodCall1Service() - { - require_once '%path%foo.php'; - - $this->services['method_call1'] = $instance = new \Bar\FooClass(); - - $instance->setBar($this->get('foo')); - $instance->setBar(NULL); - $instance->setBar(($this->get("foo")->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); - - return $instance; - } - - /** - * Gets the 'new_factory_service' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \FooBarBaz A FooBarBaz instance - */ - protected function getNewFactoryServiceService() - { - $a = new \FactoryClass(); - $a->foo = 'bar'; - - $this->services['new_factory_service'] = $instance = $a->getInstance(); - - $instance->foo = 'bar'; - - return $instance; - } - - /** - * Gets the 'request' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @throws RuntimeException always since this service is expected to be injected dynamically - */ - protected function getRequestService() - { - throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); - } - - /** - * Gets the 'service_from_static_method' service. - * - * This service is shared. - * This method always returns the same instance of the service. - * - * @return \Bar\FooClass A Bar\FooClass instance - */ - protected function getServiceFromStaticMethodService() - { - return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); - } - - /** - * {@inheritdoc} - */ - public function getParameter($name) - { - $name = strtolower($name); - - if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]))) { - throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); - } - if (isset($this->loadedDynamicParameters[$name])) { - return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - - return $this->parameters[$name]; - } - - /** - * {@inheritdoc} - */ - public function hasParameter($name) - { - $name = strtolower($name); - - return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters) || isset($this->loadedDynamicParameters[$name]); - } - - /** - * {@inheritdoc} - */ - public function setParameter($name, $value) - { - throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); - } - - /** - * {@inheritdoc} - */ - public function getParameterBag() - { - if (null === $this->parameterBag) { - $parameters = $this->parameters; - foreach ($this->loadedDynamicParameters as $name => $loaded) { - $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); - } - $this->parameterBag = new FrozenParameterBag($parameters); - } - - return $this->parameterBag; - } - - private $loadedDynamicParameters = array(); - private $dynamicParameters = array(); - - /** - * Computes a dynamic parameter. - * - * @param string The name of the dynamic parameter to load - * - * @return mixed The value of the dynamic parameter - * - * @throws InvalidArgumentException When the dynamic parameter does not exist - */ - private function getDynamicParameter($name) - { - throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); - } - - /** - * Gets the default parameters. - * - * @return array An array of the default parameters - */ - protected function getDefaultParameters() - { - return array( - 'baz_class' => 'BazClass', - 'foo_class' => 'Bar\\FooClass', - 'foo' => 'bar', - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php deleted file mode 100644 index aa4df998c..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php +++ /dev/null @@ -1,3 +0,0 @@ -setParameter('foo', 'foo'); diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml deleted file mode 100644 index 52df38d0c..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml deleted file mode 100644 index 21a7ef58c..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml deleted file mode 100644 index 792fa0705..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - BAR - - - - - - - - - %project.parameter.foo% - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml deleted file mode 100644 index 67d462be9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - BAR - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml deleted file mode 100644 index c23f02a08..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - BAR - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml deleted file mode 100644 index 2c33c3a7d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml deleted file mode 100644 index 0eaaff2d2..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml deleted file mode 100644 index a9c01030d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml deleted file mode 100644 index e77780db6..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/legacy_invalid_alias_definition.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/legacy_invalid_alias_definition.xml deleted file mode 100644 index 52386e5bf..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/legacy_invalid_alias_definition.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml deleted file mode 100644 index 5a05cedd7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - foo - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml deleted file mode 100644 index e7b5bc973..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml deleted file mode 100644 index 6aa5732f9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml deleted file mode 100644 index a4da1bf74..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml deleted file mode 100644 index 1ac493884..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml deleted file mode 100644 index 73446214e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - app - - - - - - app - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml deleted file mode 100644 index f1ac14dd5..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - a string - bar - - 0 - 4 - null - true - true - false - on - off - 1.3 - 1000.3 - a string - - foo - bar - - - - value - - PHP_EOL - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml deleted file mode 100644 index 329bc3d72..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services22.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services22.xml deleted file mode 100644 index fa79d3894..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services22.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - Bar - Baz - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml deleted file mode 100644 index 3f9e15fd4..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml deleted file mode 100644 index 476588aa4..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - A - B - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml deleted file mode 100644 index 87bf183db..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - foo - - true - false - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml deleted file mode 100644 index 03ad9f808..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml deleted file mode 100644 index 0b7f10a30..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml deleted file mode 100644 index 347df977d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml deleted file mode 100644 index 70981ff54..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - %path%/foo.php - - - foo - - - true - false - - - - - - - - - - - - - - - service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default") - - - - - foo - - - true - false - - - - - - - - - - - - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml deleted file mode 100644 index 824d8b5d7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml deleted file mode 100644 index b17e50043..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - %baz% - bar - foo is %%foo bar - @escapeme - - true - false - null - 0 - 1000.3 - true - false - null - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml deleted file mode 100644 index 9ad09ad59..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - BazClass - Bar\FooClass - bar - - - - - - foo - - - foo is %foo% - %foo% - - true - - bar - - - foo is %foo% - %foo% - - - - - - - - - - - - - - foo - - %foo_bar% - - - - - %path%foo.php - - - - - - - - - - - - - - service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default") - - - - - - - - - pub - - - - - - - - - - - - - - - - - - - - bar - - - - - - - - - The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. - - - bar - - - - - - bar - - - - - - - foo - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml deleted file mode 100644 index c19a47adf..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - The "%service_id%" service is deprecated. - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml deleted file mode 100644 index 164629246..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml deleted file mode 100644 index bc7373df7..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml deleted file mode 100644 index 5f3a28436..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - foo - bar - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml deleted file mode 100644 index f217d5bca..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml deleted file mode 100644 index 3f34b07eb..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml +++ /dev/null @@ -1,4 +0,0 @@ -services: - method_call1: - class: FooClass - calls: foo diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml deleted file mode 100644 index 79c048a84..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml +++ /dev/null @@ -1,7 +0,0 @@ -services: - foo: - class: stdClass - bar: - class: stdClass - decorates: "@foo" - arguments: ["@bar.inner"] diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml deleted file mode 100644 index 1f58cac6a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml +++ /dev/null @@ -1,2 +0,0 @@ -parameters: - FOO: bar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml deleted file mode 100644 index 0765dc8dd..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - foo.yml diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml deleted file mode 100644 index 1ce9d57c6..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - foo:bar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml deleted file mode 100644 index bbd13ac0b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml +++ /dev/null @@ -1,2 +0,0 @@ -parameters: - foo:bar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml deleted file mode 100644 index 811af3c0e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml +++ /dev/null @@ -1,2 +0,0 @@ -services: - foo: bar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml deleted file mode 100644 index cfbf17ce5..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml +++ /dev/null @@ -1 +0,0 @@ -services: foo diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_types1.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_types1.yml deleted file mode 100644 index 891e01497..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_types1.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - foo_service: - class: FooClass - # types is not an array - autowiring_types: 1 diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_types2.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_types2.yml deleted file mode 100644 index fb1d53e15..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_types2.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - foo_service: - class: FooClass - # autowiring_types is not a string - autowiring_types: [ 1 ] diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml deleted file mode 100644 index 14536fdb3..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - foo_service: - class: FooClass - # tags is not an array - tags: string diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml deleted file mode 100644 index 90288144c..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - foo_service: - class: FooClass - tags: - # tag is missing the name key - foo_tag: { foo: bar } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml deleted file mode 100644 index 72ec4e8f0..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - foo_service: - class: FooClass - tags: - # tag-attribute is not a scalar - - { name: foo, bar: { foo: foo, bar: bar } } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag4.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag4.yml deleted file mode 100644 index e8e99395b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag4.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - foo_service: - class: FooClass - tags: - # tag is not an array - - foo diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml deleted file mode 100644 index 00c011c1d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - foo: - alias: bar - factory: foo - parent: quz diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_definition.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_definition.yml deleted file mode 100644 index 8487e854d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_definition.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - # This definition is valid and should not raise any deprecation notice - foo: - class: stdClass - arguments: [ 'foo', 'bar' ] - - # This definition is invalid and must raise a deprecation notice - bar: - class: stdClass - private: true # the "private" keyword is invalid diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml deleted file mode 100644 index 4eddb872b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml +++ /dev/null @@ -1,2 +0,0 @@ -foo: - bar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml deleted file mode 100644 index c508d5366..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml +++ /dev/null @@ -1 +0,0 @@ -false diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml deleted file mode 100644 index 8b1378917..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml deleted file mode 100644 index c66084cdb..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml +++ /dev/null @@ -1,9 +0,0 @@ -parameters: - project.parameter.foo: BAR - -services: - project.service.foo: - class: BAR - -project: - test: '%project.parameter.foo%' diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml deleted file mode 100644 index 40126f005..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml +++ /dev/null @@ -1 +0,0 @@ -foobarfoobar: {} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml deleted file mode 100644 index d52d355c2..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml +++ /dev/null @@ -1,3 +0,0 @@ -# used to test imports in XML -parameters: - imported_from_yaml: true diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml deleted file mode 100644 index 4c188c5fb..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - factory: { class: FooBarClass, factory: baz:getClass} - factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml deleted file mode 100644 index 91de818f2..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml +++ /dev/null @@ -1,13 +0,0 @@ -parameters: - FOO: bar - values: - - true - - false - - 0 - - 1000.3 - - !php/const:PHP_INT_MAX - bar: foo - escape: '@@escapeme' - foo_bar: '@foo_bar' - MixedCase: - MixedCaseKey: value diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml deleted file mode 100644 index a2617c168..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml +++ /dev/null @@ -1,15 +0,0 @@ -services: - manager: - class: UserManager - arguments: - - true - calls: - - method: setLogger - arguments: - - '@logger' - - method: setClass - arguments: - - User - tags: - - name: manager - alias: user diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services22.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services22.yml deleted file mode 100644 index 55d015bae..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services22.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - foo_service: - class: FooClass - autowiring_types: [ Foo, Bar ] - - baz_service: - class: Baz - autowiring_types: Foo diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml deleted file mode 100644 index 1984c1771..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml +++ /dev/null @@ -1,4 +0,0 @@ -services: - bar_service: - class: BarClass - autowire: true diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml deleted file mode 100644 index 1894077e4..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml +++ /dev/null @@ -1,8 +0,0 @@ - -services: - foo: - class: Foo - autowire: true - autowiring_types: - - A - - B diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml deleted file mode 100644 index 2ef23c1af..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml +++ /dev/null @@ -1,10 +0,0 @@ -parameters: - env(FOO): foo - bar: '%env(FOO)%' - -services: - test: - class: '%env(FOO)%' - arguments: - - '%env(Bar)%' - - 'foo%bar%baz' diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml deleted file mode 100644 index 0e92cdf50..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml +++ /dev/null @@ -1,5 +0,0 @@ -parameters: - foo: foo - values: - - true - - false diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml deleted file mode 100644 index 8e0987fd0..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml +++ /dev/null @@ -1,7 +0,0 @@ -imports: - - { resource: services2.yml } - - { resource: services3.yml } - - { resource: "../php/simple.php" } - - { resource: "../ini/parameters.ini", class: Symfony\Component\DependencyInjection\Loader\IniFileLoader } - - { resource: "../ini/parameters2.ini" } - - { resource: "../xml/services13.xml" } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml deleted file mode 100644 index f7089fc4d..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - { resource: foo_fake.yml, ignore_errors: true } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml deleted file mode 100644 index dbd7b88f6..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml +++ /dev/null @@ -1,39 +0,0 @@ -services: - foo: { class: FooClass } - baz: { class: BazClass } - not_shared: { class: FooClass, shared: false } - file: { class: FooClass, file: '%path%/foo.php' } - arguments: { class: FooClass, arguments: [foo, '@foo', [true, false]] } - configurator1: { class: FooClass, configurator: sc_configure } - configurator2: { class: FooClass, configurator: ['@baz', configure] } - configurator3: { class: FooClass, configurator: [BazClass, configureStatic] } - method_call1: - class: FooClass - calls: - - [ setBar, [] ] - - [ setBar ] - - [ setBar, ['@=service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'] ] - method_call2: - class: FooClass - calls: - - [ setBar, [ foo, '@foo', [true, false] ] ] - alias_for_foo: '@foo' - another_alias_for_foo: - alias: foo - public: false - request: - class: Request - synthetic: true - lazy: true - decorator_service: - decorates: decorated - decorator_service_with_name: - decorates: decorated - decoration_inner_name: decorated.pif-pouf - decorator_service_with_name_and_priority: - decorates: decorated - decoration_inner_name: decorated.pif-pouf - decoration_priority: 5 - new_factory1: { class: FooBarClass, factory: factory} - new_factory2: { class: FooBarClass, factory: ['@baz', getClass]} - new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml deleted file mode 100644 index 09064f200..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml +++ /dev/null @@ -1,2 +0,0 @@ -services: - foo: { class: BarClass } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml deleted file mode 100644 index a1fb59035..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml +++ /dev/null @@ -1,7 +0,0 @@ -parameters: - foo: '%baz%' - baz: bar - bar: 'foo is %%foo bar' - escape: '@@escapeme' - values: [true, false, null, 0, 1000.3, 'true', 'false', 'null'] - diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml deleted file mode 100644 index 44a174b6e..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml +++ /dev/null @@ -1,111 +0,0 @@ -parameters: - baz_class: BazClass - foo_class: Bar\FooClass - foo: bar - -services: - foo: - class: Bar\FooClass - tags: - - { name: foo, foo: foo } - - { name: foo, bar: bar, baz: baz } - arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container'] - properties: { foo: bar, moo: '@foo.baz', qux: { '%foo%': 'foo is %foo%', foobar: '%foo%' } } - calls: - - [setBar, ['@bar']] - - [initialize, { }] - - factory: [Bar\FooClass, getInstance] - configurator: sc_configure - foo.baz: - class: '%baz_class%' - factory: ['%baz_class%', getInstance] - configurator: ['%baz_class%', configureStatic1] - bar: - class: Bar\FooClass - arguments: [foo, '@foo.baz', '%foo_bar%'] - configurator: ['@foo.baz', configure] - foo_bar: - class: '%foo_class%' - shared: false - method_call1: - class: Bar\FooClass - file: '%path%foo.php' - calls: - - [setBar, ['@foo']] - - [setBar, ['@?foo2']] - - [setBar, ['@?foo3']] - - [setBar, ['@?foobaz']] - - [setBar, ['@=service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")']] - - foo_with_inline: - class: Foo - calls: - - [setBar, ['@inlined']] - - inlined: - class: Bar - public: false - properties: { pub: pub } - calls: - - [setBaz, ['@baz']] - - baz: - class: Baz - calls: - - [setFoo, ['@foo_with_inline']] - - request: - class: Request - synthetic: true - configurator_service: - class: ConfClass - public: false - calls: - - [setFoo, ['@baz']] - - configured_service: - class: stdClass - configurator: ['@configurator_service', configureStdClass] - configurator_service_simple: - class: ConfClass - public: false - arguments: ['bar'] - configured_service_simple: - class: stdClass - configurator: ['@configurator_service_simple', configureStdClass] - decorated: - class: stdClass - decorator_service: - class: stdClass - decorates: decorated - decorator_service_with_name: - class: stdClass - decorates: decorated - decoration_inner_name: decorated.pif-pouf - deprecated_service: - class: stdClass - deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. - new_factory: - class: FactoryClass - public: false - properties: { foo: bar } - factory_service: - class: Bar - factory: ['@foo.baz', getInstance] - new_factory_service: - class: FooBarBaz - properties: { foo: bar } - factory: ['@new_factory', getInstance] - service_from_static_method: - class: Bar\FooClass - factory: [Bar\FooClass, getInstance] - factory_simple: - class: SimpleFactoryClass - public: false - arguments: ['foo'] - factory_service_simple: - class: Bar - factory: ['@factory_simple', getInstance] - alias_for_foo: '@foo' - alias_for_alias: '@foo' diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml deleted file mode 100644 index 68e8137ec..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml +++ /dev/null @@ -1,9 +0,0 @@ -services: - - foo_bar: - class: FooBarClass - configurator: foo_bar_configurator:configure - - foo_bar_with_static_call: - class: FooBarClass - configurator: FooBarConfigurator::configureFooBar diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml deleted file mode 100644 index 0ea5f53da..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - foo_service: - class: FooClass - tags: - # tag name is an empty string - - { name: '', foo: bar } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml b/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml deleted file mode 100644 index f24cafd22..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - foo_service: - class: FooClass - tags: - # tag name is not a string - - { name: [], foo: bar } diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php deleted file mode 100644 index 6dcf25d2b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\LazyProxy\Instantiator; - -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; - -/** - * Tests for {@see \Symfony\Component\DependencyInjection\Instantiator\RealServiceInstantiator}. - * - * @author Marco Pivetta - */ -class RealServiceInstantiatorTest extends \PHPUnit_Framework_TestCase -{ - public function testInstantiateProxy() - { - $instantiator = new RealServiceInstantiator(); - $instance = new \stdClass(); - $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $callback = function () use ($instance) { - return $instance; - }; - - $this->assertSame($instance, $instantiator->instantiateProxy($container, new Definition(), 'foo', $callback)); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.php deleted file mode 100644 index 1fcedca5f..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\LazyProxy\PhpDumper; - -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; - -/** - * Tests for {@see \Symfony\Component\DependencyInjection\PhpDumper\NullDumper}. - * - * @author Marco Pivetta - */ -class NullDumperTest extends \PHPUnit_Framework_TestCase -{ - public function testNullDumper() - { - $dumper = new NullDumper(); - $definition = new Definition('stdClass'); - - $this->assertFalse($dumper->isProxyCandidate($definition)); - $this->assertSame('', $dumper->getProxyFactoryCode($definition, 'foo')); - $this->assertSame('', $dumper->getProxyCode($definition)); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.php deleted file mode 100644 index be057313a..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Loader; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\ClosureLoader; - -class ClosureLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testSupports() - { - $loader = new ClosureLoader(new ContainerBuilder()); - - $this->assertTrue($loader->supports(function ($container) {}), '->supports() returns true if the resource is loadable'); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); - } - - public function testLoad() - { - $loader = new ClosureLoader($container = new ContainerBuilder()); - - $loader->load(function ($container) { - $container->setParameter('foo', 'foo'); - }); - - $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a \Closure resource'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php deleted file mode 100644 index 104976166..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Loader; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\Config\FileLocator; - -class DirectoryLoaderTest extends \PHPUnit_Framework_TestCase -{ - private static $fixturesPath; - - private $container; - private $loader; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); - } - - protected function setUp() - { - $locator = new FileLocator(self::$fixturesPath); - $this->container = new ContainerBuilder(); - $this->loader = new DirectoryLoader($this->container, $locator); - $resolver = new LoaderResolver(array( - new PhpFileLoader($this->container, $locator), - new IniFileLoader($this->container, $locator), - new YamlFileLoader($this->container, $locator), - $this->loader, - )); - $this->loader->setResolver($resolver); - } - - public function testDirectoryCanBeLoadedRecursively() - { - $this->loader->load('directory/'); - $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml', 'php' => 'php'), $this->container->getParameterBag()->all(), '->load() takes a single directory'); - } - - public function testImports() - { - $this->loader->resolve('directory/import/import.yml')->load('directory/import/import.yml'); - $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml'), $this->container->getParameterBag()->all(), '->load() takes a single file that imports a directory'); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The file "foo" does not exist (in: - */ - public function testExceptionIsRaisedWhenDirectoryDoesNotExist() - { - $this->loader->load('foo/'); - } - - public function testSupports() - { - $loader = new DirectoryLoader(new ContainerBuilder(), new FileLocator()); - - $this->assertTrue($loader->supports('directory/'), '->supports("directory/") returns true'); - $this->assertTrue($loader->supports('directory/', 'directory'), '->supports("directory/", "directory") returns true'); - $this->assertFalse($loader->supports('directory'), '->supports("directory") returns false'); - $this->assertTrue($loader->supports('directory', 'directory'), '->supports("directory", "directory") returns true'); - $this->assertFalse($loader->supports('directory', 'foo'), '->supports("directory", "foo") returns false'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.php deleted file mode 100644 index 5dc3c6dee..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.php +++ /dev/null @@ -1,131 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Loader; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\Config\FileLocator; - -class IniFileLoaderTest extends \PHPUnit_Framework_TestCase -{ - protected $container; - protected $loader; - - protected function setUp() - { - $this->container = new ContainerBuilder(); - $this->loader = new IniFileLoader($this->container, new FileLocator(realpath(__DIR__.'/../Fixtures/').'/ini')); - } - - public function testIniFileCanBeLoaded() - { - $this->loader->load('parameters.ini'); - $this->assertEquals(array('foo' => 'bar', 'bar' => '%foo%'), $this->container->getParameterBag()->all(), '->load() takes a single file name as its first argument'); - } - - /** - * @dataProvider getTypeConversions - */ - public function testTypeConversions($key, $value, $supported) - { - $this->loader->load('types.ini'); - $parameters = $this->container->getParameterBag()->all(); - $this->assertSame($value, $parameters[$key], '->load() converts values to PHP types'); - } - - /** - * @dataProvider getTypeConversions - * @requires PHP 5.6.1 - * This test illustrates where our conversions differs from INI_SCANNER_TYPED introduced in PHP 5.6.1 - */ - public function testTypeConversionsWithNativePhp($key, $value, $supported) - { - if (defined('HHVM_VERSION_ID')) { - return $this->markTestSkipped(); - } - - if (!$supported) { - return; - } - - $this->loader->load('types.ini'); - $expected = parse_ini_file(__DIR__.'/../Fixtures/ini/types.ini', true, INI_SCANNER_TYPED); - $this->assertSame($value, $expected['parameters'][$key], '->load() converts values to PHP types'); - } - - public function getTypeConversions() - { - return array( - array('true_comment', true, true), - array('true', true, true), - array('false', false, true), - array('on', true, true), - array('off', false, true), - array('yes', true, true), - array('no', false, true), - array('none', false, true), - array('null', null, true), - array('constant', PHP_VERSION, true), - array('12', 12, true), - array('12_string', '12', true), - array('12_comment', 12, true), - array('12_string_comment', '12', true), - array('12_string_comment_again', '12', true), - array('-12', -12, true), - array('1', 1, true), - array('0', 0, true), - array('0b0110', bindec('0b0110'), false), // not supported by INI_SCANNER_TYPED - array('11112222333344445555', '1111,2222,3333,4444,5555', true), - array('0777', 0777, false), // not supported by INI_SCANNER_TYPED - array('255', 0xFF, false), // not supported by INI_SCANNER_TYPED - array('100.0', 1e2, false), // not supported by INI_SCANNER_TYPED - array('-120.0', -1.2E2, false), // not supported by INI_SCANNER_TYPED - array('-10100.1', -10100.1, false), // not supported by INI_SCANNER_TYPED - array('-10,100.1', '-10,100.1', true), - ); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The file "foo.ini" does not exist (in: - */ - public function testExceptionIsRaisedWhenIniFileDoesNotExist() - { - $this->loader->load('foo.ini'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage The "nonvalid.ini" file is not valid. - */ - public function testExceptionIsRaisedWhenIniFileCannotBeParsed() - { - @$this->loader->load('nonvalid.ini'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage The "almostvalid.ini" file is not valid. - */ - public function testExceptionIsRaisedWhenIniFileIsAlmostValid() - { - @$this->loader->load('almostvalid.ini'); - } - - public function testSupports() - { - $loader = new IniFileLoader(new ContainerBuilder(), new FileLocator()); - - $this->assertTrue($loader->supports('foo.ini'), '->supports() returns true if the resource is loadable'); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.php deleted file mode 100644 index 00b957b73..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Loader; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\Config\FileLocator; - -class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase -{ - public function testSupports() - { - $loader = new PhpFileLoader(new ContainerBuilder(), new FileLocator()); - - $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable'); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); - } - - public function testLoad() - { - $loader = new PhpFileLoader($container = new ContainerBuilder(), new FileLocator()); - - $loader->load(__DIR__.'/../Fixtures/php/simple.php'); - - $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a PHP file resource'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php deleted file mode 100644 index e94f94081..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php +++ /dev/null @@ -1,583 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Loader; - -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\ExpressionLanguage\Expression; - -class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); - require_once self::$fixturesPath.'/includes/foo.php'; - require_once self::$fixturesPath.'/includes/ProjectExtension.php'; - require_once self::$fixturesPath.'/includes/ProjectWithXsdExtension.php'; - } - - public function testLoad() - { - $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); - - try { - $loader->load('foo.xml'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not exist'); - $this->assertStringStartsWith('The file "foo.xml" does not exist (in:', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); - } - } - - public function testParseFile() - { - $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); - $r = new \ReflectionObject($loader); - $m = $r->getMethod('parseFileToDOM'); - $m->setAccessible(true); - - try { - $m->invoke($loader, self::$fixturesPath.'/ini/parameters.ini'); - $this->fail('->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); - $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'parameters.ini'), $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); - - $e = $e->getPrevious(); - $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); - $this->assertStringStartsWith('[ERROR 4] Start tag expected, \'<\' not found (in', $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); - } - - $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/xml')); - - try { - $m->invoke($loader, self::$fixturesPath.'/xml/nonvalid.xml'); - $this->fail('->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); - $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'nonvalid.xml'), $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); - - $e = $e->getPrevious(); - $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); - $this->assertStringStartsWith('[ERROR 1845] Element \'nonvalid\': No matching global declaration available for the validation root. (in', $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); - } - - $xml = $m->invoke($loader, self::$fixturesPath.'/xml/services1.xml'); - $this->assertInstanceOf('DOMDocument', $xml, '->parseFileToDOM() returns an SimpleXMLElement object'); - } - - public function testLoadWithExternalEntitiesDisabled() - { - $disableEntities = libxml_disable_entity_loader(true); - - $containerBuilder = new ContainerBuilder(); - $loader = new XmlFileLoader($containerBuilder, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services2.xml'); - - libxml_disable_entity_loader($disableEntities); - - $this->assertTrue(count($containerBuilder->getParameterBag()->all()) > 0, 'Parameters can be read from the config file.'); - } - - public function testLoadParameters() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services2.xml'); - - $actual = $container->getParameterBag()->all(); - $expected = array( - 'a string', - 'foo' => 'bar', - 'values' => array( - 0, - 'integer' => 4, - 100 => null, - 'true', - true, - false, - 'on', - 'off', - 'float' => 1.3, - 1000.3, - 'a string', - array('foo', 'bar'), - ), - 'mixedcase' => array('MixedCaseKey' => 'value'), - 'constant' => PHP_EOL, - ); - - $this->assertEquals($expected, $actual, '->load() converts XML values to PHP ones'); - } - - public function testLoadImports() - { - $container = new ContainerBuilder(); - $resolver = new LoaderResolver(array( - new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), - new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yml')), - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), - )); - $loader->setResolver($resolver); - $loader->load('services4.xml'); - - $actual = $container->getParameterBag()->all(); - $expected = array( - 'a string', - 'foo' => 'bar', - 'values' => array( - 0, - 'integer' => 4, - 100 => null, - 'true', - true, - false, - 'on', - 'off', - 'float' => 1.3, - 1000.3, - 'a string', - array('foo', 'bar'), - ), - 'mixedcase' => array('MixedCaseKey' => 'value'), - 'constant' => PHP_EOL, - 'bar' => '%foo%', - 'imported_from_ini' => true, - 'imported_from_yaml' => true, - ); - - $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); - $this->assertTrue($actual['imported_from_ini']); - - // Bad import throws no exception due to ignore_errors value. - $loader->load('services4_bad_import.xml'); - } - - public function testLoadAnonymousServices() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services5.xml'); - $services = $container->getDefinitions(); - $this->assertCount(6, $services, '->load() attributes unique ids to anonymous services'); - - // anonymous service as an argument - $args = $services['foo']->getArguments(); - $this->assertCount(1, $args, '->load() references anonymous services as "normal" ones'); - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $args[0], '->load() converts anonymous services to references to "normal" services'); - $this->assertTrue(isset($services[(string) $args[0]]), '->load() makes a reference to the created ones'); - $inner = $services[(string) $args[0]]; - $this->assertEquals('BarClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $this->assertFalse($inner->isPublic()); - - // inner anonymous services - $args = $inner->getArguments(); - $this->assertCount(1, $args, '->load() references anonymous services as "normal" ones'); - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $args[0], '->load() converts anonymous services to references to "normal" services'); - $this->assertTrue(isset($services[(string) $args[0]]), '->load() makes a reference to the created ones'); - $inner = $services[(string) $args[0]]; - $this->assertEquals('BazClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $this->assertFalse($inner->isPublic()); - - // anonymous service as a property - $properties = $services['foo']->getProperties(); - $property = $properties['p']; - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $property, '->load() converts anonymous services to references to "normal" services'); - $this->assertTrue(isset($services[(string) $property]), '->load() makes a reference to the created ones'); - $inner = $services[(string) $property]; - $this->assertEquals('BuzClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $this->assertFalse($inner->isPublic()); - - // "wild" service - $service = $container->findTaggedServiceIds('biz_tag'); - $this->assertCount(1, $service); - - foreach ($service as $id => $tag) { - $service = $container->getDefinition($id); - } - $this->assertEquals('BizClass', $service->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $this->assertTrue($service->isPublic()); - - // anonymous services are shared when using decoration definitions - $container->compile(); - $services = $container->getDefinitions(); - $fooArgs = $services['foo']->getArguments(); - $barArgs = $services['bar']->getArguments(); - $this->assertSame($fooArgs[0], $barArgs[0]); - } - - public function testLoadServices() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services6.xml'); - $services = $container->getDefinitions(); - $this->assertTrue(isset($services['foo']), '->load() parses elements'); - $this->assertFalse($services['not_shared']->isShared(), '->load() parses shared flag'); - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts element to Definition instances'); - $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); - $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); - $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); - $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array(new Reference('baz', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array(array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); - $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); - $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); - $this->assertEquals(array(new Reference('baz', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); - $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag'); - - $aliases = $container->getAliases(); - $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses elements'); - $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); - $this->assertTrue($aliases['alias_for_foo']->isPublic()); - $this->assertTrue(isset($aliases['another_alias_for_foo'])); - $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); - $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); - - $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); - } - - public function testParsesTags() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services10.xml'); - - $services = $container->findTaggedServiceIds('foo_tag'); - $this->assertCount(1, $services); - - foreach ($services as $id => $tagAttributes) { - foreach ($tagAttributes as $attributes) { - $this->assertArrayHasKey('other_option', $attributes); - $this->assertEquals('lorem', $attributes['other_option']); - $this->assertArrayHasKey('other-option', $attributes, 'unnormalized tag attributes should not be removed'); - - $this->assertEquals('ciz', $attributes['some_option'], 'no overriding should be done when normalizing'); - $this->assertEquals('cat', $attributes['some-option']); - - $this->assertArrayNotHasKey('an_other_option', $attributes, 'normalization should not be done when an underscore is already found'); - } - } - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testParseTagsWithoutNameThrowsException() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('tag_without_name.xml'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessageRegExp /The tag name for service ".+" in .* must be a non-empty string/ - */ - public function testParseTagWithEmptyNameThrowsException() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('tag_with_empty_name.xml'); - } - - public function testDeprecated() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services_deprecated.xml'); - - $this->assertTrue($container->getDefinition('foo')->isDeprecated()); - $message = 'The "foo" service is deprecated. You should stop using it, as it will soon be removed.'; - $this->assertSame($message, $container->getDefinition('foo')->getDeprecationMessage('foo')); - - $this->assertTrue($container->getDefinition('bar')->isDeprecated()); - $message = 'The "bar" service is deprecated.'; - $this->assertSame($message, $container->getDefinition('bar')->getDeprecationMessage('bar')); - } - - public function testConvertDomElementToArray() - { - $doc = new \DOMDocument('1.0'); - $doc->loadXML('bar'); - $this->assertEquals('bar', XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - - $doc = new \DOMDocument('1.0'); - $doc->loadXML(''); - $this->assertEquals(array('foo' => 'bar'), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - - $doc = new \DOMDocument('1.0'); - $doc->loadXML('bar'); - $this->assertEquals(array('foo' => 'bar'), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - - $doc = new \DOMDocument('1.0'); - $doc->loadXML('barbar'); - $this->assertEquals(array('foo' => array('value' => 'bar', 'foo' => 'bar')), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - - $doc = new \DOMDocument('1.0'); - $doc->loadXML(''); - $this->assertEquals(array('foo' => null), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - - $doc = new \DOMDocument('1.0'); - $doc->loadXML(''); - $this->assertEquals(array('foo' => null), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - - $doc = new \DOMDocument('1.0'); - $doc->loadXML(''); - $this->assertEquals(array('foo' => array(array('foo' => 'bar'), array('foo' => 'bar'))), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); - } - - public function testExtensions() - { - $container = new ContainerBuilder(); - $container->registerExtension(new \ProjectExtension()); - $container->registerExtension(new \ProjectWithXsdExtension()); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - - // extension without an XSD - $loader->load('extensions/services1.xml'); - $container->compile(); - $services = $container->getDefinitions(); - $parameters = $container->getParameterBag()->all(); - - $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements'); - $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements'); - - $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); - $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); - - // extension with an XSD - $container = new ContainerBuilder(); - $container->registerExtension(new \ProjectExtension()); - $container->registerExtension(new \ProjectWithXsdExtension()); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('extensions/services2.xml'); - $container->compile(); - $services = $container->getDefinitions(); - $parameters = $container->getParameterBag()->all(); - - $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements'); - $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements'); - - $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); - $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); - - $container = new ContainerBuilder(); - $container->registerExtension(new \ProjectExtension()); - $container->registerExtension(new \ProjectWithXsdExtension()); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - - // extension with an XSD (does not validate) - try { - $loader->load('extensions/services3.xml'); - $this->fail('->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'services3.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - - $e = $e->getPrevious(); - $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - $this->assertContains('The attribute \'bar\' is not allowed', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - } - - // non-registered extension - try { - $loader->load('extensions/services4.xml'); - $this->fail('->load() throws an InvalidArgumentException if the tag is not valid'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid'); - $this->assertStringStartsWith('There is no extension able to load the configuration for "project:bar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid'); - } - } - - public function testExtensionInPhar() - { - if (extension_loaded('suhosin') && false === strpos(ini_get('suhosin.executor.include.whitelist'), 'phar')) { - $this->markTestSkipped('To run this test, add "phar" to the "suhosin.executor.include.whitelist" settings in your php.ini file.'); - } - - require_once self::$fixturesPath.'/includes/ProjectWithXsdExtensionInPhar.phar'; - - // extension with an XSD in PHAR archive - $container = new ContainerBuilder(); - $container->registerExtension(new \ProjectWithXsdExtensionInPhar()); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('extensions/services6.xml'); - - // extension with an XSD in PHAR archive (does not validate) - try { - $loader->load('extensions/services7.xml'); - $this->fail('->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'services7.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - - $e = $e->getPrevious(); - $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - $this->assertContains('The attribute \'bar\' is not allowed', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); - } - } - - public function testSupports() - { - $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator()); - - $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); - } - - public function testNoNamingConflictsForAnonymousServices() - { - $container = new ContainerBuilder(); - - $loader1 = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml/extension1')); - $loader1->load('services.xml'); - $services = $container->getDefinitions(); - $this->assertCount(2, $services, '->load() attributes unique ids to anonymous services'); - $loader2 = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml/extension2')); - $loader2->load('services.xml'); - $services = $container->getDefinitions(); - $this->assertCount(4, $services, '->load() attributes unique ids to anonymous services'); - - $services = $container->getDefinitions(); - $args1 = $services['extension1.foo']->getArguments(); - $inner1 = $services[(string) $args1[0]]; - $this->assertEquals('BarClass1', $inner1->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $args2 = $services['extension2.foo']->getArguments(); - $inner2 = $services[(string) $args2[0]]; - $this->assertEquals('BarClass2', $inner2->getClass(), '->load() uses the same configuration as for the anonymous ones'); - } - - public function testDocTypeIsNotAllowed() - { - $container = new ContainerBuilder(); - - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - - // document types are not allowed. - try { - $loader->load('withdoctype.xml'); - $this->fail('->load() throws an InvalidArgumentException if the configuration contains a document type'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration contains a document type'); - $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'withdoctype.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration contains a document type'); - - $e = $e->getPrevious(); - $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration contains a document type'); - $this->assertSame('Document types are not allowed.', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration contains a document type'); - } - } - - public function testXmlNamespaces() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('namespaces.xml'); - $services = $container->getDefinitions(); - - $this->assertTrue(isset($services['foo']), '->load() parses elements'); - $this->assertEquals(1, count($services['foo']->getTag('foo.tag')), '->load parses elements'); - $this->assertEquals(array(array('setBar', array('foo'))), $services['foo']->getMethodCalls(), '->load() parses the tag'); - } - - public function testLoadIndexedArguments() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services14.xml'); - - $this->assertEquals(array('index_0' => 'app'), $container->findDefinition('logger')->getArguments()); - } - - public function testLoadInlinedServices() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services21.xml'); - - $foo = $container->getDefinition('foo'); - - $fooFactory = $foo->getFactory(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $fooFactory[0]); - $this->assertSame('FooFactory', $fooFactory[0]->getClass()); - $this->assertSame('createFoo', $fooFactory[1]); - - $fooFactoryFactory = $fooFactory[0]->getFactory(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $fooFactoryFactory[0]); - $this->assertSame('Foobar', $fooFactoryFactory[0]->getClass()); - $this->assertSame('createFooFactory', $fooFactoryFactory[1]); - - $fooConfigurator = $foo->getConfigurator(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $fooConfigurator[0]); - $this->assertSame('Bar', $fooConfigurator[0]->getClass()); - $this->assertSame('configureFoo', $fooConfigurator[1]); - - $barConfigurator = $fooConfigurator[0]->getConfigurator(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $barConfigurator[0]); - $this->assertSame('Baz', $barConfigurator[0]->getClass()); - $this->assertSame('configureBar', $barConfigurator[1]); - } - - public function testType() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services22.xml'); - - $this->assertEquals(array('Bar', 'Baz'), $container->getDefinition('foo')->getAutowiringTypes()); - } - - public function testAutowire() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('services23.xml'); - - $this->assertTrue($container->getDefinition('bar')->isAutowired()); - } - - /** - * @group legacy - * @expectedDeprecation Using the attribute "class" is deprecated for the service "bar" which is defined as an alias %s. - * @expectedDeprecation Using the element "tag" is deprecated for the service "bar" which is defined as an alias %s. - * @expectedDeprecation Using the element "factory" is deprecated for the service "bar" which is defined as an alias %s. - */ - public function testAliasDefinitionContainsUnsupportedElements() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - - $loader->load('legacy_invalid_alias_definition.xml'); - - $this->assertTrue($container->has('bar')); - } - - public function testArgumentWithKeyOutsideCollection() - { - $container = new ContainerBuilder(); - $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); - $loader->load('with_key_outside_collection.xml'); - - $this->assertSame(array('type' => 'foo', 'bar'), $container->getDefinition('foo')->getArguments()); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php deleted file mode 100644 index fabaa4859..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php +++ /dev/null @@ -1,338 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\Loader; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\ExpressionLanguage\Expression; - -class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase -{ - protected static $fixturesPath; - - public static function setUpBeforeClass() - { - self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); - require_once self::$fixturesPath.'/includes/foo.php'; - require_once self::$fixturesPath.'/includes/ProjectExtension.php'; - } - - public function testLoadFile() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); - $r = new \ReflectionObject($loader); - $m = $r->getMethod('loadFile'); - $m->setAccessible(true); - - try { - $m->invoke($loader, 'foo.yml'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not exist'); - $this->assertEquals('The service file "foo.yml" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); - } - - try { - $m->invoke($loader, 'parameters.ini'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); - $this->assertEquals('The service file "parameters.ini" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); - } - - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - - foreach (array('nonvalid1', 'nonvalid2') as $fixture) { - try { - $m->invoke($loader, $fixture.'.yml'); - $this->fail('->load() throws an InvalidArgumentException if the loaded file does not validate'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not validate'); - $this->assertStringMatchesFormat('The service file "nonvalid%d.yml" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not validate'); - } - } - } - - /** - * @dataProvider provideInvalidFiles - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testLoadInvalidFile($file) - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - - $loader->load($file.'.yml'); - } - - public function provideInvalidFiles() - { - return array( - array('bad_parameters'), - array('bad_imports'), - array('bad_import'), - array('bad_services'), - array('bad_service'), - array('bad_calls'), - array('bad_format'), - ); - } - - public function testLoadParameters() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services2.yml'); - $this->assertEquals(array('foo' => 'bar', 'mixedcase' => array('MixedCaseKey' => 'value'), 'values' => array(true, false, 0, 1000.3, PHP_INT_MAX), 'bar' => 'foo', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar')), $container->getParameterBag()->all(), '->load() converts YAML keys to lowercase'); - } - - public function testLoadImports() - { - $container = new ContainerBuilder(); - $resolver = new LoaderResolver(array( - new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), - new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), - new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')), - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), - )); - $loader->setResolver($resolver); - $loader->load('services4.yml'); - - $actual = $container->getParameterBag()->all(); - $expected = array('foo' => 'bar', 'values' => array(true, false, PHP_INT_MAX), 'bar' => '%foo%', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar'), 'mixedcase' => array('MixedCaseKey' => 'value'), 'imported_from_ini' => true, 'imported_from_xml' => true); - $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); - $this->assertTrue($actual['imported_from_ini']); - - // Bad import throws no exception due to ignore_errors value. - $loader->load('services4_bad_import.yml'); - } - - public function testLoadServices() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services6.yml'); - $services = $container->getDefinitions(); - $this->assertTrue(isset($services['foo']), '->load() parses service elements'); - $this->assertFalse($services['not_shared']->isShared(), '->load() parses the shared flag'); - $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts service element to Definition instances'); - $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); - $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); - $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); - $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array(new Reference('baz'), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); - $this->assertEquals(array(array('setBar', array()), array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); - $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); - $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); - $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); - $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag'); - - $aliases = $container->getAliases(); - $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases'); - $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); - $this->assertTrue($aliases['alias_for_foo']->isPublic()); - $this->assertTrue(isset($aliases['another_alias_for_foo'])); - $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); - $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); - - $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); - } - - public function testLoadFactoryShortSyntax() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services14.yml'); - $services = $container->getDefinitions(); - - $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag with service:method'); - $this->assertEquals(array('FooBacFactory', 'createFooBar'), $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method'); - } - - public function testLoadConfiguratorShortSyntax() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services_configurator_short_syntax.yml'); - $services = $container->getDefinitions(); - - $this->assertEquals(array(new Reference('foo_bar_configurator'), 'configure'), $services['foo_bar']->getConfigurator(), '->load() parses the configurator tag with service:method'); - $this->assertEquals(array('FooBarConfigurator', 'configureFooBar'), $services['foo_bar_with_static_call']->getConfigurator(), '->load() parses the configurator tag with Class::method'); - } - - public function testExtensions() - { - $container = new ContainerBuilder(); - $container->registerExtension(new \ProjectExtension()); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services10.yml'); - $container->compile(); - $services = $container->getDefinitions(); - $parameters = $container->getParameterBag()->all(); - - $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements'); - $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements'); - - $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); - $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); - - try { - $loader->load('services11.yml'); - $this->fail('->load() throws an InvalidArgumentException if the tag is not valid'); - } catch (\Exception $e) { - $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid'); - $this->assertStringStartsWith('There is no extension able to load the configuration for "foobarfoobar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid'); - } - } - - public function testSupports() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator()); - - $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable'); - $this->assertTrue($loader->supports('foo.yaml'), '->supports() returns true if the resource is loadable'); - $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); - } - - public function testNonArrayTagsThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - try { - $loader->load('badtag1.yml'); - $this->fail('->load() should throw an exception when the tags key of a service is not an array'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tags key is not an array'); - $this->assertStringStartsWith('Parameter "tags" must be an array for service', $e->getMessage(), '->load() throws an InvalidArgumentException if the tags key is not an array'); - } - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage A "tags" entry must be an array for service - */ - public function testNonArrayTagThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('badtag4.yml'); - } - - public function testTagWithoutNameThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - try { - $loader->load('badtag2.yml'); - $this->fail('->load() should throw an exception when a tag is missing the name key'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag is missing the name key'); - $this->assertStringStartsWith('A "tags" entry is missing a "name" key for service ', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag is missing the name key'); - } - } - - public function testTagWithAttributeArrayThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - try { - $loader->load('badtag3.yml'); - $this->fail('->load() should throw an exception when a tag-attribute is not a scalar'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); - $this->assertStringStartsWith('A "tags" attribute must be of a scalar-type for service "foo_service", tag "foo", attribute "bar"', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); - } - } - - public function testLoadYamlOnlyWithKeys() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services21.yml'); - - $definition = $container->getDefinition('manager'); - $this->assertEquals(array(array('setLogger', array(new Reference('logger'))), array('setClass', array('User'))), $definition->getMethodCalls()); - $this->assertEquals(array(true), $definition->getArguments()); - $this->assertEquals(array('manager' => array(array('alias' => 'user'))), $definition->getTags()); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessageRegExp /The tag name for service ".+" in .+ must be a non-empty string/ - */ - public function testTagWithEmptyNameThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('tag_name_empty_string.yml'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessageREgExp /The tag name for service "\.+" must be a non-empty string/ - */ - public function testTagWithNonStringNameThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('tag_name_no_string.yml'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testTypesNotArray() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('bad_types1.yml'); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testTypeNotString() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('bad_types2.yml'); - } - - public function testTypes() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services22.yml'); - - $this->assertEquals(array('Foo', 'Bar'), $container->getDefinition('foo_service')->getAutowiringTypes()); - $this->assertEquals(array('Foo'), $container->getDefinition('baz_service')->getAutowiringTypes()); - } - - public function testAutowire() - { - $container = new ContainerBuilder(); - $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('services23.yml'); - - $this->assertTrue($container->getDefinition('bar_service')->isAutowired()); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - * @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo"). - */ - public function testDecoratedServicesWithWrongSyntaxThrowsException() - { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - $loader->load('bad_decorates.yml'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php deleted file mode 100644 index c898038e3..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php +++ /dev/null @@ -1,164 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; - -use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; - -class EnvPlaceholderParameterBagTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException - */ - public function testGetThrowsInvalidArgumentExceptionIfEnvNameContainsNonWordCharacters() - { - $bag = new EnvPlaceholderParameterBag(); - $bag->get('env(%foo%)'); - } - - public function testMergeWillNotDuplicateIdenticalParameters() - { - $envVariableName = 'DB_HOST'; - $parameter = sprintf('env(%s)', $envVariableName); - $firstBag = new EnvPlaceholderParameterBag(); - - // initialize placeholders - $firstBag->get($parameter); - $secondBag = clone $firstBag; - - $firstBag->mergeEnvPlaceholders($secondBag); - $mergedPlaceholders = $firstBag->getEnvPlaceholders(); - - $placeholderForVariable = $mergedPlaceholders[$envVariableName]; - $placeholder = array_values($placeholderForVariable)[0]; - - $this->assertCount(1, $placeholderForVariable); - $this->assertInternalType('string', $placeholder); - $this->assertContains($envVariableName, $placeholder); - } - - public function testMergeWhereFirstBagIsEmptyWillWork() - { - $envVariableName = 'DB_HOST'; - $parameter = sprintf('env(%s)', $envVariableName); - $firstBag = new EnvPlaceholderParameterBag(); - $secondBag = new EnvPlaceholderParameterBag(); - - // initialize placeholder only in second bag - $secondBag->get($parameter); - - $this->assertEmpty($firstBag->getEnvPlaceholders()); - - $firstBag->mergeEnvPlaceholders($secondBag); - $mergedPlaceholders = $firstBag->getEnvPlaceholders(); - - $placeholderForVariable = $mergedPlaceholders[$envVariableName]; - $placeholder = array_values($placeholderForVariable)[0]; - - $this->assertCount(1, $placeholderForVariable); - $this->assertInternalType('string', $placeholder); - $this->assertContains($envVariableName, $placeholder); - } - - public function testMergeWherePlaceholderOnlyExistsInSecond() - { - $uniqueEnvName = 'DB_HOST'; - $commonEnvName = 'DB_USER'; - - $uniqueParamName = sprintf('env(%s)', $uniqueEnvName); - $commonParamName = sprintf('env(%s)', $commonEnvName); - - $firstBag = new EnvPlaceholderParameterBag(); - // initialize common placeholder - $firstBag->get($commonParamName); - $secondBag = clone $firstBag; - - // initialize unique placeholder - $secondBag->get($uniqueParamName); - - $firstBag->mergeEnvPlaceholders($secondBag); - $merged = $firstBag->getEnvPlaceholders(); - - $this->assertCount(1, $merged[$uniqueEnvName]); - // second bag has same placeholder for commonEnvName - $this->assertCount(1, $merged[$commonEnvName]); - } - - public function testMergeWithDifferentIdentifiersForPlaceholders() - { - $envName = 'DB_USER'; - $paramName = sprintf('env(%s)', $envName); - - $firstBag = new EnvPlaceholderParameterBag(); - $secondBag = new EnvPlaceholderParameterBag(); - // initialize placeholders - $firstPlaceholder = $firstBag->get($paramName); - $secondPlaceholder = $secondBag->get($paramName); - - $firstBag->mergeEnvPlaceholders($secondBag); - $merged = $firstBag->getEnvPlaceholders(); - - $this->assertNotEquals($firstPlaceholder, $secondPlaceholder); - $this->assertCount(2, $merged[$envName]); - } - - public function testResolveEnvCastsIntToString() - { - $bag = new EnvPlaceholderParameterBag(); - $bag->get('env(INT_VAR)'); - $bag->set('env(Int_Var)', 2); - $bag->resolve(); - $this->assertSame('2', $bag->all()['env(int_var)']); - } - - public function testResolveEnvAllowsNull() - { - $bag = new EnvPlaceholderParameterBag(); - $bag->get('env(NULL_VAR)'); - $bag->set('env(Null_Var)', null); - $bag->resolve(); - $this->assertNull($bag->all()['env(null_var)']); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage The default value of env parameter "ARRAY_VAR" must be scalar or null, array given. - */ - public function testResolveThrowsOnBadDefaultValue() - { - $bag = new EnvPlaceholderParameterBag(); - $bag->get('env(ARRAY_VAR)'); - $bag->set('env(Array_Var)', array()); - $bag->resolve(); - } - - public function testGetEnvAllowsNull() - { - $bag = new EnvPlaceholderParameterBag(); - $bag->set('env(NULL_VAR)', null); - $bag->get('env(NULL_VAR)'); - $bag->resolve(); - - $this->assertNull($bag->all()['env(null_var)']); - } - - /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage The default value of an env() parameter must be scalar or null, but "array" given to "env(ARRAY_VAR)". - */ - public function testGetThrowsOnBadDefaultValue() - { - $bag = new EnvPlaceholderParameterBag(); - $bag->set('env(ARRAY_VAR)', array()); - $bag->get('env(ARRAY_VAR)'); - $bag->resolve(); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.php deleted file mode 100644 index b87ca916b..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; - -use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; - -class FrozenParameterBagTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $parameters = array( - 'foo' => 'foo', - 'bar' => 'bar', - ); - $bag = new FrozenParameterBag($parameters); - $this->assertEquals($parameters, $bag->all(), '__construct() takes an array of parameters as its first argument'); - } - - /** - * @expectedException \LogicException - */ - public function testClear() - { - $bag = new FrozenParameterBag(array()); - $bag->clear(); - } - - /** - * @expectedException \LogicException - */ - public function testSet() - { - $bag = new FrozenParameterBag(array()); - $bag->set('foo', 'bar'); - } - - /** - * @expectedException \LogicException - */ - public function testAdd() - { - $bag = new FrozenParameterBag(array()); - $bag->add(array()); - } - - /** - * @expectedException \LogicException - */ - public function testRemove() - { - $bag = new FrozenParameterBag(array('foo' => 'bar')); - $bag->remove('foo'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php deleted file mode 100644 index 79a8b9f07..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php +++ /dev/null @@ -1,245 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; - -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; -use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; -use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; -use Symfony\Component\DependencyInjection\Exception\RuntimeException; - -class ParameterBagTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $bag = new ParameterBag($parameters = array( - 'foo' => 'foo', - 'bar' => 'bar', - )); - $this->assertEquals($parameters, $bag->all(), '__construct() takes an array of parameters as its first argument'); - } - - public function testClear() - { - $bag = new ParameterBag($parameters = array( - 'foo' => 'foo', - 'bar' => 'bar', - )); - $bag->clear(); - $this->assertEquals(array(), $bag->all(), '->clear() removes all parameters'); - } - - public function testRemove() - { - $bag = new ParameterBag(array( - 'foo' => 'foo', - 'bar' => 'bar', - )); - $bag->remove('foo'); - $this->assertEquals(array('bar' => 'bar'), $bag->all(), '->remove() removes a parameter'); - $bag->remove('BAR'); - $this->assertEquals(array(), $bag->all(), '->remove() converts key to lowercase before removing'); - } - - public function testGetSet() - { - $bag = new ParameterBag(array('foo' => 'bar')); - $bag->set('bar', 'foo'); - $this->assertEquals('foo', $bag->get('bar'), '->set() sets the value of a new parameter'); - - $bag->set('foo', 'baz'); - $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter'); - - $bag->set('Foo', 'baz1'); - $this->assertEquals('baz1', $bag->get('foo'), '->set() converts the key to lowercase'); - $this->assertEquals('baz1', $bag->get('FOO'), '->get() converts the key to lowercase'); - - try { - $bag->get('baba'); - $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); - $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); - } - } - - /** - * @dataProvider provideGetThrowParameterNotFoundExceptionData - */ - public function testGetThrowParameterNotFoundException($parameterKey, $exceptionMessage) - { - $bag = new ParameterBag(array( - 'foo' => 'foo', - 'bar' => 'bar', - 'baz' => 'baz', - 'fiz' => array('bar' => array('boo' => 12)), - )); - - $this->setExpectedException('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $exceptionMessage); - - $bag->get($parameterKey); - } - - public function provideGetThrowParameterNotFoundExceptionData() - { - return array( - array('foo1', 'You have requested a non-existent parameter "foo1". Did you mean this: "foo"?'), - array('bag', 'You have requested a non-existent parameter "bag". Did you mean one of these: "bar", "baz"?'), - array('', 'You have requested a non-existent parameter "".'), - - array('fiz.bar.boo', 'You have requested a non-existent parameter "fiz.bar.boo". You cannot access nested array items, do you want to inject "fiz" instead?'), - ); - } - - public function testHas() - { - $bag = new ParameterBag(array('foo' => 'bar')); - $this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined'); - $this->assertTrue($bag->has('Foo'), '->has() converts the key to lowercase'); - $this->assertFalse($bag->has('bar'), '->has() returns false if a parameter is not defined'); - } - - public function testResolveValue() - { - $bag = new ParameterBag(array()); - $this->assertEquals('foo', $bag->resolveValue('foo'), '->resolveValue() returns its argument unmodified if no placeholders are found'); - - $bag = new ParameterBag(array('foo' => 'bar')); - $this->assertEquals('I\'m a bar', $bag->resolveValue('I\'m a %foo%'), '->resolveValue() replaces placeholders by their values'); - $this->assertEquals(array('bar' => 'bar'), $bag->resolveValue(array('%foo%' => '%foo%')), '->resolveValue() replaces placeholders in keys and values of arrays'); - $this->assertEquals(array('bar' => array('bar' => array('bar' => 'bar'))), $bag->resolveValue(array('%foo%' => array('%foo%' => array('%foo%' => '%foo%')))), '->resolveValue() replaces placeholders in nested arrays'); - $this->assertEquals('I\'m a %%foo%%', $bag->resolveValue('I\'m a %%foo%%'), '->resolveValue() supports % escaping by doubling it'); - $this->assertEquals('I\'m a bar %%foo bar', $bag->resolveValue('I\'m a %foo% %%foo %foo%'), '->resolveValue() supports % escaping by doubling it'); - $this->assertEquals(array('foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar'))), $bag->resolveValue(array('foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar')))), '->resolveValue() supports % escaping by doubling it'); - - $bag = new ParameterBag(array('foo' => true)); - $this->assertTrue($bag->resolveValue('%foo%'), '->resolveValue() replaces arguments that are just a placeholder by their value without casting them to strings'); - $bag = new ParameterBag(array('foo' => null)); - $this->assertNull($bag->resolveValue('%foo%'), '->resolveValue() replaces arguments that are just a placeholder by their value without casting them to strings'); - - $bag = new ParameterBag(array('foo' => 'bar', 'baz' => '%%%foo% %foo%%% %%foo%% %%%foo%%%')); - $this->assertEquals('%%bar bar%% %%foo%% %%bar%%', $bag->resolveValue('%baz%'), '->resolveValue() replaces params placed besides escaped %'); - - $bag = new ParameterBag(array('baz' => '%%s?%%s')); - $this->assertEquals('%%s?%%s', $bag->resolveValue('%baz%'), '->resolveValue() is not replacing greedily'); - - $bag = new ParameterBag(array()); - try { - $bag->resolveValue('%foobar%'); - $this->fail('->resolveValue() throws an InvalidArgumentException if a placeholder references a non-existent parameter'); - } catch (ParameterNotFoundException $e) { - $this->assertEquals('You have requested a non-existent parameter "foobar".', $e->getMessage(), '->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); - } - - try { - $bag->resolveValue('foo %foobar% bar'); - $this->fail('->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); - } catch (ParameterNotFoundException $e) { - $this->assertEquals('You have requested a non-existent parameter "foobar".', $e->getMessage(), '->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); - } - - $bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => array())); - try { - $bag->resolveValue('%foo%'); - $this->fail('->resolveValue() throws a RuntimeException when a parameter embeds another non-string parameter'); - } catch (RuntimeException $e) { - $this->assertEquals('A string value must be composed of strings and/or numbers, but found parameter "bar" of type array inside string value "a %bar%".', $e->getMessage(), '->resolveValue() throws a RuntimeException when a parameter embeds another non-string parameter'); - } - - $bag = new ParameterBag(array('foo' => '%bar%', 'bar' => '%foobar%', 'foobar' => '%foo%')); - try { - $bag->resolveValue('%foo%'); - $this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); - } catch (ParameterCircularReferenceException $e) { - $this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); - } - - $bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => 'a %foobar%', 'foobar' => 'a %foo%')); - try { - $bag->resolveValue('%foo%'); - $this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); - } catch (ParameterCircularReferenceException $e) { - $this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); - } - - $bag = new ParameterBag(array('host' => 'foo.bar', 'port' => 1337)); - $this->assertEquals('foo.bar:1337', $bag->resolveValue('%host%:%port%')); - } - - public function testResolveIndicatesWhyAParameterIsNeeded() - { - $bag = new ParameterBag(array('foo' => '%bar%')); - - try { - $bag->resolve(); - } catch (ParameterNotFoundException $e) { - $this->assertEquals('The parameter "foo" has a dependency on a non-existent parameter "bar".', $e->getMessage()); - } - - $bag = new ParameterBag(array('foo' => '%bar%')); - - try { - $bag->resolve(); - } catch (ParameterNotFoundException $e) { - $this->assertEquals('The parameter "foo" has a dependency on a non-existent parameter "bar".', $e->getMessage()); - } - } - - public function testResolveUnescapesValue() - { - $bag = new ParameterBag(array( - 'foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar')), - 'bar' => 'I\'m a %%foo%%', - )); - - $bag->resolve(); - - $this->assertEquals('I\'m a %foo%', $bag->get('bar'), '->resolveValue() supports % escaping by doubling it'); - $this->assertEquals(array('bar' => array('ding' => 'I\'m a bar %foo %bar')), $bag->get('foo'), '->resolveValue() supports % escaping by doubling it'); - } - - public function testEscapeValue() - { - $bag = new ParameterBag(); - - $bag->add(array( - 'foo' => $bag->escapeValue(array('bar' => array('ding' => 'I\'m a bar %foo %bar', 'zero' => null))), - 'bar' => $bag->escapeValue('I\'m a %foo%'), - )); - - $this->assertEquals('I\'m a %%foo%%', $bag->get('bar'), '->escapeValue() escapes % by doubling it'); - $this->assertEquals(array('bar' => array('ding' => 'I\'m a bar %%foo %%bar', 'zero' => null)), $bag->get('foo'), '->escapeValue() escapes % by doubling it'); - } - - /** - * @dataProvider stringsWithSpacesProvider - */ - public function testResolveStringWithSpacesReturnsString($expected, $test, $description) - { - $bag = new ParameterBag(array('foo' => 'bar')); - - try { - $this->assertEquals($expected, $bag->resolveString($test), $description); - } catch (ParameterNotFoundException $e) { - $this->fail(sprintf('%s - "%s"', $description, $expected)); - } - } - - public function stringsWithSpacesProvider() - { - return array( - array('bar', '%foo%', 'Parameters must be wrapped by %.'), - array('% foo %', '% foo %', 'Parameters should not have spaces.'), - array('{% set my_template = "foo" %}', '{% set my_template = "foo" %}', 'Twig-like strings are not parameters.'), - array('50% is less than 100%', '50% is less than 100%', 'Text between % signs is allowed, if there are spaces.'), - ); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterTest.php deleted file mode 100644 index 571ca0298..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ParameterTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use Symfony\Component\DependencyInjection\Parameter; - -class ParameterTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $ref = new Parameter('foo'); - $this->assertEquals('foo', (string) $ref, '__construct() sets the id of the parameter, which is used for the __toString() method'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/Tests/ReferenceTest.php b/tests/integration/vendor/symfony/dependency-injection/Tests/ReferenceTest.php deleted file mode 100644 index 6800267c9..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/Tests/ReferenceTest.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\DependencyInjection\Tests; - -use Symfony\Component\DependencyInjection\Reference; - -class ReferenceTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $ref = new Reference('foo'); - $this->assertEquals('foo', (string) $ref, '__construct() sets the id of the reference, which is used for the __toString() method'); - } - - public function testCaseInsensitive() - { - $ref = new Reference('FooBar'); - $this->assertEquals('foobar', (string) $ref, 'the id is lowercased as the container is case insensitive'); - } -} diff --git a/tests/integration/vendor/symfony/dependency-injection/TypedReference.php b/tests/integration/vendor/symfony/dependency-injection/TypedReference.php new file mode 100644 index 000000000..4099a0059 --- /dev/null +++ b/tests/integration/vendor/symfony/dependency-injection/TypedReference.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * Represents a PHP type-hinted service reference. + * + * @author Nicolas Grekas + */ +class TypedReference extends Reference +{ + private $type; + private $name; + + /** + * @param string $id The service identifier + * @param string $type The PHP type of the identified service + * @param int $invalidBehavior The behavior when the service does not exist + * @param string|null $name The name of the argument targeting the service + */ + public function __construct(string $id, string $type, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, string $name = null) + { + $this->name = $type === $id ? $name : null; + parent::__construct($id, $invalidBehavior); + $this->type = $type; + } + + public function getType() + { + return $this->type; + } + + public function getName(): ?string + { + return $this->name; + } +} diff --git a/tests/integration/vendor/symfony/dependency-injection/Variable.php b/tests/integration/vendor/symfony/dependency-injection/Variable.php index ddd437438..21d33ebb2 100644 --- a/tests/integration/vendor/symfony/dependency-injection/Variable.php +++ b/tests/integration/vendor/symfony/dependency-injection/Variable.php @@ -28,17 +28,12 @@ class Variable { private $name; - /** - * @param string $name - */ - public function __construct($name) + public function __construct(string $name) { $this->name = $name; } /** - * Converts the object to a string. - * * @return string */ public function __toString() diff --git a/tests/integration/vendor/symfony/dependency-injection/composer.json b/tests/integration/vendor/symfony/dependency-injection/composer.json index 0f3ae8def..9a80c4703 100644 --- a/tests/integration/vendor/symfony/dependency-injection/composer.json +++ b/tests/integration/vendor/symfony/dependency-injection/composer.json @@ -1,7 +1,7 @@ { "name": "symfony/dependency-injection", "type": "library", - "description": "Symfony DependencyInjection Component", + "description": "Allows you to standardize and centralize the way objects are constructed in your application", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", @@ -16,21 +16,33 @@ } ], "require": { - "php": ">=5.5.9" + "php": ">=7.2.5", + "psr/container": "^1.0", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1.6|^2" }, "require-dev": { - "symfony/yaml": "~3.2", - "symfony/config": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0" + "symfony/yaml": "^4.4|^5.0", + "symfony/config": "^5.1", + "symfony/expression-language": "^4.4|^5.0" }, "suggest": { "symfony/yaml": "", "symfony/config": "", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", "symfony/expression-language": "For using expressions in service container configuration", "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" }, "conflict": { - "symfony/yaml": "<3.2" + "symfony/config": "<5.1", + "symfony/finder": "<4.4", + "symfony/proxy-manager-bridge": "<4.4", + "symfony/yaml": "<4.4" + }, + "provide": { + "psr/container-implementation": "1.0", + "symfony/service-implementation": "1.0|2.0" }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" }, @@ -38,10 +50,5 @@ "/Tests/" ] }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } + "minimum-stability": "dev" } diff --git a/tests/integration/vendor/symfony/dependency-injection/phpunit.xml.dist b/tests/integration/vendor/symfony/dependency-injection/phpunit.xml.dist deleted file mode 100644 index 86252d045..000000000 --- a/tests/integration/vendor/symfony/dependency-injection/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/tests/integration/vendor/symfony/class-loader/.gitignore b/tests/integration/vendor/symfony/deprecation-contracts/.gitignore similarity index 100% rename from tests/integration/vendor/symfony/class-loader/.gitignore rename to tests/integration/vendor/symfony/deprecation-contracts/.gitignore diff --git a/tests/integration/vendor/symfony/deprecation-contracts/CHANGELOG.md b/tests/integration/vendor/symfony/deprecation-contracts/CHANGELOG.md new file mode 100644 index 000000000..7932e2613 --- /dev/null +++ b/tests/integration/vendor/symfony/deprecation-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/tests/integration/vendor/symfony/deprecation-contracts/LICENSE b/tests/integration/vendor/symfony/deprecation-contracts/LICENSE new file mode 100644 index 000000000..ad85e1737 --- /dev/null +++ b/tests/integration/vendor/symfony/deprecation-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020-2021 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/deprecation-contracts/README.md b/tests/integration/vendor/symfony/deprecation-contracts/README.md new file mode 100644 index 000000000..4957933a6 --- /dev/null +++ b/tests/integration/vendor/symfony/deprecation-contracts/README.md @@ -0,0 +1,26 @@ +Symfony Deprecation Contracts +============================= + +A generic function and convention to trigger deprecation notices. + +This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices. + +By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component, +the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments. + +The function requires at least 3 arguments: + - the name of the Composer package that is triggering the deprecation + - the version of the package that introduced the deprecation + - the message of the deprecation + - more arguments can be provided: they will be inserted in the message using `printf()` formatting + +Example: +```php +trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin'); +``` + +This will generate the following message: +`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.` + +While not necessarily recommended, the deprecation notices can be completely ignored by declaring an empty +`function trigger_deprecation() {}` in your application. diff --git a/tests/integration/vendor/symfony/deprecation-contracts/composer.json b/tests/integration/vendor/symfony/deprecation-contracts/composer.json new file mode 100644 index 000000000..cc7cc1237 --- /dev/null +++ b/tests/integration/vendor/symfony/deprecation-contracts/composer.json @@ -0,0 +1,35 @@ +{ + "name": "symfony/deprecation-contracts", + "type": "library", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/tests/integration/vendor/symfony/deprecation-contracts/function.php b/tests/integration/vendor/symfony/deprecation-contracts/function.php new file mode 100644 index 000000000..d4371504a --- /dev/null +++ b/tests/integration/vendor/symfony/deprecation-contracts/function.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!function_exists('trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, ...$args): void + { + @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/tests/integration/vendor/symfony/config/.gitignore b/tests/integration/vendor/symfony/event-dispatcher-contracts/.gitignore similarity index 100% rename from tests/integration/vendor/symfony/config/.gitignore rename to tests/integration/vendor/symfony/event-dispatcher-contracts/.gitignore diff --git a/tests/integration/vendor/symfony/event-dispatcher-contracts/CHANGELOG.md b/tests/integration/vendor/symfony/event-dispatcher-contracts/CHANGELOG.md new file mode 100644 index 000000000..7932e2613 --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/tests/integration/vendor/symfony/event-dispatcher-contracts/Event.php b/tests/integration/vendor/symfony/event-dispatcher-contracts/Event.php new file mode 100644 index 000000000..46dcb2ba0 --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher-contracts/Event.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\EventDispatcher; + +use Psr\EventDispatcher\StoppableEventInterface; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Nicolas Grekas + */ +class Event implements StoppableEventInterface +{ + private $propagationStopped = false; + + /** + * {@inheritdoc} + */ + public function isPropagationStopped(): bool + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + */ + public function stopPropagation(): void + { + $this->propagationStopped = true; + } +} diff --git a/tests/integration/vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php b/tests/integration/vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php new file mode 100644 index 000000000..351dc5131 --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\EventDispatcher; + +use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface; + +/** + * Allows providing hooks on domain-specific lifecycles by dispatching events. + */ +interface EventDispatcherInterface extends PsrEventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param object $event The event to pass to the event handlers/listeners + * @param string|null $eventName The name of the event to dispatch. If not supplied, + * the class of $event should be used instead. + * + * @return object The passed $event MUST be returned + */ + public function dispatch(object $event, string $eventName = null): object; +} diff --git a/tests/integration/vendor/symfony/event-dispatcher-contracts/LICENSE b/tests/integration/vendor/symfony/event-dispatcher-contracts/LICENSE new file mode 100644 index 000000000..235841453 --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2021 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/event-dispatcher-contracts/README.md b/tests/integration/vendor/symfony/event-dispatcher-contracts/README.md new file mode 100644 index 000000000..b1ab4c00c --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher-contracts/README.md @@ -0,0 +1,9 @@ +Symfony EventDispatcher Contracts +================================= + +A set of abstractions extracted out of the Symfony components. + +Can be used to build on semantics that the Symfony components proved useful - and +that already have battle tested implementations. + +See https://github.com/symfony/contracts/blob/main/README.md for more information. diff --git a/tests/integration/vendor/symfony/event-dispatcher-contracts/composer.json b/tests/integration/vendor/symfony/event-dispatcher-contracts/composer.json new file mode 100644 index 000000000..660df81a0 --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher-contracts/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/event-dispatcher-contracts", + "type": "library", + "description": "Generic abstractions related to dispatching event", + "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\EventDispatcher\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/tests/integration/vendor/symfony/event-dispatcher/.gitignore b/tests/integration/vendor/symfony/event-dispatcher/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/tests/integration/vendor/symfony/event-dispatcher/Attribute/AsEventListener.php b/tests/integration/vendor/symfony/event-dispatcher/Attribute/AsEventListener.php new file mode 100644 index 000000000..bb931b82d --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher/Attribute/AsEventListener.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Attribute; + +/** + * Service tag to autoconfigure event listeners. + * + * @author Alexander M. Turek + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class AsEventListener +{ + public function __construct( + public ?string $event = null, + public ?string $method = null, + public int $priority = 0, + public ?string $dispatcher = null, + ) { + } +} diff --git a/tests/integration/vendor/symfony/event-dispatcher/CHANGELOG.md b/tests/integration/vendor/symfony/event-dispatcher/CHANGELOG.md index 8feda35d5..0f9859895 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/CHANGELOG.md +++ b/tests/integration/vendor/symfony/event-dispatcher/CHANGELOG.md @@ -1,14 +1,73 @@ CHANGELOG ========= +5.4 +--- + + * Allow `#[AsEventListener]` attribute on methods + +5.3 +--- + + * Add `#[AsEventListener]` attribute for declaring listeners on PHP 8 + +5.1.0 +----- + + * The `LegacyEventDispatcherProxy` class has been deprecated. + * Added an optional `dispatcher` attribute to the listener and subscriber tags in `RegisterListenerPass`. + +5.0.0 +----- + + * The signature of the `EventDispatcherInterface::dispatch()` method has been changed to `dispatch($event, string $eventName = null): object`. + * The `Event` class has been removed in favor of `Symfony\Contracts\EventDispatcher\Event`. + * The `TraceableEventDispatcherInterface` has been removed. + * The `WrappedListener` class is now final. + +4.4.0 +----- + + * `AddEventAliasesPass` has been added, allowing applications and bundles to extend the event alias mapping used by `RegisterListenersPass`. + * Made the `event` attribute of the `kernel.event_listener` tag optional for FQCN events. + +4.3.0 +----- + + * The signature of the `EventDispatcherInterface::dispatch()` method should be updated to `dispatch($event, string $eventName = null)`, not doing so is deprecated + * deprecated the `Event` class, use `Symfony\Contracts\EventDispatcher\Event` instead + +4.1.0 +----- + + * added support for invokable event listeners tagged with `kernel.event_listener` by default + * The `TraceableEventDispatcher::getOrphanedEvents()` method has been added. + * The `TraceableEventDispatcherInterface` has been deprecated. + +4.0.0 +----- + + * removed the `ContainerAwareEventDispatcher` class + * added the `reset()` method to the `TraceableEventDispatcherInterface` + +3.4.0 +----- + + * Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated. + +3.3.0 +----- + + * The ContainerAwareEventDispatcher class has been deprecated. Use EventDispatcher with closure factories instead. + 3.0.0 ----- - * The method `getListenerPriority($eventName, $listener)` has been added to the - `EventDispatcherInterface`. - * The methods `Event::setDispatcher()`, `Event::getDispatcher()`, `Event::setName()` - and `Event::getName()` have been removed. - The event dispatcher and the event name are passed to the listener call. + * The method `getListenerPriority($eventName, $listener)` has been added to the + `EventDispatcherInterface`. + * The methods `Event::setDispatcher()`, `Event::getDispatcher()`, `Event::setName()` + and `Event::getName()` have been removed. + The event dispatcher and the event name are passed to the listener call. 2.5.0 ----- diff --git a/tests/integration/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/tests/integration/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php deleted file mode 100644 index 5982b85f3..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php +++ /dev/null @@ -1,195 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * Lazily loads listeners and subscribers from the dependency injection - * container. - * - * @author Fabien Potencier - * @author Bernhard Schussek - * @author Jordan Alliot - */ -class ContainerAwareEventDispatcher extends EventDispatcher -{ - /** - * The container from where services are loaded. - * - * @var ContainerInterface - */ - private $container; - - /** - * The service IDs of the event listeners and subscribers. - * - * @var array - */ - private $listenerIds = array(); - - /** - * The services registered as listeners. - * - * @var array - */ - private $listeners = array(); - - /** - * Constructor. - * - * @param ContainerInterface $container A ContainerInterface instance - */ - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - /** - * Adds a service as event listener. - * - * @param string $eventName Event for which the listener is added - * @param array $callback The service ID of the listener service & the method - * name that has to be called - * @param int $priority The higher this value, the earlier an event listener - * will be triggered in the chain. - * Defaults to 0. - * - * @throws \InvalidArgumentException - */ - public function addListenerService($eventName, $callback, $priority = 0) - { - if (!is_array($callback) || 2 !== count($callback)) { - throw new \InvalidArgumentException('Expected an array("service", "method") argument'); - } - - $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); - } - - public function removeListener($eventName, $listener) - { - $this->lazyLoad($eventName); - - if (isset($this->listenerIds[$eventName])) { - foreach ($this->listenerIds[$eventName] as $i => list($serviceId, $method, $priority)) { - $key = $serviceId.'.'.$method; - if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) { - unset($this->listeners[$eventName][$key]); - if (empty($this->listeners[$eventName])) { - unset($this->listeners[$eventName]); - } - unset($this->listenerIds[$eventName][$i]); - if (empty($this->listenerIds[$eventName])) { - unset($this->listenerIds[$eventName]); - } - } - } - } - - parent::removeListener($eventName, $listener); - } - - /** - * {@inheritdoc} - */ - public function hasListeners($eventName = null) - { - if (null === $eventName) { - return (bool) count($this->listenerIds) || (bool) count($this->listeners); - } - - if (isset($this->listenerIds[$eventName])) { - return true; - } - - return parent::hasListeners($eventName); - } - - /** - * {@inheritdoc} - */ - public function getListeners($eventName = null) - { - if (null === $eventName) { - foreach ($this->listenerIds as $serviceEventName => $args) { - $this->lazyLoad($serviceEventName); - } - } else { - $this->lazyLoad($eventName); - } - - return parent::getListeners($eventName); - } - - /** - * {@inheritdoc} - */ - public function getListenerPriority($eventName, $listener) - { - $this->lazyLoad($eventName); - - return parent::getListenerPriority($eventName, $listener); - } - - /** - * Adds a service as event subscriber. - * - * @param string $serviceId The service ID of the subscriber service - * @param string $class The service's class name (which must implement EventSubscriberInterface) - */ - public function addSubscriberService($serviceId, $class) - { - foreach ($class::getSubscribedEvents() as $eventName => $params) { - if (is_string($params)) { - $this->listenerIds[$eventName][] = array($serviceId, $params, 0); - } elseif (is_string($params[0])) { - $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); - } else { - foreach ($params as $listener) { - $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); - } - } - } - } - - public function getContainer() - { - return $this->container; - } - - /** - * Lazily loads listeners for this event from the dependency injection - * container. - * - * @param string $eventName The name of the event to dispatch. The name of - * the event is the name of the method that is - * invoked on listeners. - */ - protected function lazyLoad($eventName) - { - if (isset($this->listenerIds[$eventName])) { - foreach ($this->listenerIds[$eventName] as list($serviceId, $method, $priority)) { - $listener = $this->container->get($serviceId); - - $key = $serviceId.'.'.$method; - if (!isset($this->listeners[$eventName][$key])) { - $this->addListener($eventName, array($listener, $method), $priority); - } elseif ($listener !== $this->listeners[$eventName][$key]) { - parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); - $this->addListener($eventName, array($listener, $method), $priority); - } - - $this->listeners[$eventName][$key] = $listener; - } - } - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php index e74ac05f8..e1d2007fd 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php +++ b/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @@ -11,11 +11,14 @@ namespace Symfony\Component\EventDispatcher\Debug; +use Psr\EventDispatcher\StoppableEventInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Stopwatch\Stopwatch; -use Psr\Log\LoggerInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Collects some data about event listeners. @@ -24,35 +27,35 @@ * * @author Fabien Potencier */ -class TraceableEventDispatcher implements TraceableEventDispatcherInterface +class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterface { protected $logger; protected $stopwatch; - private $called; + /** + * @var \SplObjectStorage + */ + private $callStack; private $dispatcher; private $wrappedListeners; + private $orphanedEvents; + private $requestStack; + private $currentRequestHash = ''; - /** - * Constructor. - * - * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance - * @param Stopwatch $stopwatch A Stopwatch instance - * @param LoggerInterface $logger A LoggerInterface instance - */ - public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null) { $this->dispatcher = $dispatcher; $this->stopwatch = $stopwatch; $this->logger = $logger; - $this->called = array(); - $this->wrappedListeners = array(); + $this->wrappedListeners = []; + $this->orphanedEvents = []; + $this->requestStack = $requestStack; } /** * {@inheritdoc} */ - public function addListener($eventName, $listener, $priority = 0) + public function addListener(string $eventName, $listener, int $priority = 0) { $this->dispatcher->addListener($eventName, $listener, $priority); } @@ -68,7 +71,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function removeListener($eventName, $listener) + public function removeListener(string $eventName, $listener) { if (isset($this->wrappedListeners[$eventName])) { foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { @@ -94,7 +97,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function getListeners($eventName = null) + public function getListeners(string $eventName = null) { return $this->dispatcher->getListeners($eventName); } @@ -102,15 +105,25 @@ public function getListeners($eventName = null) /** * {@inheritdoc} */ - public function getListenerPriority($eventName, $listener) + public function getListenerPriority(string $eventName, $listener) { + // we might have wrapped listeners for the event (if called while dispatching) + // in that case get the priority by wrapper + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + return $this->dispatcher->getListenerPriority($eventName, $wrappedListener); + } + } + } + return $this->dispatcher->getListenerPriority($eventName, $listener); } /** * {@inheritdoc} */ - public function hasListeners($eventName = null) + public function hasListeners(string $eventName = null) { return $this->dispatcher->hasListeners($eventName); } @@ -118,42 +131,58 @@ public function hasListeners($eventName = null) /** * {@inheritdoc} */ - public function dispatch($eventName, Event $event = null) + public function dispatch(object $event, string $eventName = null): object { - if (null === $event) { - $event = new Event(); + $eventName = $eventName ?? \get_class($event); + + if (null === $this->callStack) { + $this->callStack = new \SplObjectStorage(); } - if (null !== $this->logger && $event->isPropagationStopped()) { + $currentRequestHash = $this->currentRequestHash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : ''; + + if (null !== $this->logger && $event instanceof StoppableEventInterface && $event->isPropagationStopped()) { $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); } $this->preProcess($eventName); - $this->preDispatch($eventName, $event); - - $e = $this->stopwatch->start($eventName, 'section'); - - $this->dispatcher->dispatch($eventName, $event); - - if ($e->isStarted()) { - $e->stop(); + try { + $this->beforeDispatch($eventName, $event); + try { + $e = $this->stopwatch->start($eventName, 'section'); + try { + $this->dispatcher->dispatch($event, $eventName); + } finally { + if ($e->isStarted()) { + $e->stop(); + } + } + } finally { + $this->afterDispatch($eventName, $event); + } + } finally { + $this->currentRequestHash = $currentRequestHash; + $this->postProcess($eventName); } - $this->postDispatch($eventName, $event); - $this->postProcess($eventName); - return $event; } /** - * {@inheritdoc} + * @return array */ - public function getCalledListeners() + public function getCalledListeners(Request $request = null) { - $called = array(); - foreach ($this->called as $eventName => $listeners) { - foreach ($listeners as $listener) { - $called[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName); + if (null === $this->callStack) { + return []; + } + + $hash = $request ? spl_object_hash($request) : null; + $called = []; + foreach ($this->callStack as $listener) { + [$eventName, $requestHash] = $this->callStack->getInfo(); + if (null === $hash || $hash === $requestHash) { + $called[] = $listener->getInfo($eventName); } } @@ -161,49 +190,71 @@ public function getCalledListeners() } /** - * {@inheritdoc} + * @return array */ - public function getNotCalledListeners() + public function getNotCalledListeners(Request $request = null) { try { $allListeners = $this->getListeners(); } catch (\Exception $e) { if (null !== $this->logger) { - $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e)); + $this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]); } // unable to retrieve the uncalled listeners - return array(); + return []; } - $notCalled = array(); - foreach ($allListeners as $eventName => $listeners) { - foreach ($listeners as $listener) { - $called = false; - if (isset($this->called[$eventName])) { - foreach ($this->called[$eventName] as $l) { - if ($l->getWrappedListener() === $listener) { - $called = true; - - break; - } - } + $hash = $request ? spl_object_hash($request) : null; + $calledListeners = []; + + if (null !== $this->callStack) { + foreach ($this->callStack as $calledListener) { + [, $requestHash] = $this->callStack->getInfo(); + + if (null === $hash || $hash === $requestHash) { + $calledListeners[] = $calledListener->getWrappedListener(); } + } + } - if (!$called) { + $notCalled = []; + foreach ($allListeners as $eventName => $listeners) { + foreach ($listeners as $listener) { + if (!\in_array($listener, $calledListeners, true)) { if (!$listener instanceof WrappedListener) { $listener = new WrappedListener($listener, null, $this->stopwatch, $this); } - $notCalled[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName); + $notCalled[] = $listener->getInfo($eventName); } } } - uasort($notCalled, array($this, 'sortListenersByPriority')); + uasort($notCalled, [$this, 'sortNotCalledListeners']); return $notCalled; } + public function getOrphanedEvents(Request $request = null): array + { + if ($request) { + return $this->orphanedEvents[spl_object_hash($request)] ?? []; + } + + if (!$this->orphanedEvents) { + return []; + } + + return array_merge(...array_values($this->orphanedEvents)); + } + + public function reset() + { + $this->callStack = null; + $this->orphanedEvents = []; + $this->currentRequestHash = ''; + } + /** * Proxies all method calls to the original event dispatcher. * @@ -212,43 +263,44 @@ public function getNotCalledListeners() * * @return mixed */ - public function __call($method, $arguments) + public function __call(string $method, array $arguments) { - return call_user_func_array(array($this->dispatcher, $method), $arguments); + return $this->dispatcher->{$method}(...$arguments); } /** * Called before dispatching the event. - * - * @param string $eventName The event name - * @param Event $event The event */ - protected function preDispatch($eventName, Event $event) + protected function beforeDispatch(string $eventName, object $event) { } /** * Called after dispatching the event. - * - * @param string $eventName The event name - * @param Event $event The event */ - protected function postDispatch($eventName, Event $event) + protected function afterDispatch(string $eventName, object $event) { } - private function preProcess($eventName) + private function preProcess(string $eventName): void { + if (!$this->dispatcher->hasListeners($eventName)) { + $this->orphanedEvents[$this->currentRequestHash][] = $eventName; + + return; + } + foreach ($this->dispatcher->getListeners($eventName) as $listener) { $priority = $this->getListenerPriority($eventName, $listener); - $wrappedListener = new WrappedListener($listener, null, $this->stopwatch, $this); + $wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this); $this->wrappedListeners[$eventName][] = $wrappedListener; $this->dispatcher->removeListener($eventName, $listener); $this->dispatcher->addListener($eventName, $wrappedListener, $priority); + $this->callStack->attach($wrappedListener, [$eventName, $this->currentRequestHash]); } } - private function postProcess($eventName) + private function postProcess(string $eventName): void { unset($this->wrappedListeners[$eventName]); $skipped = false; @@ -262,19 +314,15 @@ private function postProcess($eventName) $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority); if (null !== $this->logger) { - $context = array('event' => $eventName, 'listener' => $listener->getPretty()); + $context = ['event' => $eventName, 'listener' => $listener->getPretty()]; } if ($listener->wasCalled()) { if (null !== $this->logger) { $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context); } - - if (!isset($this->called[$eventName])) { - $this->called[$eventName] = new \SplObjectStorage(); - } - - $this->called[$eventName]->attach($listener); + } else { + $this->callStack->detach($listener); } if (null !== $this->logger && $skipped) { @@ -291,13 +339,17 @@ private function postProcess($eventName) } } - private function sortListenersByPriority($a, $b) + private function sortNotCalledListeners(array $a, array $b) { - if (is_int($a['priority']) && !is_int($b['priority'])) { + if (0 !== $cmp = strcmp($a['event'], $b['event'])) { + return $cmp; + } + + if (\is_int($a['priority']) && !\is_int($b['priority'])) { return 1; } - if (!is_int($a['priority']) && is_int($b['priority'])) { + if (!\is_int($a['priority']) && \is_int($b['priority'])) { return -1; } diff --git a/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php deleted file mode 100644 index 5483e8150..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Debug; - -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -/** - * @author Fabien Potencier - */ -interface TraceableEventDispatcherInterface extends EventDispatcherInterface -{ - /** - * Gets the called listeners. - * - * @return array An array of called listeners - */ - public function getCalledListeners(); - - /** - * Gets the not called listeners. - * - * @return array An array of not called listeners - */ - public function getNotCalledListeners(); -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/tests/integration/vendor/symfony/event-dispatcher/Debug/WrappedListener.php index 45208a19b..3916716ec 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/Debug/WrappedListener.php +++ b/tests/integration/vendor/symfony/event-dispatcher/Debug/WrappedListener.php @@ -11,46 +11,54 @@ namespace Symfony\Component\EventDispatcher\Debug; -use Symfony\Component\Stopwatch\Stopwatch; -use Symfony\Component\EventDispatcher\Event; +use Psr\EventDispatcher\StoppableEventInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\VarDumper\Caster\ClassStub; -use Symfony\Component\VarDumper\Cloner\VarCloner; /** * @author Fabien Potencier */ -class WrappedListener +final class WrappedListener { private $listener; + private $optimizedListener; private $name; private $called; private $stoppedPropagation; private $stopwatch; private $dispatcher; private $pretty; - private $data; + private $stub; + private $priority; + private static $hasClassStub; - private static $cloner; - - public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) + public function __construct($listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) { $this->listener = $listener; - $this->name = $name; + $this->optimizedListener = $listener instanceof \Closure ? $listener : (\is_callable($listener) ? \Closure::fromCallable($listener) : null); $this->stopwatch = $stopwatch; $this->dispatcher = $dispatcher; $this->called = false; $this->stoppedPropagation = false; - if (is_array($listener)) { - $this->name = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + if (\is_array($listener)) { + $this->name = \is_object($listener[0]) ? get_debug_type($listener[0]) : $listener[0]; $this->pretty = $this->name.'::'.$listener[1]; } elseif ($listener instanceof \Closure) { - $this->pretty = $this->name = 'closure'; - } elseif (is_string($listener)) { + $r = new \ReflectionFunction($listener); + if (str_contains($r->name, '{closure}')) { + $this->pretty = $this->name = 'closure'; + } elseif ($class = $r->getClosureScopeClass()) { + $this->name = $class->name; + $this->pretty = $this->name.'::'.$r->name; + } else { + $this->pretty = $this->name = $r->name; + } + } elseif (\is_string($listener)) { $this->pretty = $this->name = $listener; } else { - $this->name = get_class($listener); + $this->name = get_debug_type($listener); $this->pretty = $this->name.'::__invoke'; } @@ -58,8 +66,8 @@ public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatc $this->name = $name; } - if (null === self::$cloner) { - self::$cloner = class_exists(ClassStub::class) ? new VarCloner() : false; + if (null === self::$hasClassStub) { + self::$hasClassStub = class_exists(ClassStub::class); } } @@ -68,48 +76,51 @@ public function getWrappedListener() return $this->listener; } - public function wasCalled() + public function wasCalled(): bool { return $this->called; } - public function stoppedPropagation() + public function stoppedPropagation(): bool { return $this->stoppedPropagation; } - public function getPretty() + public function getPretty(): string { return $this->pretty; } - public function getInfo($eventName) + public function getInfo(string $eventName): array { - if (null === $this->data) { - $this->data = false !== self::$cloner ? self::$cloner->cloneVar(array(new ClassStub($this->pretty.'()', $this->listener)))->seek(0) : $this->pretty; + if (null === $this->stub) { + $this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()'; } - return array( + return [ 'event' => $eventName, - 'priority' => null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null, + 'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null), 'pretty' => $this->pretty, - 'data' => $this->data, - ); + 'stub' => $this->stub, + ]; } - public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) + public function __invoke(object $event, string $eventName, EventDispatcherInterface $dispatcher): void { + $dispatcher = $this->dispatcher ?: $dispatcher; + $this->called = true; + $this->priority = $dispatcher->getListenerPriority($eventName, $this->listener); $e = $this->stopwatch->start($this->name, 'event_listener'); - call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher); + ($this->optimizedListener ?? $this->listener)($event, $eventName, $dispatcher); if ($e->isStarted()) { $e->stop(); } - if ($event->isPropagationStopped()) { + if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) { $this->stoppedPropagation = true; } } diff --git a/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php b/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php new file mode 100644 index 000000000..6e7292b4a --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This pass allows bundles to extend the list of event aliases. + * + * @author Alexander M. Turek + */ +class AddEventAliasesPass implements CompilerPassInterface +{ + private $eventAliases; + private $eventAliasesParameter; + + public function __construct(array $eventAliases, string $eventAliasesParameter = 'event_dispatcher.event_aliases') + { + if (1 < \func_num_args()) { + trigger_deprecation('symfony/event-dispatcher', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->eventAliases = $eventAliases; + $this->eventAliasesParameter = $eventAliasesParameter; + } + + public function process(ContainerBuilder $container): void + { + $eventAliases = $container->hasParameter($this->eventAliasesParameter) ? $container->getParameter($this->eventAliasesParameter) : []; + + $container->setParameter( + $this->eventAliasesParameter, + array_merge($eventAliases, $this->eventAliases) + ); + } +} diff --git a/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php index 4636ba3ad..8eabe7d74 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php +++ b/tests/integration/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php @@ -11,42 +11,70 @@ namespace Symfony\Component\EventDispatcher\DependencyInjection; -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Contracts\EventDispatcher\Event; /** * Compiler pass to register tagged services for an event dispatcher. */ class RegisterListenersPass implements CompilerPassInterface { - /** - * @var string - */ protected $dispatcherService; - - /** - * @var string - */ protected $listenerTag; + protected $subscriberTag; + protected $eventAliasesParameter; + + private $hotPathEvents = []; + private $hotPathTagName = 'container.hot_path'; + private $noPreloadEvents = []; + private $noPreloadTagName = 'container.no_preload'; + + public function __construct(string $dispatcherService = 'event_dispatcher', string $listenerTag = 'kernel.event_listener', string $subscriberTag = 'kernel.event_subscriber', string $eventAliasesParameter = 'event_dispatcher.event_aliases') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/event-dispatcher', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + $this->eventAliasesParameter = $eventAliasesParameter; + } /** - * @var string + * @return $this */ - protected $subscriberTag; + public function setHotPathEvents(array $hotPathEvents) + { + $this->hotPathEvents = array_flip($hotPathEvents); + + if (1 < \func_num_args()) { + trigger_deprecation('symfony/event-dispatcher', '5.4', 'Configuring "$tagName" in "%s" is deprecated.', __METHOD__); + $this->hotPathTagName = func_get_arg(1); + } + + return $this; + } /** - * Constructor. - * - * @param string $dispatcherService Service name of the event dispatcher in processed container - * @param string $listenerTag Tag name used for listener - * @param string $subscriberTag Tag name used for subscribers + * @return $this */ - public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + public function setNoPreloadEvents(array $noPreloadEvents): self { - $this->dispatcherService = $dispatcherService; - $this->listenerTag = $listenerTag; - $this->subscriberTag = $subscriberTag; + $this->noPreloadEvents = array_flip($noPreloadEvents); + + if (1 < \func_num_args()) { + trigger_deprecation('symfony/event-dispatcher', '5.4', 'Configuring "$tagName" in "%s" is deprecated.', __METHOD__); + $this->noPreloadTagName = func_get_arg(1); + } + + return $this; } public function process(ContainerBuilder $container) @@ -55,60 +83,156 @@ public function process(ContainerBuilder $container) return; } - $definition = $container->findDefinition($this->dispatcherService); + $aliases = []; - foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) { - $def = $container->getDefinition($id); - if (!$def->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id)); - } + if ($container->hasParameter($this->eventAliasesParameter)) { + $aliases = $container->getParameter($this->eventAliasesParameter); + } - if ($def->isAbstract()) { - throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id)); - } + $globalDispatcherDefinition = $container->findDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) { + $noPreload = 0; foreach ($events as $event) { - $priority = isset($event['priority']) ? $event['priority'] : 0; + $priority = $event['priority'] ?? 0; if (!isset($event['event'])) { - throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + if ($container->getDefinition($id)->hasTag($this->subscriberTag)) { + continue; + } + + $event['method'] = $event['method'] ?? '__invoke'; + $event['event'] = $this->getEventFromTypeDeclaration($container, $id, $event['method']); } + $event['event'] = $aliases[$event['event']] ?? $event['event']; + if (!isset($event['method'])) { - $event['method'] = 'on'.preg_replace_callback(array( - '/(?<=\b)[a-z]/i', + $event['method'] = 'on'.preg_replace_callback([ + '/(?<=\b|_)[a-z]/i', '/[^a-z0-9]/i', - ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + ], function ($matches) { return strtoupper($matches[0]); }, $event['event']); $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + + if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) { + $event['method'] = '__invoke'; + } } - $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority)); + $dispatcherDefinition = $globalDispatcherDefinition; + if (isset($event['dispatcher'])) { + $dispatcherDefinition = $container->getDefinition($event['dispatcher']); + } + + $dispatcherDefinition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]); + + if (isset($this->hotPathEvents[$event['event']])) { + $container->getDefinition($id)->addTag($this->hotPathTagName); + } elseif (isset($this->noPreloadEvents[$event['event']])) { + ++$noPreload; + } + } + + if ($noPreload && \count($events) === $noPreload) { + $container->getDefinition($id)->addTag($this->noPreloadTagName); } } - foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) { + $extractingDispatcher = new ExtractingEventDispatcher(); + + foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $tags) { $def = $container->getDefinition($id); - if (!$def->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id)); + + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $def->getClass(); + + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(EventSubscriberInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class)); } + $class = $r->name; - if ($def->isAbstract()) { - throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id)); + $dispatcherDefinitions = []; + foreach ($tags as $attributes) { + if (!isset($attributes['dispatcher']) || isset($dispatcherDefinitions[$attributes['dispatcher']])) { + continue; + } + + $dispatcherDefinitions[$attributes['dispatcher']] = $container->getDefinition($attributes['dispatcher']); } - // We must assume that the class value has been correctly filled, even if the service is created by a factory - $class = $container->getParameterBag()->resolveValue($def->getClass()); - $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + if (!$dispatcherDefinitions) { + $dispatcherDefinitions = [$globalDispatcherDefinition]; + } - if (!is_subclass_of($class, $interface)) { - if (!class_exists($class, false)) { - throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + $noPreload = 0; + ExtractingEventDispatcher::$aliases = $aliases; + ExtractingEventDispatcher::$subscriber = $class; + $extractingDispatcher->addSubscriber($extractingDispatcher); + foreach ($extractingDispatcher->listeners as $args) { + $args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]]; + foreach ($dispatcherDefinitions as $dispatcherDefinition) { + $dispatcherDefinition->addMethodCall('addListener', $args); } - throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + if (isset($this->hotPathEvents[$args[0]])) { + $container->getDefinition($id)->addTag($this->hotPathTagName); + } elseif (isset($this->noPreloadEvents[$args[0]])) { + ++$noPreload; + } } + if ($noPreload && \count($extractingDispatcher->listeners) === $noPreload) { + $container->getDefinition($id)->addTag($this->noPreloadTagName); + } + $extractingDispatcher->listeners = []; + ExtractingEventDispatcher::$aliases = []; + } + } + + private function getEventFromTypeDeclaration(ContainerBuilder $container, string $id, string $method): string + { + if ( + null === ($class = $container->getDefinition($id)->getClass()) + || !($r = $container->getReflectionClass($class, false)) + || !$r->hasMethod($method) + || 1 > ($m = $r->getMethod($method))->getNumberOfParameters() + || !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType + || $type->isBuiltin() + || Event::class === ($name = $type->getName()) + ) { + throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + } + + return $name; + } +} + +/** + * @internal + */ +class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface +{ + public $listeners = []; + + public static $aliases = []; + public static $subscriber; - $definition->addMethodCall('addSubscriberService', array($id, $class)); + public function addListener(string $eventName, $listener, int $priority = 0) + { + $this->listeners[] = [$eventName, $listener[1], $priority]; + } + + public static function getSubscribedEvents(): array + { + $events = []; + + foreach ([self::$subscriber, 'getSubscribedEvents']() as $eventName => $params) { + $events[self::$aliases[$eventName] ?? $eventName] = $params; } + + return $events; } } diff --git a/tests/integration/vendor/symfony/event-dispatcher/Event.php b/tests/integration/vendor/symfony/event-dispatcher/Event.php deleted file mode 100644 index 9c56b2f55..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Event.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * Event is the base class for classes containing event data. - * - * This class contains no event data. It is used by events that do not pass - * state information to an event handler when an event is raised. - * - * You can call the method stopPropagation() to abort the execution of - * further listeners in your event listener. - * - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - * @author Bernhard Schussek - */ -class Event -{ - /** - * @var bool Whether no further event listeners should be triggered - */ - private $propagationStopped = false; - - /** - * Returns whether further event listeners should be triggered. - * - * @see Event::stopPropagation() - * - * @return bool Whether propagation was already stopped for this event - */ - public function isPropagationStopped() - { - return $this->propagationStopped; - } - - /** - * Stops the propagation of the event to further event listeners. - * - * If multiple event listeners are connected to the same event, no - * further event listener will be triggered once any trigger calls - * stopPropagation(). - */ - public function stopPropagation() - { - $this->propagationStopped = true; - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/EventDispatcher.php b/tests/integration/vendor/symfony/event-dispatcher/EventDispatcher.php index 2c977f473..c479a7544 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/EventDispatcher.php +++ b/tests/integration/vendor/symfony/event-dispatcher/EventDispatcher.php @@ -11,6 +11,9 @@ namespace Symfony\Component\EventDispatcher; +use Psr\EventDispatcher\StoppableEventInterface; +use Symfony\Component\EventDispatcher\Debug\WrappedListener; + /** * The EventDispatcherInterface is the central point of Symfony's event listener system. * @@ -24,23 +27,36 @@ * @author Fabien Potencier * @author Jordi Boggiano * @author Jordan Alliot + * @author Nicolas Grekas */ class EventDispatcher implements EventDispatcherInterface { - private $listeners = array(); - private $sorted = array(); + private $listeners = []; + private $sorted = []; + private $optimized; + + public function __construct() + { + if (__CLASS__ === static::class) { + $this->optimized = []; + } + } /** * {@inheritdoc} */ - public function dispatch($eventName, Event $event = null) + public function dispatch(object $event, string $eventName = null): object { - if (null === $event) { - $event = new Event(); + $eventName = $eventName ?? \get_class($event); + + if (null !== $this->optimized) { + $listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName)); + } else { + $listeners = $this->getListeners($eventName); } - if ($listeners = $this->getListeners($eventName)) { - $this->doDispatch($listeners, $eventName, $event); + if ($listeners) { + $this->callListeners($listeners, $eventName, $event); } return $event; @@ -49,11 +65,11 @@ public function dispatch($eventName, Event $event = null) /** * {@inheritdoc} */ - public function getListeners($eventName = null) + public function getListeners(string $eventName = null) { if (null !== $eventName) { - if (!isset($this->listeners[$eventName])) { - return array(); + if (empty($this->listeners[$eventName])) { + return []; } if (!isset($this->sorted[$eventName])) { @@ -75,48 +91,86 @@ public function getListeners($eventName = null) /** * {@inheritdoc} */ - public function getListenerPriority($eventName, $listener) + public function getListenerPriority(string $eventName, $listener) { - if (!isset($this->listeners[$eventName])) { - return; + if (empty($this->listeners[$eventName])) { + return null; + } + + if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) { + $listener[0] = $listener[0](); + $listener[1] = $listener[1] ?? '__invoke'; } - foreach ($this->listeners[$eventName] as $priority => $listeners) { - if (false !== in_array($listener, $listeners, true)) { - return $priority; + foreach ($this->listeners[$eventName] as $priority => &$listeners) { + foreach ($listeners as &$v) { + if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) { + $v[0] = $v[0](); + $v[1] = $v[1] ?? '__invoke'; + } + if ($v === $listener) { + return $priority; + } } } + + return null; } /** * {@inheritdoc} */ - public function hasListeners($eventName = null) + public function hasListeners(string $eventName = null) { - return (bool) count($this->getListeners($eventName)); + if (null !== $eventName) { + return !empty($this->listeners[$eventName]); + } + + foreach ($this->listeners as $eventListeners) { + if ($eventListeners) { + return true; + } + } + + return false; } /** * {@inheritdoc} */ - public function addListener($eventName, $listener, $priority = 0) + public function addListener(string $eventName, $listener, int $priority = 0) { $this->listeners[$eventName][$priority][] = $listener; - unset($this->sorted[$eventName]); + unset($this->sorted[$eventName], $this->optimized[$eventName]); } /** * {@inheritdoc} */ - public function removeListener($eventName, $listener) + public function removeListener(string $eventName, $listener) { - if (!isset($this->listeners[$eventName])) { + if (empty($this->listeners[$eventName])) { return; } - foreach ($this->listeners[$eventName] as $priority => $listeners) { - if (false !== ($key = array_search($listener, $listeners, true))) { - unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) { + $listener[0] = $listener[0](); + $listener[1] = $listener[1] ?? '__invoke'; + } + + foreach ($this->listeners[$eventName] as $priority => &$listeners) { + foreach ($listeners as $k => &$v) { + if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) { + $v[0] = $v[0](); + $v[1] = $v[1] ?? '__invoke'; + } + if ($v === $listener) { + unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]); + } + } + + if (!$listeners) { + unset($this->listeners[$eventName][$priority]); } } } @@ -127,13 +181,13 @@ public function removeListener($eventName, $listener) public function addSubscriber(EventSubscriberInterface $subscriber) { foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { - if (is_string($params)) { - $this->addListener($eventName, array($subscriber, $params)); - } elseif (is_string($params[0])) { - $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + if (\is_string($params)) { + $this->addListener($eventName, [$subscriber, $params]); + } elseif (\is_string($params[0])) { + $this->addListener($eventName, [$subscriber, $params[0]], $params[1] ?? 0); } else { foreach ($params as $listener) { - $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + $this->addListener($eventName, [$subscriber, $listener[0]], $listener[1] ?? 0); } } } @@ -145,12 +199,12 @@ public function addSubscriber(EventSubscriberInterface $subscriber) public function removeSubscriber(EventSubscriberInterface $subscriber) { foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { - if (is_array($params) && is_array($params[0])) { + if (\is_array($params) && \is_array($params[0])) { foreach ($params as $listener) { - $this->removeListener($eventName, array($subscriber, $listener[0])); + $this->removeListener($eventName, [$subscriber, $listener[0]]); } } else { - $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + $this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]); } } } @@ -163,26 +217,64 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) * * @param callable[] $listeners The event listeners * @param string $eventName The name of the event to dispatch - * @param Event $event The event object to pass to the event handlers/listeners + * @param object $event The event object to pass to the event handlers/listeners */ - protected function doDispatch($listeners, $eventName, Event $event) + protected function callListeners(iterable $listeners, string $eventName, object $event) { + $stoppable = $event instanceof StoppableEventInterface; + foreach ($listeners as $listener) { - if ($event->isPropagationStopped()) { + if ($stoppable && $event->isPropagationStopped()) { break; } - call_user_func($listener, $event, $eventName, $this); + $listener($event, $eventName, $this); } } /** * Sorts the internal list of listeners for the given event by priority. - * - * @param string $eventName The name of the event */ - private function sortListeners($eventName) + private function sortListeners(string $eventName) + { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = []; + + foreach ($this->listeners[$eventName] as &$listeners) { + foreach ($listeners as $k => &$listener) { + if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) { + $listener[0] = $listener[0](); + $listener[1] = $listener[1] ?? '__invoke'; + } + $this->sorted[$eventName][] = $listener; + } + } + } + + /** + * Optimizes the internal list of listeners for the given event by priority. + */ + private function optimizeListeners(string $eventName): array { krsort($this->listeners[$eventName]); - $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + $this->optimized[$eventName] = []; + + foreach ($this->listeners[$eventName] as &$listeners) { + foreach ($listeners as &$listener) { + $closure = &$this->optimized[$eventName][]; + if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) { + $closure = static function (...$args) use (&$listener, &$closure) { + if ($listener[0] instanceof \Closure) { + $listener[0] = $listener[0](); + $listener[1] = $listener[1] ?? '__invoke'; + } + ($closure = \Closure::fromCallable($listener))(...$args); + }; + } else { + $closure = $listener instanceof \Closure || $listener instanceof WrappedListener ? $listener : \Closure::fromCallable($listener); + } + } + } + + return $this->optimized[$eventName]; } } diff --git a/tests/integration/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/tests/integration/vendor/symfony/event-dispatcher/EventDispatcherInterface.php index 08ebf3400..cc324e1c6 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/EventDispatcherInterface.php +++ b/tests/integration/vendor/symfony/event-dispatcher/EventDispatcherInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\EventDispatcher; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface; + /** * The EventDispatcherInterface is the central point of Symfony's event listener system. * Listeners are registered on the manager and events are dispatched through the @@ -18,83 +20,51 @@ * * @author Bernhard Schussek */ -interface EventDispatcherInterface +interface EventDispatcherInterface extends ContractsEventDispatcherInterface { - /** - * Dispatches an event to all registered listeners. - * - * @param string $eventName The name of the event to dispatch. The name of - * the event is the name of the method that is - * invoked on listeners. - * @param Event $event The event to pass to the event handlers/listeners - * If not supplied, an empty Event instance is created. - * - * @return Event - */ - public function dispatch($eventName, Event $event = null); - /** * Adds an event listener that listens on the specified events. * - * @param string $eventName The event to listen on - * @param callable $listener The listener - * @param int $priority The higher this value, the earlier an event - * listener will be triggered in the chain (defaults to 0) + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) */ - public function addListener($eventName, $listener, $priority = 0); + public function addListener(string $eventName, callable $listener, int $priority = 0); /** * Adds an event subscriber. * - * The subscriber is asked for all the events he is + * The subscriber is asked for all the events it is * interested in and added as a listener for these events. - * - * @param EventSubscriberInterface $subscriber The subscriber */ public function addSubscriber(EventSubscriberInterface $subscriber); /** * Removes an event listener from the specified events. - * - * @param string $eventName The event to remove a listener from - * @param callable $listener The listener to remove */ - public function removeListener($eventName, $listener); + public function removeListener(string $eventName, callable $listener); - /** - * Removes an event subscriber. - * - * @param EventSubscriberInterface $subscriber The subscriber - */ public function removeSubscriber(EventSubscriberInterface $subscriber); /** * Gets the listeners of a specific event or all listeners sorted by descending priority. * - * @param string $eventName The name of the event - * - * @return array The event listeners for the specified event, or all event listeners by event name + * @return array */ - public function getListeners($eventName = null); + public function getListeners(string $eventName = null); /** * Gets the listener priority for a specific event. * * Returns null if the event or the listener does not exist. * - * @param string $eventName The name of the event - * @param callable $listener The listener - * - * @return int|null The event listener priority + * @return int|null */ - public function getListenerPriority($eventName, $listener); + public function getListenerPriority(string $eventName, callable $listener); /** * Checks whether an event has any registered listeners. * - * @param string $eventName The name of the event - * - * @return bool true if the specified event has any listeners, false otherwise + * @return bool */ - public function hasListeners($eventName = null); + public function hasListeners(string $eventName = null); } diff --git a/tests/integration/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/tests/integration/vendor/symfony/event-dispatcher/EventSubscriberInterface.php index 8af778919..2085e428e 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/EventSubscriberInterface.php +++ b/tests/integration/vendor/symfony/event-dispatcher/EventSubscriberInterface.php @@ -12,7 +12,7 @@ namespace Symfony\Component\EventDispatcher; /** - * An EventSubscriber knows himself what events he is interested in. + * An EventSubscriber knows itself what events it is interested in. * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes * {@link getSubscribedEvents} and registers the subscriber as a listener for all * returned events. @@ -36,11 +36,14 @@ interface EventSubscriberInterface * * For instance: * - * * array('eventName' => 'methodName') - * * array('eventName' => array('methodName', $priority)) - * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) + * * ['eventName' => 'methodName'] + * * ['eventName' => ['methodName', $priority]] + * * ['eventName' => [['methodName1', $priority], ['methodName2']]] * - * @return array The event names to listen to + * The code must not depend on runtime state as it will only be called at compile time. + * All logic depending on runtime state must be put into the individual methods handling the events. + * + * @return array> */ public static function getSubscribedEvents(); } diff --git a/tests/integration/vendor/symfony/event-dispatcher/GenericEvent.php b/tests/integration/vendor/symfony/event-dispatcher/GenericEvent.php index 2b9f40e26..b32a301ae 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/GenericEvent.php +++ b/tests/integration/vendor/symfony/event-dispatcher/GenericEvent.php @@ -11,36 +11,30 @@ namespace Symfony\Component\EventDispatcher; +use Symfony\Contracts\EventDispatcher\Event; + /** * Event encapsulation class. * * Encapsulates events thus decoupling the observer from the subject they encapsulate. * * @author Drak + * + * @implements \ArrayAccess + * @implements \IteratorAggregate */ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate { - /** - * Event subject. - * - * @var mixed usually object or callable - */ protected $subject; - - /** - * Array of arguments. - * - * @var array - */ protected $arguments; /** * Encapsulate an event with $subject and $args. * - * @param mixed $subject The subject of the event, usually an object + * @param mixed $subject The subject of the event, usually an object or a callable * @param array $arguments Arguments to store in the event */ - public function __construct($subject = null, array $arguments = array()) + public function __construct($subject = null, array $arguments = []) { $this->subject = $subject; $this->arguments = $arguments; @@ -49,7 +43,7 @@ public function __construct($subject = null, array $arguments = array()) /** * Getter for subject property. * - * @return mixed $subject The observer subject + * @return mixed */ public function getSubject() { @@ -59,13 +53,11 @@ public function getSubject() /** * Get argument by key. * - * @param string $key Key - * - * @return mixed Contents of array key + * @return mixed * - * @throws \InvalidArgumentException If key is not found. + * @throws \InvalidArgumentException if key is not found */ - public function getArgument($key) + public function getArgument(string $key) { if ($this->hasArgument($key)) { return $this->arguments[$key]; @@ -77,12 +69,11 @@ public function getArgument($key) /** * Add argument to event. * - * @param string $key Argument name - * @param mixed $value Value + * @param mixed $value Value * - * @return GenericEvent + * @return $this */ - public function setArgument($key, $value) + public function setArgument(string $key, $value) { $this->arguments[$key] = $value; @@ -102,11 +93,9 @@ public function getArguments() /** * Set args property. * - * @param array $args Arguments - * - * @return GenericEvent + * @return $this */ - public function setArguments(array $args = array()) + public function setArguments(array $args = []) { $this->arguments = $args; @@ -116,13 +105,11 @@ public function setArguments(array $args = array()) /** * Has argument. * - * @param string $key Key of arguments array - * * @return bool */ - public function hasArgument($key) + public function hasArgument(string $key) { - return array_key_exists($key, $this->arguments); + return \array_key_exists($key, $this->arguments); } /** @@ -132,8 +119,9 @@ public function hasArgument($key) * * @return mixed * - * @throws \InvalidArgumentException If key does not exist in $this->args. + * @throws \InvalidArgumentException if key does not exist in $this->args */ + #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->getArgument($key); @@ -144,7 +132,10 @@ public function offsetGet($key) * * @param string $key Array key to set * @param mixed $value Value + * + * @return void */ + #[\ReturnTypeWillChange] public function offsetSet($key, $value) { $this->setArgument($key, $value); @@ -154,7 +145,10 @@ public function offsetSet($key, $value) * ArrayAccess for unset argument. * * @param string $key Array key + * + * @return void */ + #[\ReturnTypeWillChange] public function offsetUnset($key) { if ($this->hasArgument($key)) { @@ -169,6 +163,7 @@ public function offsetUnset($key) * * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($key) { return $this->hasArgument($key); @@ -177,8 +172,9 @@ public function offsetExists($key) /** * IteratorAggregate for iterating over the object like an array. * - * @return \ArrayIterator + * @return \ArrayIterator */ + #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->arguments); diff --git a/tests/integration/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/tests/integration/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php index 7f2be8d31..568d79c3a 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php +++ b/tests/integration/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php @@ -18,18 +18,8 @@ */ class ImmutableEventDispatcher implements EventDispatcherInterface { - /** - * The proxied dispatcher. - * - * @var EventDispatcherInterface - */ private $dispatcher; - /** - * Creates an unmodifiable proxy for an event dispatcher. - * - * @param EventDispatcherInterface $dispatcher The proxied event dispatcher - */ public function __construct(EventDispatcherInterface $dispatcher) { $this->dispatcher = $dispatcher; @@ -38,15 +28,15 @@ public function __construct(EventDispatcherInterface $dispatcher) /** * {@inheritdoc} */ - public function dispatch($eventName, Event $event = null) + public function dispatch(object $event, string $eventName = null): object { - return $this->dispatcher->dispatch($eventName, $event); + return $this->dispatcher->dispatch($event, $eventName); } /** * {@inheritdoc} */ - public function addListener($eventName, $listener, $priority = 0) + public function addListener(string $eventName, $listener, int $priority = 0) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); } @@ -62,7 +52,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function removeListener($eventName, $listener) + public function removeListener(string $eventName, $listener) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); } @@ -78,7 +68,7 @@ public function removeSubscriber(EventSubscriberInterface $subscriber) /** * {@inheritdoc} */ - public function getListeners($eventName = null) + public function getListeners(string $eventName = null) { return $this->dispatcher->getListeners($eventName); } @@ -86,7 +76,7 @@ public function getListeners($eventName = null) /** * {@inheritdoc} */ - public function getListenerPriority($eventName, $listener) + public function getListenerPriority(string $eventName, $listener) { return $this->dispatcher->getListenerPriority($eventName, $listener); } @@ -94,7 +84,7 @@ public function getListenerPriority($eventName, $listener) /** * {@inheritdoc} */ - public function hasListeners($eventName = null) + public function hasListeners(string $eventName = null) { return $this->dispatcher->hasListeners($eventName); } diff --git a/tests/integration/vendor/symfony/event-dispatcher/LICENSE b/tests/integration/vendor/symfony/event-dispatcher/LICENSE index 12a74531e..88bf75bb4 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/LICENSE +++ b/tests/integration/vendor/symfony/event-dispatcher/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php b/tests/integration/vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php new file mode 100644 index 000000000..6e17c8fcc --- /dev/null +++ b/tests/integration/vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +trigger_deprecation('symfony/event-dispatcher', '5.1', '%s is deprecated, use the event dispatcher without the proxy.', LegacyEventDispatcherProxy::class); + +/** + * A helper class to provide BC/FC with the legacy signature of EventDispatcherInterface::dispatch(). + * + * @author Nicolas Grekas + * + * @deprecated since Symfony 5.1 + */ +final class LegacyEventDispatcherProxy +{ + public static function decorate(?EventDispatcherInterface $dispatcher): ?EventDispatcherInterface + { + return $dispatcher; + } +} diff --git a/tests/integration/vendor/symfony/event-dispatcher/README.md b/tests/integration/vendor/symfony/event-dispatcher/README.md index 185c3fecf..dcdb68d21 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/README.md +++ b/tests/integration/vendor/symfony/event-dispatcher/README.md @@ -8,8 +8,8 @@ them. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/components/event_dispatcher.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php deleted file mode 100644 index 30429d3f7..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php +++ /dev/null @@ -1,373 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase -{ - /* Some pseudo events */ - const preFoo = 'pre.foo'; - const postFoo = 'post.foo'; - const preBar = 'pre.bar'; - const postBar = 'post.bar'; - - /** - * @var EventDispatcher - */ - private $dispatcher; - - private $listener; - - protected function setUp() - { - $this->dispatcher = $this->createEventDispatcher(); - $this->listener = new TestEventListener(); - } - - protected function tearDown() - { - $this->dispatcher = null; - $this->listener = null; - } - - abstract protected function createEventDispatcher(); - - public function testInitialState() - { - $this->assertEquals(array(), $this->dispatcher->getListeners()); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testAddListener() - { - $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); - $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); - $this->assertCount(2, $this->dispatcher->getListeners()); - } - - public function testGetListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener1->name = '1'; - $listener2->name = '2'; - $listener3->name = '3'; - - $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); - $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); - $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); - - $expected = array( - array($listener2, 'preFoo'), - array($listener3, 'preFoo'), - array($listener1, 'preFoo'), - ); - - $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); - } - - public function testGetAllListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener4 = new TestEventListener(); - $listener5 = new TestEventListener(); - $listener6 = new TestEventListener(); - - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - $this->dispatcher->addListener('pre.foo', $listener3, 10); - $this->dispatcher->addListener('post.foo', $listener4, -10); - $this->dispatcher->addListener('post.foo', $listener5); - $this->dispatcher->addListener('post.foo', $listener6, 10); - - $expected = array( - 'pre.foo' => array($listener3, $listener2, $listener1), - 'post.foo' => array($listener6, $listener5, $listener4), - ); - - $this->assertSame($expected, $this->dispatcher->getListeners()); - } - - public function testGetListenerPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - - $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1)); - $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2)); - $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2)); - $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {})); - } - - public function testDispatch() - { - $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); - $this->dispatcher->dispatch(self::preFoo); - $this->assertTrue($this->listener->preFooInvoked); - $this->assertFalse($this->listener->postFooInvoked); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); - $event = new Event(); - $return = $this->dispatcher->dispatch(self::preFoo, $event); - $this->assertSame($event, $return); - } - - public function testDispatchForClosure() - { - $invoked = 0; - $listener = function () use (&$invoked) { - ++$invoked; - }; - $this->dispatcher->addListener('pre.foo', $listener); - $this->dispatcher->addListener('post.foo', $listener); - $this->dispatcher->dispatch(self::preFoo); - $this->assertEquals(1, $invoked); - } - - public function testStopEventPropagation() - { - $otherListener = new TestEventListener(); - - // postFoo() stops the propagation, so only one listener should - // be executed - // Manually set priority to enforce $this->listener to be called first - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); - $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); - $this->dispatcher->dispatch(self::postFoo); - $this->assertTrue($this->listener->postFooInvoked); - $this->assertFalse($otherListener->postFooInvoked); - } - - public function testDispatchByPriority() - { - $invoked = array(); - $listener1 = function () use (&$invoked) { - $invoked[] = '1'; - }; - $listener2 = function () use (&$invoked) { - $invoked[] = '2'; - }; - $listener3 = function () use (&$invoked) { - $invoked[] = '3'; - }; - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - $this->dispatcher->addListener('pre.foo', $listener3, 10); - $this->dispatcher->dispatch(self::preFoo); - $this->assertEquals(array('3', '2', '1'), $invoked); - } - - public function testRemoveListener() - { - $this->dispatcher->addListener('pre.bar', $this->listener); - $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); - $this->dispatcher->removeListener('pre.bar', $this->listener); - $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); - $this->dispatcher->removeListener('notExists', $this->listener); - } - - public function testAddSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testAddSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $listeners = $this->dispatcher->getListeners('pre.foo'); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); - } - - public function testAddSubscriberWithMultipleListeners() - { - $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $listeners = $this->dispatcher->getListeners('pre.foo'); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertEquals('preFoo2', $listeners[0][1]); - } - - public function testRemoveSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testRemoveSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - } - - public function testRemoveSubscriberWithMultipleListeners() - { - $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - } - - public function testEventReceivesTheDispatcherInstanceAsArgument() - { - $listener = new TestWithDispatcher(); - $this->dispatcher->addListener('test', array($listener, 'foo')); - $this->assertNull($listener->name); - $this->assertNull($listener->dispatcher); - $this->dispatcher->dispatch('test'); - $this->assertEquals('test', $listener->name); - $this->assertSame($this->dispatcher, $listener->dispatcher); - } - - /** - * @see https://bugs.php.net/bug.php?id=62976 - * - * This bug affects: - * - The PHP 5.3 branch for versions < 5.3.18 - * - The PHP 5.4 branch for versions < 5.4.8 - * - The PHP 5.5 branch is not affected - */ - public function testWorkaroundForPhpBug62976() - { - $dispatcher = $this->createEventDispatcher(); - $dispatcher->addListener('bug.62976', new CallableClass()); - $dispatcher->removeListener('bug.62976', function () {}); - $this->assertTrue($dispatcher->hasListeners('bug.62976')); - } - - public function testHasListenersWhenAddedCallbackListenerIsRemoved() - { - $listener = function () {}; - $this->dispatcher->addListener('foo', $listener); - $this->dispatcher->removeListener('foo', $listener); - $this->assertFalse($this->dispatcher->hasListeners()); - } - - public function testGetListenersWhenAddedCallbackListenerIsRemoved() - { - $listener = function () {}; - $this->dispatcher->addListener('foo', $listener); - $this->dispatcher->removeListener('foo', $listener); - $this->assertSame(array(), $this->dispatcher->getListeners()); - } - - public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() - { - $this->assertFalse($this->dispatcher->hasListeners('foo')); - $this->assertFalse($this->dispatcher->hasListeners()); - } -} - -class CallableClass -{ - public function __invoke() - { - } -} - -class TestEventListener -{ - public $preFooInvoked = false; - public $postFooInvoked = false; - - /* Listener methods */ - - public function preFoo(Event $e) - { - $this->preFooInvoked = true; - } - - public function postFoo(Event $e) - { - $this->postFooInvoked = true; - - $e->stopPropagation(); - } -} - -class TestWithDispatcher -{ - public $name; - public $dispatcher; - - public function foo(Event $e, $name, $dispatcher) - { - $this->name = $name; - $this->dispatcher = $dispatcher; - } -} - -class TestEventSubscriber implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); - } -} - -class TestEventSubscriberWithPriorities implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array( - 'pre.foo' => array('preFoo', 10), - 'post.foo' => array('postFoo'), - ); - } -} - -class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array('pre.foo' => array( - array('preFoo1'), - array('preFoo2', 10), - )); - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php deleted file mode 100644 index 04b1ec145..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php +++ /dev/null @@ -1,207 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest -{ - protected function createEventDispatcher() - { - $container = new Container(); - - return new ContainerAwareEventDispatcher($container); - } - - public function testAddAListenerService() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->dispatch('onEvent', $event); - } - - public function testAddASubscriberService() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $service - ->expects($this->once()) - ->method('onEventWithPriority') - ->with($event) - ; - - $service - ->expects($this->once()) - ->method('onEventNested') - ->with($event) - ; - - $container = new Container(); - $container->set('service.subscriber', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); - - $dispatcher->dispatch('onEvent', $event); - $dispatcher->dispatch('onEventWithPriority', $event); - $dispatcher->dispatch('onEventNested', $event); - } - - public function testPreventDuplicateListenerService() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); - - $dispatcher->dispatch('onEvent', $event); - } - - public function testHasListenersOnLazyLoad() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $this->assertTrue($dispatcher->hasListeners()); - - if ($dispatcher->hasListeners('onEvent')) { - $dispatcher->dispatch('onEvent'); - } - } - - public function testGetListenersOnLazyLoad() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $listeners = $dispatcher->getListeners(); - - $this->assertTrue(isset($listeners['onEvent'])); - - $this->assertCount(1, $dispatcher->getListeners('onEvent')); - } - - public function testRemoveAfterDispatch() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->dispatch('onEvent', new Event()); - $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); - $this->assertFalse($dispatcher->hasListeners('onEvent')); - } - - public function testRemoveBeforeDispatch() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); - $this->assertFalse($dispatcher->hasListeners('onEvent')); - } -} - -class Service -{ - public function onEvent(Event $e) - { - } -} - -class SubscriberService implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array( - 'onEvent' => 'onEvent', - 'onEventWithPriority' => array('onEventWithPriority', 10), - 'onEventNested' => array(array('onEventNested')), - ); - } - - public function onEvent(Event $e) - { - } - - public function onEventWithPriority(Event $e) - { - } - - public function onEventNested(Event $e) - { - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php deleted file mode 100644 index eed3ec466..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ /dev/null @@ -1,222 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests\Debug; - -use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\Stopwatch\Stopwatch; - -class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase -{ - public function testAddRemoveListener() - { - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - - $tdispatcher->addListener('foo', $listener = function () {}); - $listeners = $dispatcher->getListeners('foo'); - $this->assertCount(1, $listeners); - $this->assertSame($listener, $listeners[0]); - - $tdispatcher->removeListener('foo', $listener); - $this->assertCount(0, $dispatcher->getListeners('foo')); - } - - public function testGetListeners() - { - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - - $tdispatcher->addListener('foo', $listener = function () {}); - $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); - } - - public function testHasListeners() - { - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - - $this->assertFalse($dispatcher->hasListeners('foo')); - $this->assertFalse($tdispatcher->hasListeners('foo')); - - $tdispatcher->addListener('foo', $listener = function () {}); - $this->assertTrue($dispatcher->hasListeners('foo')); - $this->assertTrue($tdispatcher->hasListeners('foo')); - } - - public function testGetListenerPriority() - { - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - - $tdispatcher->addListener('foo', function () {}, 123); - - $listeners = $dispatcher->getListeners('foo'); - $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0])); - - // Verify that priority is preserved when listener is removed and re-added - // in preProcess() and postProcess(). - $tdispatcher->dispatch('foo', new Event()); - $listeners = $dispatcher->getListeners('foo'); - $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0])); - } - - public function testAddRemoveSubscriber() - { - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - - $subscriber = new EventSubscriber(); - - $tdispatcher->addSubscriber($subscriber); - $listeners = $dispatcher->getListeners('foo'); - $this->assertCount(1, $listeners); - $this->assertSame(array($subscriber, 'call'), $listeners[0]); - - $tdispatcher->removeSubscriber($subscriber); - $this->assertCount(0, $dispatcher->getListeners('foo')); - } - - public function testGetCalledListeners() - { - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - $tdispatcher->addListener('foo', $listener = function () {}); - - $listeners = $tdispatcher->getNotCalledListeners(); - $this->assertArrayHasKey('data', $listeners['foo.closure']); - unset($listeners['foo.closure']['data']); - $this->assertEquals(array(), $tdispatcher->getCalledListeners()); - $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 0)), $listeners); - - $tdispatcher->dispatch('foo'); - - $listeners = $tdispatcher->getCalledListeners(); - $this->assertArrayHasKey('data', $listeners['foo.closure']); - unset($listeners['foo.closure']['data']); - $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => null)), $listeners); - $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); - } - - public function testGetCalledListenersNested() - { - $tdispatcher = null; - $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); - $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { - $tdispatcher = $dispatcher; - $dispatcher->dispatch('bar'); - }); - $dispatcher->addListener('bar', function (Event $event) {}); - $dispatcher->dispatch('foo'); - $this->assertSame($dispatcher, $tdispatcher); - $this->assertCount(2, $dispatcher->getCalledListeners()); - } - - public function testLogger() - { - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); - $tdispatcher->addListener('foo', $listener1 = function () {}); - $tdispatcher->addListener('foo', $listener2 = function () {}); - - $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', array('event' => 'foo', 'listener' => 'closure')); - $logger->expects($this->at(1))->method('debug')->with('Notified event "{event}" to listener "{listener}".', array('event' => 'foo', 'listener' => 'closure')); - - $tdispatcher->dispatch('foo'); - } - - public function testLoggerWithStoppedEvent() - { - $logger = $this->getMock('Psr\Log\LoggerInterface'); - - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); - $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); - $tdispatcher->addListener('foo', $listener2 = function () {}); - - $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', array('event' => 'foo', 'listener' => 'closure')); - $logger->expects($this->at(1))->method('debug')->with('Listener "{listener}" stopped propagation of the event "{event}".', array('event' => 'foo', 'listener' => 'closure')); - $logger->expects($this->at(2))->method('debug')->with('Listener "{listener}" was not called for event "{event}".', array('event' => 'foo', 'listener' => 'closure')); - - $tdispatcher->dispatch('foo'); - } - - public function testDispatchCallListeners() - { - $called = array(); - - $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10); - $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20); - - $tdispatcher->dispatch('foo'); - - $this->assertSame(array('foo2', 'foo1'), $called); - } - - public function testDispatchNested() - { - $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); - $loop = 1; - $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { - ++$loop; - if (2 == $loop) { - $dispatcher->dispatch('foo'); - } - }); - - $dispatcher->dispatch('foo'); - } - - public function testDispatchReusedEventNested() - { - $nestedCall = false; - $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); - $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) { - $dispatcher->dispatch('bar', $e); - }); - $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) { - $nestedCall = true; - }); - - $this->assertFalse($nestedCall); - $dispatcher->dispatch('foo'); - $this->assertTrue($nestedCall); - } - - public function testListenerCanRemoveItselfWhenExecuted() - { - $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); - $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) { - $dispatcher->removeListener('foo', $listener1); - }; - $eventDispatcher->addListener('foo', $listener1); - $eventDispatcher->addListener('foo', function () {}); - $eventDispatcher->dispatch('foo'); - - $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); - } -} - -class EventSubscriber implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array('foo' => 'call'); - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php deleted file mode 100644 index 0fdd6372b..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php +++ /dev/null @@ -1,200 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; - -class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase -{ - /** - * Tests that event subscribers not implementing EventSubscriberInterface - * trigger an exception. - * - * @expectedException \InvalidArgumentException - */ - public function testEventSubscriberWithoutInterface() - { - // one service, not implementing any interface - $services = array( - 'my_event_subscriber' => array(0 => array()), - ); - - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $definition->expects($this->atLeastOnce()) - ->method('isPublic') - ->will($this->returnValue(true)); - $definition->expects($this->atLeastOnce()) - ->method('getClass') - ->will($this->returnValue('stdClass')); - - $builder = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') - ); - $builder->expects($this->any()) - ->method('hasDefinition') - ->will($this->returnValue(true)); - - // We don't test kernel.event_listener here - $builder->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->onConsecutiveCalls(array(), $services)); - - $builder->expects($this->atLeastOnce()) - ->method('getDefinition') - ->will($this->returnValue($definition)); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($builder); - } - - public function testValidEventSubscriber() - { - $services = array( - 'my_event_subscriber' => array(0 => array()), - ); - - $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); - $definition->expects($this->atLeastOnce()) - ->method('isPublic') - ->will($this->returnValue(true)); - $definition->expects($this->atLeastOnce()) - ->method('getClass') - ->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')); - - $builder = $this->getMock( - 'Symfony\Component\DependencyInjection\ContainerBuilder', - array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'findDefinition') - ); - $builder->expects($this->any()) - ->method('hasDefinition') - ->will($this->returnValue(true)); - - // We don't test kernel.event_listener here - $builder->expects($this->atLeastOnce()) - ->method('findTaggedServiceIds') - ->will($this->onConsecutiveCalls(array(), $services)); - - $builder->expects($this->atLeastOnce()) - ->method('getDefinition') - ->will($this->returnValue($definition)); - - $builder->expects($this->atLeastOnce()) - ->method('findDefinition') - ->will($this->returnValue($definition)); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($builder); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded. - */ - public function testPrivateEventListener() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array()); - $container->register('event_dispatcher', 'stdClass'); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($container); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded. - */ - public function testPrivateEventSubscriber() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array()); - $container->register('event_dispatcher', 'stdClass'); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($container); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded. - */ - public function testAbstractEventListener() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array()); - $container->register('event_dispatcher', 'stdClass'); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($container); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded. - */ - public function testAbstractEventSubscriber() - { - $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array()); - $container->register('event_dispatcher', 'stdClass'); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($container); - } - - public function testEventSubscriberResolvableClassName() - { - $container = new ContainerBuilder(); - - $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'); - $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); - $container->register('event_dispatcher', 'stdClass'); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($container); - - $definition = $container->getDefinition('event_dispatcher'); - $expected_calls = array( - array( - 'addSubscriberService', - array( - 'foo', - 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService', - ), - ), - ); - $this->assertSame($expected_calls, $definition->getMethodCalls()); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class" - */ - public function testEventSubscriberUnresolvableClassName() - { - $container = new ContainerBuilder(); - $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); - $container->register('event_dispatcher', 'stdClass'); - - $registerListenersPass = new RegisterListenersPass(); - $registerListenersPass->process($container); - } -} - -class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php deleted file mode 100644 index 5faa5c8be..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\EventDispatcher; - -class EventDispatcherTest extends AbstractEventDispatcherTest -{ - protected function createEventDispatcher() - { - return new EventDispatcher(); - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/EventTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/EventTest.php deleted file mode 100644 index 1a6f4c4ae..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/EventTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\Event; - -/** - * Test class for Event. - */ -class EventTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Symfony\Component\EventDispatcher\Event - */ - protected $event; - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - protected function setUp() - { - $this->event = new Event(); - } - - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - $this->event = null; - } - - public function testIsPropagationStopped() - { - $this->assertFalse($this->event->isPropagationStopped()); - } - - public function testStopPropagationAndIsPropagationStopped() - { - $this->event->stopPropagation(); - $this->assertTrue($this->event->isPropagationStopped()); - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php deleted file mode 100644 index aebd82dab..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\GenericEvent; - -/** - * Test class for Event. - */ -class GenericEventTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var GenericEvent - */ - private $event; - - private $subject; - - /** - * Prepares the environment before running a test. - */ - protected function setUp() - { - parent::setUp(); - - $this->subject = new \stdClass(); - $this->event = new GenericEvent($this->subject, array('name' => 'Event')); - } - - /** - * Cleans up the environment after running a test. - */ - protected function tearDown() - { - $this->subject = null; - $this->event = null; - - parent::tearDown(); - } - - public function testConstruct() - { - $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); - } - - /** - * Tests Event->getArgs(). - */ - public function testGetArguments() - { - // test getting all - $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); - } - - public function testSetArguments() - { - $result = $this->event->setArguments(array('foo' => 'bar')); - $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); - $this->assertSame($this->event, $result); - } - - public function testSetArgument() - { - $result = $this->event->setArgument('foo2', 'bar2'); - $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); - $this->assertEquals($this->event, $result); - } - - public function testGetArgument() - { - // test getting key - $this->assertEquals('Event', $this->event->getArgument('name')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testGetArgException() - { - $this->event->getArgument('nameNotExist'); - } - - public function testOffsetGet() - { - // test getting key - $this->assertEquals('Event', $this->event['name']); - - // test getting invalid arg - $this->setExpectedException('InvalidArgumentException'); - $this->assertFalse($this->event['nameNotExist']); - } - - public function testOffsetSet() - { - $this->event['foo2'] = 'bar2'; - $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); - } - - public function testOffsetUnset() - { - unset($this->event['name']); - $this->assertAttributeSame(array(), 'arguments', $this->event); - } - - public function testOffsetIsset() - { - $this->assertTrue(isset($this->event['name'])); - $this->assertFalse(isset($this->event['nameNotExist'])); - } - - public function testHasArgument() - { - $this->assertTrue($this->event->hasArgument('name')); - $this->assertFalse($this->event->hasArgument('nameNotExist')); - } - - public function testGetSubject() - { - $this->assertSame($this->subject, $this->event->getSubject()); - } - - public function testHasIterator() - { - $data = array(); - foreach ($this->event as $key => $value) { - $data[$key] = $value; - } - $this->assertEquals(array('name' => 'Event'), $data); - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/tests/integration/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php deleted file mode 100644 index 80a7e43be..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php +++ /dev/null @@ -1,105 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; - -/** - * @author Bernhard Schussek - */ -class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $innerDispatcher; - - /** - * @var ImmutableEventDispatcher - */ - private $dispatcher; - - protected function setUp() - { - $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); - $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); - } - - public function testDispatchDelegates() - { - $event = new Event(); - - $this->innerDispatcher->expects($this->once()) - ->method('dispatch') - ->with('event', $event) - ->will($this->returnValue('result')); - - $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); - } - - public function testGetListenersDelegates() - { - $this->innerDispatcher->expects($this->once()) - ->method('getListeners') - ->with('event') - ->will($this->returnValue('result')); - - $this->assertSame('result', $this->dispatcher->getListeners('event')); - } - - public function testHasListenersDelegates() - { - $this->innerDispatcher->expects($this->once()) - ->method('hasListeners') - ->with('event') - ->will($this->returnValue('result')); - - $this->assertSame('result', $this->dispatcher->hasListeners('event')); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testAddListenerDisallowed() - { - $this->dispatcher->addListener('event', function () { return 'foo'; }); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testAddSubscriberDisallowed() - { - $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); - - $this->dispatcher->addSubscriber($subscriber); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testRemoveListenerDisallowed() - { - $this->dispatcher->removeListener('event', function () { return 'foo'; }); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testRemoveSubscriberDisallowed() - { - $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); - - $this->dispatcher->removeSubscriber($subscriber); - } -} diff --git a/tests/integration/vendor/symfony/event-dispatcher/composer.json b/tests/integration/vendor/symfony/event-dispatcher/composer.json index 49031330f..32b42e408 100644 --- a/tests/integration/vendor/symfony/event-dispatcher/composer.json +++ b/tests/integration/vendor/symfony/event-dispatcher/composer.json @@ -1,7 +1,7 @@ { "name": "symfony/event-dispatcher", "type": "library", - "description": "Symfony EventDispatcher Component", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", @@ -16,14 +16,27 @@ } ], "require": { - "php": ">=5.5.9" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "symfony/dependency-injection": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0", - "symfony/config": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0", - "psr/log": "~1.0" + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0", + "psr/log": "^1|^2|^3" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" }, "suggest": { "symfony/dependency-injection": "", @@ -35,10 +48,5 @@ "/Tests/" ] }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } + "minimum-stability": "dev" } diff --git a/tests/integration/vendor/symfony/event-dispatcher/phpunit.xml.dist b/tests/integration/vendor/symfony/event-dispatcher/phpunit.xml.dist deleted file mode 100644 index ae0586e0b..000000000 --- a/tests/integration/vendor/symfony/event-dispatcher/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/tests/integration/vendor/symfony/filesystem/.gitignore b/tests/integration/vendor/symfony/filesystem/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/tests/integration/vendor/symfony/filesystem/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/tests/integration/vendor/symfony/filesystem/CHANGELOG.md b/tests/integration/vendor/symfony/filesystem/CHANGELOG.md index 09ca3508c..fcb7170ca 100644 --- a/tests/integration/vendor/symfony/filesystem/CHANGELOG.md +++ b/tests/integration/vendor/symfony/filesystem/CHANGELOG.md @@ -1,6 +1,45 @@ CHANGELOG ========= +5.4 +--- + + * Add `Path` class + * Add `$lock` argument to `Filesystem::appendToFile()` + +5.0.0 +----- + + * `Filesystem::dumpFile()` and `appendToFile()` don't accept arrays anymore + +4.4.0 +----- + + * support for passing a `null` value to `Filesystem::isAbsolutePath()` is deprecated and will be removed in 5.0 + * `tempnam()` now accepts a third argument `$suffix`. + +4.3.0 +----- + + * support for passing arrays to `Filesystem::dumpFile()` is deprecated and will be removed in 5.0 + * support for passing arrays to `Filesystem::appendToFile()` is deprecated and will be removed in 5.0 + +4.0.0 +----- + + * removed `LockHandler` + * Support for passing relative paths to `Filesystem::makePathRelative()` has been removed. + +3.4.0 +----- + + * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0 + +3.3.0 +----- + + * added `appendToFile()` to append contents to existing files + 3.2.0 ----- diff --git a/tests/integration/vendor/symfony/filesystem/Exception/ExceptionInterface.php b/tests/integration/vendor/symfony/filesystem/Exception/ExceptionInterface.php index 8f4f10aac..fc438d9f3 100644 --- a/tests/integration/vendor/symfony/filesystem/Exception/ExceptionInterface.php +++ b/tests/integration/vendor/symfony/filesystem/Exception/ExceptionInterface.php @@ -16,6 +16,6 @@ * * @author Romain Neutron */ -interface ExceptionInterface +interface ExceptionInterface extends \Throwable { } diff --git a/tests/integration/vendor/symfony/filesystem/Exception/FileNotFoundException.php b/tests/integration/vendor/symfony/filesystem/Exception/FileNotFoundException.php index bcc8fe81f..48b640809 100644 --- a/tests/integration/vendor/symfony/filesystem/Exception/FileNotFoundException.php +++ b/tests/integration/vendor/symfony/filesystem/Exception/FileNotFoundException.php @@ -19,7 +19,7 @@ */ class FileNotFoundException extends IOException { - public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) + public function __construct(string $message = null, int $code = 0, \Throwable $previous = null, string $path = null) { if (null === $message) { if (null === $path) { diff --git a/tests/integration/vendor/symfony/filesystem/Exception/IOException.php b/tests/integration/vendor/symfony/filesystem/Exception/IOException.php index 144e0e602..fea26e4dd 100644 --- a/tests/integration/vendor/symfony/filesystem/Exception/IOException.php +++ b/tests/integration/vendor/symfony/filesystem/Exception/IOException.php @@ -22,7 +22,7 @@ class IOException extends \RuntimeException implements IOExceptionInterface { private $path; - public function __construct($message, $code = 0, \Exception $previous = null, $path = null) + public function __construct(string $message, int $code = 0, \Throwable $previous = null, string $path = null) { $this->path = $path; diff --git a/tests/integration/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/tests/integration/vendor/symfony/filesystem/Exception/IOExceptionInterface.php index c11965a42..42829ab6c 100644 --- a/tests/integration/vendor/symfony/filesystem/Exception/IOExceptionInterface.php +++ b/tests/integration/vendor/symfony/filesystem/Exception/IOExceptionInterface.php @@ -21,7 +21,7 @@ interface IOExceptionInterface extends ExceptionInterface /** * Returns the associated path for the exception. * - * @return string The path + * @return string|null */ public function getPath(); } diff --git a/tests/integration/vendor/symfony/filesystem/Exception/InvalidArgumentException.php b/tests/integration/vendor/symfony/filesystem/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..abadc2002 --- /dev/null +++ b/tests/integration/vendor/symfony/filesystem/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Christian Flothmann + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/tests/integration/vendor/symfony/filesystem/Exception/RuntimeException.php b/tests/integration/vendor/symfony/filesystem/Exception/RuntimeException.php new file mode 100644 index 000000000..a7512dca7 --- /dev/null +++ b/tests/integration/vendor/symfony/filesystem/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Théo Fidry + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/tests/integration/vendor/symfony/filesystem/Filesystem.php b/tests/integration/vendor/symfony/filesystem/Filesystem.php index b24caa07a..524d17c2b 100644 --- a/tests/integration/vendor/symfony/filesystem/Filesystem.php +++ b/tests/integration/vendor/symfony/filesystem/Filesystem.php @@ -11,8 +11,9 @@ namespace Symfony\Component\Filesystem; -use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\FileNotFoundException; +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\IOException; /** * Provides basic utility to manipulate the file system. @@ -21,6 +22,8 @@ */ class Filesystem { + private static $lastError; + /** * Copies a file. * @@ -28,35 +31,32 @@ class Filesystem * If the target file is newer, it is overwritten only when the * $overwriteNewerFiles option is set to true. * - * @param string $originFile The original filename - * @param string $targetFile The target filename - * @param bool $overwriteNewerFiles If true, target files newer than origin files are overwritten - * * @throws FileNotFoundException When originFile doesn't exist * @throws IOException When copy fails */ - public function copy($originFile, $targetFile, $overwriteNewerFiles = false) + public function copy(string $originFile, string $targetFile, bool $overwriteNewerFiles = false) { - if (stream_is_local($originFile) && !is_file($originFile)) { + $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://'); + if ($originIsLocal && !is_file($originFile)) { throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); } - $this->mkdir(dirname($targetFile)); + $this->mkdir(\dirname($targetFile)); $doCopy = true; - if (!$overwriteNewerFiles && null === parse_url($originFile, PHP_URL_HOST) && is_file($targetFile)) { + if (!$overwriteNewerFiles && null === parse_url($originFile, \PHP_URL_HOST) && is_file($targetFile)) { $doCopy = filemtime($originFile) > filemtime($targetFile); } if ($doCopy) { - // https://bugs.php.net/bug.php?id=64634 - if (false === $source = @fopen($originFile, 'r')) { - throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile); + // https://bugs.php.net/64634 + if (!$source = self::box('fopen', $originFile, 'r')) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile); } // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default - if (false === $target = @fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))))) { - throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile); + if (!$target = self::box('fopen', $targetFile, 'w', false, stream_context_create(['ftp' => ['overwrite' => true]]))) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing: ', $originFile, $targetFile).self::$lastError, 0, null, $originFile); } $bytesCopied = stream_copy_to_stream($source, $target); @@ -68,11 +68,13 @@ public function copy($originFile, $targetFile, $overwriteNewerFiles = false) throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); } - // Like `cp`, preserve executable permission bits - @chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); + if ($originIsLocal) { + // Like `cp`, preserve executable permission bits + self::box('chmod', $targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); - if (stream_is_local($originFile) && $bytesCopied !== ($bytesOrigin = filesize($originFile))) { - throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + if ($bytesCopied !== $bytesOrigin = filesize($originFile)) { + throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + } } } } @@ -80,27 +82,19 @@ public function copy($originFile, $targetFile, $overwriteNewerFiles = false) /** * Creates a directory recursively. * - * @param string|array|\Traversable $dirs The directory path - * @param int $mode The directory mode + * @param string|iterable $dirs The directory path * * @throws IOException On any directory creation failure */ - public function mkdir($dirs, $mode = 0777) + public function mkdir($dirs, int $mode = 0777) { - foreach ($this->toIterator($dirs) as $dir) { + foreach ($this->toIterable($dirs) as $dir) { if (is_dir($dir)) { continue; } - if (true !== @mkdir($dir, $mode, true)) { - $error = error_get_last(); - if (!is_dir($dir)) { - // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one - if ($error) { - throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']), 0, null, $dir); - } - throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir); - } + if (!self::box('mkdir', $dir, $mode, true) && !is_dir($dir)) { + throw new IOException(sprintf('Failed to create "%s": ', $dir).self::$lastError, 0, null, $dir); } } } @@ -108,15 +102,17 @@ public function mkdir($dirs, $mode = 0777) /** * Checks the existence of files or directories. * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to check * - * @return bool true if the file exists, false otherwise + * @return bool */ public function exists($files) { - foreach ($this->toIterator($files) as $file) { - if ('\\' === DIRECTORY_SEPARATOR && strlen($file) > 258) { - throw new IOException('Could not check if file exist because path length exceeds 258 characters.', 0, null, $file); + $maxPathLength = \PHP_MAXPATHLEN - 2; + + foreach ($this->toIterable($files) as $file) { + if (\strlen($file) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); } if (!file_exists($file)) { @@ -130,18 +126,17 @@ public function exists($files) /** * Sets access and modification time of file. * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create - * @param int $time The touch time as a Unix timestamp - * @param int $atime The access time as a Unix timestamp + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to create + * @param int|null $time The touch time as a Unix timestamp, if not supplied the current system time is used + * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used * * @throws IOException When touch fails */ - public function touch($files, $time = null, $atime = null) + public function touch($files, int $time = null, int $atime = null) { - foreach ($this->toIterator($files) as $file) { - $touch = $time ? @touch($file, $time, $atime) : @touch($file); - if (true !== $touch) { - throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file); + foreach ($this->toIterable($files) as $file) { + if (!($time ? self::box('touch', $file, $time, $atime) : self::box('touch', $file))) { + throw new IOException(sprintf('Failed to touch "%s": ', $file).self::$lastError, 0, null, $file); } } } @@ -149,7 +144,7 @@ public function touch($files, $time = null, $atime = null) /** * Removes files or directories. * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to remove * * @throws IOException When removal fails */ @@ -157,27 +152,55 @@ public function remove($files) { if ($files instanceof \Traversable) { $files = iterator_to_array($files, false); - } elseif (!is_array($files)) { - $files = array($files); + } elseif (!\is_array($files)) { + $files = [$files]; } + + self::doRemove($files, false); + } + + private static function doRemove(array $files, bool $isRecursive): void + { $files = array_reverse($files); foreach ($files as $file) { if (is_link($file)) { // See https://bugs.php.net/52176 - if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError); } } elseif (is_dir($file)) { - $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); + if (!$isRecursive) { + $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.')); + + if (file_exists($tmpName)) { + try { + self::doRemove([$tmpName], true); + } catch (IOException $e) { + } + } + + if (!file_exists($tmpName) && self::box('rename', $file, $tmpName)) { + $origFile = $file; + $file = $tmpName; + } else { + $origFile = null; + } + } + + $files = new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS); + self::doRemove(iterator_to_array($files, true), true); + + if (!self::box('rmdir', $file) && file_exists($file) && !$isRecursive) { + $lastError = self::$lastError; - if (!@rmdir($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message'])); + if (null !== $origFile && self::box('rename', $file, $origFile)) { + $file = $origFile; + } + + throw new IOException(sprintf('Failed to remove directory "%s": ', $file).$lastError); } - } elseif (!@unlink($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message'])); + } elseif (!self::box('unlink', $file) && (str_contains(self::$lastError, 'Permission denied') || file_exists($file))) { + throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError); } } } @@ -185,18 +208,18 @@ public function remove($files) /** * Change mode for an array of files or directories. * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode - * @param int $mode The new mode (octal) - * @param int $umask The mode mask (octal) - * @param bool $recursive Whether change the mod recursively or not + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change mode + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not * - * @throws IOException When the change fail + * @throws IOException When the change fails */ - public function chmod($files, $mode, $umask = 0000, $recursive = false) + public function chmod($files, int $mode, int $umask = 0000, bool $recursive = false) { - foreach ($this->toIterator($files) as $file) { - if (true !== @chmod($file, $mode & ~$umask)) { - throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file); + foreach ($this->toIterable($files) as $file) { + if ((\PHP_VERSION_ID < 80000 || \is_int($mode)) && !self::box('chmod', $file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file "%s": ', $file).self::$lastError, 0, null, $file); } if ($recursive && is_dir($file) && !is_link($file)) { $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); @@ -207,25 +230,25 @@ public function chmod($files, $mode, $umask = 0000, $recursive = false) /** * Change the owner of an array of files or directories. * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner - * @param string $user The new owner user name - * @param bool $recursive Whether change the owner recursively or not + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string|int $user A user name or number + * @param bool $recursive Whether change the owner recursively or not * - * @throws IOException When the change fail + * @throws IOException When the change fails */ - public function chown($files, $user, $recursive = false) + public function chown($files, $user, bool $recursive = false) { - foreach ($this->toIterator($files) as $file) { + foreach ($this->toIterable($files) as $file) { if ($recursive && is_dir($file) && !is_link($file)) { $this->chown(new \FilesystemIterator($file), $user, true); } - if (is_link($file) && function_exists('lchown')) { - if (true !== @lchown($file, $user)) { - throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); + if (is_link($file) && \function_exists('lchown')) { + if (!self::box('lchown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file); } } else { - if (true !== @chown($file, $user)) { - throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); + if (!self::box('chown', $file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s": ', $file).self::$lastError, 0, null, $file); } } } @@ -234,25 +257,25 @@ public function chown($files, $user, $recursive = false) /** * Change the group of an array of files or directories. * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group - * @param string $group The group name - * @param bool $recursive Whether change the group recursively or not + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group + * @param string|int $group A group name or number + * @param bool $recursive Whether change the group recursively or not * - * @throws IOException When the change fail + * @throws IOException When the change fails */ - public function chgrp($files, $group, $recursive = false) + public function chgrp($files, $group, bool $recursive = false) { - foreach ($this->toIterator($files) as $file) { + foreach ($this->toIterable($files) as $file) { if ($recursive && is_dir($file) && !is_link($file)) { $this->chgrp(new \FilesystemIterator($file), $group, true); } - if (is_link($file) && function_exists('lchgrp')) { - if (true !== @lchgrp($file, $group) || (defined('HHVM_VERSION') && !posix_getgrnam($group))) { - throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + if (is_link($file) && \function_exists('lchgrp')) { + if (!self::box('lchgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file); } } else { - if (true !== @chgrp($file, $group)) { - throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + if (!self::box('chgrp', $file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s": ', $file).self::$lastError, 0, null, $file); } } } @@ -261,38 +284,39 @@ public function chgrp($files, $group, $recursive = false) /** * Renames a file or a directory. * - * @param string $origin The origin filename or directory - * @param string $target The new filename or directory - * @param bool $overwrite Whether to overwrite the target if it already exists - * * @throws IOException When target file or directory already exists * @throws IOException When origin cannot be renamed */ - public function rename($origin, $target, $overwrite = false) + public function rename(string $origin, string $target, bool $overwrite = false) { // we check that target does not exist if (!$overwrite && $this->isReadable($target)) { throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); } - if (true !== @rename($origin, $target)) { - throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); + if (!self::box('rename', $origin, $target)) { + if (is_dir($origin)) { + // See https://bugs.php.net/54097 & https://php.net/rename#113943 + $this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]); + $this->remove($origin); + + return; + } + throw new IOException(sprintf('Cannot rename "%s" to "%s": ', $origin, $target).self::$lastError, 0, null, $target); } } /** * Tells whether a file exists and is readable. * - * @param string $filename Path to the file - * - * @return bool - * * @throws IOException When windows path is longer than 258 characters */ - private function isReadable($filename) + private function isReadable(string $filename): bool { - if ('\\' === DIRECTORY_SEPARATOR && strlen($filename) > 258) { - throw new IOException('Could not check if file is readable because path length exceeds 258 characters.', 0, null, $filename); + $maxPathLength = \PHP_MAXPATHLEN - 2; + + if (\strlen($filename) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); } return is_readable($filename); @@ -301,15 +325,11 @@ private function isReadable($filename) /** * Creates a symbolic link or copy a directory. * - * @param string $originDir The origin directory path - * @param string $targetDir The symbolic link name - * @param bool $copyOnWindows Whether to copy files if on Windows - * * @throws IOException When symlink fails */ - public function symlink($originDir, $targetDir, $copyOnWindows = false) + public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false) { - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' === \DIRECTORY_SEPARATOR) { $originDir = strtr($originDir, '/', '\\'); $targetDir = strtr($targetDir, '/', '\\'); @@ -320,18 +340,16 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false) } } - $this->mkdir(dirname($targetDir)); + $this->mkdir(\dirname($targetDir)); - $ok = false; if (is_link($targetDir)) { - if (readlink($targetDir) != $originDir) { - $this->remove($targetDir); - } else { - $ok = true; + if (readlink($targetDir) === $originDir) { + return; } + $this->remove($targetDir); } - if (!$ok && true !== @symlink($originDir, $targetDir)) { + if (!self::box('symlink', $originDir, $targetDir)) { $this->linkException($originDir, $targetDir, 'symbolic'); } } @@ -339,23 +357,22 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false) /** * Creates a hard link, or several hard links to a file. * - * @param string $originFile The original file * @param string|string[] $targetFiles The target file(s) * * @throws FileNotFoundException When original file is missing or not a file * @throws IOException When link fails, including if link already exists */ - public function hardlink($originFile, $targetFiles) + public function hardlink(string $originFile, $targetFiles) { if (!$this->exists($originFile)) { throw new FileNotFoundException(null, 0, null, $originFile); } if (!is_file($originFile)) { - throw new FileNotFoundException(sprintf('Origin file "%s" is not a file', $originFile)); + throw new FileNotFoundException(sprintf('Origin file "%s" is not a file.', $originFile)); } - foreach ($this->toIterator($targetFiles) as $targetFile) { + foreach ($this->toIterable($targetFiles) as $targetFile) { if (is_file($targetFile)) { if (fileinode($originFile) === fileinode($targetFile)) { continue; @@ -363,26 +380,23 @@ public function hardlink($originFile, $targetFiles) $this->remove($targetFile); } - if (true !== @link($originFile, $targetFile)) { + if (!self::box('link', $originFile, $targetFile)) { $this->linkException($originFile, $targetFile, 'hard'); } } } /** - * @param string $origin - * @param string $target * @param string $linkType Name of the link type, typically 'symbolic' or 'hard' */ - private function linkException($origin, $target, $linkType) + private function linkException(string $origin, string $target, string $linkType) { - $report = error_get_last(); - if (is_array($report)) { - if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) { - throw new IOException(sprintf('Unable to create %s link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); + if (self::$lastError) { + if ('\\' === \DIRECTORY_SEPARATOR && str_contains(self::$lastError, 'error code(1314)')) { + throw new IOException(sprintf('Unable to create "%s" link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); } } - throw new IOException(sprintf('Failed to create %s link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target); + throw new IOException(sprintf('Failed to create "%s" link from "%s" to "%s": ', $linkType, $origin, $target).self::$lastError, 0, null, $target); } /** @@ -396,30 +410,27 @@ private function linkException($origin, $target, $linkType) * - if $path does not exist, returns null * - if $path exists, returns its absolute fully resolved final version * - * @param string $path A filesystem path - * @param bool $canonicalize Whether or not to return a canonicalized path - * * @return string|null */ - public function readlink($path, $canonicalize = false) + public function readlink(string $path, bool $canonicalize = false) { if (!$canonicalize && !is_link($path)) { - return; + return null; } if ($canonicalize) { if (!$this->exists($path)) { - return; + return null; } - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70410) { $path = readlink($path); } return realpath($path); } - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' === \DIRECTORY_SEPARATOR && \PHP_VERSION_ID < 70400) { return realpath($path); } @@ -429,22 +440,54 @@ public function readlink($path, $canonicalize = false) /** * Given an existing path, convert it to a path relative to a given starting path. * - * @param string $endPath Absolute path of target - * @param string $startPath Absolute path where traversal begins - * - * @return string Path of target relative to starting path + * @return string */ - public function makePathRelative($endPath, $startPath) + public function makePathRelative(string $endPath, string $startPath) { + if (!$this->isAbsolutePath($startPath)) { + throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath)); + } + + if (!$this->isAbsolutePath($endPath)) { + throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath)); + } + // Normalize separators on Windows - if ('\\' === DIRECTORY_SEPARATOR) { + if ('\\' === \DIRECTORY_SEPARATOR) { $endPath = str_replace('\\', '/', $endPath); $startPath = str_replace('\\', '/', $startPath); } - // Split the paths into arrays - $startPathArr = explode('/', trim($startPath, '/')); - $endPathArr = explode('/', trim($endPath, '/')); + $splitDriveLetter = function ($path) { + return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) + ? [substr($path, 2), strtoupper($path[0])] + : [$path, null]; + }; + + $splitPath = function ($path) { + $result = []; + + foreach (explode('/', trim($path, '/')) as $segment) { + if ('..' === $segment) { + array_pop($result); + } elseif ('.' !== $segment && '' !== $segment) { + $result[] = $segment; + } + } + + return $result; + }; + + [$endPath, $endDriveLetter] = $splitDriveLetter($endPath); + [$startPath, $startDriveLetter] = $splitDriveLetter($startPath); + + $startPathArr = $splitPath($startPath); + $endPathArr = $splitPath($endPath); + + if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) { + // End path is on another drive, so no relative path exists + return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : ''); + } // Find for which directory the common path stops $index = 0; @@ -453,21 +496,16 @@ public function makePathRelative($endPath, $startPath) } // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) - if (count($startPathArr) === 1 && $startPathArr[0] === '') { + if (1 === \count($startPathArr) && '' === $startPathArr[0]) { $depth = 0; } else { - $depth = count($startPathArr) - $index; + $depth = \count($startPathArr) - $index; } - // When we need to traverse from the start, and we are starting from a root path, don't add '../' - if ('/' === $startPath[0] && 0 === $index && 0 === $depth) { - $traverser = ''; - } else { - // Repeated "../" for each level need to reach the common path - $traverser = str_repeat('../', $depth); - } + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); - $endPathRemainder = implode('/', array_slice($endPathArr, $index)); + $endPathRemainder = implode('/', \array_slice($endPathArr, $index)); // Construct $endPath from traversing to the common path, then to the remaining $endPath $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : ''); @@ -478,21 +516,29 @@ public function makePathRelative($endPath, $startPath) /** * Mirrors a directory to another. * - * @param string $originDir The origin directory - * @param string $targetDir The target directory - * @param \Traversable $iterator A Traversable instance - * @param array $options An array of boolean options - * Valid options are: - * - $options['override'] Whether to override an existing file on copy or not (see copy()) - * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) - * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * Copies files and directories from the origin directory into the target directory. By default: + * + * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) + * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) + * + * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) * * @throws IOException When file type is unknown */ - public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) + public function mirror(string $originDir, string $targetDir, \Traversable $iterator = null, array $options = []) { $targetDir = rtrim($targetDir, '/\\'); $originDir = rtrim($originDir, '/\\'); + $originDirLen = \strlen($originDir); + + if (!$this->exists($originDir)) { + throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir); + } // Iterate in destination folder to remove obsolete entries if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { @@ -501,49 +547,41 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o $flags = \FilesystemIterator::SKIP_DOTS; $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); } + $targetDirLen = \strlen($targetDir); foreach ($deleteIterator as $file) { - $origin = str_replace($targetDir, $originDir, $file->getPathname()); + $origin = $originDir.substr($file->getPathname(), $targetDirLen); if (!$this->exists($origin)) { $this->remove($file); } } } - $copyOnWindows = false; - if (isset($options['copy_on_windows'])) { - $copyOnWindows = $options['copy_on_windows']; - } + $copyOnWindows = $options['copy_on_windows'] ?? false; if (null === $iterator) { $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); } - if ($this->exists($originDir)) { - $this->mkdir($targetDir); - } + $this->mkdir($targetDir); + $filesCreatedWhileMirroring = []; foreach ($iterator as $file) { - $target = str_replace($originDir, $targetDir, $file->getPathname()); + if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || isset($filesCreatedWhileMirroring[$file->getRealPath()])) { + continue; + } - if ($copyOnWindows) { - if (is_file($file)) { - $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); - } elseif (is_dir($file)) { - $this->mkdir($target); - } else { - throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); - } + $target = $targetDir.substr($file->getPathname(), $originDirLen); + $filesCreatedWhileMirroring[$target] = true; + + if (!$copyOnWindows && is_link($file)) { + $this->symlink($file->getLinkTarget(), $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, $options['override'] ?? false); } else { - if (is_link($file)) { - $this->symlink($file->getLinkTarget(), $target); - } elseif (is_dir($file)) { - $this->mkdir($target); - } elseif (is_file($file)) { - $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); - } else { - throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); - } + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); } } } @@ -551,40 +589,37 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o /** * Returns whether the file path is an absolute path. * - * @param string $file A file path - * * @return bool */ - public function isAbsolutePath($file) + public function isAbsolutePath(string $file) { - return strspn($file, '/\\', 0, 1) - || (strlen($file) > 3 && ctype_alpha($file[0]) - && substr($file, 1, 1) === ':' + return '' !== $file && (strspn($file, '/\\', 0, 1) + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] && strspn($file, '/\\', 2, 1) ) - || null !== parse_url($file, PHP_URL_SCHEME) - ; + || null !== parse_url($file, \PHP_URL_SCHEME) + ); } /** * Creates a temporary file with support for custom stream wrappers. * - * @param string $dir The directory where the temporary filename will be created * @param string $prefix The prefix of the generated temporary filename * Note: Windows uses only the first three characters of prefix + * @param string $suffix The suffix of the generated temporary filename * * @return string The new temporary filename (with path), or throw an exception on failure */ - public function tempnam($dir, $prefix) + public function tempnam(string $dir, string $prefix/*, string $suffix = ''*/) { - list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir); + $suffix = \func_num_args() > 2 ? func_get_arg(2) : ''; + [$scheme, $hierarchy] = $this->getSchemeAndHierarchy($dir); // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem - if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) { - $tmpFile = @tempnam($hierarchy, $prefix); - + if ((null === $scheme || 'file' === $scheme || 'gs' === $scheme) && '' === $suffix) { // If tempnam failed or no scheme return the filename otherwise prepend the scheme - if (false !== $tmpFile) { + if ($tmpFile = self::box('tempnam', $hierarchy, $prefix)) { if (null !== $scheme && 'gs' !== $scheme) { return $scheme.'://'.$tmpFile; } @@ -592,87 +627,130 @@ public function tempnam($dir, $prefix) return $tmpFile; } - throw new IOException('A temporary file could not be created.'); + throw new IOException('A temporary file could not be created: '.self::$lastError); } // Loop until we create a valid temp file or have reached 10 attempts for ($i = 0; $i < 10; ++$i) { // Create a unique filename - $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true); + $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true).$suffix; // Use fopen instead of file_exists as some streams do not support stat // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability - $handle = @fopen($tmpFile, 'x+'); - - // If unsuccessful restart the loop - if (false === $handle) { + if (!$handle = self::box('fopen', $tmpFile, 'x+')) { continue; } // Close the file if it was successfully opened - @fclose($handle); + self::box('fclose', $handle); return $tmpFile; } - throw new IOException('A temporary file could not be created.'); + throw new IOException('A temporary file could not be created: '.self::$lastError); } /** * Atomically dumps content into a file. * - * @param string $filename The file to be written to - * @param string $content The data to write into the file + * @param string|resource $content The data to write into the file * - * @throws IOException If the file cannot be written to. + * @throws IOException if the file cannot be written to */ - public function dumpFile($filename, $content) + public function dumpFile(string $filename, $content) { - $dir = dirname($filename); + if (\is_array($content)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); + } + + $dir = \dirname($filename); if (!is_dir($dir)) { $this->mkdir($dir); - } elseif (!is_writable($dir)) { - throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); } // Will create a temp file with 0600 access rights // when the filesystem supports chmod. $tmpFile = $this->tempnam($dir, basename($filename)); - if (false === @file_put_contents($tmpFile, $content)) { - throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); - } + try { + if (false === self::box('file_put_contents', $tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + + self::box('chmod', $tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); - @chmod($tmpFile, 0666 & ~umask()); - $this->rename($tmpFile, $filename, true); + $this->rename($tmpFile, $filename, true); + } finally { + if (file_exists($tmpFile)) { + self::box('unlink', $tmpFile); + } + } } /** - * @param mixed $files + * Appends content to an existing file. + * + * @param string|resource $content The content to append + * @param bool $lock Whether the file should be locked when writing to it * - * @return \Traversable + * @throws IOException If the file is not writable */ - private function toIterator($files) + public function appendToFile(string $filename, $content/*, bool $lock = false*/) { - if (!$files instanceof \Traversable) { - $files = new \ArrayObject(is_array($files) ? $files : array($files)); + if (\is_array($content)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be string or resource, array given.', __METHOD__)); } - return $files; + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + $lock = \func_num_args() > 2 && func_get_arg(2); + + if (false === self::box('file_put_contents', $filename, $content, \FILE_APPEND | ($lock ? \LOCK_EX : 0))) { + throw new IOException(sprintf('Failed to write file "%s": ', $filename).self::$lastError, 0, null, $filename); + } + } + + private function toIterable($files): iterable + { + return is_iterable($files) ? $files : [$files]; } /** - * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)). - * - * @param string $filename The filename to be parsed - * - * @return array The filename scheme and hierarchical part + * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> [file, tmp]). */ - private function getSchemeAndHierarchy($filename) + private function getSchemeAndHierarchy(string $filename): array { $components = explode('://', $filename, 2); - return 2 === count($components) ? array($components[0], $components[1]) : array(null, $components[0]); + return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]]; + } + + /** + * @param mixed ...$args + * + * @return mixed + */ + private static function box(callable $func, ...$args) + { + self::$lastError = null; + set_error_handler(__CLASS__.'::handleError'); + try { + return $func(...$args); + } finally { + restore_error_handler(); + } + } + + /** + * @internal + */ + public static function handleError(int $type, string $msg) + { + self::$lastError = $msg; } } diff --git a/tests/integration/vendor/symfony/filesystem/LICENSE b/tests/integration/vendor/symfony/filesystem/LICENSE index 12a74531e..88bf75bb4 100644 --- a/tests/integration/vendor/symfony/filesystem/LICENSE +++ b/tests/integration/vendor/symfony/filesystem/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/symfony/filesystem/LockHandler.php b/tests/integration/vendor/symfony/filesystem/LockHandler.php deleted file mode 100644 index 67e6f8f52..000000000 --- a/tests/integration/vendor/symfony/filesystem/LockHandler.php +++ /dev/null @@ -1,112 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem; - -use Symfony\Component\Filesystem\Exception\IOException; - -/** - * LockHandler class provides a simple abstraction to lock anything by means of - * a file lock. - * - * A locked file is created based on the lock name when calling lock(). Other - * lock handlers will not be able to lock the same name until it is released - * (explicitly by calling release() or implicitly when the instance holding the - * lock is destroyed). - * - * @author Grégoire Pineau - * @author Romain Neutron - * @author Nicolas Grekas - */ -class LockHandler -{ - private $file; - private $handle; - - /** - * @param string $name The lock name - * @param string|null $lockPath The directory to store the lock. Default values will use temporary directory - * - * @throws IOException If the lock directory could not be created or is not writable - */ - public function __construct($name, $lockPath = null) - { - $lockPath = $lockPath ?: sys_get_temp_dir(); - - if (!is_dir($lockPath)) { - $fs = new Filesystem(); - $fs->mkdir($lockPath); - } - - if (!is_writable($lockPath)) { - throw new IOException(sprintf('The directory "%s" is not writable.', $lockPath), 0, null, $lockPath); - } - - $this->file = sprintf('%s/sf.%s.%s.lock', $lockPath, preg_replace('/[^a-z0-9\._-]+/i', '-', $name), hash('sha256', $name)); - } - - /** - * Lock the resource. - * - * @param bool $blocking wait until the lock is released - * - * @return bool Returns true if the lock was acquired, false otherwise - * - * @throws IOException If the lock file could not be created or opened - */ - public function lock($blocking = false) - { - if ($this->handle) { - return true; - } - - // Silence error reporting - set_error_handler(function () {}); - - if (!$this->handle = fopen($this->file, 'r')) { - if ($this->handle = fopen($this->file, 'x')) { - chmod($this->file, 0444); - } elseif (!$this->handle = fopen($this->file, 'r')) { - usleep(100); // Give some time for chmod() to complete - $this->handle = fopen($this->file, 'r'); - } - } - restore_error_handler(); - - if (!$this->handle) { - $error = error_get_last(); - throw new IOException($error['message'], 0, null, $this->file); - } - - // On Windows, even if PHP doc says the contrary, LOCK_NB works, see - // https://bugs.php.net/54129 - if (!flock($this->handle, LOCK_EX | ($blocking ? 0 : LOCK_NB))) { - fclose($this->handle); - $this->handle = null; - - return false; - } - - return true; - } - - /** - * Release the resource. - */ - public function release() - { - if ($this->handle) { - flock($this->handle, LOCK_UN | LOCK_NB); - fclose($this->handle); - $this->handle = null; - } - } -} diff --git a/tests/integration/vendor/symfony/filesystem/Path.php b/tests/integration/vendor/symfony/filesystem/Path.php new file mode 100644 index 000000000..6ccb2e99a --- /dev/null +++ b/tests/integration/vendor/symfony/filesystem/Path.php @@ -0,0 +1,819 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\RuntimeException; + +/** + * Contains utility methods for handling path strings. + * + * The methods in this class are able to deal with both UNIX and Windows paths + * with both forward and backward slashes. All methods return normalized parts + * containing only forward slashes and no excess "." and ".." segments. + * + * @author Bernhard Schussek + * @author Thomas Schulz + * @author Théo Fidry + */ +final class Path +{ + /** + * The number of buffer entries that triggers a cleanup operation. + */ + private const CLEANUP_THRESHOLD = 1250; + + /** + * The buffer size after the cleanup operation. + */ + private const CLEANUP_SIZE = 1000; + + /** + * Buffers input/output of {@link canonicalize()}. + * + * @var array + */ + private static $buffer = []; + + /** + * @var int + */ + private static $bufferSize = 0; + + /** + * Canonicalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Furthermore, all "." and ".." segments are removed as far as possible. + * ".." segments at the beginning of relative paths are not removed. + * + * ```php + * echo Path::canonicalize("\symfony\puli\..\css\style.css"); + * // => /symfony/css/style.css + * + * echo Path::canonicalize("../css/./style.css"); + * // => ../css/style.css + * ``` + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function canonicalize(string $path): string + { + if ('' === $path) { + return ''; + } + + // This method is called by many other methods in this class. Buffer + // the canonicalized paths to make up for the severe performance + // decrease. + if (isset(self::$buffer[$path])) { + return self::$buffer[$path]; + } + + // Replace "~" with user's home directory. + if ('~' === $path[0]) { + $path = self::getHomeDirectory().mb_substr($path, 1); + } + + $path = self::normalize($path); + + [$root, $pathWithoutRoot] = self::split($path); + + $canonicalParts = self::findCanonicalParts($root, $pathWithoutRoot); + + // Add the root directory again + self::$buffer[$path] = $canonicalPath = $root.implode('/', $canonicalParts); + ++self::$bufferSize; + + // Clean up regularly to prevent memory leaks + if (self::$bufferSize > self::CLEANUP_THRESHOLD) { + self::$buffer = \array_slice(self::$buffer, -self::CLEANUP_SIZE, null, true); + self::$bufferSize = self::CLEANUP_SIZE; + } + + return $canonicalPath; + } + + /** + * Normalizes the given path. + * + * During normalization, all slashes are replaced by forward slashes ("/"). + * Contrary to {@link canonicalize()}, this method does not remove invalid + * or dot path segments. Consequently, it is much more efficient and should + * be used whenever the given path is known to be a valid, absolute system + * path. + * + * This method is able to deal with both UNIX and Windows paths. + */ + public static function normalize(string $path): string + { + return str_replace('\\', '/', $path); + } + + /** + * Returns the directory part of the path. + * + * This method is similar to PHP's dirname(), but handles various cases + * where dirname() returns a weird result: + * + * - dirname() does not accept backslashes on UNIX + * - dirname("C:/symfony") returns "C:", not "C:/" + * - dirname("C:/") returns ".", not "C:/" + * - dirname("C:") returns ".", not "C:/" + * - dirname("symfony") returns ".", not "" + * - dirname() does not canonicalize the result + * + * This method fixes these shortcomings and behaves like dirname() + * otherwise. + * + * The result is a canonical path. + * + * @return string The canonical directory part. Returns the root directory + * if the root directory is passed. Returns an empty string + * if a relative path is passed that contains no slashes. + * Returns an empty string if an empty string is passed. + */ + public static function getDirectory(string $path): string + { + if ('' === $path) { + return ''; + } + + $path = self::canonicalize($path); + + // Maintain scheme + if (false !== ($schemeSeparatorPosition = mb_strpos($path, '://'))) { + $scheme = mb_substr($path, 0, $schemeSeparatorPosition + 3); + $path = mb_substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + if (false === ($dirSeparatorPosition = strrpos($path, '/'))) { + return ''; + } + + // Directory equals root directory "/" + if (0 === $dirSeparatorPosition) { + return $scheme.'/'; + } + + // Directory equals Windows root "C:/" + if (2 === $dirSeparatorPosition && ctype_alpha($path[0]) && ':' === $path[1]) { + return $scheme.mb_substr($path, 0, 3); + } + + return $scheme.mb_substr($path, 0, $dirSeparatorPosition); + } + + /** + * Returns canonical path of the user's home directory. + * + * Supported operating systems: + * + * - UNIX + * - Windows8 and upper + * + * If your operating system or environment isn't supported, an exception is thrown. + * + * The result is a canonical path. + * + * @throws RuntimeException If your operating system or environment isn't supported + */ + public static function getHomeDirectory(): string + { + // For UNIX support + if (getenv('HOME')) { + return self::canonicalize(getenv('HOME')); + } + + // For >= Windows8 support + if (getenv('HOMEDRIVE') && getenv('HOMEPATH')) { + return self::canonicalize(getenv('HOMEDRIVE').getenv('HOMEPATH')); + } + + throw new RuntimeException("Cannot find the home directory path: Your environment or operating system isn't supported."); + } + + /** + * Returns the root directory of a path. + * + * The result is a canonical path. + * + * @return string The canonical root directory. Returns an empty string if + * the given path is relative or empty. + */ + public static function getRoot(string $path): string + { + if ('' === $path) { + return ''; + } + + // Maintain scheme + if (false !== ($schemeSeparatorPosition = strpos($path, '://'))) { + $scheme = substr($path, 0, $schemeSeparatorPosition + 3); + $path = substr($path, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + $firstCharacter = $path[0]; + + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return $scheme.'/'; + } + + $length = mb_strlen($path); + + // Windows root + if ($length > 1 && ':' === $path[1] && ctype_alpha($firstCharacter)) { + // Special case: "C:" + if (2 === $length) { + return $scheme.$path.'/'; + } + + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return $scheme.$firstCharacter.$path[1].'/'; + } + } + + return ''; + } + + /** + * Returns the file name without the extension from a file path. + * + * @param string|null $extension if specified, only that extension is cut + * off (may contain leading dot) + */ + public static function getFilenameWithoutExtension(string $path, string $extension = null) + { + if ('' === $path) { + return ''; + } + + if (null !== $extension) { + // remove extension and trailing dot + return rtrim(basename($path, $extension), '.'); + } + + return pathinfo($path, \PATHINFO_FILENAME); + } + + /** + * Returns the extension from a file path (without leading dot). + * + * @param bool $forceLowerCase forces the extension to be lower-case + */ + public static function getExtension(string $path, bool $forceLowerCase = false): string + { + if ('' === $path) { + return ''; + } + + $extension = pathinfo($path, \PATHINFO_EXTENSION); + + if ($forceLowerCase) { + $extension = self::toLower($extension); + } + + return $extension; + } + + /** + * Returns whether the path has an (or the specified) extension. + * + * @param string $path the path string + * @param string|string[]|null $extensions if null or not provided, checks if + * an extension exists, otherwise + * checks for the specified extension + * or array of extensions (with or + * without leading dot) + * @param bool $ignoreCase whether to ignore case-sensitivity + */ + public static function hasExtension(string $path, $extensions = null, bool $ignoreCase = false): bool + { + if ('' === $path) { + return false; + } + + $actualExtension = self::getExtension($path, $ignoreCase); + + // Only check if path has any extension + if ([] === $extensions || null === $extensions) { + return '' !== $actualExtension; + } + + if (\is_string($extensions)) { + $extensions = [$extensions]; + } + + foreach ($extensions as $key => $extension) { + if ($ignoreCase) { + $extension = self::toLower($extension); + } + + // remove leading '.' in extensions array + $extensions[$key] = ltrim($extension, '.'); + } + + return \in_array($actualExtension, $extensions, true); + } + + /** + * Changes the extension of a path string. + * + * @param string $path The path string with filename.ext to change. + * @param string $extension new extension (with or without leading dot) + * + * @return string the path string with new file extension + */ + public static function changeExtension(string $path, string $extension): string + { + if ('' === $path) { + return ''; + } + + $actualExtension = self::getExtension($path); + $extension = ltrim($extension, '.'); + + // No extension for paths + if ('/' === mb_substr($path, -1)) { + return $path; + } + + // No actual extension in path + if (empty($actualExtension)) { + return $path.('.' === mb_substr($path, -1) ? '' : '.').$extension; + } + + return mb_substr($path, 0, -mb_strlen($actualExtension)).$extension; + } + + public static function isAbsolute(string $path): bool + { + if ('' === $path) { + return false; + } + + // Strip scheme + if (false !== ($schemeSeparatorPosition = mb_strpos($path, '://'))) { + $path = mb_substr($path, $schemeSeparatorPosition + 3); + } + + $firstCharacter = $path[0]; + + // UNIX root "/" or "\" (Windows style) + if ('/' === $firstCharacter || '\\' === $firstCharacter) { + return true; + } + + // Windows root + if (mb_strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) { + // Special case: "C:" + if (2 === mb_strlen($path)) { + return true; + } + + // Normal case: "C:/ or "C:\" + if ('/' === $path[2] || '\\' === $path[2]) { + return true; + } + } + + return false; + } + + public static function isRelative(string $path): bool + { + return !self::isAbsolute($path); + } + + /** + * Turns a relative path into an absolute path in canonical form. + * + * Usually, the relative path is appended to the given base path. Dot + * segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * echo Path::makeAbsolute("../style.css", "/symfony/puli/css"); + * // => /symfony/puli/style.css + * ``` + * + * If an absolute path is passed, that path is returned unless its root + * directory is different than the one of the base path. In that case, an + * exception is thrown. + * + * ```php + * Path::makeAbsolute("/style.css", "/symfony/puli/css"); + * // => /style.css + * + * Path::makeAbsolute("C:/style.css", "C:/symfony/puli/css"); + * // => C:/style.css + * + * Path::makeAbsolute("C:/style.css", "/symfony/puli/css"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @param string $basePath an absolute base path + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path is an absolute path with + * a different root than the base path + */ + public static function makeAbsolute(string $path, string $basePath): string + { + if ('' === $basePath) { + throw new InvalidArgumentException(sprintf('The base path must be a non-empty string. Got: "%s".', $basePath)); + } + + if (!self::isAbsolute($basePath)) { + throw new InvalidArgumentException(sprintf('The base path "%s" is not an absolute path.', $basePath)); + } + + if (self::isAbsolute($path)) { + return self::canonicalize($path); + } + + if (false !== ($schemeSeparatorPosition = mb_strpos($basePath, '://'))) { + $scheme = mb_substr($basePath, 0, $schemeSeparatorPosition + 3); + $basePath = mb_substr($basePath, $schemeSeparatorPosition + 3); + } else { + $scheme = ''; + } + + return $scheme.self::canonicalize(rtrim($basePath, '/\\').'/'.$path); + } + + /** + * Turns a path into a relative path. + * + * The relative path is created relative to the given base path: + * + * ```php + * echo Path::makeRelative("/symfony/style.css", "/symfony/puli"); + * // => ../style.css + * ``` + * + * If a relative path is passed and the base path is absolute, the relative + * path is returned unchanged: + * + * ```php + * Path::makeRelative("style.css", "/symfony/puli/css"); + * // => style.css + * ``` + * + * If both paths are relative, the relative path is created with the + * assumption that both paths are relative to the same directory: + * + * ```php + * Path::makeRelative("style.css", "symfony/puli/css"); + * // => ../../../style.css + * ``` + * + * If both paths are absolute, their root directory must be the same, + * otherwise an exception is thrown: + * + * ```php + * Path::makeRelative("C:/symfony/style.css", "/symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the passed path is absolute, but the base path is not, an exception + * is thrown as well: + * + * ```php + * Path::makeRelative("/symfony/style.css", "symfony/puli"); + * // InvalidArgumentException + * ``` + * + * If the base path is not an absolute path, an exception is thrown. + * + * The result is a canonical path. + * + * @throws InvalidArgumentException if the base path is not absolute or if + * the given path has a different root + * than the base path + */ + public static function makeRelative(string $path, string $basePath): string + { + $path = self::canonicalize($path); + $basePath = self::canonicalize($basePath); + + [$root, $relativePath] = self::split($path); + [$baseRoot, $relativeBasePath] = self::split($basePath); + + // If the base path is given as absolute path and the path is already + // relative, consider it to be relative to the given absolute path + // already + if ('' === $root && '' !== $baseRoot) { + // If base path is already in its root + if ('' === $relativeBasePath) { + $relativePath = ltrim($relativePath, './\\'); + } + + return $relativePath; + } + + // If the passed path is absolute, but the base path is not, we + // cannot generate a relative path + if ('' !== $root && '' === $baseRoot) { + throw new InvalidArgumentException(sprintf('The absolute path "%s" cannot be made relative to the relative path "%s". You should provide an absolute base path instead.', $path, $basePath)); + } + + // Fail if the roots of the two paths are different + if ($baseRoot && $root !== $baseRoot) { + throw new InvalidArgumentException(sprintf('The path "%s" cannot be made relative to "%s", because they have different roots ("%s" and "%s").', $path, $basePath, $root, $baseRoot)); + } + + if ('' === $relativeBasePath) { + return $relativePath; + } + + // Build a "../../" prefix with as many "../" parts as necessary + $parts = explode('/', $relativePath); + $baseParts = explode('/', $relativeBasePath); + $dotDotPrefix = ''; + + // Once we found a non-matching part in the prefix, we need to add + // "../" parts for all remaining parts + $match = true; + + foreach ($baseParts as $index => $basePart) { + if ($match && isset($parts[$index]) && $basePart === $parts[$index]) { + unset($parts[$index]); + + continue; + } + + $match = false; + $dotDotPrefix .= '../'; + } + + return rtrim($dotDotPrefix.implode('/', $parts), '/'); + } + + /** + * Returns whether the given path is on the local filesystem. + */ + public static function isLocal(string $path): bool + { + return '' !== $path && false === mb_strpos($path, '://'); + } + + /** + * Returns the longest common base path in canonical form of a set of paths or + * `null` if the paths are on different Windows partitions. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/symfony/css/..' + * ); + * // => /symfony + * ``` + * + * The root is returned if no common base path can be found: + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * '/symfony/css/style.css', + * '/puli/css/..' + * ); + * // => / + * ``` + * + * If the paths are located on different Windows partitions, `null` is + * returned. + * + * ```php + * $basePath = Path::getLongestCommonBasePath( + * 'C:/symfony/css/style.css', + * 'D:/symfony/css/..' + * ); + * // => null + * ``` + */ + public static function getLongestCommonBasePath(string ...$paths): ?string + { + [$bpRoot, $basePath] = self::split(self::canonicalize(reset($paths))); + + for (next($paths); null !== key($paths) && '' !== $basePath; next($paths)) { + [$root, $path] = self::split(self::canonicalize(current($paths))); + + // If we deal with different roots (e.g. C:/ vs. D:/), it's time + // to quit + if ($root !== $bpRoot) { + return null; + } + + // Make the base path shorter until it fits into path + while (true) { + if ('.' === $basePath) { + // No more base paths + $basePath = ''; + + // next path + continue 2; + } + + // Prevent false positives for common prefixes + // see isBasePath() + if (0 === mb_strpos($path.'/', $basePath.'/')) { + // next path + continue 2; + } + + $basePath = \dirname($basePath); + } + } + + return $bpRoot.$basePath; + } + + /** + * Joins two or more path strings into a canonical path. + */ + public static function join(string ...$paths): string + { + $finalPath = null; + $wasScheme = false; + + foreach ($paths as $path) { + if ('' === $path) { + continue; + } + + if (null === $finalPath) { + // For first part we keep slashes, like '/top', 'C:\' or 'phar://' + $finalPath = $path; + $wasScheme = (false !== mb_strpos($path, '://')); + continue; + } + + // Only add slash if previous part didn't end with '/' or '\' + if (!\in_array(mb_substr($finalPath, -1), ['/', '\\'])) { + $finalPath .= '/'; + } + + // If first part included a scheme like 'phar://' we allow \current part to start with '/', otherwise trim + $finalPath .= $wasScheme ? $path : ltrim($path, '/'); + $wasScheme = false; + } + + if (null === $finalPath) { + return ''; + } + + return self::canonicalize($finalPath); + } + + /** + * Returns whether a path is a base path of another path. + * + * Dot segments ("." and "..") are removed/collapsed and all slashes turned + * into forward slashes. + * + * ```php + * Path::isBasePath('/symfony', '/symfony/css'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony'); + * // => true + * + * Path::isBasePath('/symfony', '/symfony/..'); + * // => false + * + * Path::isBasePath('/symfony', '/puli'); + * // => false + * ``` + */ + public static function isBasePath(string $basePath, string $ofPath): bool + { + $basePath = self::canonicalize($basePath); + $ofPath = self::canonicalize($ofPath); + + // Append slashes to prevent false positives when two paths have + // a common prefix, for example /base/foo and /base/foobar. + // Don't append a slash for the root "/", because then that root + // won't be discovered as common prefix ("//" is not a prefix of + // "/foobar/"). + return 0 === mb_strpos($ofPath.'/', rtrim($basePath, '/').'/'); + } + + /** + * @return non-empty-string[] + */ + private static function findCanonicalParts(string $root, string $pathWithoutRoot): array + { + $parts = explode('/', $pathWithoutRoot); + + $canonicalParts = []; + + // Collapse "." and "..", if possible + foreach ($parts as $part) { + if ('.' === $part || '' === $part) { + continue; + } + + // Collapse ".." with the previous part, if one exists + // Don't collapse ".." if the previous part is also ".." + if ('..' === $part && \count($canonicalParts) > 0 && '..' !== $canonicalParts[\count($canonicalParts) - 1]) { + array_pop($canonicalParts); + + continue; + } + + // Only add ".." prefixes for relative paths + if ('..' !== $part || '' === $root) { + $canonicalParts[] = $part; + } + } + + return $canonicalParts; + } + + /** + * Splits a canonical path into its root directory and the remainder. + * + * If the path has no root directory, an empty root directory will be + * returned. + * + * If the root directory is a Windows style partition, the resulting root + * will always contain a trailing slash. + * + * list ($root, $path) = Path::split("C:/symfony") + * // => ["C:/", "symfony"] + * + * list ($root, $path) = Path::split("C:") + * // => ["C:/", ""] + * + * @return array{string, string} an array with the root directory and the remaining relative path + */ + private static function split(string $path): array + { + if ('' === $path) { + return ['', '']; + } + + // Remember scheme as part of the root, if any + if (false !== ($schemeSeparatorPosition = mb_strpos($path, '://'))) { + $root = mb_substr($path, 0, $schemeSeparatorPosition + 3); + $path = mb_substr($path, $schemeSeparatorPosition + 3); + } else { + $root = ''; + } + + $length = mb_strlen($path); + + // Remove and remember root directory + if (0 === mb_strpos($path, '/')) { + $root .= '/'; + $path = $length > 1 ? mb_substr($path, 1) : ''; + } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) { + if (2 === $length) { + // Windows special case: "C:" + $root .= $path.'/'; + $path = ''; + } elseif ('/' === $path[2]) { + // Windows normal case: "C:/".. + $root .= mb_substr($path, 0, 3); + $path = $length > 3 ? mb_substr($path, 3) : ''; + } + } + + return [$root, $path]; + } + + private static function toLower(string $string): string + { + if (false !== $encoding = mb_detect_encoding($string)) { + return mb_strtolower($string, $encoding); + } + + return strtolower($string, $encoding); + } + + private function __construct() + { + } +} diff --git a/tests/integration/vendor/symfony/filesystem/README.md b/tests/integration/vendor/symfony/filesystem/README.md index 877ab3543..f2f6d45f7 100644 --- a/tests/integration/vendor/symfony/filesystem/README.md +++ b/tests/integration/vendor/symfony/filesystem/README.md @@ -6,8 +6,8 @@ The Filesystem component provides basic utilities for the filesystem. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/filesystem/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/components/filesystem.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/tests/integration/vendor/symfony/filesystem/Tests/ExceptionTest.php b/tests/integration/vendor/symfony/filesystem/Tests/ExceptionTest.php deleted file mode 100644 index 53bd8db76..000000000 --- a/tests/integration/vendor/symfony/filesystem/Tests/ExceptionTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Exception\FileNotFoundException; - -/** - * Test class for Filesystem. - */ -class ExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testGetPath() - { - $e = new IOException('', 0, null, '/foo'); - $this->assertEquals('/foo', $e->getPath(), 'The pass should be returned.'); - } - - public function testGeneratedMessage() - { - $e = new FileNotFoundException(null, 0, null, '/foo'); - $this->assertEquals('/foo', $e->getPath()); - $this->assertEquals('File "/foo" could not be found.', $e->getMessage(), 'A message should be generated.'); - } - - public function testGeneratedMessageWithoutPath() - { - $e = new FileNotFoundException(); - $this->assertEquals('File could not be found.', $e->getMessage(), 'A message should be generated.'); - } - - public function testCustomMessage() - { - $e = new FileNotFoundException('bar', 0, null, '/foo'); - $this->assertEquals('bar', $e->getMessage(), 'A custom message should be possible still.'); - } -} diff --git a/tests/integration/vendor/symfony/filesystem/Tests/FilesystemTest.php b/tests/integration/vendor/symfony/filesystem/Tests/FilesystemTest.php deleted file mode 100644 index c1ec8a413..000000000 --- a/tests/integration/vendor/symfony/filesystem/Tests/FilesystemTest.php +++ /dev/null @@ -1,1435 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -/** - * Test class for Filesystem. - */ -class FilesystemTest extends FilesystemTestCase -{ - public function testCopyCreatesNewFile() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyFails() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyUnreadableFileFails() - { - // skip test on Windows; PHP can't easily set file as unreadable on Windows - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test cannot run on Windows.'); - } - - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - // make sure target cannot be read - $this->filesystem->chmod($sourceFilePath, 0222); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - } - - public function testCopyOverridesExistingFileIfModified() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - touch($targetFilePath, time() - 1000); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyDoesNotOverrideExistingFileByDefault() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath)); - } - - public function testCopyOverridesExistingFileIfForced() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, true); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyWithOverrideWithReadOnlyTargetFails() - { - // skip test on Windows; PHP can't easily set file as unwritable on Windows - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('This test cannot run on Windows.'); - } - - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - // make sure target is read-only - $this->filesystem->chmod($targetFilePath, 0444); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, true); - } - - public function testCopyCreatesTargetDirectoryIfItDoesNotExist() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertTrue(is_dir($targetFileDirectory)); - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy() - { - $sourceFilePath = 'http://symfony.com/images/common/logo/logo_symfony_header.png'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($targetFilePath, 'TARGET FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, false); - - $this->assertFileExists($targetFilePath); - $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath)); - } - - public function testMkdirCreatesDirectoriesRecursively() - { - $directory = $this->workspace - .DIRECTORY_SEPARATOR.'directory' - .DIRECTORY_SEPARATOR.'sub_directory'; - - $this->filesystem->mkdir($directory); - - $this->assertTrue(is_dir($directory)); - } - - public function testMkdirCreatesDirectoriesFromArray() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $directories = array( - $basePath.'1', $basePath.'2', $basePath.'3', - ); - - $this->filesystem->mkdir($directories); - - $this->assertTrue(is_dir($basePath.'1')); - $this->assertTrue(is_dir($basePath.'2')); - $this->assertTrue(is_dir($basePath.'3')); - } - - public function testMkdirCreatesDirectoriesFromTraversableObject() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $directories = new \ArrayObject(array( - $basePath.'1', $basePath.'2', $basePath.'3', - )); - - $this->filesystem->mkdir($directories); - - $this->assertTrue(is_dir($basePath.'1')); - $this->assertTrue(is_dir($basePath.'2')); - $this->assertTrue(is_dir($basePath.'3')); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testMkdirCreatesDirectoriesFails() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $dir = $basePath.'2'; - - file_put_contents($dir, ''); - - $this->filesystem->mkdir($dir); - } - - public function testTouchCreatesEmptyFile() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'1'; - - $this->filesystem->touch($file); - - $this->assertFileExists($file); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTouchFails() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2'; - - $this->filesystem->touch($file); - } - - public function testTouchCreatesEmptyFilesFromArray() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $files = array( - $basePath.'1', $basePath.'2', $basePath.'3', - ); - - $this->filesystem->touch($files); - - $this->assertFileExists($basePath.'1'); - $this->assertFileExists($basePath.'2'); - $this->assertFileExists($basePath.'3'); - } - - public function testTouchCreatesEmptyFilesFromTraversableObject() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $files = new \ArrayObject(array( - $basePath.'1', $basePath.'2', $basePath.'3', - )); - - $this->filesystem->touch($files); - - $this->assertFileExists($basePath.'1'); - $this->assertFileExists($basePath.'2'); - $this->assertFileExists($basePath.'3'); - } - - public function testRemoveCleansFilesAndDirectoriesIteratively() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $this->filesystem->remove($basePath); - - $this->assertFileNotExists($basePath); - } - - public function testRemoveCleansArrayOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = array( - $basePath.'dir', $basePath.'file', - ); - - $this->filesystem->remove($files); - - $this->assertFileNotExists($basePath.'dir'); - $this->assertFileNotExists($basePath.'file'); - } - - public function testRemoveCleansTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', - )); - - $this->filesystem->remove($files); - - $this->assertFileNotExists($basePath.'dir'); - $this->assertFileNotExists($basePath.'file'); - } - - public function testRemoveIgnoresNonExistingFiles() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - - $files = array( - $basePath.'dir', $basePath.'file', - ); - - $this->filesystem->remove($files); - - $this->assertFileNotExists($basePath.'dir'); - } - - public function testRemoveCleansInvalidLinks() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - mkdir($basePath.'dir'); - // create symlink to nonexistent file - @symlink($basePath.'file', $basePath.'file-link'); - - // create symlink to dir using trailing forward slash - $this->filesystem->symlink($basePath.'dir/', $basePath.'dir-link'); - $this->assertTrue(is_dir($basePath.'dir-link')); - - // create symlink to nonexistent dir - rmdir($basePath.'dir'); - $this->assertFalse('\\' === DIRECTORY_SEPARATOR ? @readlink($basePath.'dir-link') : is_dir($basePath.'dir-link')); - - $this->filesystem->remove($basePath); - - $this->assertFileNotExists($basePath); - } - - public function testFilesExists() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - touch($basePath.'file1'); - mkdir($basePath.'folder'); - - $this->assertTrue($this->filesystem->exists($basePath.'file1')); - $this->assertTrue($this->filesystem->exists($basePath.'folder')); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testFilesExistsFails() - { - if ('\\' !== DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Test covers edge case on Windows only.'); - } - - $basePath = $this->workspace.'\\directory\\'; - - $oldPath = getcwd(); - mkdir($basePath); - chdir($basePath); - $file = str_repeat('T', 259 - strlen($basePath)); - $path = $basePath.$file; - exec('TYPE NUL >>'.$file); // equivalent of touch, we can not use the php touch() here because it suffers from the same limitation - $this->longPathNamesWindows[] = $path; // save this so we can clean up later - chdir($oldPath); - $this->filesystem->exists($path); - } - - public function testFilesExistsTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', - )); - - $this->assertTrue($this->filesystem->exists($files)); - } - - public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - touch($basePath.'file2'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', $basePath.'file2', - )); - - unlink($basePath.'file'); - - $this->assertFalse($this->filesystem->exists($files)); - } - - public function testInvalidFileNotExists() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - $this->assertFalse($this->filesystem->exists($basePath.time())); - } - - public function testChmodChangesFileMode() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0400); - $this->filesystem->chmod($dir, 0753); - - $this->assertFilePermissions(753, $dir); - $this->assertFilePermissions(400, $file); - } - - public function testChmodWrongMod() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; - touch($dir); - - $this->filesystem->chmod($dir, 'Wrongmode'); - } - - public function testChmodRecursive() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0400, 0000, true); - $this->filesystem->chmod($dir, 0753, 0000, true); - - $this->assertFilePermissions(753, $dir); - $this->assertFilePermissions(753, $file); - } - - public function testChmodAppliesUmask() - { - $this->markAsSkippedIfChmodIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0770, 0022); - $this->assertFilePermissions(750, $file); - } - - public function testChmodChangesModeOfArrayOfFiles() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $files = array($directory, $file); - - mkdir($directory); - touch($file); - - $this->filesystem->chmod($files, 0753); - - $this->assertFilePermissions(753, $file); - $this->assertFilePermissions(753, $directory); - } - - public function testChmodChangesModeOfTraversableFileObject() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $files = new \ArrayObject(array($directory, $file)); - - mkdir($directory); - touch($file); - - $this->filesystem->chmod($files, 0753); - - $this->assertFilePermissions(753, $file); - $this->assertFilePermissions(753, $directory); - } - - public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $subdirectory = $directory.DIRECTORY_SEPARATOR.'subdirectory'; - - mkdir($directory); - mkdir($subdirectory); - chmod($subdirectory, 0000); - - $this->filesystem->chmod($directory, 0753, 0000, true); - - $this->assertFilePermissions(753, $subdirectory); - } - - public function testChown() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chown($dir, $this->getFileOwner($dir)); - } - - public function testChownRecursive() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chown($dir, $this->getFileOwner($dir), true); - } - - public function testChownSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chown($link, $this->getFileOwner($link)); - } - - public function testChownLink() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->hardlink($file, $link); - - $this->filesystem->chown($link, $this->getFileOwner($link)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownSymlinkFails() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownLinkFails() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->hardlink($file, $link); - - $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownFail() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); - } - - public function testChgrp() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); - } - - public function testChgrpRecursive() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); - } - - public function testChgrpSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chgrp($link, $this->getFileGroup($link)); - } - - public function testChgrpLink() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->hardlink($file, $link); - - $this->filesystem->chgrp($link, $this->getFileGroup($link)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpSymlinkFails() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpLinkFails() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->hardlink($file, $link); - - $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpFail() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); - } - - public function testRename() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - touch($file); - - $this->filesystem->rename($file, $newPath); - - $this->assertFileNotExists($file); - $this->assertFileExists($newPath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testRenameThrowsExceptionIfTargetAlreadyExists() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - touch($file); - touch($newPath); - - $this->filesystem->rename($file, $newPath); - } - - public function testRenameOverwritesTheTargetIfItAlreadyExists() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - touch($file); - touch($newPath); - - $this->filesystem->rename($file, $newPath, true); - - $this->assertFileNotExists($file); - $this->assertFileExists($newPath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testRenameThrowsExceptionOnError() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid('fs_test_', true); - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - $this->filesystem->rename($file, $newPath); - } - - public function testSymlink() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support creating "broken" symlinks'); - } - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - // $file does not exists right now: creating "broken" links is a wanted feature - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - - // Create the linked file AFTER creating the link - touch($file); - - $this->assertEquals($file, readlink($link)); - } - - /** - * @depends testSymlink - */ - public function testRemoveSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - $this->filesystem->remove($link); - - $this->assertTrue(!is_link($link)); - $this->assertTrue(!is_file($link)); - $this->assertTrue(!is_dir($link)); - } - - public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - symlink($this->workspace, $link); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - public function testSymlinkIsNotOverwrittenIfAlreadyCreated() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - symlink($file, $link); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link'; - $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link1); - $this->filesystem->symlink($file, $link2); - - $this->assertTrue(is_link($link1)); - $this->assertEquals($file, readlink($link1)); - $this->assertTrue(is_link($link2)); - $this->assertEquals($file, readlink($link2)); - } - - public function testLink() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - $this->filesystem->hardlink($file, $link); - - $this->assertTrue(is_file($link)); - $this->assertEquals(fileinode($file), fileinode($link)); - } - - /** - * @depends testLink - */ - public function testRemoveLink() - { - $this->markAsSkippedIfLinkIsMissing(); - - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - $this->filesystem->remove($link); - - $this->assertTrue(!is_file($link)); - } - - public function testLinkIsOverwrittenIfPointsToDifferentTarget() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $file2 = $this->workspace.DIRECTORY_SEPARATOR.'file2'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - touch($file2); - link($file2, $link); - - $this->filesystem->hardlink($file, $link); - - $this->assertTrue(is_file($link)); - $this->assertEquals(fileinode($file), fileinode($link)); - } - - public function testLinkIsNotOverwrittenIfAlreadyCreated() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - link($file, $link); - - $this->filesystem->hardlink($file, $link); - - $this->assertTrue(is_file($link)); - $this->assertEquals(fileinode($file), fileinode($link)); - } - - public function testLinkWithSeveralTargets() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link1 = $this->workspace.DIRECTORY_SEPARATOR.'link'; - $link2 = $this->workspace.DIRECTORY_SEPARATOR.'link2'; - - touch($file); - - $this->filesystem->hardlink($file, array($link1, $link2)); - - $this->assertTrue(is_file($link1)); - $this->assertEquals(fileinode($file), fileinode($link1)); - $this->assertTrue(is_file($link2)); - $this->assertEquals(fileinode($file), fileinode($link2)); - } - - public function testLinkWithSameTarget() - { - $this->markAsSkippedIfLinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - // practically same as testLinkIsNotOverwrittenIfAlreadyCreated - $this->filesystem->hardlink($file, array($link, $link)); - - $this->assertTrue(is_file($link)); - $this->assertEquals(fileinode($file), fileinode($link)); - } - - public function testReadRelativeLink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Relative symbolic links are not supported on Windows'); - } - - $file = $this->workspace.'/file'; - $link1 = $this->workspace.'/dir/link'; - $link2 = $this->workspace.'/dir/link2'; - touch($file); - - $this->filesystem->symlink('../file', $link1); - $this->filesystem->symlink('link', $link2); - - $this->assertEquals($this->normalize('../file'), $this->filesystem->readlink($link1)); - $this->assertEquals('link', $this->filesystem->readlink($link2)); - $this->assertEquals($file, $this->filesystem->readlink($link1, true)); - $this->assertEquals($file, $this->filesystem->readlink($link2, true)); - $this->assertEquals($file, $this->filesystem->readlink($file, true)); - } - - public function testReadAbsoluteLink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->normalize($this->workspace.'/file'); - $link1 = $this->normalize($this->workspace.'/dir/link'); - $link2 = $this->normalize($this->workspace.'/dir/link2'); - touch($file); - - $this->filesystem->symlink($file, $link1); - $this->filesystem->symlink($link1, $link2); - - $this->assertEquals($file, $this->filesystem->readlink($link1)); - $this->assertEquals($link1, $this->filesystem->readlink($link2)); - $this->assertEquals($file, $this->filesystem->readlink($link1, true)); - $this->assertEquals($file, $this->filesystem->readlink($link2, true)); - $this->assertEquals($file, $this->filesystem->readlink($file, true)); - } - - public function testReadBrokenLink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support creating "broken" symlinks'); - } - - $file = $this->workspace.'/file'; - $link = $this->workspace.'/link'; - - $this->filesystem->symlink($file, $link); - - $this->assertEquals($file, $this->filesystem->readlink($link)); - $this->assertNull($this->filesystem->readlink($link, true)); - - touch($file); - $this->assertEquals($file, $this->filesystem->readlink($link, true)); - } - - public function testReadLinkDefaultPathDoesNotExist() - { - $this->assertNull($this->filesystem->readlink($this->normalize($this->workspace.'/invalid'))); - } - - public function testReadLinkDefaultPathNotLink() - { - $file = $this->normalize($this->workspace.'/file'); - touch($file); - - $this->assertNull($this->filesystem->readlink($file)); - } - - public function testReadLinkCanonicalizePath() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->normalize($this->workspace.'/file'); - mkdir($this->normalize($this->workspace.'/dir')); - touch($file); - - $this->assertEquals($file, $this->filesystem->readlink($this->normalize($this->workspace.'/dir/../file'), true)); - } - - public function testReadLinkCanonicalizedPathDoesNotExist() - { - $this->assertNull($this->filesystem->readlink($this->normalize($this->workspace.'invalid'), true)); - } - - /** - * @dataProvider providePathsForMakePathRelative - */ - public function testMakePathRelative($endPath, $startPath, $expectedPath) - { - $path = $this->filesystem->makePathRelative($endPath, $startPath); - - $this->assertEquals($expectedPath, $path); - } - - /** - * @return array - */ - public function providePathsForMakePathRelative() - { - $paths = array( - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), - array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), - array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), - array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'), - array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), - array('/aa/bb', '/aa/bb', './'), - array('/aa/bb', '/aa/bb/', './'), - array('/aa/bb/', '/aa/bb', './'), - array('/aa/bb/', '/aa/bb/', './'), - array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), - array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), - array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), - array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), - array('/aa/bb/cc', '/aa', 'bb/cc/'), - array('/aa/bb/cc', '/aa/', 'bb/cc/'), - array('/aa/bb/cc/', '/aa', 'bb/cc/'), - array('/aa/bb/cc/', '/aa/', 'bb/cc/'), - array('/a/aab/bb', '/a/aa', '../aab/bb/'), - array('/a/aab/bb', '/a/aa/', '../aab/bb/'), - array('/a/aab/bb/', '/a/aa', '../aab/bb/'), - array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), - array('/a/aab/bb/', '/', 'a/aab/bb/'), - array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'), - array('/aab/bb', '/aa', '../aab/bb/'), - array('/aab', '/aa', '../aab/'), - ); - - if ('\\' === DIRECTORY_SEPARATOR) { - $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); - } - - return $paths; - } - - public function testMirrorCopiesFilesAndDirectoriesRecursively() - { - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR; - $file1 = $directory.'file1'; - $file2 = $sourcePath.'file2'; - - mkdir($sourcePath); - mkdir($directory); - file_put_contents($file1, 'FILE1'); - file_put_contents($file2, 'FILE2'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertTrue(is_dir($targetPath.'directory')); - $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'); - $this->assertFileEquals($file2, $targetPath.'file2'); - - $this->filesystem->remove($file1); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); - $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - file_put_contents($file1, 'FILE1'); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - $this->filesystem->remove($directory); - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertFalse($this->filesystem->exists($targetPath.'directory')); - $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - } - - public function testMirrorCreatesEmptyDirectory() - { - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - - $this->filesystem->remove($sourcePath); - } - - public function testMirrorCopiesLinks() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath); - file_put_contents($sourcePath.'file1', 'FILE1'); - symlink($sourcePath.'file1', $sourcePath.'link1'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'file1', $targetPath.'link1'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - public function testMirrorCopiesLinkedDirectoryContents() - { - $this->markAsSkippedIfSymlinkIsMissing(true); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath.'nested/', 0777, true); - file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); - // Note: We symlink directory, not file - symlink($sourcePath.'nested', $sourcePath.'link1'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - public function testMirrorCopiesRelativeLinkedContents() - { - $this->markAsSkippedIfSymlinkIsMissing(true); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - $oldPath = getcwd(); - - mkdir($sourcePath.'nested/', 0777, true); - file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); - // Note: Create relative symlink - chdir($sourcePath); - symlink('nested', 'link1'); - - chdir($oldPath); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - $this->assertEquals('\\' === DIRECTORY_SEPARATOR ? realpath($sourcePath.'\nested') : 'nested', readlink($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - /** - * @dataProvider providePathsForIsAbsolutePath - */ - public function testIsAbsolutePath($path, $expectedResult) - { - $result = $this->filesystem->isAbsolutePath($path); - - $this->assertEquals($expectedResult, $result); - } - - /** - * @return array - */ - public function providePathsForIsAbsolutePath() - { - return array( - array('/var/lib', true), - array('c:\\\\var\\lib', true), - array('\\var\\lib', true), - array('var/lib', false), - array('../var/lib', false), - array('', false), - array(null, false), - ); - } - - public function testTempnam() - { - $dirname = $this->workspace; - - $filename = $this->filesystem->tempnam($dirname, 'foo'); - - $this->assertFileExists($filename); - } - - public function testTempnamWithFileScheme() - { - $scheme = 'file://'; - $dirname = $scheme.$this->workspace; - - $filename = $this->filesystem->tempnam($dirname, 'foo'); - - $this->assertStringStartsWith($scheme, $filename); - $this->assertFileExists($filename); - } - - public function testTempnamWithMockScheme() - { - stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream'); - - $scheme = 'mock://'; - $dirname = $scheme.$this->workspace; - - $filename = $this->filesystem->tempnam($dirname, 'foo'); - - $this->assertStringStartsWith($scheme, $filename); - $this->assertFileExists($filename); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTempnamWithZlibSchemeFails() - { - $scheme = 'compress.zlib://'; - $dirname = $scheme.$this->workspace; - - // The compress.zlib:// stream does not support mode x: creates the file, errors "failed to open stream: operation failed" and returns false - $this->filesystem->tempnam($dirname, 'bar'); - } - - public function testTempnamWithPHPTempSchemeFails() - { - $scheme = 'php://temp'; - $dirname = $scheme; - - $filename = $this->filesystem->tempnam($dirname, 'bar'); - - $this->assertStringStartsWith($scheme, $filename); - - // The php://temp stream deletes the file after close - $this->assertFileNotExists($filename); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTempnamWithPharSchemeFails() - { - // Skip test if Phar disabled phar.readonly must be 0 in php.ini - if (!\Phar::canWrite()) { - $this->markTestSkipped('This test cannot run when phar.readonly is 1.'); - } - - $scheme = 'phar://'; - $dirname = $scheme.$this->workspace; - $pharname = 'foo.phar'; - - new \Phar($this->workspace.'/'.$pharname, 0, $pharname); - // The phar:// stream does not support mode x: fails to create file, errors "failed to open stream: phar error: "$filename" is not a file in phar "$pharname"" and returns false - $this->filesystem->tempnam($dirname, $pharname.'/bar'); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTempnamWithHTTPSchemeFails() - { - $scheme = 'http://'; - $dirname = $scheme.$this->workspace; - - // The http:// scheme is read-only - $this->filesystem->tempnam($dirname, 'bar'); - } - - public function testTempnamOnUnwritableFallsBackToSysTmp() - { - $scheme = 'file://'; - $dirname = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'does_not_exist'; - - $filename = $this->filesystem->tempnam($dirname, 'bar'); - $realTempDir = realpath(sys_get_temp_dir()); - $this->assertStringStartsWith(rtrim($scheme.$realTempDir, DIRECTORY_SEPARATOR), $filename); - $this->assertFileExists($filename); - - // Tear down - @unlink($filename); - } - - public function testDumpFile() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - // skip mode check on Windows - if ('\\' !== DIRECTORY_SEPARATOR) { - $oldMask = umask(0002); - } - - $this->filesystem->dumpFile($filename, 'bar'); - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - - // skip mode check on Windows - if ('\\' !== DIRECTORY_SEPARATOR) { - $this->assertFilePermissions(664, $filename); - umask($oldMask); - } - } - - public function testDumpFileOverwritesAnExistingFile() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; - file_put_contents($filename, 'FOO BAR'); - - $this->filesystem->dumpFile($filename, 'bar'); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - } - - public function testDumpFileWithFileScheme() - { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('HHVM does not handle the file:// scheme correctly'); - } - - $scheme = 'file://'; - $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar'); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - } - - public function testDumpFileWithZlibScheme() - { - $scheme = 'compress.zlib://'; - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar'); - - // Zlib stat uses file:// wrapper so remove scheme - $this->assertFileExists(str_replace($scheme, '', $filename)); - $this->assertSame('bar', file_get_contents($filename)); - } - - public function testCopyShouldKeepExecutionPermission() - { - $this->markAsSkippedIfChmodIsMissing(); - - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - chmod($sourceFilePath, 0745); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFilePermissions(767, $targetFilePath); - } - - /** - * Normalize the given path (transform each blackslash into a real directory separator). - * - * @param string $path - * - * @return string - */ - private function normalize($path) - { - return str_replace('/', DIRECTORY_SEPARATOR, $path); - } -} diff --git a/tests/integration/vendor/symfony/filesystem/Tests/FilesystemTestCase.php b/tests/integration/vendor/symfony/filesystem/Tests/FilesystemTestCase.php deleted file mode 100644 index 9014ab41b..000000000 --- a/tests/integration/vendor/symfony/filesystem/Tests/FilesystemTestCase.php +++ /dev/null @@ -1,165 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\Filesystem; - -class FilesystemTestCase extends \PHPUnit_Framework_TestCase -{ - private $umask; - - protected $longPathNamesWindows = array(); - - /** - * @var \Symfony\Component\Filesystem\Filesystem - */ - protected $filesystem = null; - - /** - * @var string - */ - protected $workspace = null; - - /** - * @var null|bool Flag for hard links on Windows - */ - private static $linkOnWindows = null; - - /** - * @var null|bool Flag for symbolic links on Windows - */ - private static $symlinkOnWindows = null; - - public static function setUpBeforeClass() - { - if ('\\' === DIRECTORY_SEPARATOR) { - self::$linkOnWindows = true; - $originFile = tempnam(sys_get_temp_dir(), 'li'); - $targetFile = tempnam(sys_get_temp_dir(), 'li'); - if (true !== @link($originFile, $targetFile)) { - $report = error_get_last(); - if (is_array($report) && false !== strpos($report['message'], 'error code(1314)')) { - self::$linkOnWindows = false; - } - } else { - @unlink($targetFile); - } - - self::$symlinkOnWindows = true; - $originDir = tempnam(sys_get_temp_dir(), 'sl'); - $targetDir = tempnam(sys_get_temp_dir(), 'sl'); - if (true !== @symlink($originDir, $targetDir)) { - $report = error_get_last(); - if (is_array($report) && false !== strpos($report['message'], 'error code(1314)')) { - self::$symlinkOnWindows = false; - } - } else { - @unlink($targetDir); - } - } - } - - protected function setUp() - { - $this->umask = umask(0); - $this->filesystem = new Filesystem(); - $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); - mkdir($this->workspace, 0777, true); - $this->workspace = realpath($this->workspace); - } - - protected function tearDown() - { - if (!empty($this->longPathNamesWindows)) { - foreach ($this->longPathNamesWindows as $path) { - exec('DEL '.$path); - } - $this->longPathNamesWindows = array(); - } - - $this->filesystem->remove($this->workspace); - umask($this->umask); - } - - /** - * @param int $expectedFilePerms expected file permissions as three digits (i.e. 755) - * @param string $filePath - */ - protected function assertFilePermissions($expectedFilePerms, $filePath) - { - $actualFilePerms = (int) substr(sprintf('%o', fileperms($filePath)), -3); - $this->assertEquals( - $expectedFilePerms, - $actualFilePerms, - sprintf('File permissions for %s must be %s. Actual %s', $filePath, $expectedFilePerms, $actualFilePerms) - ); - } - - protected function getFileOwner($filepath) - { - $this->markAsSkippedIfPosixIsMissing(); - - $infos = stat($filepath); - if ($datas = posix_getpwuid($infos['uid'])) { - return $datas['name']; - } - } - - protected function getFileGroup($filepath) - { - $this->markAsSkippedIfPosixIsMissing(); - - $infos = stat($filepath); - if ($datas = posix_getgrgid($infos['gid'])) { - return $datas['name']; - } - - $this->markTestSkipped('Unable to retrieve file group name'); - } - - protected function markAsSkippedIfLinkIsMissing() - { - if (!function_exists('link')) { - $this->markTestSkipped('link is not supported'); - } - - if ('\\' === DIRECTORY_SEPARATOR && false === self::$linkOnWindows) { - $this->markTestSkipped('link requires "Create hard links" privilege on windows'); - } - } - - protected function markAsSkippedIfSymlinkIsMissing($relative = false) - { - if ('\\' === DIRECTORY_SEPARATOR && false === self::$symlinkOnWindows) { - $this->markTestSkipped('symlink requires "Create symbolic links" privilege on Windows'); - } - - // https://bugs.php.net/bug.php?id=69473 - if ($relative && '\\' === DIRECTORY_SEPARATOR && 1 === PHP_ZTS) { - $this->markTestSkipped('symlink does not support relative paths on thread safe Windows PHP versions'); - } - } - - protected function markAsSkippedIfChmodIsMissing() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('chmod is not supported on Windows'); - } - } - - protected function markAsSkippedIfPosixIsMissing() - { - if (!function_exists('posix_isatty')) { - $this->markTestSkipped('Function posix_isatty is required.'); - } - } -} diff --git a/tests/integration/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php b/tests/integration/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php deleted file mode 100644 index f14420fb6..000000000 --- a/tests/integration/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests\Fixtures\MockStream; - -/** - * Mock stream class to be used with stream_wrapper_register. - * stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream'). - */ -class MockStream -{ - /** - * Opens file or URL. - * - * @param string $path Specifies the URL that was passed to the original function - * @param string $mode The mode used to open the file, as detailed for fopen() - * @param int $options Holds additional flags set by the streams API - * @param string $opened_path If the path is opened successfully, and STREAM_USE_PATH is set in options, - * opened_path should be set to the full path of the file/resource that was actually opened - * - * @return bool - */ - public function stream_open($path, $mode, $options, &$opened_path) - { - return true; - } - - /** - * @param string $path The file path or URL to stat - * @param array $flags Holds additional flags set by the streams API - * - * @return array File stats - */ - public function url_stat($path, $flags) - { - return array(); - } -} diff --git a/tests/integration/vendor/symfony/filesystem/Tests/LockHandlerTest.php b/tests/integration/vendor/symfony/filesystem/Tests/LockHandlerTest.php deleted file mode 100644 index c7509f61e..000000000 --- a/tests/integration/vendor/symfony/filesystem/Tests/LockHandlerTest.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\LockHandler; - -class LockHandlerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - * @expectedExceptionMessage Failed to create "/a/b/c/d/e": mkdir(): Permission denied. - */ - public function testConstructWhenRepositoryDoesNotExist() - { - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - new LockHandler('lock', '/a/b/c/d/e'); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - * @expectedExceptionMessage The directory "/" is not writable. - */ - public function testConstructWhenRepositoryIsNotWriteable() - { - if (!getenv('USER') || 'root' === getenv('USER')) { - $this->markTestSkipped('This test will fail if run under superuser'); - } - new LockHandler('lock', '/'); - } - - public function testConstructSanitizeName() - { - $lock = new LockHandler(''); - - $file = sprintf('%s/sf.-php-echo-hello-word-.4b3d9d0d27ddef3a78a64685dda3a963e478659a9e5240feaf7b4173a8f28d5f.lock', sys_get_temp_dir()); - // ensure the file does not exist before the lock - @unlink($file); - - $lock->lock(); - - $this->assertFileExists($file); - - $lock->release(); - } - - public function testLockRelease() - { - $name = 'symfony-test-filesystem.lock'; - - $l1 = new LockHandler($name); - $l2 = new LockHandler($name); - - $this->assertTrue($l1->lock()); - $this->assertFalse($l2->lock()); - - $l1->release(); - - $this->assertTrue($l2->lock()); - $l2->release(); - } - - public function testLockTwice() - { - $name = 'symfony-test-filesystem.lock'; - - $lockHandler = new LockHandler($name); - - $this->assertTrue($lockHandler->lock()); - $this->assertTrue($lockHandler->lock()); - - $lockHandler->release(); - } - - public function testLockIsReleased() - { - $name = 'symfony-test-filesystem.lock'; - - $l1 = new LockHandler($name); - $l2 = new LockHandler($name); - - $this->assertTrue($l1->lock()); - $this->assertFalse($l2->lock()); - - $l1 = null; - - $this->assertTrue($l2->lock()); - $l2->release(); - } -} diff --git a/tests/integration/vendor/symfony/filesystem/composer.json b/tests/integration/vendor/symfony/filesystem/composer.json index e06f6b25a..e756104cd 100644 --- a/tests/integration/vendor/symfony/filesystem/composer.json +++ b/tests/integration/vendor/symfony/filesystem/composer.json @@ -1,7 +1,7 @@ { "name": "symfony/filesystem", "type": "library", - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", @@ -16,7 +16,10 @@ } ], "require": { - "php": ">=5.5.9" + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, @@ -24,10 +27,5 @@ "/Tests/" ] }, - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - } + "minimum-stability": "dev" } diff --git a/tests/integration/vendor/symfony/filesystem/phpunit.xml.dist b/tests/integration/vendor/symfony/filesystem/phpunit.xml.dist deleted file mode 100644 index d066ed796..000000000 --- a/tests/integration/vendor/symfony/filesystem/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - ./Tests/ - - - - - - ./ - - ./Tests - ./vendor - - - - diff --git a/tests/integration/vendor/symfony/polyfill-ctype/Ctype.php b/tests/integration/vendor/symfony/polyfill-ctype/Ctype.php new file mode 100644 index 000000000..ba75a2c95 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-ctype/Ctype.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Ctype; + +/** + * Ctype implementation through regex. + * + * @internal + * + * @author Gert de Pagter + */ +final class Ctype +{ + /** + * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. + * + * @see https://php.net/ctype-alnum + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alnum($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is a letter, FALSE otherwise. + * + * @see https://php.net/ctype-alpha + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_alpha($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); + } + + /** + * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. + * + * @see https://php.net/ctype-cntrl + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_cntrl($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); + } + + /** + * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. + * + * @see https://php.net/ctype-digit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_digit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. + * + * @see https://php.net/ctype-graph + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_graph($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); + } + + /** + * Returns TRUE if every character in text is a lowercase letter. + * + * @see https://php.net/ctype-lower + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_lower($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); + } + + /** + * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. + * + * @see https://php.net/ctype-print + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_print($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); + } + + /** + * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. + * + * @see https://php.net/ctype-punct + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_punct($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); + } + + /** + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. + * + * @see https://php.net/ctype-space + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_space($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); + } + + /** + * Returns TRUE if every character in text is an uppercase letter. + * + * @see https://php.net/ctype-upper + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_upper($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); + } + + /** + * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + * + * @see https://php.net/ctype-xdigit + * + * @param mixed $text + * + * @return bool + */ + public static function ctype_xdigit($text) + { + $text = self::convert_int_to_char_for_ctype($text, __FUNCTION__); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); + } + + /** + * Converts integers to their char versions according to normal ctype behaviour, if needed. + * + * If an integer between -128 and 255 inclusive is provided, + * it is interpreted as the ASCII value of a single character + * (negative values have 256 added in order to allow characters in the Extended ASCII range). + * Any other integer is interpreted as a string containing the decimal digits of the integer. + * + * @param mixed $int + * @param string $function + * + * @return mixed + */ + private static function convert_int_to_char_for_ctype($int, $function) + { + if (!\is_int($int)) { + return $int; + } + + if ($int < -128 || $int > 255) { + return (string) $int; + } + + if (\PHP_VERSION_ID >= 80100) { + @trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED); + } + + if ($int < 0) { + $int += 256; + } + + return \chr($int); + } +} diff --git a/tests/integration/vendor/symfony/polyfill-ctype/LICENSE b/tests/integration/vendor/symfony/polyfill-ctype/LICENSE new file mode 100644 index 000000000..3f853aaf3 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-ctype/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2019 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/polyfill-ctype/README.md b/tests/integration/vendor/symfony/polyfill-ctype/README.md new file mode 100644 index 000000000..8add1ab00 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-ctype/README.md @@ -0,0 +1,12 @@ +Symfony Polyfill / Ctype +======================== + +This component provides `ctype_*` functions to users who run php versions without the ctype extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/tests/integration/vendor/symfony/polyfill-ctype/bootstrap.php b/tests/integration/vendor/symfony/polyfill-ctype/bootstrap.php new file mode 100644 index 000000000..d54524b31 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-ctype/bootstrap.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('ctype_alnum')) { + function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit($text) { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph($text) { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower($text) { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print($text) { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct($text) { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space($text) { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper($text) { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); } +} diff --git a/tests/integration/vendor/symfony/polyfill-ctype/bootstrap80.php b/tests/integration/vendor/symfony/polyfill-ctype/bootstrap80.php new file mode 100644 index 000000000..ab2f8611d --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-ctype/bootstrap80.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (!function_exists('ctype_alnum')) { + function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); } +} +if (!function_exists('ctype_alpha')) { + function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); } +} +if (!function_exists('ctype_cntrl')) { + function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); } +} +if (!function_exists('ctype_digit')) { + function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); } +} +if (!function_exists('ctype_graph')) { + function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); } +} +if (!function_exists('ctype_lower')) { + function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); } +} +if (!function_exists('ctype_print')) { + function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); } +} +if (!function_exists('ctype_punct')) { + function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); } +} +if (!function_exists('ctype_space')) { + function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); } +} +if (!function_exists('ctype_upper')) { + function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); } +} +if (!function_exists('ctype_xdigit')) { + function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); } +} diff --git a/tests/integration/vendor/symfony/polyfill-ctype/composer.json b/tests/integration/vendor/symfony/polyfill-ctype/composer.json new file mode 100644 index 000000000..ccb8e5703 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-ctype/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/polyfill-ctype", + "type": "library", + "description": "Symfony polyfill for ctype functions", + "keywords": ["polyfill", "compatibility", "portable", "ctype"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-grapheme/Grapheme.php b/tests/integration/vendor/symfony/polyfill-intl-grapheme/Grapheme.php new file mode 100644 index 000000000..6f7c0c78d --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-grapheme/Grapheme.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Intl\Grapheme; + +\define('SYMFONY_GRAPHEME_CLUSTER_RX', ((float) \PCRE_VERSION < 10 ? (float) \PCRE_VERSION >= 8.32 : (float) \PCRE_VERSION >= 10.39) ? '\X' : Grapheme::GRAPHEME_CLUSTER_RX); + +/** + * Partial intl implementation in pure PHP. + * + * Implemented: + * - grapheme_extract - Extract a sequence of grapheme clusters from a text buffer, which must be encoded in UTF-8 + * - grapheme_stripos - Find position (in grapheme units) of first occurrence of a case-insensitive string + * - grapheme_stristr - Returns part of haystack string from the first occurrence of case-insensitive needle to the end of haystack + * - grapheme_strlen - Get string length in grapheme units + * - grapheme_strpos - Find position (in grapheme units) of first occurrence of a string + * - grapheme_strripos - Find position (in grapheme units) of last occurrence of a case-insensitive string + * - grapheme_strrpos - Find position (in grapheme units) of last occurrence of a string + * - grapheme_strstr - Returns part of haystack string from the first occurrence of needle to the end of haystack + * - grapheme_substr - Return part of a string + * + * @author Nicolas Grekas + * + * @internal + */ +final class Grapheme +{ + // (CRLF|([ZWNJ-ZWJ]|T+|L*(LV?V+|LV|LVT)T*|L+|[^Control])[Extend]*|[Control]) + // This regular expression is a work around for http://bugs.exim.org/1279 + public const GRAPHEME_CLUSTER_RX = '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])'; + + private const CASE_FOLD = [ + ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], + ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], + ]; + + public static function grapheme_extract($s, $size, $type = \GRAPHEME_EXTR_COUNT, $start = 0, &$next = 0) + { + if (0 > $start) { + $start = \strlen($s) + $start; + } + + if (!is_scalar($s)) { + $hasError = false; + set_error_handler(function () use (&$hasError) { $hasError = true; }); + $next = substr($s, $start); + restore_error_handler(); + if ($hasError) { + substr($s, $start); + $s = ''; + } else { + $s = $next; + } + } else { + $s = substr($s, $start); + } + $size = (int) $size; + $type = (int) $type; + $start = (int) $start; + + if (\GRAPHEME_EXTR_COUNT !== $type && \GRAPHEME_EXTR_MAXBYTES !== $type && \GRAPHEME_EXTR_MAXCHARS !== $type) { + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError('grapheme_extract(): Argument #3 ($type) must be one of GRAPHEME_EXTR_COUNT, GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS'); + } + + if (!isset($s[0]) || 0 > $size || 0 > $start) { + return false; + } + if (0 === $size) { + return ''; + } + + $next = $start; + + $s = preg_split('/('.SYMFONY_GRAPHEME_CLUSTER_RX.')/u', "\r\n".$s, $size + 1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); + + if (!isset($s[1])) { + return false; + } + + $i = 1; + $ret = ''; + + do { + if (\GRAPHEME_EXTR_COUNT === $type) { + --$size; + } elseif (\GRAPHEME_EXTR_MAXBYTES === $type) { + $size -= \strlen($s[$i]); + } else { + $size -= iconv_strlen($s[$i], 'UTF-8//IGNORE'); + } + + if ($size >= 0) { + $ret .= $s[$i]; + } + } while (isset($s[++$i]) && $size > 0); + + $next += \strlen($ret); + + return $ret; + } + + public static function grapheme_strlen($s) + { + preg_replace('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', '', $s, -1, $len); + + return 0 === $len && '' !== $s ? null : $len; + } + + public static function grapheme_substr($s, $start, $len = null) + { + if (null === $len) { + $len = 2147483647; + } + + preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s, $s); + + $slen = \count($s[0]); + $start = (int) $start; + + if (0 > $start) { + $start += $slen; + } + if (0 > $start) { + if (\PHP_VERSION_ID < 80000) { + return false; + } + + $start = 0; + } + if ($start >= $slen) { + return \PHP_VERSION_ID >= 80000 ? '' : false; + } + + $rem = $slen - $start; + + if (0 > $len) { + $len += $rem; + } + if (0 === $len) { + return ''; + } + if (0 > $len) { + return \PHP_VERSION_ID >= 80000 ? '' : false; + } + if ($len > $rem) { + $len = $rem; + } + + return implode('', \array_slice($s[0], $start, $len)); + } + + public static function grapheme_strpos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 0); + } + + public static function grapheme_stripos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 1); + } + + public static function grapheme_strrpos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 2); + } + + public static function grapheme_strripos($s, $needle, $offset = 0) + { + return self::grapheme_position($s, $needle, $offset, 3); + } + + public static function grapheme_stristr($s, $needle, $beforeNeedle = false) + { + return mb_stristr($s, $needle, $beforeNeedle, 'UTF-8'); + } + + public static function grapheme_strstr($s, $needle, $beforeNeedle = false) + { + return mb_strstr($s, $needle, $beforeNeedle, 'UTF-8'); + } + + private static function grapheme_position($s, $needle, $offset, $mode) + { + $needle = (string) $needle; + if (80000 > \PHP_VERSION_ID && !preg_match('/./us', $needle)) { + return false; + } + $s = (string) $s; + if (!preg_match('/./us', $s)) { + return false; + } + if ($offset > 0) { + $s = self::grapheme_substr($s, $offset); + } elseif ($offset < 0) { + if (2 > $mode) { + $offset += self::grapheme_strlen($s); + $s = self::grapheme_substr($s, $offset); + if (0 > $offset) { + $offset = 0; + } + } elseif (0 > $offset += self::grapheme_strlen($needle)) { + $s = self::grapheme_substr($s, 0, $offset); + $offset = 0; + } else { + $offset = 0; + } + } + + // As UTF-8 is self-synchronizing, and we have ensured the strings are valid UTF-8, + // we can use normal binary string functions here. For case-insensitive searches, + // case fold the strings first. + $caseInsensitive = $mode & 1; + $reverse = $mode & 2; + if ($caseInsensitive) { + // Use the same case folding mode as mbstring does for mb_stripos(). + // Stick to SIMPLE case folding to avoid changing the length of the string, which + // might result in offsets being shifted. + $mode = \defined('MB_CASE_FOLD_SIMPLE') ? \MB_CASE_FOLD_SIMPLE : \MB_CASE_LOWER; + $s = mb_convert_case($s, $mode, 'UTF-8'); + $needle = mb_convert_case($needle, $mode, 'UTF-8'); + + if (!\defined('MB_CASE_FOLD_SIMPLE')) { + $s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s); + $needle = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $needle); + } + } + if ($reverse) { + $needlePos = strrpos($s, $needle); + } else { + $needlePos = strpos($s, $needle); + } + + return false !== $needlePos ? self::grapheme_strlen(substr($s, 0, $needlePos)) + $offset : false; + } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-grapheme/LICENSE b/tests/integration/vendor/symfony/polyfill-intl-grapheme/LICENSE new file mode 100644 index 000000000..4cd8bdd30 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-grapheme/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/polyfill-intl-grapheme/README.md b/tests/integration/vendor/symfony/polyfill-intl-grapheme/README.md new file mode 100644 index 000000000..77523ea27 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-grapheme/README.md @@ -0,0 +1,31 @@ +Symfony Polyfill / Intl: Grapheme +================================= + +This component provides a partial, native PHP implementation of the +[Grapheme functions](https://php.net/intl.grapheme) from the +[Intl](https://php.net/intl) extension. + +- [`grapheme_extract`](https://php.net/grapheme_extract): Extract a sequence of grapheme + clusters from a text buffer, which must be encoded in UTF-8 +- [`grapheme_stripos`](https://php.net/grapheme_stripos): Find position (in grapheme units) + of first occurrence of a case-insensitive string +- [`grapheme_stristr`](https://php.net/grapheme_stristr): Returns part of haystack string + from the first occurrence of case-insensitive needle to the end of haystack +- [`grapheme_strlen`](https://php.net/grapheme_strlen): Get string length in grapheme units +- [`grapheme_strpos`](https://php.net/grapheme_strpos): Find position (in grapheme units) + of first occurrence of a string +- [`grapheme_strripos`](https://php.net/grapheme_strripos): Find position (in grapheme units) + of last occurrence of a case-insensitive string +- [`grapheme_strrpos`](https://php.net/grapheme_strrpos): Find position (in grapheme units) + of last occurrence of a string +- [`grapheme_strstr`](https://php.net/grapheme_strstr): Returns part of haystack string from + the first occurrence of needle to the end of haystack +- [`grapheme_substr`](https://php.net/grapheme_substr): Return part of a string + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/tests/integration/vendor/symfony/polyfill-intl-grapheme/bootstrap.php b/tests/integration/vendor/symfony/polyfill-intl-grapheme/bootstrap.php new file mode 100644 index 000000000..a9ea03c7e --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-grapheme/bootstrap.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Grapheme as p; + +if (extension_loaded('intl')) { + return; +} + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!defined('GRAPHEME_EXTR_COUNT')) { + define('GRAPHEME_EXTR_COUNT', 0); +} +if (!defined('GRAPHEME_EXTR_MAXBYTES')) { + define('GRAPHEME_EXTR_MAXBYTES', 1); +} +if (!defined('GRAPHEME_EXTR_MAXCHARS')) { + define('GRAPHEME_EXTR_MAXCHARS', 2); +} + +if (!function_exists('grapheme_extract')) { + function grapheme_extract($haystack, $size, $type = 0, $start = 0, &$next = 0) { return p\Grapheme::grapheme_extract($haystack, $size, $type, $start, $next); } +} +if (!function_exists('grapheme_stripos')) { + function grapheme_stripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_stripos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_stristr')) { + function grapheme_stristr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_stristr($haystack, $needle, $beforeNeedle); } +} +if (!function_exists('grapheme_strlen')) { + function grapheme_strlen($input) { return p\Grapheme::grapheme_strlen($input); } +} +if (!function_exists('grapheme_strpos')) { + function grapheme_strpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strpos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_strripos')) { + function grapheme_strripos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strripos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_strrpos')) { + function grapheme_strrpos($haystack, $needle, $offset = 0) { return p\Grapheme::grapheme_strrpos($haystack, $needle, $offset); } +} +if (!function_exists('grapheme_strstr')) { + function grapheme_strstr($haystack, $needle, $beforeNeedle = false) { return p\Grapheme::grapheme_strstr($haystack, $needle, $beforeNeedle); } +} +if (!function_exists('grapheme_substr')) { + function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::grapheme_substr($string, $offset, $length); } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php b/tests/integration/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php new file mode 100644 index 000000000..b8c078677 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Grapheme as p; + +if (!defined('GRAPHEME_EXTR_COUNT')) { + define('GRAPHEME_EXTR_COUNT', 0); +} +if (!defined('GRAPHEME_EXTR_MAXBYTES')) { + define('GRAPHEME_EXTR_MAXBYTES', 1); +} +if (!defined('GRAPHEME_EXTR_MAXCHARS')) { + define('GRAPHEME_EXTR_MAXCHARS', 2); +} + +if (!function_exists('grapheme_extract')) { + function grapheme_extract(?string $haystack, ?int $size, ?int $type = GRAPHEME_EXTR_COUNT, ?int $offset = 0, &$next = null): string|false { return p\Grapheme::grapheme_extract((string) $haystack, (int) $size, (int) $type, (int) $offset, $next); } +} +if (!function_exists('grapheme_stripos')) { + function grapheme_stripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_stripos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_stristr')) { + function grapheme_stristr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_stristr((string) $haystack, (string) $needle, (bool) $beforeNeedle); } +} +if (!function_exists('grapheme_strlen')) { + function grapheme_strlen(?string $string): int|false|null { return p\Grapheme::grapheme_strlen((string) $string); } +} +if (!function_exists('grapheme_strpos')) { + function grapheme_strpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strpos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_strripos')) { + function grapheme_strripos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strripos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_strrpos')) { + function grapheme_strrpos(?string $haystack, ?string $needle, ?int $offset = 0): int|false { return p\Grapheme::grapheme_strrpos((string) $haystack, (string) $needle, (int) $offset); } +} +if (!function_exists('grapheme_strstr')) { + function grapheme_strstr(?string $haystack, ?string $needle, ?bool $beforeNeedle = false): string|false { return p\Grapheme::grapheme_strstr((string) $haystack, (string) $needle, (bool) $beforeNeedle); } +} +if (!function_exists('grapheme_substr')) { + function grapheme_substr(?string $string, ?int $offset, ?int $length = null): string|false { return p\Grapheme::grapheme_substr((string) $string, (int) $offset, $length); } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-grapheme/composer.json b/tests/integration/vendor/symfony/polyfill-intl-grapheme/composer.json new file mode 100644 index 000000000..02c98ee30 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-grapheme/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/polyfill-intl-grapheme", + "type": "library", + "description": "Symfony polyfill for intl's grapheme_* functions", + "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "grapheme"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/LICENSE b/tests/integration/vendor/symfony/polyfill-intl-normalizer/LICENSE new file mode 100644 index 000000000..4cd8bdd30 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/Normalizer.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Normalizer.php new file mode 100644 index 000000000..4443c2322 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Normalizer.php @@ -0,0 +1,310 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Intl\Normalizer; + +/** + * Normalizer is a PHP fallback implementation of the Normalizer class provided by the intl extension. + * + * It has been validated with Unicode 6.3 Normalization Conformance Test. + * See http://www.unicode.org/reports/tr15/ for detailed info about Unicode normalizations. + * + * @author Nicolas Grekas + * + * @internal + */ +class Normalizer +{ + public const FORM_D = \Normalizer::FORM_D; + public const FORM_KD = \Normalizer::FORM_KD; + public const FORM_C = \Normalizer::FORM_C; + public const FORM_KC = \Normalizer::FORM_KC; + public const NFD = \Normalizer::NFD; + public const NFKD = \Normalizer::NFKD; + public const NFC = \Normalizer::NFC; + public const NFKC = \Normalizer::NFKC; + + private static $C; + private static $D; + private static $KD; + private static $cC; + private static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; + private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; + + public static function isNormalized(string $s, int $form = self::FORM_C) + { + if (!\in_array($form, [self::NFD, self::NFKD, self::NFC, self::NFKC])) { + return false; + } + if (!isset($s[strspn($s, self::$ASCII)])) { + return true; + } + if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) { + return true; + } + + return self::normalize($s, $form) === $s; + } + + public static function normalize(string $s, int $form = self::FORM_C) + { + if (!preg_match('//u', $s)) { + return false; + } + + switch ($form) { + case self::NFC: $C = true; $K = false; break; + case self::NFD: $C = false; $K = false; break; + case self::NFKC: $C = true; $K = true; break; + case self::NFKD: $C = false; $K = true; break; + default: + if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) { + return $s; + } + + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError('normalizer_normalize(): Argument #2 ($form) must be a a valid normalization form'); + } + + if ('' === $s) { + return ''; + } + + if ($K && null === self::$KD) { + self::$KD = self::getData('compatibilityDecomposition'); + } + + if (null === self::$D) { + self::$D = self::getData('canonicalDecomposition'); + self::$cC = self::getData('combiningClass'); + } + + if (null !== $mbEncoding = (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) { + mb_internal_encoding('8bit'); + } + + $r = self::decompose($s, $K); + + if ($C) { + if (null === self::$C) { + self::$C = self::getData('canonicalComposition'); + } + + $r = self::recompose($r); + } + if (null !== $mbEncoding) { + mb_internal_encoding($mbEncoding); + } + + return $r; + } + + private static function recompose($s) + { + $ASCII = self::$ASCII; + $compMap = self::$C; + $combClass = self::$cC; + $ulenMask = self::$ulenMask; + + $result = $tail = ''; + + $i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"]; + $len = \strlen($s); + + $lastUchr = substr($s, 0, $i); + $lastUcls = isset($combClass[$lastUchr]) ? 256 : 0; + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($tail) { + $lastUchr .= $tail; + $tail = ''; + } + + if ($j = strspn($s, $ASCII, $i + 1)) { + $lastUchr .= substr($s, $i, $j); + $i += $j; + } + + $result .= $lastUchr; + $lastUchr = $s[$i]; + $lastUcls = 0; + ++$i; + continue; + } + + $ulen = $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + + if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr + || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr + || $lastUcls) { + // Table lookup and combining chars composition + + $ucls = $combClass[$uchr] ?? 0; + + if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) { + $lastUchr = $compMap[$lastUchr.$uchr]; + } elseif ($lastUcls = $ucls) { + $tail .= $uchr; + } else { + if ($tail) { + $lastUchr .= $tail; + $tail = ''; + } + + $result .= $lastUchr; + $lastUchr = $uchr; + } + } else { + // Hangul chars + + $L = \ord($lastUchr[2]) - 0x80; + $V = \ord($uchr[2]) - 0xA1; + $T = 0; + + $uchr = substr($s, $i + $ulen, 3); + + if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") { + $T = \ord($uchr[2]) - 0xA7; + 0 > $T && $T += 0x40; + $ulen += 3; + } + + $L = 0xAC00 + ($L * 21 + $V) * 28 + $T; + $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F); + } + + $i += $ulen; + } + + return $result.$lastUchr.$tail; + } + + private static function decompose($s, $c) + { + $result = ''; + + $ASCII = self::$ASCII; + $decompMap = self::$D; + $combClass = self::$cC; + $ulenMask = self::$ulenMask; + if ($c) { + $compatMap = self::$KD; + } + + $c = []; + $i = 0; + $len = \strlen($s); + + while ($i < $len) { + if ($s[$i] < "\x80") { + // ASCII chars + + if ($c) { + ksort($c); + $result .= implode('', $c); + $c = []; + } + + $j = 1 + strspn($s, $ASCII, $i + 1); + $result .= substr($s, $i, $j); + $i += $j; + continue; + } + + $ulen = $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) { + // Table lookup + + if ($uchr !== $j = $compatMap[$uchr] ?? ($decompMap[$uchr] ?? $uchr)) { + $uchr = $j; + + $j = \strlen($uchr); + $ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"]; + + if ($ulen != $j) { + // Put trailing chars in $s + + $j -= $ulen; + $i -= $j; + + if (0 > $i) { + $s = str_repeat(' ', -$i).$s; + $len -= $i; + $i = 0; + } + + while ($j--) { + $s[$i + $j] = $uchr[$ulen + $j]; + } + + $uchr = substr($uchr, 0, $ulen); + } + } + if (isset($combClass[$uchr])) { + // Combining chars, for sorting + + if (!isset($c[$combClass[$uchr]])) { + $c[$combClass[$uchr]] = ''; + } + $c[$combClass[$uchr]] .= $uchr; + continue; + } + } else { + // Hangul chars + + $uchr = unpack('C*', $uchr); + $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80; + + $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588)) + ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); + + if ($j %= 28) { + $uchr .= $j < 25 + ? ("\xE1\x86".\chr(0xA7 + $j)) + : ("\xE1\x87".\chr(0x67 + $j)); + } + } + if ($c) { + ksort($c); + $result .= implode('', $c); + $c = []; + } + + $result .= $uchr; + } + + if ($c) { + ksort($c); + $result .= implode('', $c); + } + + return $result; + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/README.md b/tests/integration/vendor/symfony/polyfill-intl-normalizer/README.md new file mode 100644 index 000000000..15060c5f1 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/README.md @@ -0,0 +1,14 @@ +Symfony Polyfill / Intl: Normalizer +=================================== + +This component provides a fallback implementation for the +[`Normalizer`](https://php.net/Normalizer) class provided +by the [Intl](https://php.net/intl) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php new file mode 100644 index 000000000..0fdfc890a --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php @@ -0,0 +1,17 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '΅' => '΅', + 'Ά' => 'Ά', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ὲ' => 'ὲ', + 'ὴ' => 'ὴ', + 'ὶ' => 'ὶ', + 'ὸ' => 'ὸ', + 'ὺ' => 'ὺ', + 'ὼ' => 'ὼ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'ᾼ' => 'ᾼ', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Ὴ' => 'Ὴ', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ὼ' => 'Ὼ', + 'ῼ' => 'ῼ', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', +); diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php new file mode 100644 index 000000000..5a3e8e096 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php @@ -0,0 +1,2065 @@ + 'À', + 'Á' => 'Á', + 'Â' => 'Â', + 'Ã' => 'Ã', + 'Ä' => 'Ä', + 'Å' => 'Å', + 'Ç' => 'Ç', + 'È' => 'È', + 'É' => 'É', + 'Ê' => 'Ê', + 'Ë' => 'Ë', + 'Ì' => 'Ì', + 'Í' => 'Í', + 'Î' => 'Î', + 'Ï' => 'Ï', + 'Ñ' => 'Ñ', + 'Ò' => 'Ò', + 'Ó' => 'Ó', + 'Ô' => 'Ô', + 'Õ' => 'Õ', + 'Ö' => 'Ö', + 'Ù' => 'Ù', + 'Ú' => 'Ú', + 'Û' => 'Û', + 'Ü' => 'Ü', + 'Ý' => 'Ý', + 'à' => 'à', + 'á' => 'á', + 'â' => 'â', + 'ã' => 'ã', + 'ä' => 'ä', + 'å' => 'å', + 'ç' => 'ç', + 'è' => 'è', + 'é' => 'é', + 'ê' => 'ê', + 'ë' => 'ë', + 'ì' => 'ì', + 'í' => 'í', + 'î' => 'î', + 'ï' => 'ï', + 'ñ' => 'ñ', + 'ò' => 'ò', + 'ó' => 'ó', + 'ô' => 'ô', + 'õ' => 'õ', + 'ö' => 'ö', + 'ù' => 'ù', + 'ú' => 'ú', + 'û' => 'û', + 'ü' => 'ü', + 'ý' => 'ý', + 'ÿ' => 'ÿ', + 'Ā' => 'Ā', + 'ā' => 'ā', + 'Ă' => 'Ă', + 'ă' => 'ă', + 'Ą' => 'Ą', + 'ą' => 'ą', + 'Ć' => 'Ć', + 'ć' => 'ć', + 'Ĉ' => 'Ĉ', + 'ĉ' => 'ĉ', + 'Ċ' => 'Ċ', + 'ċ' => 'ċ', + 'Č' => 'Č', + 'č' => 'č', + 'Ď' => 'Ď', + 'ď' => 'ď', + 'Ē' => 'Ē', + 'ē' => 'ē', + 'Ĕ' => 'Ĕ', + 'ĕ' => 'ĕ', + 'Ė' => 'Ė', + 'ė' => 'ė', + 'Ę' => 'Ę', + 'ę' => 'ę', + 'Ě' => 'Ě', + 'ě' => 'ě', + 'Ĝ' => 'Ĝ', + 'ĝ' => 'ĝ', + 'Ğ' => 'Ğ', + 'ğ' => 'ğ', + 'Ġ' => 'Ġ', + 'ġ' => 'ġ', + 'Ģ' => 'Ģ', + 'ģ' => 'ģ', + 'Ĥ' => 'Ĥ', + 'ĥ' => 'ĥ', + 'Ĩ' => 'Ĩ', + 'ĩ' => 'ĩ', + 'Ī' => 'Ī', + 'ī' => 'ī', + 'Ĭ' => 'Ĭ', + 'ĭ' => 'ĭ', + 'Į' => 'Į', + 'į' => 'į', + 'İ' => 'İ', + 'Ĵ' => 'Ĵ', + 'ĵ' => 'ĵ', + 'Ķ' => 'Ķ', + 'ķ' => 'ķ', + 'Ĺ' => 'Ĺ', + 'ĺ' => 'ĺ', + 'Ļ' => 'Ļ', + 'ļ' => 'ļ', + 'Ľ' => 'Ľ', + 'ľ' => 'ľ', + 'Ń' => 'Ń', + 'ń' => 'ń', + 'Ņ' => 'Ņ', + 'ņ' => 'ņ', + 'Ň' => 'Ň', + 'ň' => 'ň', + 'Ō' => 'Ō', + 'ō' => 'ō', + 'Ŏ' => 'Ŏ', + 'ŏ' => 'ŏ', + 'Ő' => 'Ő', + 'ő' => 'ő', + 'Ŕ' => 'Ŕ', + 'ŕ' => 'ŕ', + 'Ŗ' => 'Ŗ', + 'ŗ' => 'ŗ', + 'Ř' => 'Ř', + 'ř' => 'ř', + 'Ś' => 'Ś', + 'ś' => 'ś', + 'Ŝ' => 'Ŝ', + 'ŝ' => 'ŝ', + 'Ş' => 'Ş', + 'ş' => 'ş', + 'Š' => 'Š', + 'š' => 'š', + 'Ţ' => 'Ţ', + 'ţ' => 'ţ', + 'Ť' => 'Ť', + 'ť' => 'ť', + 'Ũ' => 'Ũ', + 'ũ' => 'ũ', + 'Ū' => 'Ū', + 'ū' => 'ū', + 'Ŭ' => 'Ŭ', + 'ŭ' => 'ŭ', + 'Ů' => 'Ů', + 'ů' => 'ů', + 'Ű' => 'Ű', + 'ű' => 'ű', + 'Ų' => 'Ų', + 'ų' => 'ų', + 'Ŵ' => 'Ŵ', + 'ŵ' => 'ŵ', + 'Ŷ' => 'Ŷ', + 'ŷ' => 'ŷ', + 'Ÿ' => 'Ÿ', + 'Ź' => 'Ź', + 'ź' => 'ź', + 'Ż' => 'Ż', + 'ż' => 'ż', + 'Ž' => 'Ž', + 'ž' => 'ž', + 'Ơ' => 'Ơ', + 'ơ' => 'ơ', + 'Ư' => 'Ư', + 'ư' => 'ư', + 'Ǎ' => 'Ǎ', + 'ǎ' => 'ǎ', + 'Ǐ' => 'Ǐ', + 'ǐ' => 'ǐ', + 'Ǒ' => 'Ǒ', + 'ǒ' => 'ǒ', + 'Ǔ' => 'Ǔ', + 'ǔ' => 'ǔ', + 'Ǖ' => 'Ǖ', + 'ǖ' => 'ǖ', + 'Ǘ' => 'Ǘ', + 'ǘ' => 'ǘ', + 'Ǚ' => 'Ǚ', + 'ǚ' => 'ǚ', + 'Ǜ' => 'Ǜ', + 'ǜ' => 'ǜ', + 'Ǟ' => 'Ǟ', + 'ǟ' => 'ǟ', + 'Ǡ' => 'Ǡ', + 'ǡ' => 'ǡ', + 'Ǣ' => 'Ǣ', + 'ǣ' => 'ǣ', + 'Ǧ' => 'Ǧ', + 'ǧ' => 'ǧ', + 'Ǩ' => 'Ǩ', + 'ǩ' => 'ǩ', + 'Ǫ' => 'Ǫ', + 'ǫ' => 'ǫ', + 'Ǭ' => 'Ǭ', + 'ǭ' => 'ǭ', + 'Ǯ' => 'Ǯ', + 'ǯ' => 'ǯ', + 'ǰ' => 'ǰ', + 'Ǵ' => 'Ǵ', + 'ǵ' => 'ǵ', + 'Ǹ' => 'Ǹ', + 'ǹ' => 'ǹ', + 'Ǻ' => 'Ǻ', + 'ǻ' => 'ǻ', + 'Ǽ' => 'Ǽ', + 'ǽ' => 'ǽ', + 'Ǿ' => 'Ǿ', + 'ǿ' => 'ǿ', + 'Ȁ' => 'Ȁ', + 'ȁ' => 'ȁ', + 'Ȃ' => 'Ȃ', + 'ȃ' => 'ȃ', + 'Ȅ' => 'Ȅ', + 'ȅ' => 'ȅ', + 'Ȇ' => 'Ȇ', + 'ȇ' => 'ȇ', + 'Ȉ' => 'Ȉ', + 'ȉ' => 'ȉ', + 'Ȋ' => 'Ȋ', + 'ȋ' => 'ȋ', + 'Ȍ' => 'Ȍ', + 'ȍ' => 'ȍ', + 'Ȏ' => 'Ȏ', + 'ȏ' => 'ȏ', + 'Ȑ' => 'Ȑ', + 'ȑ' => 'ȑ', + 'Ȓ' => 'Ȓ', + 'ȓ' => 'ȓ', + 'Ȕ' => 'Ȕ', + 'ȕ' => 'ȕ', + 'Ȗ' => 'Ȗ', + 'ȗ' => 'ȗ', + 'Ș' => 'Ș', + 'ș' => 'ș', + 'Ț' => 'Ț', + 'ț' => 'ț', + 'Ȟ' => 'Ȟ', + 'ȟ' => 'ȟ', + 'Ȧ' => 'Ȧ', + 'ȧ' => 'ȧ', + 'Ȩ' => 'Ȩ', + 'ȩ' => 'ȩ', + 'Ȫ' => 'Ȫ', + 'ȫ' => 'ȫ', + 'Ȭ' => 'Ȭ', + 'ȭ' => 'ȭ', + 'Ȯ' => 'Ȯ', + 'ȯ' => 'ȯ', + 'Ȱ' => 'Ȱ', + 'ȱ' => 'ȱ', + 'Ȳ' => 'Ȳ', + 'ȳ' => 'ȳ', + '̀' => '̀', + '́' => '́', + '̓' => '̓', + '̈́' => '̈́', + 'ʹ' => 'ʹ', + ';' => ';', + '΅' => '΅', + 'Ά' => 'Ά', + '·' => '·', + 'Έ' => 'Έ', + 'Ή' => 'Ή', + 'Ί' => 'Ί', + 'Ό' => 'Ό', + 'Ύ' => 'Ύ', + 'Ώ' => 'Ώ', + 'ΐ' => 'ΐ', + 'Ϊ' => 'Ϊ', + 'Ϋ' => 'Ϋ', + 'ά' => 'ά', + 'έ' => 'έ', + 'ή' => 'ή', + 'ί' => 'ί', + 'ΰ' => 'ΰ', + 'ϊ' => 'ϊ', + 'ϋ' => 'ϋ', + 'ό' => 'ό', + 'ύ' => 'ύ', + 'ώ' => 'ώ', + 'ϓ' => 'ϓ', + 'ϔ' => 'ϔ', + 'Ѐ' => 'Ѐ', + 'Ё' => 'Ё', + 'Ѓ' => 'Ѓ', + 'Ї' => 'Ї', + 'Ќ' => 'Ќ', + 'Ѝ' => 'Ѝ', + 'Ў' => 'Ў', + 'Й' => 'Й', + 'й' => 'й', + 'ѐ' => 'ѐ', + 'ё' => 'ё', + 'ѓ' => 'ѓ', + 'ї' => 'ї', + 'ќ' => 'ќ', + 'ѝ' => 'ѝ', + 'ў' => 'ў', + 'Ѷ' => 'Ѷ', + 'ѷ' => 'ѷ', + 'Ӂ' => 'Ӂ', + 'ӂ' => 'ӂ', + 'Ӑ' => 'Ӑ', + 'ӑ' => 'ӑ', + 'Ӓ' => 'Ӓ', + 'ӓ' => 'ӓ', + 'Ӗ' => 'Ӗ', + 'ӗ' => 'ӗ', + 'Ӛ' => 'Ӛ', + 'ӛ' => 'ӛ', + 'Ӝ' => 'Ӝ', + 'ӝ' => 'ӝ', + 'Ӟ' => 'Ӟ', + 'ӟ' => 'ӟ', + 'Ӣ' => 'Ӣ', + 'ӣ' => 'ӣ', + 'Ӥ' => 'Ӥ', + 'ӥ' => 'ӥ', + 'Ӧ' => 'Ӧ', + 'ӧ' => 'ӧ', + 'Ӫ' => 'Ӫ', + 'ӫ' => 'ӫ', + 'Ӭ' => 'Ӭ', + 'ӭ' => 'ӭ', + 'Ӯ' => 'Ӯ', + 'ӯ' => 'ӯ', + 'Ӱ' => 'Ӱ', + 'ӱ' => 'ӱ', + 'Ӳ' => 'Ӳ', + 'ӳ' => 'ӳ', + 'Ӵ' => 'Ӵ', + 'ӵ' => 'ӵ', + 'Ӹ' => 'Ӹ', + 'ӹ' => 'ӹ', + 'آ' => 'آ', + 'أ' => 'أ', + 'ؤ' => 'ؤ', + 'إ' => 'إ', + 'ئ' => 'ئ', + 'ۀ' => 'ۀ', + 'ۂ' => 'ۂ', + 'ۓ' => 'ۓ', + 'ऩ' => 'ऩ', + 'ऱ' => 'ऱ', + 'ऴ' => 'ऴ', + 'क़' => 'क़', + 'ख़' => 'ख़', + 'ग़' => 'ग़', + 'ज़' => 'ज़', + 'ड़' => 'ड़', + 'ढ़' => 'ढ़', + 'फ़' => 'फ़', + 'य़' => 'य़', + 'ো' => 'ো', + 'ৌ' => 'ৌ', + 'ড়' => 'ড়', + 'ঢ়' => 'ঢ়', + 'য়' => 'য়', + 'ਲ਼' => 'ਲ਼', + 'ਸ਼' => 'ਸ਼', + 'ਖ਼' => 'ਖ਼', + 'ਗ਼' => 'ਗ਼', + 'ਜ਼' => 'ਜ਼', + 'ਫ਼' => 'ਫ਼', + 'ୈ' => 'ୈ', + 'ୋ' => 'ୋ', + 'ୌ' => 'ୌ', + 'ଡ଼' => 'ଡ଼', + 'ଢ଼' => 'ଢ଼', + 'ஔ' => 'ஔ', + 'ொ' => 'ொ', + 'ோ' => 'ோ', + 'ௌ' => 'ௌ', + 'ై' => 'ై', + 'ೀ' => 'ೀ', + 'ೇ' => 'ೇ', + 'ೈ' => 'ೈ', + 'ೊ' => 'ೊ', + 'ೋ' => 'ೋ', + 'ൊ' => 'ൊ', + 'ോ' => 'ോ', + 'ൌ' => 'ൌ', + 'ේ' => 'ේ', + 'ො' => 'ො', + 'ෝ' => 'ෝ', + 'ෞ' => 'ෞ', + 'གྷ' => 'གྷ', + 'ཌྷ' => 'ཌྷ', + 'དྷ' => 'དྷ', + 'བྷ' => 'བྷ', + 'ཛྷ' => 'ཛྷ', + 'ཀྵ' => 'ཀྵ', + 'ཱི' => 'ཱི', + 'ཱུ' => 'ཱུ', + 'ྲྀ' => 'ྲྀ', + 'ླྀ' => 'ླྀ', + 'ཱྀ' => 'ཱྀ', + 'ྒྷ' => 'ྒྷ', + 'ྜྷ' => 'ྜྷ', + 'ྡྷ' => 'ྡྷ', + 'ྦྷ' => 'ྦྷ', + 'ྫྷ' => 'ྫྷ', + 'ྐྵ' => 'ྐྵ', + 'ဦ' => 'ဦ', + 'ᬆ' => 'ᬆ', + 'ᬈ' => 'ᬈ', + 'ᬊ' => 'ᬊ', + 'ᬌ' => 'ᬌ', + 'ᬎ' => 'ᬎ', + 'ᬒ' => 'ᬒ', + 'ᬻ' => 'ᬻ', + 'ᬽ' => 'ᬽ', + 'ᭀ' => 'ᭀ', + 'ᭁ' => 'ᭁ', + 'ᭃ' => 'ᭃ', + 'Ḁ' => 'Ḁ', + 'ḁ' => 'ḁ', + 'Ḃ' => 'Ḃ', + 'ḃ' => 'ḃ', + 'Ḅ' => 'Ḅ', + 'ḅ' => 'ḅ', + 'Ḇ' => 'Ḇ', + 'ḇ' => 'ḇ', + 'Ḉ' => 'Ḉ', + 'ḉ' => 'ḉ', + 'Ḋ' => 'Ḋ', + 'ḋ' => 'ḋ', + 'Ḍ' => 'Ḍ', + 'ḍ' => 'ḍ', + 'Ḏ' => 'Ḏ', + 'ḏ' => 'ḏ', + 'Ḑ' => 'Ḑ', + 'ḑ' => 'ḑ', + 'Ḓ' => 'Ḓ', + 'ḓ' => 'ḓ', + 'Ḕ' => 'Ḕ', + 'ḕ' => 'ḕ', + 'Ḗ' => 'Ḗ', + 'ḗ' => 'ḗ', + 'Ḙ' => 'Ḙ', + 'ḙ' => 'ḙ', + 'Ḛ' => 'Ḛ', + 'ḛ' => 'ḛ', + 'Ḝ' => 'Ḝ', + 'ḝ' => 'ḝ', + 'Ḟ' => 'Ḟ', + 'ḟ' => 'ḟ', + 'Ḡ' => 'Ḡ', + 'ḡ' => 'ḡ', + 'Ḣ' => 'Ḣ', + 'ḣ' => 'ḣ', + 'Ḥ' => 'Ḥ', + 'ḥ' => 'ḥ', + 'Ḧ' => 'Ḧ', + 'ḧ' => 'ḧ', + 'Ḩ' => 'Ḩ', + 'ḩ' => 'ḩ', + 'Ḫ' => 'Ḫ', + 'ḫ' => 'ḫ', + 'Ḭ' => 'Ḭ', + 'ḭ' => 'ḭ', + 'Ḯ' => 'Ḯ', + 'ḯ' => 'ḯ', + 'Ḱ' => 'Ḱ', + 'ḱ' => 'ḱ', + 'Ḳ' => 'Ḳ', + 'ḳ' => 'ḳ', + 'Ḵ' => 'Ḵ', + 'ḵ' => 'ḵ', + 'Ḷ' => 'Ḷ', + 'ḷ' => 'ḷ', + 'Ḹ' => 'Ḹ', + 'ḹ' => 'ḹ', + 'Ḻ' => 'Ḻ', + 'ḻ' => 'ḻ', + 'Ḽ' => 'Ḽ', + 'ḽ' => 'ḽ', + 'Ḿ' => 'Ḿ', + 'ḿ' => 'ḿ', + 'Ṁ' => 'Ṁ', + 'ṁ' => 'ṁ', + 'Ṃ' => 'Ṃ', + 'ṃ' => 'ṃ', + 'Ṅ' => 'Ṅ', + 'ṅ' => 'ṅ', + 'Ṇ' => 'Ṇ', + 'ṇ' => 'ṇ', + 'Ṉ' => 'Ṉ', + 'ṉ' => 'ṉ', + 'Ṋ' => 'Ṋ', + 'ṋ' => 'ṋ', + 'Ṍ' => 'Ṍ', + 'ṍ' => 'ṍ', + 'Ṏ' => 'Ṏ', + 'ṏ' => 'ṏ', + 'Ṑ' => 'Ṑ', + 'ṑ' => 'ṑ', + 'Ṓ' => 'Ṓ', + 'ṓ' => 'ṓ', + 'Ṕ' => 'Ṕ', + 'ṕ' => 'ṕ', + 'Ṗ' => 'Ṗ', + 'ṗ' => 'ṗ', + 'Ṙ' => 'Ṙ', + 'ṙ' => 'ṙ', + 'Ṛ' => 'Ṛ', + 'ṛ' => 'ṛ', + 'Ṝ' => 'Ṝ', + 'ṝ' => 'ṝ', + 'Ṟ' => 'Ṟ', + 'ṟ' => 'ṟ', + 'Ṡ' => 'Ṡ', + 'ṡ' => 'ṡ', + 'Ṣ' => 'Ṣ', + 'ṣ' => 'ṣ', + 'Ṥ' => 'Ṥ', + 'ṥ' => 'ṥ', + 'Ṧ' => 'Ṧ', + 'ṧ' => 'ṧ', + 'Ṩ' => 'Ṩ', + 'ṩ' => 'ṩ', + 'Ṫ' => 'Ṫ', + 'ṫ' => 'ṫ', + 'Ṭ' => 'Ṭ', + 'ṭ' => 'ṭ', + 'Ṯ' => 'Ṯ', + 'ṯ' => 'ṯ', + 'Ṱ' => 'Ṱ', + 'ṱ' => 'ṱ', + 'Ṳ' => 'Ṳ', + 'ṳ' => 'ṳ', + 'Ṵ' => 'Ṵ', + 'ṵ' => 'ṵ', + 'Ṷ' => 'Ṷ', + 'ṷ' => 'ṷ', + 'Ṹ' => 'Ṹ', + 'ṹ' => 'ṹ', + 'Ṻ' => 'Ṻ', + 'ṻ' => 'ṻ', + 'Ṽ' => 'Ṽ', + 'ṽ' => 'ṽ', + 'Ṿ' => 'Ṿ', + 'ṿ' => 'ṿ', + 'Ẁ' => 'Ẁ', + 'ẁ' => 'ẁ', + 'Ẃ' => 'Ẃ', + 'ẃ' => 'ẃ', + 'Ẅ' => 'Ẅ', + 'ẅ' => 'ẅ', + 'Ẇ' => 'Ẇ', + 'ẇ' => 'ẇ', + 'Ẉ' => 'Ẉ', + 'ẉ' => 'ẉ', + 'Ẋ' => 'Ẋ', + 'ẋ' => 'ẋ', + 'Ẍ' => 'Ẍ', + 'ẍ' => 'ẍ', + 'Ẏ' => 'Ẏ', + 'ẏ' => 'ẏ', + 'Ẑ' => 'Ẑ', + 'ẑ' => 'ẑ', + 'Ẓ' => 'Ẓ', + 'ẓ' => 'ẓ', + 'Ẕ' => 'Ẕ', + 'ẕ' => 'ẕ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẛ' => 'ẛ', + 'Ạ' => 'Ạ', + 'ạ' => 'ạ', + 'Ả' => 'Ả', + 'ả' => 'ả', + 'Ấ' => 'Ấ', + 'ấ' => 'ấ', + 'Ầ' => 'Ầ', + 'ầ' => 'ầ', + 'Ẩ' => 'Ẩ', + 'ẩ' => 'ẩ', + 'Ẫ' => 'Ẫ', + 'ẫ' => 'ẫ', + 'Ậ' => 'Ậ', + 'ậ' => 'ậ', + 'Ắ' => 'Ắ', + 'ắ' => 'ắ', + 'Ằ' => 'Ằ', + 'ằ' => 'ằ', + 'Ẳ' => 'Ẳ', + 'ẳ' => 'ẳ', + 'Ẵ' => 'Ẵ', + 'ẵ' => 'ẵ', + 'Ặ' => 'Ặ', + 'ặ' => 'ặ', + 'Ẹ' => 'Ẹ', + 'ẹ' => 'ẹ', + 'Ẻ' => 'Ẻ', + 'ẻ' => 'ẻ', + 'Ẽ' => 'Ẽ', + 'ẽ' => 'ẽ', + 'Ế' => 'Ế', + 'ế' => 'ế', + 'Ề' => 'Ề', + 'ề' => 'ề', + 'Ể' => 'Ể', + 'ể' => 'ể', + 'Ễ' => 'Ễ', + 'ễ' => 'ễ', + 'Ệ' => 'Ệ', + 'ệ' => 'ệ', + 'Ỉ' => 'Ỉ', + 'ỉ' => 'ỉ', + 'Ị' => 'Ị', + 'ị' => 'ị', + 'Ọ' => 'Ọ', + 'ọ' => 'ọ', + 'Ỏ' => 'Ỏ', + 'ỏ' => 'ỏ', + 'Ố' => 'Ố', + 'ố' => 'ố', + 'Ồ' => 'Ồ', + 'ồ' => 'ồ', + 'Ổ' => 'Ổ', + 'ổ' => 'ổ', + 'Ỗ' => 'Ỗ', + 'ỗ' => 'ỗ', + 'Ộ' => 'Ộ', + 'ộ' => 'ộ', + 'Ớ' => 'Ớ', + 'ớ' => 'ớ', + 'Ờ' => 'Ờ', + 'ờ' => 'ờ', + 'Ở' => 'Ở', + 'ở' => 'ở', + 'Ỡ' => 'Ỡ', + 'ỡ' => 'ỡ', + 'Ợ' => 'Ợ', + 'ợ' => 'ợ', + 'Ụ' => 'Ụ', + 'ụ' => 'ụ', + 'Ủ' => 'Ủ', + 'ủ' => 'ủ', + 'Ứ' => 'Ứ', + 'ứ' => 'ứ', + 'Ừ' => 'Ừ', + 'ừ' => 'ừ', + 'Ử' => 'Ử', + 'ử' => 'ử', + 'Ữ' => 'Ữ', + 'ữ' => 'ữ', + 'Ự' => 'Ự', + 'ự' => 'ự', + 'Ỳ' => 'Ỳ', + 'ỳ' => 'ỳ', + 'Ỵ' => 'Ỵ', + 'ỵ' => 'ỵ', + 'Ỷ' => 'Ỷ', + 'ỷ' => 'ỷ', + 'Ỹ' => 'Ỹ', + 'ỹ' => 'ỹ', + 'ἀ' => 'ἀ', + 'ἁ' => 'ἁ', + 'ἂ' => 'ἂ', + 'ἃ' => 'ἃ', + 'ἄ' => 'ἄ', + 'ἅ' => 'ἅ', + 'ἆ' => 'ἆ', + 'ἇ' => 'ἇ', + 'Ἀ' => 'Ἀ', + 'Ἁ' => 'Ἁ', + 'Ἂ' => 'Ἂ', + 'Ἃ' => 'Ἃ', + 'Ἄ' => 'Ἄ', + 'Ἅ' => 'Ἅ', + 'Ἆ' => 'Ἆ', + 'Ἇ' => 'Ἇ', + 'ἐ' => 'ἐ', + 'ἑ' => 'ἑ', + 'ἒ' => 'ἒ', + 'ἓ' => 'ἓ', + 'ἔ' => 'ἔ', + 'ἕ' => 'ἕ', + 'Ἐ' => 'Ἐ', + 'Ἑ' => 'Ἑ', + 'Ἒ' => 'Ἒ', + 'Ἓ' => 'Ἓ', + 'Ἔ' => 'Ἔ', + 'Ἕ' => 'Ἕ', + 'ἠ' => 'ἠ', + 'ἡ' => 'ἡ', + 'ἢ' => 'ἢ', + 'ἣ' => 'ἣ', + 'ἤ' => 'ἤ', + 'ἥ' => 'ἥ', + 'ἦ' => 'ἦ', + 'ἧ' => 'ἧ', + 'Ἠ' => 'Ἠ', + 'Ἡ' => 'Ἡ', + 'Ἢ' => 'Ἢ', + 'Ἣ' => 'Ἣ', + 'Ἤ' => 'Ἤ', + 'Ἥ' => 'Ἥ', + 'Ἦ' => 'Ἦ', + 'Ἧ' => 'Ἧ', + 'ἰ' => 'ἰ', + 'ἱ' => 'ἱ', + 'ἲ' => 'ἲ', + 'ἳ' => 'ἳ', + 'ἴ' => 'ἴ', + 'ἵ' => 'ἵ', + 'ἶ' => 'ἶ', + 'ἷ' => 'ἷ', + 'Ἰ' => 'Ἰ', + 'Ἱ' => 'Ἱ', + 'Ἲ' => 'Ἲ', + 'Ἳ' => 'Ἳ', + 'Ἴ' => 'Ἴ', + 'Ἵ' => 'Ἵ', + 'Ἶ' => 'Ἶ', + 'Ἷ' => 'Ἷ', + 'ὀ' => 'ὀ', + 'ὁ' => 'ὁ', + 'ὂ' => 'ὂ', + 'ὃ' => 'ὃ', + 'ὄ' => 'ὄ', + 'ὅ' => 'ὅ', + 'Ὀ' => 'Ὀ', + 'Ὁ' => 'Ὁ', + 'Ὂ' => 'Ὂ', + 'Ὃ' => 'Ὃ', + 'Ὄ' => 'Ὄ', + 'Ὅ' => 'Ὅ', + 'ὐ' => 'ὐ', + 'ὑ' => 'ὑ', + 'ὒ' => 'ὒ', + 'ὓ' => 'ὓ', + 'ὔ' => 'ὔ', + 'ὕ' => 'ὕ', + 'ὖ' => 'ὖ', + 'ὗ' => 'ὗ', + 'Ὑ' => 'Ὑ', + 'Ὓ' => 'Ὓ', + 'Ὕ' => 'Ὕ', + 'Ὗ' => 'Ὗ', + 'ὠ' => 'ὠ', + 'ὡ' => 'ὡ', + 'ὢ' => 'ὢ', + 'ὣ' => 'ὣ', + 'ὤ' => 'ὤ', + 'ὥ' => 'ὥ', + 'ὦ' => 'ὦ', + 'ὧ' => 'ὧ', + 'Ὠ' => 'Ὠ', + 'Ὡ' => 'Ὡ', + 'Ὢ' => 'Ὢ', + 'Ὣ' => 'Ὣ', + 'Ὤ' => 'Ὤ', + 'Ὥ' => 'Ὥ', + 'Ὦ' => 'Ὦ', + 'Ὧ' => 'Ὧ', + 'ὰ' => 'ὰ', + 'ά' => 'ά', + 'ὲ' => 'ὲ', + 'έ' => 'έ', + 'ὴ' => 'ὴ', + 'ή' => 'ή', + 'ὶ' => 'ὶ', + 'ί' => 'ί', + 'ὸ' => 'ὸ', + 'ό' => 'ό', + 'ὺ' => 'ὺ', + 'ύ' => 'ύ', + 'ὼ' => 'ὼ', + 'ώ' => 'ώ', + 'ᾀ' => 'ᾀ', + 'ᾁ' => 'ᾁ', + 'ᾂ' => 'ᾂ', + 'ᾃ' => 'ᾃ', + 'ᾄ' => 'ᾄ', + 'ᾅ' => 'ᾅ', + 'ᾆ' => 'ᾆ', + 'ᾇ' => 'ᾇ', + 'ᾈ' => 'ᾈ', + 'ᾉ' => 'ᾉ', + 'ᾊ' => 'ᾊ', + 'ᾋ' => 'ᾋ', + 'ᾌ' => 'ᾌ', + 'ᾍ' => 'ᾍ', + 'ᾎ' => 'ᾎ', + 'ᾏ' => 'ᾏ', + 'ᾐ' => 'ᾐ', + 'ᾑ' => 'ᾑ', + 'ᾒ' => 'ᾒ', + 'ᾓ' => 'ᾓ', + 'ᾔ' => 'ᾔ', + 'ᾕ' => 'ᾕ', + 'ᾖ' => 'ᾖ', + 'ᾗ' => 'ᾗ', + 'ᾘ' => 'ᾘ', + 'ᾙ' => 'ᾙ', + 'ᾚ' => 'ᾚ', + 'ᾛ' => 'ᾛ', + 'ᾜ' => 'ᾜ', + 'ᾝ' => 'ᾝ', + 'ᾞ' => 'ᾞ', + 'ᾟ' => 'ᾟ', + 'ᾠ' => 'ᾠ', + 'ᾡ' => 'ᾡ', + 'ᾢ' => 'ᾢ', + 'ᾣ' => 'ᾣ', + 'ᾤ' => 'ᾤ', + 'ᾥ' => 'ᾥ', + 'ᾦ' => 'ᾦ', + 'ᾧ' => 'ᾧ', + 'ᾨ' => 'ᾨ', + 'ᾩ' => 'ᾩ', + 'ᾪ' => 'ᾪ', + 'ᾫ' => 'ᾫ', + 'ᾬ' => 'ᾬ', + 'ᾭ' => 'ᾭ', + 'ᾮ' => 'ᾮ', + 'ᾯ' => 'ᾯ', + 'ᾰ' => 'ᾰ', + 'ᾱ' => 'ᾱ', + 'ᾲ' => 'ᾲ', + 'ᾳ' => 'ᾳ', + 'ᾴ' => 'ᾴ', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾷ', + 'Ᾰ' => 'Ᾰ', + 'Ᾱ' => 'Ᾱ', + 'Ὰ' => 'Ὰ', + 'Ά' => 'Ά', + 'ᾼ' => 'ᾼ', + 'ι' => 'ι', + '῁' => '῁', + 'ῂ' => 'ῂ', + 'ῃ' => 'ῃ', + 'ῄ' => 'ῄ', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῇ', + 'Ὲ' => 'Ὲ', + 'Έ' => 'Έ', + 'Ὴ' => 'Ὴ', + 'Ή' => 'Ή', + 'ῌ' => 'ῌ', + '῍' => '῍', + '῎' => '῎', + '῏' => '῏', + 'ῐ' => 'ῐ', + 'ῑ' => 'ῑ', + 'ῒ' => 'ῒ', + 'ΐ' => 'ΐ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'Ῐ' => 'Ῐ', + 'Ῑ' => 'Ῑ', + 'Ὶ' => 'Ὶ', + 'Ί' => 'Ί', + '῝' => '῝', + '῞' => '῞', + '῟' => '῟', + 'ῠ' => 'ῠ', + 'ῡ' => 'ῡ', + 'ῢ' => 'ῢ', + 'ΰ' => 'ΰ', + 'ῤ' => 'ῤ', + 'ῥ' => 'ῥ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'Ῠ' => 'Ῠ', + 'Ῡ' => 'Ῡ', + 'Ὺ' => 'Ὺ', + 'Ύ' => 'Ύ', + 'Ῥ' => 'Ῥ', + '῭' => '῭', + '΅' => '΅', + '`' => '`', + 'ῲ' => 'ῲ', + 'ῳ' => 'ῳ', + 'ῴ' => 'ῴ', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῷ', + 'Ὸ' => 'Ὸ', + 'Ό' => 'Ό', + 'Ὼ' => 'Ὼ', + 'Ώ' => 'Ώ', + 'ῼ' => 'ῼ', + '´' => '´', + ' ' => ' ', + ' ' => ' ', + 'Ω' => 'Ω', + 'K' => 'K', + 'Å' => 'Å', + '↚' => '↚', + '↛' => '↛', + '↮' => '↮', + '⇍' => '⇍', + '⇎' => '⇎', + '⇏' => '⇏', + '∄' => '∄', + '∉' => '∉', + '∌' => '∌', + '∤' => '∤', + '∦' => '∦', + '≁' => '≁', + '≄' => '≄', + '≇' => '≇', + '≉' => '≉', + '≠' => '≠', + '≢' => '≢', + '≭' => '≭', + '≮' => '≮', + '≯' => '≯', + '≰' => '≰', + '≱' => '≱', + '≴' => '≴', + '≵' => '≵', + '≸' => '≸', + '≹' => '≹', + '⊀' => '⊀', + '⊁' => '⊁', + '⊄' => '⊄', + '⊅' => '⊅', + '⊈' => '⊈', + '⊉' => '⊉', + '⊬' => '⊬', + '⊭' => '⊭', + '⊮' => '⊮', + '⊯' => '⊯', + '⋠' => '⋠', + '⋡' => '⋡', + '⋢' => '⋢', + '⋣' => '⋣', + '⋪' => '⋪', + '⋫' => '⋫', + '⋬' => '⋬', + '⋭' => '⋭', + '〈' => '〈', + '〉' => '〉', + '⫝̸' => '⫝̸', + 'が' => 'が', + 'ぎ' => 'ぎ', + 'ぐ' => 'ぐ', + 'げ' => 'げ', + 'ご' => 'ご', + 'ざ' => 'ざ', + 'じ' => 'じ', + 'ず' => 'ず', + 'ぜ' => 'ぜ', + 'ぞ' => 'ぞ', + 'だ' => 'だ', + 'ぢ' => 'ぢ', + 'づ' => 'づ', + 'で' => 'で', + 'ど' => 'ど', + 'ば' => 'ば', + 'ぱ' => 'ぱ', + 'び' => 'び', + 'ぴ' => 'ぴ', + 'ぶ' => 'ぶ', + 'ぷ' => 'ぷ', + 'べ' => 'べ', + 'ぺ' => 'ぺ', + 'ぼ' => 'ぼ', + 'ぽ' => 'ぽ', + 'ゔ' => 'ゔ', + 'ゞ' => 'ゞ', + 'ガ' => 'ガ', + 'ギ' => 'ギ', + 'グ' => 'グ', + 'ゲ' => 'ゲ', + 'ゴ' => 'ゴ', + 'ザ' => 'ザ', + 'ジ' => 'ジ', + 'ズ' => 'ズ', + 'ゼ' => 'ゼ', + 'ゾ' => 'ゾ', + 'ダ' => 'ダ', + 'ヂ' => 'ヂ', + 'ヅ' => 'ヅ', + 'デ' => 'デ', + 'ド' => 'ド', + 'バ' => 'バ', + 'パ' => 'パ', + 'ビ' => 'ビ', + 'ピ' => 'ピ', + 'ブ' => 'ブ', + 'プ' => 'プ', + 'ベ' => 'ベ', + 'ペ' => 'ペ', + 'ボ' => 'ボ', + 'ポ' => 'ポ', + 'ヴ' => 'ヴ', + 'ヷ' => 'ヷ', + 'ヸ' => 'ヸ', + 'ヹ' => 'ヹ', + 'ヺ' => 'ヺ', + 'ヾ' => 'ヾ', + '豈' => '豈', + '更' => '更', + '車' => '車', + '賈' => '賈', + '滑' => '滑', + '串' => '串', + '句' => '句', + '龜' => '龜', + '龜' => '龜', + '契' => '契', + '金' => '金', + '喇' => '喇', + '奈' => '奈', + '懶' => '懶', + '癩' => '癩', + '羅' => '羅', + '蘿' => '蘿', + '螺' => '螺', + '裸' => '裸', + '邏' => '邏', + '樂' => '樂', + '洛' => '洛', + '烙' => '烙', + '珞' => '珞', + '落' => '落', + '酪' => '酪', + '駱' => '駱', + '亂' => '亂', + '卵' => '卵', + '欄' => '欄', + '爛' => '爛', + '蘭' => '蘭', + '鸞' => '鸞', + '嵐' => '嵐', + '濫' => '濫', + '藍' => '藍', + '襤' => '襤', + '拉' => '拉', + '臘' => '臘', + '蠟' => '蠟', + '廊' => '廊', + '朗' => '朗', + '浪' => '浪', + '狼' => '狼', + '郎' => '郎', + '來' => '來', + '冷' => '冷', + '勞' => '勞', + '擄' => '擄', + '櫓' => '櫓', + '爐' => '爐', + '盧' => '盧', + '老' => '老', + '蘆' => '蘆', + '虜' => '虜', + '路' => '路', + '露' => '露', + '魯' => '魯', + '鷺' => '鷺', + '碌' => '碌', + '祿' => '祿', + '綠' => '綠', + '菉' => '菉', + '錄' => '錄', + '鹿' => '鹿', + '論' => '論', + '壟' => '壟', + '弄' => '弄', + '籠' => '籠', + '聾' => '聾', + '牢' => '牢', + '磊' => '磊', + '賂' => '賂', + '雷' => '雷', + '壘' => '壘', + '屢' => '屢', + '樓' => '樓', + '淚' => '淚', + '漏' => '漏', + '累' => '累', + '縷' => '縷', + '陋' => '陋', + '勒' => '勒', + '肋' => '肋', + '凜' => '凜', + '凌' => '凌', + '稜' => '稜', + '綾' => '綾', + '菱' => '菱', + '陵' => '陵', + '讀' => '讀', + '拏' => '拏', + '樂' => '樂', + '諾' => '諾', + '丹' => '丹', + '寧' => '寧', + '怒' => '怒', + '率' => '率', + '異' => '異', + '北' => '北', + '磻' => '磻', + '便' => '便', + '復' => '復', + '不' => '不', + '泌' => '泌', + '數' => '數', + '索' => '索', + '參' => '參', + '塞' => '塞', + '省' => '省', + '葉' => '葉', + '說' => '說', + '殺' => '殺', + '辰' => '辰', + '沈' => '沈', + '拾' => '拾', + '若' => '若', + '掠' => '掠', + '略' => '略', + '亮' => '亮', + '兩' => '兩', + '凉' => '凉', + '梁' => '梁', + '糧' => '糧', + '良' => '良', + '諒' => '諒', + '量' => '量', + '勵' => '勵', + '呂' => '呂', + '女' => '女', + '廬' => '廬', + '旅' => '旅', + '濾' => '濾', + '礪' => '礪', + '閭' => '閭', + '驪' => '驪', + '麗' => '麗', + '黎' => '黎', + '力' => '力', + '曆' => '曆', + '歷' => '歷', + '轢' => '轢', + '年' => '年', + '憐' => '憐', + '戀' => '戀', + '撚' => '撚', + '漣' => '漣', + '煉' => '煉', + '璉' => '璉', + '秊' => '秊', + '練' => '練', + '聯' => '聯', + '輦' => '輦', + '蓮' => '蓮', + '連' => '連', + '鍊' => '鍊', + '列' => '列', + '劣' => '劣', + '咽' => '咽', + '烈' => '烈', + '裂' => '裂', + '說' => '說', + '廉' => '廉', + '念' => '念', + '捻' => '捻', + '殮' => '殮', + '簾' => '簾', + '獵' => '獵', + '令' => '令', + '囹' => '囹', + '寧' => '寧', + '嶺' => '嶺', + '怜' => '怜', + '玲' => '玲', + '瑩' => '瑩', + '羚' => '羚', + '聆' => '聆', + '鈴' => '鈴', + '零' => '零', + '靈' => '靈', + '領' => '領', + '例' => '例', + '禮' => '禮', + '醴' => '醴', + '隸' => '隸', + '惡' => '惡', + '了' => '了', + '僚' => '僚', + '寮' => '寮', + '尿' => '尿', + '料' => '料', + '樂' => '樂', + '燎' => '燎', + '療' => '療', + '蓼' => '蓼', + '遼' => '遼', + '龍' => '龍', + '暈' => '暈', + '阮' => '阮', + '劉' => '劉', + '杻' => '杻', + '柳' => '柳', + '流' => '流', + '溜' => '溜', + '琉' => '琉', + '留' => '留', + '硫' => '硫', + '紐' => '紐', + '類' => '類', + '六' => '六', + '戮' => '戮', + '陸' => '陸', + '倫' => '倫', + '崙' => '崙', + '淪' => '淪', + '輪' => '輪', + '律' => '律', + '慄' => '慄', + '栗' => '栗', + '率' => '率', + '隆' => '隆', + '利' => '利', + '吏' => '吏', + '履' => '履', + '易' => '易', + '李' => '李', + '梨' => '梨', + '泥' => '泥', + '理' => '理', + '痢' => '痢', + '罹' => '罹', + '裏' => '裏', + '裡' => '裡', + '里' => '里', + '離' => '離', + '匿' => '匿', + '溺' => '溺', + '吝' => '吝', + '燐' => '燐', + '璘' => '璘', + '藺' => '藺', + '隣' => '隣', + '鱗' => '鱗', + '麟' => '麟', + '林' => '林', + '淋' => '淋', + '臨' => '臨', + '立' => '立', + '笠' => '笠', + '粒' => '粒', + '狀' => '狀', + '炙' => '炙', + '識' => '識', + '什' => '什', + '茶' => '茶', + '刺' => '刺', + '切' => '切', + '度' => '度', + '拓' => '拓', + '糖' => '糖', + '宅' => '宅', + '洞' => '洞', + '暴' => '暴', + '輻' => '輻', + '行' => '行', + '降' => '降', + '見' => '見', + '廓' => '廓', + '兀' => '兀', + '嗀' => '嗀', + '塚' => '塚', + '晴' => '晴', + '凞' => '凞', + '猪' => '猪', + '益' => '益', + '礼' => '礼', + '神' => '神', + '祥' => '祥', + '福' => '福', + '靖' => '靖', + '精' => '精', + '羽' => '羽', + '蘒' => '蘒', + '諸' => '諸', + '逸' => '逸', + '都' => '都', + '飯' => '飯', + '飼' => '飼', + '館' => '館', + '鶴' => '鶴', + '郞' => '郞', + '隷' => '隷', + '侮' => '侮', + '僧' => '僧', + '免' => '免', + '勉' => '勉', + '勤' => '勤', + '卑' => '卑', + '喝' => '喝', + '嘆' => '嘆', + '器' => '器', + '塀' => '塀', + '墨' => '墨', + '層' => '層', + '屮' => '屮', + '悔' => '悔', + '慨' => '慨', + '憎' => '憎', + '懲' => '懲', + '敏' => '敏', + '既' => '既', + '暑' => '暑', + '梅' => '梅', + '海' => '海', + '渚' => '渚', + '漢' => '漢', + '煮' => '煮', + '爫' => '爫', + '琢' => '琢', + '碑' => '碑', + '社' => '社', + '祉' => '祉', + '祈' => '祈', + '祐' => '祐', + '祖' => '祖', + '祝' => '祝', + '禍' => '禍', + '禎' => '禎', + '穀' => '穀', + '突' => '突', + '節' => '節', + '練' => '練', + '縉' => '縉', + '繁' => '繁', + '署' => '署', + '者' => '者', + '臭' => '臭', + '艹' => '艹', + '艹' => '艹', + '著' => '著', + '褐' => '褐', + '視' => '視', + '謁' => '謁', + '謹' => '謹', + '賓' => '賓', + '贈' => '贈', + '辶' => '辶', + '逸' => '逸', + '難' => '難', + '響' => '響', + '頻' => '頻', + '恵' => '恵', + '𤋮' => '𤋮', + '舘' => '舘', + '並' => '並', + '况' => '况', + '全' => '全', + '侀' => '侀', + '充' => '充', + '冀' => '冀', + '勇' => '勇', + '勺' => '勺', + '喝' => '喝', + '啕' => '啕', + '喙' => '喙', + '嗢' => '嗢', + '塚' => '塚', + '墳' => '墳', + '奄' => '奄', + '奔' => '奔', + '婢' => '婢', + '嬨' => '嬨', + '廒' => '廒', + '廙' => '廙', + '彩' => '彩', + '徭' => '徭', + '惘' => '惘', + '慎' => '慎', + '愈' => '愈', + '憎' => '憎', + '慠' => '慠', + '懲' => '懲', + '戴' => '戴', + '揄' => '揄', + '搜' => '搜', + '摒' => '摒', + '敖' => '敖', + '晴' => '晴', + '朗' => '朗', + '望' => '望', + '杖' => '杖', + '歹' => '歹', + '殺' => '殺', + '流' => '流', + '滛' => '滛', + '滋' => '滋', + '漢' => '漢', + '瀞' => '瀞', + '煮' => '煮', + '瞧' => '瞧', + '爵' => '爵', + '犯' => '犯', + '猪' => '猪', + '瑱' => '瑱', + '甆' => '甆', + '画' => '画', + '瘝' => '瘝', + '瘟' => '瘟', + '益' => '益', + '盛' => '盛', + '直' => '直', + '睊' => '睊', + '着' => '着', + '磌' => '磌', + '窱' => '窱', + '節' => '節', + '类' => '类', + '絛' => '絛', + '練' => '練', + '缾' => '缾', + '者' => '者', + '荒' => '荒', + '華' => '華', + '蝹' => '蝹', + '襁' => '襁', + '覆' => '覆', + '視' => '視', + '調' => '調', + '諸' => '諸', + '請' => '請', + '謁' => '謁', + '諾' => '諾', + '諭' => '諭', + '謹' => '謹', + '變' => '變', + '贈' => '贈', + '輸' => '輸', + '遲' => '遲', + '醙' => '醙', + '鉶' => '鉶', + '陼' => '陼', + '難' => '難', + '靖' => '靖', + '韛' => '韛', + '響' => '響', + '頋' => '頋', + '頻' => '頻', + '鬒' => '鬒', + '龜' => '龜', + '𢡊' => '𢡊', + '𢡄' => '𢡄', + '𣏕' => '𣏕', + '㮝' => '㮝', + '䀘' => '䀘', + '䀹' => '䀹', + '𥉉' => '𥉉', + '𥳐' => '𥳐', + '𧻓' => '𧻓', + '齃' => '齃', + '龎' => '龎', + 'יִ' => 'יִ', + 'ײַ' => 'ײַ', + 'שׁ' => 'שׁ', + 'שׂ' => 'שׂ', + 'שּׁ' => 'שּׁ', + 'שּׂ' => 'שּׂ', + 'אַ' => 'אַ', + 'אָ' => 'אָ', + 'אּ' => 'אּ', + 'בּ' => 'בּ', + 'גּ' => 'גּ', + 'דּ' => 'דּ', + 'הּ' => 'הּ', + 'וּ' => 'וּ', + 'זּ' => 'זּ', + 'טּ' => 'טּ', + 'יּ' => 'יּ', + 'ךּ' => 'ךּ', + 'כּ' => 'כּ', + 'לּ' => 'לּ', + 'מּ' => 'מּ', + 'נּ' => 'נּ', + 'סּ' => 'סּ', + 'ףּ' => 'ףּ', + 'פּ' => 'פּ', + 'צּ' => 'צּ', + 'קּ' => 'קּ', + 'רּ' => 'רּ', + 'שּ' => 'שּ', + 'תּ' => 'תּ', + 'וֹ' => 'וֹ', + 'בֿ' => 'בֿ', + 'כֿ' => 'כֿ', + 'פֿ' => 'פֿ', + '𑂚' => '𑂚', + '𑂜' => '𑂜', + '𑂫' => '𑂫', + '𑄮' => '𑄮', + '𑄯' => '𑄯', + '𑍋' => '𑍋', + '𑍌' => '𑍌', + '𑒻' => '𑒻', + '𑒼' => '𑒼', + '𑒾' => '𑒾', + '𑖺' => '𑖺', + '𑖻' => '𑖻', + '𑤸' => '𑤸', + '𝅗𝅥' => '𝅗𝅥', + '𝅘𝅥' => '𝅘𝅥', + '𝅘𝅥𝅮' => '𝅘𝅥𝅮', + '𝅘𝅥𝅯' => '𝅘𝅥𝅯', + '𝅘𝅥𝅰' => '𝅘𝅥𝅰', + '𝅘𝅥𝅱' => '𝅘𝅥𝅱', + '𝅘𝅥𝅲' => '𝅘𝅥𝅲', + '𝆹𝅥' => '𝆹𝅥', + '𝆺𝅥' => '𝆺𝅥', + '𝆹𝅥𝅮' => '𝆹𝅥𝅮', + '𝆺𝅥𝅮' => '𝆺𝅥𝅮', + '𝆹𝅥𝅯' => '𝆹𝅥𝅯', + '𝆺𝅥𝅯' => '𝆺𝅥𝅯', + '丽' => '丽', + '丸' => '丸', + '乁' => '乁', + '𠄢' => '𠄢', + '你' => '你', + '侮' => '侮', + '侻' => '侻', + '倂' => '倂', + '偺' => '偺', + '備' => '備', + '僧' => '僧', + '像' => '像', + '㒞' => '㒞', + '𠘺' => '𠘺', + '免' => '免', + '兔' => '兔', + '兤' => '兤', + '具' => '具', + '𠔜' => '𠔜', + '㒹' => '㒹', + '內' => '內', + '再' => '再', + '𠕋' => '𠕋', + '冗' => '冗', + '冤' => '冤', + '仌' => '仌', + '冬' => '冬', + '况' => '况', + '𩇟' => '𩇟', + '凵' => '凵', + '刃' => '刃', + '㓟' => '㓟', + '刻' => '刻', + '剆' => '剆', + '割' => '割', + '剷' => '剷', + '㔕' => '㔕', + '勇' => '勇', + '勉' => '勉', + '勤' => '勤', + '勺' => '勺', + '包' => '包', + '匆' => '匆', + '北' => '北', + '卉' => '卉', + '卑' => '卑', + '博' => '博', + '即' => '即', + '卽' => '卽', + '卿' => '卿', + '卿' => '卿', + '卿' => '卿', + '𠨬' => '𠨬', + '灰' => '灰', + '及' => '及', + '叟' => '叟', + '𠭣' => '𠭣', + '叫' => '叫', + '叱' => '叱', + '吆' => '吆', + '咞' => '咞', + '吸' => '吸', + '呈' => '呈', + '周' => '周', + '咢' => '咢', + '哶' => '哶', + '唐' => '唐', + '啓' => '啓', + '啣' => '啣', + '善' => '善', + '善' => '善', + '喙' => '喙', + '喫' => '喫', + '喳' => '喳', + '嗂' => '嗂', + '圖' => '圖', + '嘆' => '嘆', + '圗' => '圗', + '噑' => '噑', + '噴' => '噴', + '切' => '切', + '壮' => '壮', + '城' => '城', + '埴' => '埴', + '堍' => '堍', + '型' => '型', + '堲' => '堲', + '報' => '報', + '墬' => '墬', + '𡓤' => '𡓤', + '売' => '売', + '壷' => '壷', + '夆' => '夆', + '多' => '多', + '夢' => '夢', + '奢' => '奢', + '𡚨' => '𡚨', + '𡛪' => '𡛪', + '姬' => '姬', + '娛' => '娛', + '娧' => '娧', + '姘' => '姘', + '婦' => '婦', + '㛮' => '㛮', + '㛼' => '㛼', + '嬈' => '嬈', + '嬾' => '嬾', + '嬾' => '嬾', + '𡧈' => '𡧈', + '寃' => '寃', + '寘' => '寘', + '寧' => '寧', + '寳' => '寳', + '𡬘' => '𡬘', + '寿' => '寿', + '将' => '将', + '当' => '当', + '尢' => '尢', + '㞁' => '㞁', + '屠' => '屠', + '屮' => '屮', + '峀' => '峀', + '岍' => '岍', + '𡷤' => '𡷤', + '嵃' => '嵃', + '𡷦' => '𡷦', + '嵮' => '嵮', + '嵫' => '嵫', + '嵼' => '嵼', + '巡' => '巡', + '巢' => '巢', + '㠯' => '㠯', + '巽' => '巽', + '帨' => '帨', + '帽' => '帽', + '幩' => '幩', + '㡢' => '㡢', + '𢆃' => '𢆃', + '㡼' => '㡼', + '庰' => '庰', + '庳' => '庳', + '庶' => '庶', + '廊' => '廊', + '𪎒' => '𪎒', + '廾' => '廾', + '𢌱' => '𢌱', + '𢌱' => '𢌱', + '舁' => '舁', + '弢' => '弢', + '弢' => '弢', + '㣇' => '㣇', + '𣊸' => '𣊸', + '𦇚' => '𦇚', + '形' => '形', + '彫' => '彫', + '㣣' => '㣣', + '徚' => '徚', + '忍' => '忍', + '志' => '志', + '忹' => '忹', + '悁' => '悁', + '㤺' => '㤺', + '㤜' => '㤜', + '悔' => '悔', + '𢛔' => '𢛔', + '惇' => '惇', + '慈' => '慈', + '慌' => '慌', + '慎' => '慎', + '慌' => '慌', + '慺' => '慺', + '憎' => '憎', + '憲' => '憲', + '憤' => '憤', + '憯' => '憯', + '懞' => '懞', + '懲' => '懲', + '懶' => '懶', + '成' => '成', + '戛' => '戛', + '扝' => '扝', + '抱' => '抱', + '拔' => '拔', + '捐' => '捐', + '𢬌' => '𢬌', + '挽' => '挽', + '拼' => '拼', + '捨' => '捨', + '掃' => '掃', + '揤' => '揤', + '𢯱' => '𢯱', + '搢' => '搢', + '揅' => '揅', + '掩' => '掩', + '㨮' => '㨮', + '摩' => '摩', + '摾' => '摾', + '撝' => '撝', + '摷' => '摷', + '㩬' => '㩬', + '敏' => '敏', + '敬' => '敬', + '𣀊' => '𣀊', + '旣' => '旣', + '書' => '書', + '晉' => '晉', + '㬙' => '㬙', + '暑' => '暑', + '㬈' => '㬈', + '㫤' => '㫤', + '冒' => '冒', + '冕' => '冕', + '最' => '最', + '暜' => '暜', + '肭' => '肭', + '䏙' => '䏙', + '朗' => '朗', + '望' => '望', + '朡' => '朡', + '杞' => '杞', + '杓' => '杓', + '𣏃' => '𣏃', + '㭉' => '㭉', + '柺' => '柺', + '枅' => '枅', + '桒' => '桒', + '梅' => '梅', + '𣑭' => '𣑭', + '梎' => '梎', + '栟' => '栟', + '椔' => '椔', + '㮝' => '㮝', + '楂' => '楂', + '榣' => '榣', + '槪' => '槪', + '檨' => '檨', + '𣚣' => '𣚣', + '櫛' => '櫛', + '㰘' => '㰘', + '次' => '次', + '𣢧' => '𣢧', + '歔' => '歔', + '㱎' => '㱎', + '歲' => '歲', + '殟' => '殟', + '殺' => '殺', + '殻' => '殻', + '𣪍' => '𣪍', + '𡴋' => '𡴋', + '𣫺' => '𣫺', + '汎' => '汎', + '𣲼' => '𣲼', + '沿' => '沿', + '泍' => '泍', + '汧' => '汧', + '洖' => '洖', + '派' => '派', + '海' => '海', + '流' => '流', + '浩' => '浩', + '浸' => '浸', + '涅' => '涅', + '𣴞' => '𣴞', + '洴' => '洴', + '港' => '港', + '湮' => '湮', + '㴳' => '㴳', + '滋' => '滋', + '滇' => '滇', + '𣻑' => '𣻑', + '淹' => '淹', + '潮' => '潮', + '𣽞' => '𣽞', + '𣾎' => '𣾎', + '濆' => '濆', + '瀹' => '瀹', + '瀞' => '瀞', + '瀛' => '瀛', + '㶖' => '㶖', + '灊' => '灊', + '災' => '災', + '灷' => '灷', + '炭' => '炭', + '𠔥' => '𠔥', + '煅' => '煅', + '𤉣' => '𤉣', + '熜' => '熜', + '𤎫' => '𤎫', + '爨' => '爨', + '爵' => '爵', + '牐' => '牐', + '𤘈' => '𤘈', + '犀' => '犀', + '犕' => '犕', + '𤜵' => '𤜵', + '𤠔' => '𤠔', + '獺' => '獺', + '王' => '王', + '㺬' => '㺬', + '玥' => '玥', + '㺸' => '㺸', + '㺸' => '㺸', + '瑇' => '瑇', + '瑜' => '瑜', + '瑱' => '瑱', + '璅' => '璅', + '瓊' => '瓊', + '㼛' => '㼛', + '甤' => '甤', + '𤰶' => '𤰶', + '甾' => '甾', + '𤲒' => '𤲒', + '異' => '異', + '𢆟' => '𢆟', + '瘐' => '瘐', + '𤾡' => '𤾡', + '𤾸' => '𤾸', + '𥁄' => '𥁄', + '㿼' => '㿼', + '䀈' => '䀈', + '直' => '直', + '𥃳' => '𥃳', + '𥃲' => '𥃲', + '𥄙' => '𥄙', + '𥄳' => '𥄳', + '眞' => '眞', + '真' => '真', + '真' => '真', + '睊' => '睊', + '䀹' => '䀹', + '瞋' => '瞋', + '䁆' => '䁆', + '䂖' => '䂖', + '𥐝' => '𥐝', + '硎' => '硎', + '碌' => '碌', + '磌' => '磌', + '䃣' => '䃣', + '𥘦' => '𥘦', + '祖' => '祖', + '𥚚' => '𥚚', + '𥛅' => '𥛅', + '福' => '福', + '秫' => '秫', + '䄯' => '䄯', + '穀' => '穀', + '穊' => '穊', + '穏' => '穏', + '𥥼' => '𥥼', + '𥪧' => '𥪧', + '𥪧' => '𥪧', + '竮' => '竮', + '䈂' => '䈂', + '𥮫' => '𥮫', + '篆' => '篆', + '築' => '築', + '䈧' => '䈧', + '𥲀' => '𥲀', + '糒' => '糒', + '䊠' => '䊠', + '糨' => '糨', + '糣' => '糣', + '紀' => '紀', + '𥾆' => '𥾆', + '絣' => '絣', + '䌁' => '䌁', + '緇' => '緇', + '縂' => '縂', + '繅' => '繅', + '䌴' => '䌴', + '𦈨' => '𦈨', + '𦉇' => '𦉇', + '䍙' => '䍙', + '𦋙' => '𦋙', + '罺' => '罺', + '𦌾' => '𦌾', + '羕' => '羕', + '翺' => '翺', + '者' => '者', + '𦓚' => '𦓚', + '𦔣' => '𦔣', + '聠' => '聠', + '𦖨' => '𦖨', + '聰' => '聰', + '𣍟' => '𣍟', + '䏕' => '䏕', + '育' => '育', + '脃' => '脃', + '䐋' => '䐋', + '脾' => '脾', + '媵' => '媵', + '𦞧' => '𦞧', + '𦞵' => '𦞵', + '𣎓' => '𣎓', + '𣎜' => '𣎜', + '舁' => '舁', + '舄' => '舄', + '辞' => '辞', + '䑫' => '䑫', + '芑' => '芑', + '芋' => '芋', + '芝' => '芝', + '劳' => '劳', + '花' => '花', + '芳' => '芳', + '芽' => '芽', + '苦' => '苦', + '𦬼' => '𦬼', + '若' => '若', + '茝' => '茝', + '荣' => '荣', + '莭' => '莭', + '茣' => '茣', + '莽' => '莽', + '菧' => '菧', + '著' => '著', + '荓' => '荓', + '菊' => '菊', + '菌' => '菌', + '菜' => '菜', + '𦰶' => '𦰶', + '𦵫' => '𦵫', + '𦳕' => '𦳕', + '䔫' => '䔫', + '蓱' => '蓱', + '蓳' => '蓳', + '蔖' => '蔖', + '𧏊' => '𧏊', + '蕤' => '蕤', + '𦼬' => '𦼬', + '䕝' => '䕝', + '䕡' => '䕡', + '𦾱' => '𦾱', + '𧃒' => '𧃒', + '䕫' => '䕫', + '虐' => '虐', + '虜' => '虜', + '虧' => '虧', + '虩' => '虩', + '蚩' => '蚩', + '蚈' => '蚈', + '蜎' => '蜎', + '蛢' => '蛢', + '蝹' => '蝹', + '蜨' => '蜨', + '蝫' => '蝫', + '螆' => '螆', + '䗗' => '䗗', + '蟡' => '蟡', + '蠁' => '蠁', + '䗹' => '䗹', + '衠' => '衠', + '衣' => '衣', + '𧙧' => '𧙧', + '裗' => '裗', + '裞' => '裞', + '䘵' => '䘵', + '裺' => '裺', + '㒻' => '㒻', + '𧢮' => '𧢮', + '𧥦' => '𧥦', + '䚾' => '䚾', + '䛇' => '䛇', + '誠' => '誠', + '諭' => '諭', + '變' => '變', + '豕' => '豕', + '𧲨' => '𧲨', + '貫' => '貫', + '賁' => '賁', + '贛' => '贛', + '起' => '起', + '𧼯' => '𧼯', + '𠠄' => '𠠄', + '跋' => '跋', + '趼' => '趼', + '跰' => '跰', + '𠣞' => '𠣞', + '軔' => '軔', + '輸' => '輸', + '𨗒' => '𨗒', + '𨗭' => '𨗭', + '邔' => '邔', + '郱' => '郱', + '鄑' => '鄑', + '𨜮' => '𨜮', + '鄛' => '鄛', + '鈸' => '鈸', + '鋗' => '鋗', + '鋘' => '鋘', + '鉼' => '鉼', + '鏹' => '鏹', + '鐕' => '鐕', + '𨯺' => '𨯺', + '開' => '開', + '䦕' => '䦕', + '閷' => '閷', + '𨵷' => '𨵷', + '䧦' => '䧦', + '雃' => '雃', + '嶲' => '嶲', + '霣' => '霣', + '𩅅' => '𩅅', + '𩈚' => '𩈚', + '䩮' => '䩮', + '䩶' => '䩶', + '韠' => '韠', + '𩐊' => '𩐊', + '䪲' => '䪲', + '𩒖' => '𩒖', + '頋' => '頋', + '頋' => '頋', + '頩' => '頩', + '𩖶' => '𩖶', + '飢' => '飢', + '䬳' => '䬳', + '餩' => '餩', + '馧' => '馧', + '駂' => '駂', + '駾' => '駾', + '䯎' => '䯎', + '𩬰' => '𩬰', + '鬒' => '鬒', + '鱀' => '鱀', + '鳽' => '鳽', + '䳎' => '䳎', + '䳭' => '䳭', + '鵧' => '鵧', + '𪃎' => '𪃎', + '䳸' => '䳸', + '𪄅' => '𪄅', + '𪈎' => '𪈎', + '𪊑' => '𪊑', + '麻' => '麻', + '䵖' => '䵖', + '黹' => '黹', + '黾' => '黾', + '鼅' => '鼅', + '鼏' => '鼏', + '鼖' => '鼖', + '鼻' => '鼻', + '𪘀' => '𪘀', +); diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php new file mode 100644 index 000000000..ec90f36eb --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php @@ -0,0 +1,876 @@ + 230, + '́' => 230, + '̂' => 230, + '̃' => 230, + '̄' => 230, + '̅' => 230, + '̆' => 230, + '̇' => 230, + '̈' => 230, + '̉' => 230, + '̊' => 230, + '̋' => 230, + '̌' => 230, + '̍' => 230, + '̎' => 230, + '̏' => 230, + '̐' => 230, + '̑' => 230, + '̒' => 230, + '̓' => 230, + '̔' => 230, + '̕' => 232, + '̖' => 220, + '̗' => 220, + '̘' => 220, + '̙' => 220, + '̚' => 232, + '̛' => 216, + '̜' => 220, + '̝' => 220, + '̞' => 220, + '̟' => 220, + '̠' => 220, + '̡' => 202, + '̢' => 202, + '̣' => 220, + '̤' => 220, + '̥' => 220, + '̦' => 220, + '̧' => 202, + '̨' => 202, + '̩' => 220, + '̪' => 220, + '̫' => 220, + '̬' => 220, + '̭' => 220, + '̮' => 220, + '̯' => 220, + '̰' => 220, + '̱' => 220, + '̲' => 220, + '̳' => 220, + '̴' => 1, + '̵' => 1, + '̶' => 1, + '̷' => 1, + '̸' => 1, + '̹' => 220, + '̺' => 220, + '̻' => 220, + '̼' => 220, + '̽' => 230, + '̾' => 230, + '̿' => 230, + '̀' => 230, + '́' => 230, + '͂' => 230, + '̓' => 230, + '̈́' => 230, + 'ͅ' => 240, + '͆' => 230, + '͇' => 220, + '͈' => 220, + '͉' => 220, + '͊' => 230, + '͋' => 230, + '͌' => 230, + '͍' => 220, + '͎' => 220, + '͐' => 230, + '͑' => 230, + '͒' => 230, + '͓' => 220, + '͔' => 220, + '͕' => 220, + '͖' => 220, + '͗' => 230, + '͘' => 232, + '͙' => 220, + '͚' => 220, + '͛' => 230, + '͜' => 233, + '͝' => 234, + '͞' => 234, + '͟' => 233, + '͠' => 234, + '͡' => 234, + '͢' => 233, + 'ͣ' => 230, + 'ͤ' => 230, + 'ͥ' => 230, + 'ͦ' => 230, + 'ͧ' => 230, + 'ͨ' => 230, + 'ͩ' => 230, + 'ͪ' => 230, + 'ͫ' => 230, + 'ͬ' => 230, + 'ͭ' => 230, + 'ͮ' => 230, + 'ͯ' => 230, + '҃' => 230, + '҄' => 230, + '҅' => 230, + '҆' => 230, + '҇' => 230, + '֑' => 220, + '֒' => 230, + '֓' => 230, + '֔' => 230, + '֕' => 230, + '֖' => 220, + '֗' => 230, + '֘' => 230, + '֙' => 230, + '֚' => 222, + '֛' => 220, + '֜' => 230, + '֝' => 230, + '֞' => 230, + '֟' => 230, + '֠' => 230, + '֡' => 230, + '֢' => 220, + '֣' => 220, + '֤' => 220, + '֥' => 220, + '֦' => 220, + '֧' => 220, + '֨' => 230, + '֩' => 230, + '֪' => 220, + '֫' => 230, + '֬' => 230, + '֭' => 222, + '֮' => 228, + '֯' => 230, + 'ְ' => 10, + 'ֱ' => 11, + 'ֲ' => 12, + 'ֳ' => 13, + 'ִ' => 14, + 'ֵ' => 15, + 'ֶ' => 16, + 'ַ' => 17, + 'ָ' => 18, + 'ֹ' => 19, + 'ֺ' => 19, + 'ֻ' => 20, + 'ּ' => 21, + 'ֽ' => 22, + 'ֿ' => 23, + 'ׁ' => 24, + 'ׂ' => 25, + 'ׄ' => 230, + 'ׅ' => 220, + 'ׇ' => 18, + 'ؐ' => 230, + 'ؑ' => 230, + 'ؒ' => 230, + 'ؓ' => 230, + 'ؔ' => 230, + 'ؕ' => 230, + 'ؖ' => 230, + 'ؗ' => 230, + 'ؘ' => 30, + 'ؙ' => 31, + 'ؚ' => 32, + 'ً' => 27, + 'ٌ' => 28, + 'ٍ' => 29, + 'َ' => 30, + 'ُ' => 31, + 'ِ' => 32, + 'ّ' => 33, + 'ْ' => 34, + 'ٓ' => 230, + 'ٔ' => 230, + 'ٕ' => 220, + 'ٖ' => 220, + 'ٗ' => 230, + '٘' => 230, + 'ٙ' => 230, + 'ٚ' => 230, + 'ٛ' => 230, + 'ٜ' => 220, + 'ٝ' => 230, + 'ٞ' => 230, + 'ٟ' => 220, + 'ٰ' => 35, + 'ۖ' => 230, + 'ۗ' => 230, + 'ۘ' => 230, + 'ۙ' => 230, + 'ۚ' => 230, + 'ۛ' => 230, + 'ۜ' => 230, + '۟' => 230, + '۠' => 230, + 'ۡ' => 230, + 'ۢ' => 230, + 'ۣ' => 220, + 'ۤ' => 230, + 'ۧ' => 230, + 'ۨ' => 230, + '۪' => 220, + '۫' => 230, + '۬' => 230, + 'ۭ' => 220, + 'ܑ' => 36, + 'ܰ' => 230, + 'ܱ' => 220, + 'ܲ' => 230, + 'ܳ' => 230, + 'ܴ' => 220, + 'ܵ' => 230, + 'ܶ' => 230, + 'ܷ' => 220, + 'ܸ' => 220, + 'ܹ' => 220, + 'ܺ' => 230, + 'ܻ' => 220, + 'ܼ' => 220, + 'ܽ' => 230, + 'ܾ' => 220, + 'ܿ' => 230, + '݀' => 230, + '݁' => 230, + '݂' => 220, + '݃' => 230, + '݄' => 220, + '݅' => 230, + '݆' => 220, + '݇' => 230, + '݈' => 220, + '݉' => 230, + '݊' => 230, + '߫' => 230, + '߬' => 230, + '߭' => 230, + '߮' => 230, + '߯' => 230, + '߰' => 230, + '߱' => 230, + '߲' => 220, + '߳' => 230, + '߽' => 220, + 'ࠖ' => 230, + 'ࠗ' => 230, + '࠘' => 230, + '࠙' => 230, + 'ࠛ' => 230, + 'ࠜ' => 230, + 'ࠝ' => 230, + 'ࠞ' => 230, + 'ࠟ' => 230, + 'ࠠ' => 230, + 'ࠡ' => 230, + 'ࠢ' => 230, + 'ࠣ' => 230, + 'ࠥ' => 230, + 'ࠦ' => 230, + 'ࠧ' => 230, + 'ࠩ' => 230, + 'ࠪ' => 230, + 'ࠫ' => 230, + 'ࠬ' => 230, + '࠭' => 230, + '࡙' => 220, + '࡚' => 220, + '࡛' => 220, + '࣓' => 220, + 'ࣔ' => 230, + 'ࣕ' => 230, + 'ࣖ' => 230, + 'ࣗ' => 230, + 'ࣘ' => 230, + 'ࣙ' => 230, + 'ࣚ' => 230, + 'ࣛ' => 230, + 'ࣜ' => 230, + 'ࣝ' => 230, + 'ࣞ' => 230, + 'ࣟ' => 230, + '࣠' => 230, + '࣡' => 230, + 'ࣣ' => 220, + 'ࣤ' => 230, + 'ࣥ' => 230, + 'ࣦ' => 220, + 'ࣧ' => 230, + 'ࣨ' => 230, + 'ࣩ' => 220, + '࣪' => 230, + '࣫' => 230, + '࣬' => 230, + '࣭' => 220, + '࣮' => 220, + '࣯' => 220, + 'ࣰ' => 27, + 'ࣱ' => 28, + 'ࣲ' => 29, + 'ࣳ' => 230, + 'ࣴ' => 230, + 'ࣵ' => 230, + 'ࣶ' => 220, + 'ࣷ' => 230, + 'ࣸ' => 230, + 'ࣹ' => 220, + 'ࣺ' => 220, + 'ࣻ' => 230, + 'ࣼ' => 230, + 'ࣽ' => 230, + 'ࣾ' => 230, + 'ࣿ' => 230, + '़' => 7, + '्' => 9, + '॑' => 230, + '॒' => 220, + '॓' => 230, + '॔' => 230, + '়' => 7, + '্' => 9, + '৾' => 230, + '਼' => 7, + '੍' => 9, + '઼' => 7, + '્' => 9, + '଼' => 7, + '୍' => 9, + '்' => 9, + '్' => 9, + 'ౕ' => 84, + 'ౖ' => 91, + '಼' => 7, + '್' => 9, + '഻' => 9, + '഼' => 9, + '്' => 9, + '්' => 9, + 'ุ' => 103, + 'ู' => 103, + 'ฺ' => 9, + '่' => 107, + '้' => 107, + '๊' => 107, + '๋' => 107, + 'ຸ' => 118, + 'ູ' => 118, + '຺' => 9, + '່' => 122, + '້' => 122, + '໊' => 122, + '໋' => 122, + '༘' => 220, + '༙' => 220, + '༵' => 220, + '༷' => 220, + '༹' => 216, + 'ཱ' => 129, + 'ི' => 130, + 'ུ' => 132, + 'ེ' => 130, + 'ཻ' => 130, + 'ོ' => 130, + 'ཽ' => 130, + 'ྀ' => 130, + 'ྂ' => 230, + 'ྃ' => 230, + '྄' => 9, + '྆' => 230, + '྇' => 230, + '࿆' => 220, + '့' => 7, + '္' => 9, + '်' => 9, + 'ႍ' => 220, + '፝' => 230, + '፞' => 230, + '፟' => 230, + '᜔' => 9, + '᜴' => 9, + '្' => 9, + '៝' => 230, + 'ᢩ' => 228, + '᤹' => 222, + '᤺' => 230, + '᤻' => 220, + 'ᨗ' => 230, + 'ᨘ' => 220, + '᩠' => 9, + '᩵' => 230, + '᩶' => 230, + '᩷' => 230, + '᩸' => 230, + '᩹' => 230, + '᩺' => 230, + '᩻' => 230, + '᩼' => 230, + '᩿' => 220, + '᪰' => 230, + '᪱' => 230, + '᪲' => 230, + '᪳' => 230, + '᪴' => 230, + '᪵' => 220, + '᪶' => 220, + '᪷' => 220, + '᪸' => 220, + '᪹' => 220, + '᪺' => 220, + '᪻' => 230, + '᪼' => 230, + '᪽' => 220, + 'ᪿ' => 220, + 'ᫀ' => 220, + '᬴' => 7, + '᭄' => 9, + '᭫' => 230, + '᭬' => 220, + '᭭' => 230, + '᭮' => 230, + '᭯' => 230, + '᭰' => 230, + '᭱' => 230, + '᭲' => 230, + '᭳' => 230, + '᮪' => 9, + '᮫' => 9, + '᯦' => 7, + '᯲' => 9, + '᯳' => 9, + '᰷' => 7, + '᳐' => 230, + '᳑' => 230, + '᳒' => 230, + '᳔' => 1, + '᳕' => 220, + '᳖' => 220, + '᳗' => 220, + '᳘' => 220, + '᳙' => 220, + '᳚' => 230, + '᳛' => 230, + '᳜' => 220, + '᳝' => 220, + '᳞' => 220, + '᳟' => 220, + '᳠' => 230, + '᳢' => 1, + '᳣' => 1, + '᳤' => 1, + '᳥' => 1, + '᳦' => 1, + '᳧' => 1, + '᳨' => 1, + '᳭' => 220, + '᳴' => 230, + '᳸' => 230, + '᳹' => 230, + '᷀' => 230, + '᷁' => 230, + '᷂' => 220, + '᷃' => 230, + '᷄' => 230, + '᷅' => 230, + '᷆' => 230, + '᷇' => 230, + '᷈' => 230, + '᷉' => 230, + '᷊' => 220, + '᷋' => 230, + '᷌' => 230, + '᷍' => 234, + '᷎' => 214, + '᷏' => 220, + '᷐' => 202, + '᷑' => 230, + '᷒' => 230, + 'ᷓ' => 230, + 'ᷔ' => 230, + 'ᷕ' => 230, + 'ᷖ' => 230, + 'ᷗ' => 230, + 'ᷘ' => 230, + 'ᷙ' => 230, + 'ᷚ' => 230, + 'ᷛ' => 230, + 'ᷜ' => 230, + 'ᷝ' => 230, + 'ᷞ' => 230, + 'ᷟ' => 230, + 'ᷠ' => 230, + 'ᷡ' => 230, + 'ᷢ' => 230, + 'ᷣ' => 230, + 'ᷤ' => 230, + 'ᷥ' => 230, + 'ᷦ' => 230, + 'ᷧ' => 230, + 'ᷨ' => 230, + 'ᷩ' => 230, + 'ᷪ' => 230, + 'ᷫ' => 230, + 'ᷬ' => 230, + 'ᷭ' => 230, + 'ᷮ' => 230, + 'ᷯ' => 230, + 'ᷰ' => 230, + 'ᷱ' => 230, + 'ᷲ' => 230, + 'ᷳ' => 230, + 'ᷴ' => 230, + '᷵' => 230, + '᷶' => 232, + '᷷' => 228, + '᷸' => 228, + '᷹' => 220, + '᷻' => 230, + '᷼' => 233, + '᷽' => 220, + '᷾' => 230, + '᷿' => 220, + '⃐' => 230, + '⃑' => 230, + '⃒' => 1, + '⃓' => 1, + '⃔' => 230, + '⃕' => 230, + '⃖' => 230, + '⃗' => 230, + '⃘' => 1, + '⃙' => 1, + '⃚' => 1, + '⃛' => 230, + '⃜' => 230, + '⃡' => 230, + '⃥' => 1, + '⃦' => 1, + '⃧' => 230, + '⃨' => 220, + '⃩' => 230, + '⃪' => 1, + '⃫' => 1, + '⃬' => 220, + '⃭' => 220, + '⃮' => 220, + '⃯' => 220, + '⃰' => 230, + '⳯' => 230, + '⳰' => 230, + '⳱' => 230, + '⵿' => 9, + 'ⷠ' => 230, + 'ⷡ' => 230, + 'ⷢ' => 230, + 'ⷣ' => 230, + 'ⷤ' => 230, + 'ⷥ' => 230, + 'ⷦ' => 230, + 'ⷧ' => 230, + 'ⷨ' => 230, + 'ⷩ' => 230, + 'ⷪ' => 230, + 'ⷫ' => 230, + 'ⷬ' => 230, + 'ⷭ' => 230, + 'ⷮ' => 230, + 'ⷯ' => 230, + 'ⷰ' => 230, + 'ⷱ' => 230, + 'ⷲ' => 230, + 'ⷳ' => 230, + 'ⷴ' => 230, + 'ⷵ' => 230, + 'ⷶ' => 230, + 'ⷷ' => 230, + 'ⷸ' => 230, + 'ⷹ' => 230, + 'ⷺ' => 230, + 'ⷻ' => 230, + 'ⷼ' => 230, + 'ⷽ' => 230, + 'ⷾ' => 230, + 'ⷿ' => 230, + '〪' => 218, + '〫' => 228, + '〬' => 232, + '〭' => 222, + '〮' => 224, + '〯' => 224, + '゙' => 8, + '゚' => 8, + '꙯' => 230, + 'ꙴ' => 230, + 'ꙵ' => 230, + 'ꙶ' => 230, + 'ꙷ' => 230, + 'ꙸ' => 230, + 'ꙹ' => 230, + 'ꙺ' => 230, + 'ꙻ' => 230, + '꙼' => 230, + '꙽' => 230, + 'ꚞ' => 230, + 'ꚟ' => 230, + '꛰' => 230, + '꛱' => 230, + '꠆' => 9, + '꠬' => 9, + '꣄' => 9, + '꣠' => 230, + '꣡' => 230, + '꣢' => 230, + '꣣' => 230, + '꣤' => 230, + '꣥' => 230, + '꣦' => 230, + '꣧' => 230, + '꣨' => 230, + '꣩' => 230, + '꣪' => 230, + '꣫' => 230, + '꣬' => 230, + '꣭' => 230, + '꣮' => 230, + '꣯' => 230, + '꣰' => 230, + '꣱' => 230, + '꤫' => 220, + '꤬' => 220, + '꤭' => 220, + '꥓' => 9, + '꦳' => 7, + '꧀' => 9, + 'ꪰ' => 230, + 'ꪲ' => 230, + 'ꪳ' => 230, + 'ꪴ' => 220, + 'ꪷ' => 230, + 'ꪸ' => 230, + 'ꪾ' => 230, + '꪿' => 230, + '꫁' => 230, + '꫶' => 9, + '꯭' => 9, + 'ﬞ' => 26, + '︠' => 230, + '︡' => 230, + '︢' => 230, + '︣' => 230, + '︤' => 230, + '︥' => 230, + '︦' => 230, + '︧' => 220, + '︨' => 220, + '︩' => 220, + '︪' => 220, + '︫' => 220, + '︬' => 220, + '︭' => 220, + '︮' => 230, + '︯' => 230, + '𐇽' => 220, + '𐋠' => 220, + '𐍶' => 230, + '𐍷' => 230, + '𐍸' => 230, + '𐍹' => 230, + '𐍺' => 230, + '𐨍' => 220, + '𐨏' => 230, + '𐨸' => 230, + '𐨹' => 1, + '𐨺' => 220, + '𐨿' => 9, + '𐫥' => 230, + '𐫦' => 220, + '𐴤' => 230, + '𐴥' => 230, + '𐴦' => 230, + '𐴧' => 230, + '𐺫' => 230, + '𐺬' => 230, + '𐽆' => 220, + '𐽇' => 220, + '𐽈' => 230, + '𐽉' => 230, + '𐽊' => 230, + '𐽋' => 220, + '𐽌' => 230, + '𐽍' => 220, + '𐽎' => 220, + '𐽏' => 220, + '𐽐' => 220, + '𑁆' => 9, + '𑁿' => 9, + '𑂹' => 9, + '𑂺' => 7, + '𑄀' => 230, + '𑄁' => 230, + '𑄂' => 230, + '𑄳' => 9, + '𑄴' => 9, + '𑅳' => 7, + '𑇀' => 9, + '𑇊' => 7, + '𑈵' => 9, + '𑈶' => 7, + '𑋩' => 7, + '𑋪' => 9, + '𑌻' => 7, + '𑌼' => 7, + '𑍍' => 9, + '𑍦' => 230, + '𑍧' => 230, + '𑍨' => 230, + '𑍩' => 230, + '𑍪' => 230, + '𑍫' => 230, + '𑍬' => 230, + '𑍰' => 230, + '𑍱' => 230, + '𑍲' => 230, + '𑍳' => 230, + '𑍴' => 230, + '𑑂' => 9, + '𑑆' => 7, + '𑑞' => 230, + '𑓂' => 9, + '𑓃' => 7, + '𑖿' => 9, + '𑗀' => 7, + '𑘿' => 9, + '𑚶' => 9, + '𑚷' => 7, + '𑜫' => 9, + '𑠹' => 9, + '𑠺' => 7, + '𑤽' => 9, + '𑤾' => 9, + '𑥃' => 7, + '𑧠' => 9, + '𑨴' => 9, + '𑩇' => 9, + '𑪙' => 9, + '𑰿' => 9, + '𑵂' => 7, + '𑵄' => 9, + '𑵅' => 9, + '𑶗' => 9, + '𖫰' => 1, + '𖫱' => 1, + '𖫲' => 1, + '𖫳' => 1, + '𖫴' => 1, + '𖬰' => 230, + '𖬱' => 230, + '𖬲' => 230, + '𖬳' => 230, + '𖬴' => 230, + '𖬵' => 230, + '𖬶' => 230, + '𖿰' => 6, + '𖿱' => 6, + '𛲞' => 1, + '𝅥' => 216, + '𝅦' => 216, + '𝅧' => 1, + '𝅨' => 1, + '𝅩' => 1, + '𝅭' => 226, + '𝅮' => 216, + '𝅯' => 216, + '𝅰' => 216, + '𝅱' => 216, + '𝅲' => 216, + '𝅻' => 220, + '𝅼' => 220, + '𝅽' => 220, + '𝅾' => 220, + '𝅿' => 220, + '𝆀' => 220, + '𝆁' => 220, + '𝆂' => 220, + '𝆅' => 230, + '𝆆' => 230, + '𝆇' => 230, + '𝆈' => 230, + '𝆉' => 230, + '𝆊' => 220, + '𝆋' => 220, + '𝆪' => 230, + '𝆫' => 230, + '𝆬' => 230, + '𝆭' => 230, + '𝉂' => 230, + '𝉃' => 230, + '𝉄' => 230, + '𞀀' => 230, + '𞀁' => 230, + '𞀂' => 230, + '𞀃' => 230, + '𞀄' => 230, + '𞀅' => 230, + '𞀆' => 230, + '𞀈' => 230, + '𞀉' => 230, + '𞀊' => 230, + '𞀋' => 230, + '𞀌' => 230, + '𞀍' => 230, + '𞀎' => 230, + '𞀏' => 230, + '𞀐' => 230, + '𞀑' => 230, + '𞀒' => 230, + '𞀓' => 230, + '𞀔' => 230, + '𞀕' => 230, + '𞀖' => 230, + '𞀗' => 230, + '𞀘' => 230, + '𞀛' => 230, + '𞀜' => 230, + '𞀝' => 230, + '𞀞' => 230, + '𞀟' => 230, + '𞀠' => 230, + '𞀡' => 230, + '𞀣' => 230, + '𞀤' => 230, + '𞀦' => 230, + '𞀧' => 230, + '𞀨' => 230, + '𞀩' => 230, + '𞀪' => 230, + '𞄰' => 230, + '𞄱' => 230, + '𞄲' => 230, + '𞄳' => 230, + '𞄴' => 230, + '𞄵' => 230, + '𞄶' => 230, + '𞋬' => 230, + '𞋭' => 230, + '𞋮' => 230, + '𞋯' => 230, + '𞣐' => 220, + '𞣑' => 220, + '𞣒' => 220, + '𞣓' => 220, + '𞣔' => 220, + '𞣕' => 220, + '𞣖' => 220, + '𞥄' => 230, + '𞥅' => 230, + '𞥆' => 230, + '𞥇' => 230, + '𞥈' => 230, + '𞥉' => 230, + '𞥊' => 7, +); diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php new file mode 100644 index 000000000..157490289 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php @@ -0,0 +1,3695 @@ + ' ', + '¨' => ' ̈', + 'ª' => 'a', + '¯' => ' ̄', + '²' => '2', + '³' => '3', + '´' => ' ́', + 'µ' => 'μ', + '¸' => ' ̧', + '¹' => '1', + 'º' => 'o', + '¼' => '1⁄4', + '½' => '1⁄2', + '¾' => '3⁄4', + 'IJ' => 'IJ', + 'ij' => 'ij', + 'Ŀ' => 'L·', + 'ŀ' => 'l·', + 'ʼn' => 'ʼn', + 'ſ' => 's', + 'DŽ' => 'DŽ', + 'Dž' => 'Dž', + 'dž' => 'dž', + 'LJ' => 'LJ', + 'Lj' => 'Lj', + 'lj' => 'lj', + 'NJ' => 'NJ', + 'Nj' => 'Nj', + 'nj' => 'nj', + 'DZ' => 'DZ', + 'Dz' => 'Dz', + 'dz' => 'dz', + 'ʰ' => 'h', + 'ʱ' => 'ɦ', + 'ʲ' => 'j', + 'ʳ' => 'r', + 'ʴ' => 'ɹ', + 'ʵ' => 'ɻ', + 'ʶ' => 'ʁ', + 'ʷ' => 'w', + 'ʸ' => 'y', + '˘' => ' ̆', + '˙' => ' ̇', + '˚' => ' ̊', + '˛' => ' ̨', + '˜' => ' ̃', + '˝' => ' ̋', + 'ˠ' => 'ɣ', + 'ˡ' => 'l', + 'ˢ' => 's', + 'ˣ' => 'x', + 'ˤ' => 'ʕ', + 'ͺ' => ' ͅ', + '΄' => ' ́', + '΅' => ' ̈́', + 'ϐ' => 'β', + 'ϑ' => 'θ', + 'ϒ' => 'Υ', + 'ϓ' => 'Ύ', + 'ϔ' => 'Ϋ', + 'ϕ' => 'φ', + 'ϖ' => 'π', + 'ϰ' => 'κ', + 'ϱ' => 'ρ', + 'ϲ' => 'ς', + 'ϴ' => 'Θ', + 'ϵ' => 'ε', + 'Ϲ' => 'Σ', + 'և' => 'եւ', + 'ٵ' => 'اٴ', + 'ٶ' => 'وٴ', + 'ٷ' => 'ۇٴ', + 'ٸ' => 'يٴ', + 'ำ' => 'ํา', + 'ຳ' => 'ໍາ', + 'ໜ' => 'ຫນ', + 'ໝ' => 'ຫມ', + '༌' => '་', + 'ཷ' => 'ྲཱྀ', + 'ཹ' => 'ླཱྀ', + 'ჼ' => 'ნ', + 'ᴬ' => 'A', + 'ᴭ' => 'Æ', + 'ᴮ' => 'B', + 'ᴰ' => 'D', + 'ᴱ' => 'E', + 'ᴲ' => 'Ǝ', + 'ᴳ' => 'G', + 'ᴴ' => 'H', + 'ᴵ' => 'I', + 'ᴶ' => 'J', + 'ᴷ' => 'K', + 'ᴸ' => 'L', + 'ᴹ' => 'M', + 'ᴺ' => 'N', + 'ᴼ' => 'O', + 'ᴽ' => 'Ȣ', + 'ᴾ' => 'P', + 'ᴿ' => 'R', + 'ᵀ' => 'T', + 'ᵁ' => 'U', + 'ᵂ' => 'W', + 'ᵃ' => 'a', + 'ᵄ' => 'ɐ', + 'ᵅ' => 'ɑ', + 'ᵆ' => 'ᴂ', + 'ᵇ' => 'b', + 'ᵈ' => 'd', + 'ᵉ' => 'e', + 'ᵊ' => 'ə', + 'ᵋ' => 'ɛ', + 'ᵌ' => 'ɜ', + 'ᵍ' => 'g', + 'ᵏ' => 'k', + 'ᵐ' => 'm', + 'ᵑ' => 'ŋ', + 'ᵒ' => 'o', + 'ᵓ' => 'ɔ', + 'ᵔ' => 'ᴖ', + 'ᵕ' => 'ᴗ', + 'ᵖ' => 'p', + 'ᵗ' => 't', + 'ᵘ' => 'u', + 'ᵙ' => 'ᴝ', + 'ᵚ' => 'ɯ', + 'ᵛ' => 'v', + 'ᵜ' => 'ᴥ', + 'ᵝ' => 'β', + 'ᵞ' => 'γ', + 'ᵟ' => 'δ', + 'ᵠ' => 'φ', + 'ᵡ' => 'χ', + 'ᵢ' => 'i', + 'ᵣ' => 'r', + 'ᵤ' => 'u', + 'ᵥ' => 'v', + 'ᵦ' => 'β', + 'ᵧ' => 'γ', + 'ᵨ' => 'ρ', + 'ᵩ' => 'φ', + 'ᵪ' => 'χ', + 'ᵸ' => 'н', + 'ᶛ' => 'ɒ', + 'ᶜ' => 'c', + 'ᶝ' => 'ɕ', + 'ᶞ' => 'ð', + 'ᶟ' => 'ɜ', + 'ᶠ' => 'f', + 'ᶡ' => 'ɟ', + 'ᶢ' => 'ɡ', + 'ᶣ' => 'ɥ', + 'ᶤ' => 'ɨ', + 'ᶥ' => 'ɩ', + 'ᶦ' => 'ɪ', + 'ᶧ' => 'ᵻ', + 'ᶨ' => 'ʝ', + 'ᶩ' => 'ɭ', + 'ᶪ' => 'ᶅ', + 'ᶫ' => 'ʟ', + 'ᶬ' => 'ɱ', + 'ᶭ' => 'ɰ', + 'ᶮ' => 'ɲ', + 'ᶯ' => 'ɳ', + 'ᶰ' => 'ɴ', + 'ᶱ' => 'ɵ', + 'ᶲ' => 'ɸ', + 'ᶳ' => 'ʂ', + 'ᶴ' => 'ʃ', + 'ᶵ' => 'ƫ', + 'ᶶ' => 'ʉ', + 'ᶷ' => 'ʊ', + 'ᶸ' => 'ᴜ', + 'ᶹ' => 'ʋ', + 'ᶺ' => 'ʌ', + 'ᶻ' => 'z', + 'ᶼ' => 'ʐ', + 'ᶽ' => 'ʑ', + 'ᶾ' => 'ʒ', + 'ᶿ' => 'θ', + 'ẚ' => 'aʾ', + 'ẛ' => 'ṡ', + '᾽' => ' ̓', + '᾿' => ' ̓', + '῀' => ' ͂', + '῁' => ' ̈͂', + '῍' => ' ̓̀', + '῎' => ' ̓́', + '῏' => ' ̓͂', + '῝' => ' ̔̀', + '῞' => ' ̔́', + '῟' => ' ̔͂', + '῭' => ' ̈̀', + '΅' => ' ̈́', + '´' => ' ́', + '῾' => ' ̔', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + ' ' => ' ', + '‑' => '‐', + '‗' => ' ̳', + '․' => '.', + '‥' => '..', + '…' => '...', + ' ' => ' ', + '″' => '′′', + '‴' => '′′′', + '‶' => '‵‵', + '‷' => '‵‵‵', + '‼' => '!!', + '‾' => ' ̅', + '⁇' => '??', + '⁈' => '?!', + '⁉' => '!?', + '⁗' => '′′′′', + ' ' => ' ', + '⁰' => '0', + 'ⁱ' => 'i', + '⁴' => '4', + '⁵' => '5', + '⁶' => '6', + '⁷' => '7', + '⁸' => '8', + '⁹' => '9', + '⁺' => '+', + '⁻' => '−', + '⁼' => '=', + '⁽' => '(', + '⁾' => ')', + 'ⁿ' => 'n', + '₀' => '0', + '₁' => '1', + '₂' => '2', + '₃' => '3', + '₄' => '4', + '₅' => '5', + '₆' => '6', + '₇' => '7', + '₈' => '8', + '₉' => '9', + '₊' => '+', + '₋' => '−', + '₌' => '=', + '₍' => '(', + '₎' => ')', + 'ₐ' => 'a', + 'ₑ' => 'e', + 'ₒ' => 'o', + 'ₓ' => 'x', + 'ₔ' => 'ə', + 'ₕ' => 'h', + 'ₖ' => 'k', + 'ₗ' => 'l', + 'ₘ' => 'm', + 'ₙ' => 'n', + 'ₚ' => 'p', + 'ₛ' => 's', + 'ₜ' => 't', + '₨' => 'Rs', + '℀' => 'a/c', + '℁' => 'a/s', + 'ℂ' => 'C', + '℃' => '°C', + '℅' => 'c/o', + '℆' => 'c/u', + 'ℇ' => 'Ɛ', + '℉' => '°F', + 'ℊ' => 'g', + 'ℋ' => 'H', + 'ℌ' => 'H', + 'ℍ' => 'H', + 'ℎ' => 'h', + 'ℏ' => 'ħ', + 'ℐ' => 'I', + 'ℑ' => 'I', + 'ℒ' => 'L', + 'ℓ' => 'l', + 'ℕ' => 'N', + '№' => 'No', + 'ℙ' => 'P', + 'ℚ' => 'Q', + 'ℛ' => 'R', + 'ℜ' => 'R', + 'ℝ' => 'R', + '℠' => 'SM', + '℡' => 'TEL', + '™' => 'TM', + 'ℤ' => 'Z', + 'ℨ' => 'Z', + 'ℬ' => 'B', + 'ℭ' => 'C', + 'ℯ' => 'e', + 'ℰ' => 'E', + 'ℱ' => 'F', + 'ℳ' => 'M', + 'ℴ' => 'o', + 'ℵ' => 'א', + 'ℶ' => 'ב', + 'ℷ' => 'ג', + 'ℸ' => 'ד', + 'ℹ' => 'i', + '℻' => 'FAX', + 'ℼ' => 'π', + 'ℽ' => 'γ', + 'ℾ' => 'Γ', + 'ℿ' => 'Π', + '⅀' => '∑', + 'ⅅ' => 'D', + 'ⅆ' => 'd', + 'ⅇ' => 'e', + 'ⅈ' => 'i', + 'ⅉ' => 'j', + '⅐' => '1⁄7', + '⅑' => '1⁄9', + '⅒' => '1⁄10', + '⅓' => '1⁄3', + '⅔' => '2⁄3', + '⅕' => '1⁄5', + '⅖' => '2⁄5', + '⅗' => '3⁄5', + '⅘' => '4⁄5', + '⅙' => '1⁄6', + '⅚' => '5⁄6', + '⅛' => '1⁄8', + '⅜' => '3⁄8', + '⅝' => '5⁄8', + '⅞' => '7⁄8', + '⅟' => '1⁄', + 'Ⅰ' => 'I', + 'Ⅱ' => 'II', + 'Ⅲ' => 'III', + 'Ⅳ' => 'IV', + 'Ⅴ' => 'V', + 'Ⅵ' => 'VI', + 'Ⅶ' => 'VII', + 'Ⅷ' => 'VIII', + 'Ⅸ' => 'IX', + 'Ⅹ' => 'X', + 'Ⅺ' => 'XI', + 'Ⅻ' => 'XII', + 'Ⅼ' => 'L', + 'Ⅽ' => 'C', + 'Ⅾ' => 'D', + 'Ⅿ' => 'M', + 'ⅰ' => 'i', + 'ⅱ' => 'ii', + 'ⅲ' => 'iii', + 'ⅳ' => 'iv', + 'ⅴ' => 'v', + 'ⅵ' => 'vi', + 'ⅶ' => 'vii', + 'ⅷ' => 'viii', + 'ⅸ' => 'ix', + 'ⅹ' => 'x', + 'ⅺ' => 'xi', + 'ⅻ' => 'xii', + 'ⅼ' => 'l', + 'ⅽ' => 'c', + 'ⅾ' => 'd', + 'ⅿ' => 'm', + '↉' => '0⁄3', + '∬' => '∫∫', + '∭' => '∫∫∫', + '∯' => '∮∮', + '∰' => '∮∮∮', + '①' => '1', + '②' => '2', + '③' => '3', + '④' => '4', + '⑤' => '5', + '⑥' => '6', + '⑦' => '7', + '⑧' => '8', + '⑨' => '9', + '⑩' => '10', + '⑪' => '11', + '⑫' => '12', + '⑬' => '13', + '⑭' => '14', + '⑮' => '15', + '⑯' => '16', + '⑰' => '17', + '⑱' => '18', + '⑲' => '19', + '⑳' => '20', + '⑴' => '(1)', + '⑵' => '(2)', + '⑶' => '(3)', + '⑷' => '(4)', + '⑸' => '(5)', + '⑹' => '(6)', + '⑺' => '(7)', + '⑻' => '(8)', + '⑼' => '(9)', + '⑽' => '(10)', + '⑾' => '(11)', + '⑿' => '(12)', + '⒀' => '(13)', + '⒁' => '(14)', + '⒂' => '(15)', + '⒃' => '(16)', + '⒄' => '(17)', + '⒅' => '(18)', + '⒆' => '(19)', + '⒇' => '(20)', + '⒈' => '1.', + '⒉' => '2.', + '⒊' => '3.', + '⒋' => '4.', + '⒌' => '5.', + '⒍' => '6.', + '⒎' => '7.', + '⒏' => '8.', + '⒐' => '9.', + '⒑' => '10.', + '⒒' => '11.', + '⒓' => '12.', + '⒔' => '13.', + '⒕' => '14.', + '⒖' => '15.', + '⒗' => '16.', + '⒘' => '17.', + '⒙' => '18.', + '⒚' => '19.', + '⒛' => '20.', + '⒜' => '(a)', + '⒝' => '(b)', + '⒞' => '(c)', + '⒟' => '(d)', + '⒠' => '(e)', + '⒡' => '(f)', + '⒢' => '(g)', + '⒣' => '(h)', + '⒤' => '(i)', + '⒥' => '(j)', + '⒦' => '(k)', + '⒧' => '(l)', + '⒨' => '(m)', + '⒩' => '(n)', + '⒪' => '(o)', + '⒫' => '(p)', + '⒬' => '(q)', + '⒭' => '(r)', + '⒮' => '(s)', + '⒯' => '(t)', + '⒰' => '(u)', + '⒱' => '(v)', + '⒲' => '(w)', + '⒳' => '(x)', + '⒴' => '(y)', + '⒵' => '(z)', + 'Ⓐ' => 'A', + 'Ⓑ' => 'B', + 'Ⓒ' => 'C', + 'Ⓓ' => 'D', + 'Ⓔ' => 'E', + 'Ⓕ' => 'F', + 'Ⓖ' => 'G', + 'Ⓗ' => 'H', + 'Ⓘ' => 'I', + 'Ⓙ' => 'J', + 'Ⓚ' => 'K', + 'Ⓛ' => 'L', + 'Ⓜ' => 'M', + 'Ⓝ' => 'N', + 'Ⓞ' => 'O', + 'Ⓟ' => 'P', + 'Ⓠ' => 'Q', + 'Ⓡ' => 'R', + 'Ⓢ' => 'S', + 'Ⓣ' => 'T', + 'Ⓤ' => 'U', + 'Ⓥ' => 'V', + 'Ⓦ' => 'W', + 'Ⓧ' => 'X', + 'Ⓨ' => 'Y', + 'Ⓩ' => 'Z', + 'ⓐ' => 'a', + 'ⓑ' => 'b', + 'ⓒ' => 'c', + 'ⓓ' => 'd', + 'ⓔ' => 'e', + 'ⓕ' => 'f', + 'ⓖ' => 'g', + 'ⓗ' => 'h', + 'ⓘ' => 'i', + 'ⓙ' => 'j', + 'ⓚ' => 'k', + 'ⓛ' => 'l', + 'ⓜ' => 'm', + 'ⓝ' => 'n', + 'ⓞ' => 'o', + 'ⓟ' => 'p', + 'ⓠ' => 'q', + 'ⓡ' => 'r', + 'ⓢ' => 's', + 'ⓣ' => 't', + 'ⓤ' => 'u', + 'ⓥ' => 'v', + 'ⓦ' => 'w', + 'ⓧ' => 'x', + 'ⓨ' => 'y', + 'ⓩ' => 'z', + '⓪' => '0', + '⨌' => '∫∫∫∫', + '⩴' => '::=', + '⩵' => '==', + '⩶' => '===', + 'ⱼ' => 'j', + 'ⱽ' => 'V', + 'ⵯ' => 'ⵡ', + '⺟' => '母', + '⻳' => '龟', + '⼀' => '一', + '⼁' => '丨', + '⼂' => '丶', + '⼃' => '丿', + '⼄' => '乙', + '⼅' => '亅', + '⼆' => '二', + '⼇' => '亠', + '⼈' => '人', + '⼉' => '儿', + '⼊' => '入', + '⼋' => '八', + '⼌' => '冂', + '⼍' => '冖', + '⼎' => '冫', + '⼏' => '几', + '⼐' => '凵', + '⼑' => '刀', + '⼒' => '力', + '⼓' => '勹', + '⼔' => '匕', + '⼕' => '匚', + '⼖' => '匸', + '⼗' => '十', + '⼘' => '卜', + '⼙' => '卩', + '⼚' => '厂', + '⼛' => '厶', + '⼜' => '又', + '⼝' => '口', + '⼞' => '囗', + '⼟' => '土', + '⼠' => '士', + '⼡' => '夂', + '⼢' => '夊', + '⼣' => '夕', + '⼤' => '大', + '⼥' => '女', + '⼦' => '子', + '⼧' => '宀', + '⼨' => '寸', + '⼩' => '小', + '⼪' => '尢', + '⼫' => '尸', + '⼬' => '屮', + '⼭' => '山', + '⼮' => '巛', + '⼯' => '工', + '⼰' => '己', + '⼱' => '巾', + '⼲' => '干', + '⼳' => '幺', + '⼴' => '广', + '⼵' => '廴', + '⼶' => '廾', + '⼷' => '弋', + '⼸' => '弓', + '⼹' => '彐', + '⼺' => '彡', + '⼻' => '彳', + '⼼' => '心', + '⼽' => '戈', + '⼾' => '戶', + '⼿' => '手', + '⽀' => '支', + '⽁' => '攴', + '⽂' => '文', + '⽃' => '斗', + '⽄' => '斤', + '⽅' => '方', + '⽆' => '无', + '⽇' => '日', + '⽈' => '曰', + '⽉' => '月', + '⽊' => '木', + '⽋' => '欠', + '⽌' => '止', + '⽍' => '歹', + '⽎' => '殳', + '⽏' => '毋', + '⽐' => '比', + '⽑' => '毛', + '⽒' => '氏', + '⽓' => '气', + '⽔' => '水', + '⽕' => '火', + '⽖' => '爪', + '⽗' => '父', + '⽘' => '爻', + '⽙' => '爿', + '⽚' => '片', + '⽛' => '牙', + '⽜' => '牛', + '⽝' => '犬', + '⽞' => '玄', + '⽟' => '玉', + '⽠' => '瓜', + '⽡' => '瓦', + '⽢' => '甘', + '⽣' => '生', + '⽤' => '用', + '⽥' => '田', + '⽦' => '疋', + '⽧' => '疒', + '⽨' => '癶', + '⽩' => '白', + '⽪' => '皮', + '⽫' => '皿', + '⽬' => '目', + '⽭' => '矛', + '⽮' => '矢', + '⽯' => '石', + '⽰' => '示', + '⽱' => '禸', + '⽲' => '禾', + '⽳' => '穴', + '⽴' => '立', + '⽵' => '竹', + '⽶' => '米', + '⽷' => '糸', + '⽸' => '缶', + '⽹' => '网', + '⽺' => '羊', + '⽻' => '羽', + '⽼' => '老', + '⽽' => '而', + '⽾' => '耒', + '⽿' => '耳', + '⾀' => '聿', + '⾁' => '肉', + '⾂' => '臣', + '⾃' => '自', + '⾄' => '至', + '⾅' => '臼', + '⾆' => '舌', + '⾇' => '舛', + '⾈' => '舟', + '⾉' => '艮', + '⾊' => '色', + '⾋' => '艸', + '⾌' => '虍', + '⾍' => '虫', + '⾎' => '血', + '⾏' => '行', + '⾐' => '衣', + '⾑' => '襾', + '⾒' => '見', + '⾓' => '角', + '⾔' => '言', + '⾕' => '谷', + '⾖' => '豆', + '⾗' => '豕', + '⾘' => '豸', + '⾙' => '貝', + '⾚' => '赤', + '⾛' => '走', + '⾜' => '足', + '⾝' => '身', + '⾞' => '車', + '⾟' => '辛', + '⾠' => '辰', + '⾡' => '辵', + '⾢' => '邑', + '⾣' => '酉', + '⾤' => '釆', + '⾥' => '里', + '⾦' => '金', + '⾧' => '長', + '⾨' => '門', + '⾩' => '阜', + '⾪' => '隶', + '⾫' => '隹', + '⾬' => '雨', + '⾭' => '靑', + '⾮' => '非', + '⾯' => '面', + '⾰' => '革', + '⾱' => '韋', + '⾲' => '韭', + '⾳' => '音', + '⾴' => '頁', + '⾵' => '風', + '⾶' => '飛', + '⾷' => '食', + '⾸' => '首', + '⾹' => '香', + '⾺' => '馬', + '⾻' => '骨', + '⾼' => '高', + '⾽' => '髟', + '⾾' => '鬥', + '⾿' => '鬯', + '⿀' => '鬲', + '⿁' => '鬼', + '⿂' => '魚', + '⿃' => '鳥', + '⿄' => '鹵', + '⿅' => '鹿', + '⿆' => '麥', + '⿇' => '麻', + '⿈' => '黃', + '⿉' => '黍', + '⿊' => '黑', + '⿋' => '黹', + '⿌' => '黽', + '⿍' => '鼎', + '⿎' => '鼓', + '⿏' => '鼠', + '⿐' => '鼻', + '⿑' => '齊', + '⿒' => '齒', + '⿓' => '龍', + '⿔' => '龜', + '⿕' => '龠', + ' ' => ' ', + '〶' => '〒', + '〸' => '十', + '〹' => '卄', + '〺' => '卅', + '゛' => ' ゙', + '゜' => ' ゚', + 'ゟ' => 'より', + 'ヿ' => 'コト', + 'ㄱ' => 'ᄀ', + 'ㄲ' => 'ᄁ', + 'ㄳ' => 'ᆪ', + 'ㄴ' => 'ᄂ', + 'ㄵ' => 'ᆬ', + 'ㄶ' => 'ᆭ', + 'ㄷ' => 'ᄃ', + 'ㄸ' => 'ᄄ', + 'ㄹ' => 'ᄅ', + 'ㄺ' => 'ᆰ', + 'ㄻ' => 'ᆱ', + 'ㄼ' => 'ᆲ', + 'ㄽ' => 'ᆳ', + 'ㄾ' => 'ᆴ', + 'ㄿ' => 'ᆵ', + 'ㅀ' => 'ᄚ', + 'ㅁ' => 'ᄆ', + 'ㅂ' => 'ᄇ', + 'ㅃ' => 'ᄈ', + 'ㅄ' => 'ᄡ', + 'ㅅ' => 'ᄉ', + 'ㅆ' => 'ᄊ', + 'ㅇ' => 'ᄋ', + 'ㅈ' => 'ᄌ', + 'ㅉ' => 'ᄍ', + 'ㅊ' => 'ᄎ', + 'ㅋ' => 'ᄏ', + 'ㅌ' => 'ᄐ', + 'ㅍ' => 'ᄑ', + 'ㅎ' => 'ᄒ', + 'ㅏ' => 'ᅡ', + 'ㅐ' => 'ᅢ', + 'ㅑ' => 'ᅣ', + 'ㅒ' => 'ᅤ', + 'ㅓ' => 'ᅥ', + 'ㅔ' => 'ᅦ', + 'ㅕ' => 'ᅧ', + 'ㅖ' => 'ᅨ', + 'ㅗ' => 'ᅩ', + 'ㅘ' => 'ᅪ', + 'ㅙ' => 'ᅫ', + 'ㅚ' => 'ᅬ', + 'ㅛ' => 'ᅭ', + 'ㅜ' => 'ᅮ', + 'ㅝ' => 'ᅯ', + 'ㅞ' => 'ᅰ', + 'ㅟ' => 'ᅱ', + 'ㅠ' => 'ᅲ', + 'ㅡ' => 'ᅳ', + 'ㅢ' => 'ᅴ', + 'ㅣ' => 'ᅵ', + 'ㅤ' => 'ᅠ', + 'ㅥ' => 'ᄔ', + 'ㅦ' => 'ᄕ', + 'ㅧ' => 'ᇇ', + 'ㅨ' => 'ᇈ', + 'ㅩ' => 'ᇌ', + 'ㅪ' => 'ᇎ', + 'ㅫ' => 'ᇓ', + 'ㅬ' => 'ᇗ', + 'ㅭ' => 'ᇙ', + 'ㅮ' => 'ᄜ', + 'ㅯ' => 'ᇝ', + 'ㅰ' => 'ᇟ', + 'ㅱ' => 'ᄝ', + 'ㅲ' => 'ᄞ', + 'ㅳ' => 'ᄠ', + 'ㅴ' => 'ᄢ', + 'ㅵ' => 'ᄣ', + 'ㅶ' => 'ᄧ', + 'ㅷ' => 'ᄩ', + 'ㅸ' => 'ᄫ', + 'ㅹ' => 'ᄬ', + 'ㅺ' => 'ᄭ', + 'ㅻ' => 'ᄮ', + 'ㅼ' => 'ᄯ', + 'ㅽ' => 'ᄲ', + 'ㅾ' => 'ᄶ', + 'ㅿ' => 'ᅀ', + 'ㆀ' => 'ᅇ', + 'ㆁ' => 'ᅌ', + 'ㆂ' => 'ᇱ', + 'ㆃ' => 'ᇲ', + 'ㆄ' => 'ᅗ', + 'ㆅ' => 'ᅘ', + 'ㆆ' => 'ᅙ', + 'ㆇ' => 'ᆄ', + 'ㆈ' => 'ᆅ', + 'ㆉ' => 'ᆈ', + 'ㆊ' => 'ᆑ', + 'ㆋ' => 'ᆒ', + 'ㆌ' => 'ᆔ', + 'ㆍ' => 'ᆞ', + 'ㆎ' => 'ᆡ', + '㆒' => '一', + '㆓' => '二', + '㆔' => '三', + '㆕' => '四', + '㆖' => '上', + '㆗' => '中', + '㆘' => '下', + '㆙' => '甲', + '㆚' => '乙', + '㆛' => '丙', + '㆜' => '丁', + '㆝' => '天', + '㆞' => '地', + '㆟' => '人', + '㈀' => '(ᄀ)', + '㈁' => '(ᄂ)', + '㈂' => '(ᄃ)', + '㈃' => '(ᄅ)', + '㈄' => '(ᄆ)', + '㈅' => '(ᄇ)', + '㈆' => '(ᄉ)', + '㈇' => '(ᄋ)', + '㈈' => '(ᄌ)', + '㈉' => '(ᄎ)', + '㈊' => '(ᄏ)', + '㈋' => '(ᄐ)', + '㈌' => '(ᄑ)', + '㈍' => '(ᄒ)', + '㈎' => '(가)', + '㈏' => '(나)', + '㈐' => '(다)', + '㈑' => '(라)', + '㈒' => '(마)', + '㈓' => '(바)', + '㈔' => '(사)', + '㈕' => '(아)', + '㈖' => '(자)', + '㈗' => '(차)', + '㈘' => '(카)', + '㈙' => '(타)', + '㈚' => '(파)', + '㈛' => '(하)', + '㈜' => '(주)', + '㈝' => '(오전)', + '㈞' => '(오후)', + '㈠' => '(一)', + '㈡' => '(二)', + '㈢' => '(三)', + '㈣' => '(四)', + '㈤' => '(五)', + '㈥' => '(六)', + '㈦' => '(七)', + '㈧' => '(八)', + '㈨' => '(九)', + '㈩' => '(十)', + '㈪' => '(月)', + '㈫' => '(火)', + '㈬' => '(水)', + '㈭' => '(木)', + '㈮' => '(金)', + '㈯' => '(土)', + '㈰' => '(日)', + '㈱' => '(株)', + '㈲' => '(有)', + '㈳' => '(社)', + '㈴' => '(名)', + '㈵' => '(特)', + '㈶' => '(財)', + '㈷' => '(祝)', + '㈸' => '(労)', + '㈹' => '(代)', + '㈺' => '(呼)', + '㈻' => '(学)', + '㈼' => '(監)', + '㈽' => '(企)', + '㈾' => '(資)', + '㈿' => '(協)', + '㉀' => '(祭)', + '㉁' => '(休)', + '㉂' => '(自)', + '㉃' => '(至)', + '㉄' => '問', + '㉅' => '幼', + '㉆' => '文', + '㉇' => '箏', + '㉐' => 'PTE', + '㉑' => '21', + '㉒' => '22', + '㉓' => '23', + '㉔' => '24', + '㉕' => '25', + '㉖' => '26', + '㉗' => '27', + '㉘' => '28', + '㉙' => '29', + '㉚' => '30', + '㉛' => '31', + '㉜' => '32', + '㉝' => '33', + '㉞' => '34', + '㉟' => '35', + '㉠' => 'ᄀ', + '㉡' => 'ᄂ', + '㉢' => 'ᄃ', + '㉣' => 'ᄅ', + '㉤' => 'ᄆ', + '㉥' => 'ᄇ', + '㉦' => 'ᄉ', + '㉧' => 'ᄋ', + '㉨' => 'ᄌ', + '㉩' => 'ᄎ', + '㉪' => 'ᄏ', + '㉫' => 'ᄐ', + '㉬' => 'ᄑ', + '㉭' => 'ᄒ', + '㉮' => '가', + '㉯' => '나', + '㉰' => '다', + '㉱' => '라', + '㉲' => '마', + '㉳' => '바', + '㉴' => '사', + '㉵' => '아', + '㉶' => '자', + '㉷' => '차', + '㉸' => '카', + '㉹' => '타', + '㉺' => '파', + '㉻' => '하', + '㉼' => '참고', + '㉽' => '주의', + '㉾' => '우', + '㊀' => '一', + '㊁' => '二', + '㊂' => '三', + '㊃' => '四', + '㊄' => '五', + '㊅' => '六', + '㊆' => '七', + '㊇' => '八', + '㊈' => '九', + '㊉' => '十', + '㊊' => '月', + '㊋' => '火', + '㊌' => '水', + '㊍' => '木', + '㊎' => '金', + '㊏' => '土', + '㊐' => '日', + '㊑' => '株', + '㊒' => '有', + '㊓' => '社', + '㊔' => '名', + '㊕' => '特', + '㊖' => '財', + '㊗' => '祝', + '㊘' => '労', + '㊙' => '秘', + '㊚' => '男', + '㊛' => '女', + '㊜' => '適', + '㊝' => '優', + '㊞' => '印', + '㊟' => '注', + '㊠' => '項', + '㊡' => '休', + '㊢' => '写', + '㊣' => '正', + '㊤' => '上', + '㊥' => '中', + '㊦' => '下', + '㊧' => '左', + '㊨' => '右', + '㊩' => '医', + '㊪' => '宗', + '㊫' => '学', + '㊬' => '監', + '㊭' => '企', + '㊮' => '資', + '㊯' => '協', + '㊰' => '夜', + '㊱' => '36', + '㊲' => '37', + '㊳' => '38', + '㊴' => '39', + '㊵' => '40', + '㊶' => '41', + '㊷' => '42', + '㊸' => '43', + '㊹' => '44', + '㊺' => '45', + '㊻' => '46', + '㊼' => '47', + '㊽' => '48', + '㊾' => '49', + '㊿' => '50', + '㋀' => '1月', + '㋁' => '2月', + '㋂' => '3月', + '㋃' => '4月', + '㋄' => '5月', + '㋅' => '6月', + '㋆' => '7月', + '㋇' => '8月', + '㋈' => '9月', + '㋉' => '10月', + '㋊' => '11月', + '㋋' => '12月', + '㋌' => 'Hg', + '㋍' => 'erg', + '㋎' => 'eV', + '㋏' => 'LTD', + '㋐' => 'ア', + '㋑' => 'イ', + '㋒' => 'ウ', + '㋓' => 'エ', + '㋔' => 'オ', + '㋕' => 'カ', + '㋖' => 'キ', + '㋗' => 'ク', + '㋘' => 'ケ', + '㋙' => 'コ', + '㋚' => 'サ', + '㋛' => 'シ', + '㋜' => 'ス', + '㋝' => 'セ', + '㋞' => 'ソ', + '㋟' => 'タ', + '㋠' => 'チ', + '㋡' => 'ツ', + '㋢' => 'テ', + '㋣' => 'ト', + '㋤' => 'ナ', + '㋥' => 'ニ', + '㋦' => 'ヌ', + '㋧' => 'ネ', + '㋨' => 'ノ', + '㋩' => 'ハ', + '㋪' => 'ヒ', + '㋫' => 'フ', + '㋬' => 'ヘ', + '㋭' => 'ホ', + '㋮' => 'マ', + '㋯' => 'ミ', + '㋰' => 'ム', + '㋱' => 'メ', + '㋲' => 'モ', + '㋳' => 'ヤ', + '㋴' => 'ユ', + '㋵' => 'ヨ', + '㋶' => 'ラ', + '㋷' => 'リ', + '㋸' => 'ル', + '㋹' => 'レ', + '㋺' => 'ロ', + '㋻' => 'ワ', + '㋼' => 'ヰ', + '㋽' => 'ヱ', + '㋾' => 'ヲ', + '㋿' => '令和', + '㌀' => 'アパート', + '㌁' => 'アルファ', + '㌂' => 'アンペア', + '㌃' => 'アール', + '㌄' => 'イニング', + '㌅' => 'インチ', + '㌆' => 'ウォン', + '㌇' => 'エスクード', + '㌈' => 'エーカー', + '㌉' => 'オンス', + '㌊' => 'オーム', + '㌋' => 'カイリ', + '㌌' => 'カラット', + '㌍' => 'カロリー', + '㌎' => 'ガロン', + '㌏' => 'ガンマ', + '㌐' => 'ギガ', + '㌑' => 'ギニー', + '㌒' => 'キュリー', + '㌓' => 'ギルダー', + '㌔' => 'キロ', + '㌕' => 'キログラム', + '㌖' => 'キロメートル', + '㌗' => 'キロワット', + '㌘' => 'グラム', + '㌙' => 'グラムトン', + '㌚' => 'クルゼイロ', + '㌛' => 'クローネ', + '㌜' => 'ケース', + '㌝' => 'コルナ', + '㌞' => 'コーポ', + '㌟' => 'サイクル', + '㌠' => 'サンチーム', + '㌡' => 'シリング', + '㌢' => 'センチ', + '㌣' => 'セント', + '㌤' => 'ダース', + '㌥' => 'デシ', + '㌦' => 'ドル', + '㌧' => 'トン', + '㌨' => 'ナノ', + '㌩' => 'ノット', + '㌪' => 'ハイツ', + '㌫' => 'パーセント', + '㌬' => 'パーツ', + '㌭' => 'バーレル', + '㌮' => 'ピアストル', + '㌯' => 'ピクル', + '㌰' => 'ピコ', + '㌱' => 'ビル', + '㌲' => 'ファラッド', + '㌳' => 'フィート', + '㌴' => 'ブッシェル', + '㌵' => 'フラン', + '㌶' => 'ヘクタール', + '㌷' => 'ペソ', + '㌸' => 'ペニヒ', + '㌹' => 'ヘルツ', + '㌺' => 'ペンス', + '㌻' => 'ページ', + '㌼' => 'ベータ', + '㌽' => 'ポイント', + '㌾' => 'ボルト', + '㌿' => 'ホン', + '㍀' => 'ポンド', + '㍁' => 'ホール', + '㍂' => 'ホーン', + '㍃' => 'マイクロ', + '㍄' => 'マイル', + '㍅' => 'マッハ', + '㍆' => 'マルク', + '㍇' => 'マンション', + '㍈' => 'ミクロン', + '㍉' => 'ミリ', + '㍊' => 'ミリバール', + '㍋' => 'メガ', + '㍌' => 'メガトン', + '㍍' => 'メートル', + '㍎' => 'ヤード', + '㍏' => 'ヤール', + '㍐' => 'ユアン', + '㍑' => 'リットル', + '㍒' => 'リラ', + '㍓' => 'ルピー', + '㍔' => 'ルーブル', + '㍕' => 'レム', + '㍖' => 'レントゲン', + '㍗' => 'ワット', + '㍘' => '0点', + '㍙' => '1点', + '㍚' => '2点', + '㍛' => '3点', + '㍜' => '4点', + '㍝' => '5点', + '㍞' => '6点', + '㍟' => '7点', + '㍠' => '8点', + '㍡' => '9点', + '㍢' => '10点', + '㍣' => '11点', + '㍤' => '12点', + '㍥' => '13点', + '㍦' => '14点', + '㍧' => '15点', + '㍨' => '16点', + '㍩' => '17点', + '㍪' => '18点', + '㍫' => '19点', + '㍬' => '20点', + '㍭' => '21点', + '㍮' => '22点', + '㍯' => '23点', + '㍰' => '24点', + '㍱' => 'hPa', + '㍲' => 'da', + '㍳' => 'AU', + '㍴' => 'bar', + '㍵' => 'oV', + '㍶' => 'pc', + '㍷' => 'dm', + '㍸' => 'dm2', + '㍹' => 'dm3', + '㍺' => 'IU', + '㍻' => '平成', + '㍼' => '昭和', + '㍽' => '大正', + '㍾' => '明治', + '㍿' => '株式会社', + '㎀' => 'pA', + '㎁' => 'nA', + '㎂' => 'μA', + '㎃' => 'mA', + '㎄' => 'kA', + '㎅' => 'KB', + '㎆' => 'MB', + '㎇' => 'GB', + '㎈' => 'cal', + '㎉' => 'kcal', + '㎊' => 'pF', + '㎋' => 'nF', + '㎌' => 'μF', + '㎍' => 'μg', + '㎎' => 'mg', + '㎏' => 'kg', + '㎐' => 'Hz', + '㎑' => 'kHz', + '㎒' => 'MHz', + '㎓' => 'GHz', + '㎔' => 'THz', + '㎕' => 'μl', + '㎖' => 'ml', + '㎗' => 'dl', + '㎘' => 'kl', + '㎙' => 'fm', + '㎚' => 'nm', + '㎛' => 'μm', + '㎜' => 'mm', + '㎝' => 'cm', + '㎞' => 'km', + '㎟' => 'mm2', + '㎠' => 'cm2', + '㎡' => 'm2', + '㎢' => 'km2', + '㎣' => 'mm3', + '㎤' => 'cm3', + '㎥' => 'm3', + '㎦' => 'km3', + '㎧' => 'm∕s', + '㎨' => 'm∕s2', + '㎩' => 'Pa', + '㎪' => 'kPa', + '㎫' => 'MPa', + '㎬' => 'GPa', + '㎭' => 'rad', + '㎮' => 'rad∕s', + '㎯' => 'rad∕s2', + '㎰' => 'ps', + '㎱' => 'ns', + '㎲' => 'μs', + '㎳' => 'ms', + '㎴' => 'pV', + '㎵' => 'nV', + '㎶' => 'μV', + '㎷' => 'mV', + '㎸' => 'kV', + '㎹' => 'MV', + '㎺' => 'pW', + '㎻' => 'nW', + '㎼' => 'μW', + '㎽' => 'mW', + '㎾' => 'kW', + '㎿' => 'MW', + '㏀' => 'kΩ', + '㏁' => 'MΩ', + '㏂' => 'a.m.', + '㏃' => 'Bq', + '㏄' => 'cc', + '㏅' => 'cd', + '㏆' => 'C∕kg', + '㏇' => 'Co.', + '㏈' => 'dB', + '㏉' => 'Gy', + '㏊' => 'ha', + '㏋' => 'HP', + '㏌' => 'in', + '㏍' => 'KK', + '㏎' => 'KM', + '㏏' => 'kt', + '㏐' => 'lm', + '㏑' => 'ln', + '㏒' => 'log', + '㏓' => 'lx', + '㏔' => 'mb', + '㏕' => 'mil', + '㏖' => 'mol', + '㏗' => 'PH', + '㏘' => 'p.m.', + '㏙' => 'PPM', + '㏚' => 'PR', + '㏛' => 'sr', + '㏜' => 'Sv', + '㏝' => 'Wb', + '㏞' => 'V∕m', + '㏟' => 'A∕m', + '㏠' => '1日', + '㏡' => '2日', + '㏢' => '3日', + '㏣' => '4日', + '㏤' => '5日', + '㏥' => '6日', + '㏦' => '7日', + '㏧' => '8日', + '㏨' => '9日', + '㏩' => '10日', + '㏪' => '11日', + '㏫' => '12日', + '㏬' => '13日', + '㏭' => '14日', + '㏮' => '15日', + '㏯' => '16日', + '㏰' => '17日', + '㏱' => '18日', + '㏲' => '19日', + '㏳' => '20日', + '㏴' => '21日', + '㏵' => '22日', + '㏶' => '23日', + '㏷' => '24日', + '㏸' => '25日', + '㏹' => '26日', + '㏺' => '27日', + '㏻' => '28日', + '㏼' => '29日', + '㏽' => '30日', + '㏾' => '31日', + '㏿' => 'gal', + 'ꚜ' => 'ъ', + 'ꚝ' => 'ь', + 'ꝰ' => 'ꝯ', + 'ꟸ' => 'Ħ', + 'ꟹ' => 'œ', + 'ꭜ' => 'ꜧ', + 'ꭝ' => 'ꬷ', + 'ꭞ' => 'ɫ', + 'ꭟ' => 'ꭒ', + 'ꭩ' => 'ʍ', + 'ff' => 'ff', + 'fi' => 'fi', + 'fl' => 'fl', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'ſt' => 'st', + 'st' => 'st', + 'ﬓ' => 'մն', + 'ﬔ' => 'մե', + 'ﬕ' => 'մի', + 'ﬖ' => 'վն', + 'ﬗ' => 'մխ', + 'ﬠ' => 'ע', + 'ﬡ' => 'א', + 'ﬢ' => 'ד', + 'ﬣ' => 'ה', + 'ﬤ' => 'כ', + 'ﬥ' => 'ל', + 'ﬦ' => 'ם', + 'ﬧ' => 'ר', + 'ﬨ' => 'ת', + '﬩' => '+', + 'ﭏ' => 'אל', + 'ﭐ' => 'ٱ', + 'ﭑ' => 'ٱ', + 'ﭒ' => 'ٻ', + 'ﭓ' => 'ٻ', + 'ﭔ' => 'ٻ', + 'ﭕ' => 'ٻ', + 'ﭖ' => 'پ', + 'ﭗ' => 'پ', + 'ﭘ' => 'پ', + 'ﭙ' => 'پ', + 'ﭚ' => 'ڀ', + 'ﭛ' => 'ڀ', + 'ﭜ' => 'ڀ', + 'ﭝ' => 'ڀ', + 'ﭞ' => 'ٺ', + 'ﭟ' => 'ٺ', + 'ﭠ' => 'ٺ', + 'ﭡ' => 'ٺ', + 'ﭢ' => 'ٿ', + 'ﭣ' => 'ٿ', + 'ﭤ' => 'ٿ', + 'ﭥ' => 'ٿ', + 'ﭦ' => 'ٹ', + 'ﭧ' => 'ٹ', + 'ﭨ' => 'ٹ', + 'ﭩ' => 'ٹ', + 'ﭪ' => 'ڤ', + 'ﭫ' => 'ڤ', + 'ﭬ' => 'ڤ', + 'ﭭ' => 'ڤ', + 'ﭮ' => 'ڦ', + 'ﭯ' => 'ڦ', + 'ﭰ' => 'ڦ', + 'ﭱ' => 'ڦ', + 'ﭲ' => 'ڄ', + 'ﭳ' => 'ڄ', + 'ﭴ' => 'ڄ', + 'ﭵ' => 'ڄ', + 'ﭶ' => 'ڃ', + 'ﭷ' => 'ڃ', + 'ﭸ' => 'ڃ', + 'ﭹ' => 'ڃ', + 'ﭺ' => 'چ', + 'ﭻ' => 'چ', + 'ﭼ' => 'چ', + 'ﭽ' => 'چ', + 'ﭾ' => 'ڇ', + 'ﭿ' => 'ڇ', + 'ﮀ' => 'ڇ', + 'ﮁ' => 'ڇ', + 'ﮂ' => 'ڍ', + 'ﮃ' => 'ڍ', + 'ﮄ' => 'ڌ', + 'ﮅ' => 'ڌ', + 'ﮆ' => 'ڎ', + 'ﮇ' => 'ڎ', + 'ﮈ' => 'ڈ', + 'ﮉ' => 'ڈ', + 'ﮊ' => 'ژ', + 'ﮋ' => 'ژ', + 'ﮌ' => 'ڑ', + 'ﮍ' => 'ڑ', + 'ﮎ' => 'ک', + 'ﮏ' => 'ک', + 'ﮐ' => 'ک', + 'ﮑ' => 'ک', + 'ﮒ' => 'گ', + 'ﮓ' => 'گ', + 'ﮔ' => 'گ', + 'ﮕ' => 'گ', + 'ﮖ' => 'ڳ', + 'ﮗ' => 'ڳ', + 'ﮘ' => 'ڳ', + 'ﮙ' => 'ڳ', + 'ﮚ' => 'ڱ', + 'ﮛ' => 'ڱ', + 'ﮜ' => 'ڱ', + 'ﮝ' => 'ڱ', + 'ﮞ' => 'ں', + 'ﮟ' => 'ں', + 'ﮠ' => 'ڻ', + 'ﮡ' => 'ڻ', + 'ﮢ' => 'ڻ', + 'ﮣ' => 'ڻ', + 'ﮤ' => 'ۀ', + 'ﮥ' => 'ۀ', + 'ﮦ' => 'ہ', + 'ﮧ' => 'ہ', + 'ﮨ' => 'ہ', + 'ﮩ' => 'ہ', + 'ﮪ' => 'ھ', + 'ﮫ' => 'ھ', + 'ﮬ' => 'ھ', + 'ﮭ' => 'ھ', + 'ﮮ' => 'ے', + 'ﮯ' => 'ے', + 'ﮰ' => 'ۓ', + 'ﮱ' => 'ۓ', + 'ﯓ' => 'ڭ', + 'ﯔ' => 'ڭ', + 'ﯕ' => 'ڭ', + 'ﯖ' => 'ڭ', + 'ﯗ' => 'ۇ', + 'ﯘ' => 'ۇ', + 'ﯙ' => 'ۆ', + 'ﯚ' => 'ۆ', + 'ﯛ' => 'ۈ', + 'ﯜ' => 'ۈ', + 'ﯝ' => 'ۇٴ', + 'ﯞ' => 'ۋ', + 'ﯟ' => 'ۋ', + 'ﯠ' => 'ۅ', + 'ﯡ' => 'ۅ', + 'ﯢ' => 'ۉ', + 'ﯣ' => 'ۉ', + 'ﯤ' => 'ې', + 'ﯥ' => 'ې', + 'ﯦ' => 'ې', + 'ﯧ' => 'ې', + 'ﯨ' => 'ى', + 'ﯩ' => 'ى', + 'ﯪ' => 'ئا', + 'ﯫ' => 'ئا', + 'ﯬ' => 'ئە', + 'ﯭ' => 'ئە', + 'ﯮ' => 'ئو', + 'ﯯ' => 'ئو', + 'ﯰ' => 'ئۇ', + 'ﯱ' => 'ئۇ', + 'ﯲ' => 'ئۆ', + 'ﯳ' => 'ئۆ', + 'ﯴ' => 'ئۈ', + 'ﯵ' => 'ئۈ', + 'ﯶ' => 'ئې', + 'ﯷ' => 'ئې', + 'ﯸ' => 'ئې', + 'ﯹ' => 'ئى', + 'ﯺ' => 'ئى', + 'ﯻ' => 'ئى', + 'ﯼ' => 'ی', + 'ﯽ' => 'ی', + 'ﯾ' => 'ی', + 'ﯿ' => 'ی', + 'ﰀ' => 'ئج', + 'ﰁ' => 'ئح', + 'ﰂ' => 'ئم', + 'ﰃ' => 'ئى', + 'ﰄ' => 'ئي', + 'ﰅ' => 'بج', + 'ﰆ' => 'بح', + 'ﰇ' => 'بخ', + 'ﰈ' => 'بم', + 'ﰉ' => 'بى', + 'ﰊ' => 'بي', + 'ﰋ' => 'تج', + 'ﰌ' => 'تح', + 'ﰍ' => 'تخ', + 'ﰎ' => 'تم', + 'ﰏ' => 'تى', + 'ﰐ' => 'تي', + 'ﰑ' => 'ثج', + 'ﰒ' => 'ثم', + 'ﰓ' => 'ثى', + 'ﰔ' => 'ثي', + 'ﰕ' => 'جح', + 'ﰖ' => 'جم', + 'ﰗ' => 'حج', + 'ﰘ' => 'حم', + 'ﰙ' => 'خج', + 'ﰚ' => 'خح', + 'ﰛ' => 'خم', + 'ﰜ' => 'سج', + 'ﰝ' => 'سح', + 'ﰞ' => 'سخ', + 'ﰟ' => 'سم', + 'ﰠ' => 'صح', + 'ﰡ' => 'صم', + 'ﰢ' => 'ضج', + 'ﰣ' => 'ضح', + 'ﰤ' => 'ضخ', + 'ﰥ' => 'ضم', + 'ﰦ' => 'طح', + 'ﰧ' => 'طم', + 'ﰨ' => 'ظم', + 'ﰩ' => 'عج', + 'ﰪ' => 'عم', + 'ﰫ' => 'غج', + 'ﰬ' => 'غم', + 'ﰭ' => 'فج', + 'ﰮ' => 'فح', + 'ﰯ' => 'فخ', + 'ﰰ' => 'فم', + 'ﰱ' => 'فى', + 'ﰲ' => 'في', + 'ﰳ' => 'قح', + 'ﰴ' => 'قم', + 'ﰵ' => 'قى', + 'ﰶ' => 'قي', + 'ﰷ' => 'كا', + 'ﰸ' => 'كج', + 'ﰹ' => 'كح', + 'ﰺ' => 'كخ', + 'ﰻ' => 'كل', + 'ﰼ' => 'كم', + 'ﰽ' => 'كى', + 'ﰾ' => 'كي', + 'ﰿ' => 'لج', + 'ﱀ' => 'لح', + 'ﱁ' => 'لخ', + 'ﱂ' => 'لم', + 'ﱃ' => 'لى', + 'ﱄ' => 'لي', + 'ﱅ' => 'مج', + 'ﱆ' => 'مح', + 'ﱇ' => 'مخ', + 'ﱈ' => 'مم', + 'ﱉ' => 'مى', + 'ﱊ' => 'مي', + 'ﱋ' => 'نج', + 'ﱌ' => 'نح', + 'ﱍ' => 'نخ', + 'ﱎ' => 'نم', + 'ﱏ' => 'نى', + 'ﱐ' => 'ني', + 'ﱑ' => 'هج', + 'ﱒ' => 'هم', + 'ﱓ' => 'هى', + 'ﱔ' => 'هي', + 'ﱕ' => 'يج', + 'ﱖ' => 'يح', + 'ﱗ' => 'يخ', + 'ﱘ' => 'يم', + 'ﱙ' => 'يى', + 'ﱚ' => 'يي', + 'ﱛ' => 'ذٰ', + 'ﱜ' => 'رٰ', + 'ﱝ' => 'ىٰ', + 'ﱞ' => ' ٌّ', + 'ﱟ' => ' ٍّ', + 'ﱠ' => ' َّ', + 'ﱡ' => ' ُّ', + 'ﱢ' => ' ِّ', + 'ﱣ' => ' ّٰ', + 'ﱤ' => 'ئر', + 'ﱥ' => 'ئز', + 'ﱦ' => 'ئم', + 'ﱧ' => 'ئن', + 'ﱨ' => 'ئى', + 'ﱩ' => 'ئي', + 'ﱪ' => 'بر', + 'ﱫ' => 'بز', + 'ﱬ' => 'بم', + 'ﱭ' => 'بن', + 'ﱮ' => 'بى', + 'ﱯ' => 'بي', + 'ﱰ' => 'تر', + 'ﱱ' => 'تز', + 'ﱲ' => 'تم', + 'ﱳ' => 'تن', + 'ﱴ' => 'تى', + 'ﱵ' => 'تي', + 'ﱶ' => 'ثر', + 'ﱷ' => 'ثز', + 'ﱸ' => 'ثم', + 'ﱹ' => 'ثن', + 'ﱺ' => 'ثى', + 'ﱻ' => 'ثي', + 'ﱼ' => 'فى', + 'ﱽ' => 'في', + 'ﱾ' => 'قى', + 'ﱿ' => 'قي', + 'ﲀ' => 'كا', + 'ﲁ' => 'كل', + 'ﲂ' => 'كم', + 'ﲃ' => 'كى', + 'ﲄ' => 'كي', + 'ﲅ' => 'لم', + 'ﲆ' => 'لى', + 'ﲇ' => 'لي', + 'ﲈ' => 'ما', + 'ﲉ' => 'مم', + 'ﲊ' => 'نر', + 'ﲋ' => 'نز', + 'ﲌ' => 'نم', + 'ﲍ' => 'نن', + 'ﲎ' => 'نى', + 'ﲏ' => 'ني', + 'ﲐ' => 'ىٰ', + 'ﲑ' => 'ير', + 'ﲒ' => 'يز', + 'ﲓ' => 'يم', + 'ﲔ' => 'ين', + 'ﲕ' => 'يى', + 'ﲖ' => 'يي', + 'ﲗ' => 'ئج', + 'ﲘ' => 'ئح', + 'ﲙ' => 'ئخ', + 'ﲚ' => 'ئم', + 'ﲛ' => 'ئه', + 'ﲜ' => 'بج', + 'ﲝ' => 'بح', + 'ﲞ' => 'بخ', + 'ﲟ' => 'بم', + 'ﲠ' => 'به', + 'ﲡ' => 'تج', + 'ﲢ' => 'تح', + 'ﲣ' => 'تخ', + 'ﲤ' => 'تم', + 'ﲥ' => 'ته', + 'ﲦ' => 'ثم', + 'ﲧ' => 'جح', + 'ﲨ' => 'جم', + 'ﲩ' => 'حج', + 'ﲪ' => 'حم', + 'ﲫ' => 'خج', + 'ﲬ' => 'خم', + 'ﲭ' => 'سج', + 'ﲮ' => 'سح', + 'ﲯ' => 'سخ', + 'ﲰ' => 'سم', + 'ﲱ' => 'صح', + 'ﲲ' => 'صخ', + 'ﲳ' => 'صم', + 'ﲴ' => 'ضج', + 'ﲵ' => 'ضح', + 'ﲶ' => 'ضخ', + 'ﲷ' => 'ضم', + 'ﲸ' => 'طح', + 'ﲹ' => 'ظم', + 'ﲺ' => 'عج', + 'ﲻ' => 'عم', + 'ﲼ' => 'غج', + 'ﲽ' => 'غم', + 'ﲾ' => 'فج', + 'ﲿ' => 'فح', + 'ﳀ' => 'فخ', + 'ﳁ' => 'فم', + 'ﳂ' => 'قح', + 'ﳃ' => 'قم', + 'ﳄ' => 'كج', + 'ﳅ' => 'كح', + 'ﳆ' => 'كخ', + 'ﳇ' => 'كل', + 'ﳈ' => 'كم', + 'ﳉ' => 'لج', + 'ﳊ' => 'لح', + 'ﳋ' => 'لخ', + 'ﳌ' => 'لم', + 'ﳍ' => 'له', + 'ﳎ' => 'مج', + 'ﳏ' => 'مح', + 'ﳐ' => 'مخ', + 'ﳑ' => 'مم', + 'ﳒ' => 'نج', + 'ﳓ' => 'نح', + 'ﳔ' => 'نخ', + 'ﳕ' => 'نم', + 'ﳖ' => 'نه', + 'ﳗ' => 'هج', + 'ﳘ' => 'هم', + 'ﳙ' => 'هٰ', + 'ﳚ' => 'يج', + 'ﳛ' => 'يح', + 'ﳜ' => 'يخ', + 'ﳝ' => 'يم', + 'ﳞ' => 'يه', + 'ﳟ' => 'ئم', + 'ﳠ' => 'ئه', + 'ﳡ' => 'بم', + 'ﳢ' => 'به', + 'ﳣ' => 'تم', + 'ﳤ' => 'ته', + 'ﳥ' => 'ثم', + 'ﳦ' => 'ثه', + 'ﳧ' => 'سم', + 'ﳨ' => 'سه', + 'ﳩ' => 'شم', + 'ﳪ' => 'شه', + 'ﳫ' => 'كل', + 'ﳬ' => 'كم', + 'ﳭ' => 'لم', + 'ﳮ' => 'نم', + 'ﳯ' => 'نه', + 'ﳰ' => 'يم', + 'ﳱ' => 'يه', + 'ﳲ' => 'ـَّ', + 'ﳳ' => 'ـُّ', + 'ﳴ' => 'ـِّ', + 'ﳵ' => 'طى', + 'ﳶ' => 'طي', + 'ﳷ' => 'عى', + 'ﳸ' => 'عي', + 'ﳹ' => 'غى', + 'ﳺ' => 'غي', + 'ﳻ' => 'سى', + 'ﳼ' => 'سي', + 'ﳽ' => 'شى', + 'ﳾ' => 'شي', + 'ﳿ' => 'حى', + 'ﴀ' => 'حي', + 'ﴁ' => 'جى', + 'ﴂ' => 'جي', + 'ﴃ' => 'خى', + 'ﴄ' => 'خي', + 'ﴅ' => 'صى', + 'ﴆ' => 'صي', + 'ﴇ' => 'ضى', + 'ﴈ' => 'ضي', + 'ﴉ' => 'شج', + 'ﴊ' => 'شح', + 'ﴋ' => 'شخ', + 'ﴌ' => 'شم', + 'ﴍ' => 'شر', + 'ﴎ' => 'سر', + 'ﴏ' => 'صر', + 'ﴐ' => 'ضر', + 'ﴑ' => 'طى', + 'ﴒ' => 'طي', + 'ﴓ' => 'عى', + 'ﴔ' => 'عي', + 'ﴕ' => 'غى', + 'ﴖ' => 'غي', + 'ﴗ' => 'سى', + 'ﴘ' => 'سي', + 'ﴙ' => 'شى', + 'ﴚ' => 'شي', + 'ﴛ' => 'حى', + 'ﴜ' => 'حي', + 'ﴝ' => 'جى', + 'ﴞ' => 'جي', + 'ﴟ' => 'خى', + 'ﴠ' => 'خي', + 'ﴡ' => 'صى', + 'ﴢ' => 'صي', + 'ﴣ' => 'ضى', + 'ﴤ' => 'ضي', + 'ﴥ' => 'شج', + 'ﴦ' => 'شح', + 'ﴧ' => 'شخ', + 'ﴨ' => 'شم', + 'ﴩ' => 'شر', + 'ﴪ' => 'سر', + 'ﴫ' => 'صر', + 'ﴬ' => 'ضر', + 'ﴭ' => 'شج', + 'ﴮ' => 'شح', + 'ﴯ' => 'شخ', + 'ﴰ' => 'شم', + 'ﴱ' => 'سه', + 'ﴲ' => 'شه', + 'ﴳ' => 'طم', + 'ﴴ' => 'سج', + 'ﴵ' => 'سح', + 'ﴶ' => 'سخ', + 'ﴷ' => 'شج', + 'ﴸ' => 'شح', + 'ﴹ' => 'شخ', + 'ﴺ' => 'طم', + 'ﴻ' => 'ظم', + 'ﴼ' => 'اً', + 'ﴽ' => 'اً', + 'ﵐ' => 'تجم', + 'ﵑ' => 'تحج', + 'ﵒ' => 'تحج', + 'ﵓ' => 'تحم', + 'ﵔ' => 'تخم', + 'ﵕ' => 'تمج', + 'ﵖ' => 'تمح', + 'ﵗ' => 'تمخ', + 'ﵘ' => 'جمح', + 'ﵙ' => 'جمح', + 'ﵚ' => 'حمي', + 'ﵛ' => 'حمى', + 'ﵜ' => 'سحج', + 'ﵝ' => 'سجح', + 'ﵞ' => 'سجى', + 'ﵟ' => 'سمح', + 'ﵠ' => 'سمح', + 'ﵡ' => 'سمج', + 'ﵢ' => 'سمم', + 'ﵣ' => 'سمم', + 'ﵤ' => 'صحح', + 'ﵥ' => 'صحح', + 'ﵦ' => 'صمم', + 'ﵧ' => 'شحم', + 'ﵨ' => 'شحم', + 'ﵩ' => 'شجي', + 'ﵪ' => 'شمخ', + 'ﵫ' => 'شمخ', + 'ﵬ' => 'شمم', + 'ﵭ' => 'شمم', + 'ﵮ' => 'ضحى', + 'ﵯ' => 'ضخم', + 'ﵰ' => 'ضخم', + 'ﵱ' => 'طمح', + 'ﵲ' => 'طمح', + 'ﵳ' => 'طمم', + 'ﵴ' => 'طمي', + 'ﵵ' => 'عجم', + 'ﵶ' => 'عمم', + 'ﵷ' => 'عمم', + 'ﵸ' => 'عمى', + 'ﵹ' => 'غمم', + 'ﵺ' => 'غمي', + 'ﵻ' => 'غمى', + 'ﵼ' => 'فخم', + 'ﵽ' => 'فخم', + 'ﵾ' => 'قمح', + 'ﵿ' => 'قمم', + 'ﶀ' => 'لحم', + 'ﶁ' => 'لحي', + 'ﶂ' => 'لحى', + 'ﶃ' => 'لجج', + 'ﶄ' => 'لجج', + 'ﶅ' => 'لخم', + 'ﶆ' => 'لخم', + 'ﶇ' => 'لمح', + 'ﶈ' => 'لمح', + 'ﶉ' => 'محج', + 'ﶊ' => 'محم', + 'ﶋ' => 'محي', + 'ﶌ' => 'مجح', + 'ﶍ' => 'مجم', + 'ﶎ' => 'مخج', + 'ﶏ' => 'مخم', + 'ﶒ' => 'مجخ', + 'ﶓ' => 'همج', + 'ﶔ' => 'همم', + 'ﶕ' => 'نحم', + 'ﶖ' => 'نحى', + 'ﶗ' => 'نجم', + 'ﶘ' => 'نجم', + 'ﶙ' => 'نجى', + 'ﶚ' => 'نمي', + 'ﶛ' => 'نمى', + 'ﶜ' => 'يمم', + 'ﶝ' => 'يمم', + 'ﶞ' => 'بخي', + 'ﶟ' => 'تجي', + 'ﶠ' => 'تجى', + 'ﶡ' => 'تخي', + 'ﶢ' => 'تخى', + 'ﶣ' => 'تمي', + 'ﶤ' => 'تمى', + 'ﶥ' => 'جمي', + 'ﶦ' => 'جحى', + 'ﶧ' => 'جمى', + 'ﶨ' => 'سخى', + 'ﶩ' => 'صحي', + 'ﶪ' => 'شحي', + 'ﶫ' => 'ضحي', + 'ﶬ' => 'لجي', + 'ﶭ' => 'لمي', + 'ﶮ' => 'يحي', + 'ﶯ' => 'يجي', + 'ﶰ' => 'يمي', + 'ﶱ' => 'ممي', + 'ﶲ' => 'قمي', + 'ﶳ' => 'نحي', + 'ﶴ' => 'قمح', + 'ﶵ' => 'لحم', + 'ﶶ' => 'عمي', + 'ﶷ' => 'كمي', + 'ﶸ' => 'نجح', + 'ﶹ' => 'مخي', + 'ﶺ' => 'لجم', + 'ﶻ' => 'كمم', + 'ﶼ' => 'لجم', + 'ﶽ' => 'نجح', + 'ﶾ' => 'جحي', + 'ﶿ' => 'حجي', + 'ﷀ' => 'مجي', + 'ﷁ' => 'فمي', + 'ﷂ' => 'بحي', + 'ﷃ' => 'كمم', + 'ﷄ' => 'عجم', + 'ﷅ' => 'صمم', + 'ﷆ' => 'سخي', + 'ﷇ' => 'نجي', + 'ﷰ' => 'صلے', + 'ﷱ' => 'قلے', + 'ﷲ' => 'الله', + 'ﷳ' => 'اكبر', + 'ﷴ' => 'محمد', + 'ﷵ' => 'صلعم', + 'ﷶ' => 'رسول', + 'ﷷ' => 'عليه', + 'ﷸ' => 'وسلم', + 'ﷹ' => 'صلى', + 'ﷺ' => 'صلى الله عليه وسلم', + 'ﷻ' => 'جل جلاله', + '﷼' => 'ریال', + '︐' => ',', + '︑' => '、', + '︒' => '。', + '︓' => ':', + '︔' => ';', + '︕' => '!', + '︖' => '?', + '︗' => '〖', + '︘' => '〗', + '︙' => '...', + '︰' => '..', + '︱' => '—', + '︲' => '–', + '︳' => '_', + '︴' => '_', + '︵' => '(', + '︶' => ')', + '︷' => '{', + '︸' => '}', + '︹' => '〔', + '︺' => '〕', + '︻' => '【', + '︼' => '】', + '︽' => '《', + '︾' => '》', + '︿' => '〈', + '﹀' => '〉', + '﹁' => '「', + '﹂' => '」', + '﹃' => '『', + '﹄' => '』', + '﹇' => '[', + '﹈' => ']', + '﹉' => ' ̅', + '﹊' => ' ̅', + '﹋' => ' ̅', + '﹌' => ' ̅', + '﹍' => '_', + '﹎' => '_', + '﹏' => '_', + '﹐' => ',', + '﹑' => '、', + '﹒' => '.', + '﹔' => ';', + '﹕' => ':', + '﹖' => '?', + '﹗' => '!', + '﹘' => '—', + '﹙' => '(', + '﹚' => ')', + '﹛' => '{', + '﹜' => '}', + '﹝' => '〔', + '﹞' => '〕', + '﹟' => '#', + '﹠' => '&', + '﹡' => '*', + '﹢' => '+', + '﹣' => '-', + '﹤' => '<', + '﹥' => '>', + '﹦' => '=', + '﹨' => '\\', + '﹩' => '$', + '﹪' => '%', + '﹫' => '@', + 'ﹰ' => ' ً', + 'ﹱ' => 'ـً', + 'ﹲ' => ' ٌ', + 'ﹴ' => ' ٍ', + 'ﹶ' => ' َ', + 'ﹷ' => 'ـَ', + 'ﹸ' => ' ُ', + 'ﹹ' => 'ـُ', + 'ﹺ' => ' ِ', + 'ﹻ' => 'ـِ', + 'ﹼ' => ' ّ', + 'ﹽ' => 'ـّ', + 'ﹾ' => ' ْ', + 'ﹿ' => 'ـْ', + 'ﺀ' => 'ء', + 'ﺁ' => 'آ', + 'ﺂ' => 'آ', + 'ﺃ' => 'أ', + 'ﺄ' => 'أ', + 'ﺅ' => 'ؤ', + 'ﺆ' => 'ؤ', + 'ﺇ' => 'إ', + 'ﺈ' => 'إ', + 'ﺉ' => 'ئ', + 'ﺊ' => 'ئ', + 'ﺋ' => 'ئ', + 'ﺌ' => 'ئ', + 'ﺍ' => 'ا', + 'ﺎ' => 'ا', + 'ﺏ' => 'ب', + 'ﺐ' => 'ب', + 'ﺑ' => 'ب', + 'ﺒ' => 'ب', + 'ﺓ' => 'ة', + 'ﺔ' => 'ة', + 'ﺕ' => 'ت', + 'ﺖ' => 'ت', + 'ﺗ' => 'ت', + 'ﺘ' => 'ت', + 'ﺙ' => 'ث', + 'ﺚ' => 'ث', + 'ﺛ' => 'ث', + 'ﺜ' => 'ث', + 'ﺝ' => 'ج', + 'ﺞ' => 'ج', + 'ﺟ' => 'ج', + 'ﺠ' => 'ج', + 'ﺡ' => 'ح', + 'ﺢ' => 'ح', + 'ﺣ' => 'ح', + 'ﺤ' => 'ح', + 'ﺥ' => 'خ', + 'ﺦ' => 'خ', + 'ﺧ' => 'خ', + 'ﺨ' => 'خ', + 'ﺩ' => 'د', + 'ﺪ' => 'د', + 'ﺫ' => 'ذ', + 'ﺬ' => 'ذ', + 'ﺭ' => 'ر', + 'ﺮ' => 'ر', + 'ﺯ' => 'ز', + 'ﺰ' => 'ز', + 'ﺱ' => 'س', + 'ﺲ' => 'س', + 'ﺳ' => 'س', + 'ﺴ' => 'س', + 'ﺵ' => 'ش', + 'ﺶ' => 'ش', + 'ﺷ' => 'ش', + 'ﺸ' => 'ش', + 'ﺹ' => 'ص', + 'ﺺ' => 'ص', + 'ﺻ' => 'ص', + 'ﺼ' => 'ص', + 'ﺽ' => 'ض', + 'ﺾ' => 'ض', + 'ﺿ' => 'ض', + 'ﻀ' => 'ض', + 'ﻁ' => 'ط', + 'ﻂ' => 'ط', + 'ﻃ' => 'ط', + 'ﻄ' => 'ط', + 'ﻅ' => 'ظ', + 'ﻆ' => 'ظ', + 'ﻇ' => 'ظ', + 'ﻈ' => 'ظ', + 'ﻉ' => 'ع', + 'ﻊ' => 'ع', + 'ﻋ' => 'ع', + 'ﻌ' => 'ع', + 'ﻍ' => 'غ', + 'ﻎ' => 'غ', + 'ﻏ' => 'غ', + 'ﻐ' => 'غ', + 'ﻑ' => 'ف', + 'ﻒ' => 'ف', + 'ﻓ' => 'ف', + 'ﻔ' => 'ف', + 'ﻕ' => 'ق', + 'ﻖ' => 'ق', + 'ﻗ' => 'ق', + 'ﻘ' => 'ق', + 'ﻙ' => 'ك', + 'ﻚ' => 'ك', + 'ﻛ' => 'ك', + 'ﻜ' => 'ك', + 'ﻝ' => 'ل', + 'ﻞ' => 'ل', + 'ﻟ' => 'ل', + 'ﻠ' => 'ل', + 'ﻡ' => 'م', + 'ﻢ' => 'م', + 'ﻣ' => 'م', + 'ﻤ' => 'م', + 'ﻥ' => 'ن', + 'ﻦ' => 'ن', + 'ﻧ' => 'ن', + 'ﻨ' => 'ن', + 'ﻩ' => 'ه', + 'ﻪ' => 'ه', + 'ﻫ' => 'ه', + 'ﻬ' => 'ه', + 'ﻭ' => 'و', + 'ﻮ' => 'و', + 'ﻯ' => 'ى', + 'ﻰ' => 'ى', + 'ﻱ' => 'ي', + 'ﻲ' => 'ي', + 'ﻳ' => 'ي', + 'ﻴ' => 'ي', + 'ﻵ' => 'لآ', + 'ﻶ' => 'لآ', + 'ﻷ' => 'لأ', + 'ﻸ' => 'لأ', + 'ﻹ' => 'لإ', + 'ﻺ' => 'لإ', + 'ﻻ' => 'لا', + 'ﻼ' => 'لا', + '!' => '!', + '"' => '"', + '#' => '#', + '$' => '$', + '%' => '%', + '&' => '&', + ''' => '\'', + '(' => '(', + ')' => ')', + '*' => '*', + '+' => '+', + ',' => ',', + '-' => '-', + '.' => '.', + '/' => '/', + '0' => '0', + '1' => '1', + '2' => '2', + '3' => '3', + '4' => '4', + '5' => '5', + '6' => '6', + '7' => '7', + '8' => '8', + '9' => '9', + ':' => ':', + ';' => ';', + '<' => '<', + '=' => '=', + '>' => '>', + '?' => '?', + '@' => '@', + 'A' => 'A', + 'B' => 'B', + 'C' => 'C', + 'D' => 'D', + 'E' => 'E', + 'F' => 'F', + 'G' => 'G', + 'H' => 'H', + 'I' => 'I', + 'J' => 'J', + 'K' => 'K', + 'L' => 'L', + 'M' => 'M', + 'N' => 'N', + 'O' => 'O', + 'P' => 'P', + 'Q' => 'Q', + 'R' => 'R', + 'S' => 'S', + 'T' => 'T', + 'U' => 'U', + 'V' => 'V', + 'W' => 'W', + 'X' => 'X', + 'Y' => 'Y', + 'Z' => 'Z', + '[' => '[', + '\' => '\\', + ']' => ']', + '^' => '^', + '_' => '_', + '`' => '`', + 'a' => 'a', + 'b' => 'b', + 'c' => 'c', + 'd' => 'd', + 'e' => 'e', + 'f' => 'f', + 'g' => 'g', + 'h' => 'h', + 'i' => 'i', + 'j' => 'j', + 'k' => 'k', + 'l' => 'l', + 'm' => 'm', + 'n' => 'n', + 'o' => 'o', + 'p' => 'p', + 'q' => 'q', + 'r' => 'r', + 's' => 's', + 't' => 't', + 'u' => 'u', + 'v' => 'v', + 'w' => 'w', + 'x' => 'x', + 'y' => 'y', + 'z' => 'z', + '{' => '{', + '|' => '|', + '}' => '}', + '~' => '~', + '⦅' => '⦅', + '⦆' => '⦆', + '。' => '。', + '「' => '「', + '」' => '」', + '、' => '、', + '・' => '・', + 'ヲ' => 'ヲ', + 'ァ' => 'ァ', + 'ィ' => 'ィ', + 'ゥ' => 'ゥ', + 'ェ' => 'ェ', + 'ォ' => 'ォ', + 'ャ' => 'ャ', + 'ュ' => 'ュ', + 'ョ' => 'ョ', + 'ッ' => 'ッ', + 'ー' => 'ー', + 'ア' => 'ア', + 'イ' => 'イ', + 'ウ' => 'ウ', + 'エ' => 'エ', + 'オ' => 'オ', + 'カ' => 'カ', + 'キ' => 'キ', + 'ク' => 'ク', + 'ケ' => 'ケ', + 'コ' => 'コ', + 'サ' => 'サ', + 'シ' => 'シ', + 'ス' => 'ス', + 'セ' => 'セ', + 'ソ' => 'ソ', + 'タ' => 'タ', + 'チ' => 'チ', + 'ツ' => 'ツ', + 'テ' => 'テ', + 'ト' => 'ト', + 'ナ' => 'ナ', + 'ニ' => 'ニ', + 'ヌ' => 'ヌ', + 'ネ' => 'ネ', + 'ノ' => 'ノ', + 'ハ' => 'ハ', + 'ヒ' => 'ヒ', + 'フ' => 'フ', + 'ヘ' => 'ヘ', + 'ホ' => 'ホ', + 'マ' => 'マ', + 'ミ' => 'ミ', + 'ム' => 'ム', + 'メ' => 'メ', + 'モ' => 'モ', + 'ヤ' => 'ヤ', + 'ユ' => 'ユ', + 'ヨ' => 'ヨ', + 'ラ' => 'ラ', + 'リ' => 'リ', + 'ル' => 'ル', + 'レ' => 'レ', + 'ロ' => 'ロ', + 'ワ' => 'ワ', + 'ン' => 'ン', + '゙' => '゙', + '゚' => '゚', + 'ᅠ' => 'ᅠ', + 'ᄀ' => 'ᄀ', + 'ᄁ' => 'ᄁ', + 'ᆪ' => 'ᆪ', + 'ᄂ' => 'ᄂ', + 'ᆬ' => 'ᆬ', + 'ᆭ' => 'ᆭ', + 'ᄃ' => 'ᄃ', + 'ᄄ' => 'ᄄ', + 'ᄅ' => 'ᄅ', + 'ᆰ' => 'ᆰ', + 'ᆱ' => 'ᆱ', + 'ᆲ' => 'ᆲ', + 'ᆳ' => 'ᆳ', + 'ᆴ' => 'ᆴ', + 'ᆵ' => 'ᆵ', + 'ᄚ' => 'ᄚ', + 'ᄆ' => 'ᄆ', + 'ᄇ' => 'ᄇ', + 'ᄈ' => 'ᄈ', + 'ᄡ' => 'ᄡ', + 'ᄉ' => 'ᄉ', + 'ᄊ' => 'ᄊ', + 'ᄋ' => 'ᄋ', + 'ᄌ' => 'ᄌ', + 'ᄍ' => 'ᄍ', + 'ᄎ' => 'ᄎ', + 'ᄏ' => 'ᄏ', + 'ᄐ' => 'ᄐ', + 'ᄑ' => 'ᄑ', + 'ᄒ' => 'ᄒ', + 'ᅡ' => 'ᅡ', + 'ᅢ' => 'ᅢ', + 'ᅣ' => 'ᅣ', + 'ᅤ' => 'ᅤ', + 'ᅥ' => 'ᅥ', + 'ᅦ' => 'ᅦ', + 'ᅧ' => 'ᅧ', + 'ᅨ' => 'ᅨ', + 'ᅩ' => 'ᅩ', + 'ᅪ' => 'ᅪ', + 'ᅫ' => 'ᅫ', + 'ᅬ' => 'ᅬ', + 'ᅭ' => 'ᅭ', + 'ᅮ' => 'ᅮ', + 'ᅯ' => 'ᅯ', + 'ᅰ' => 'ᅰ', + 'ᅱ' => 'ᅱ', + 'ᅲ' => 'ᅲ', + 'ᅳ' => 'ᅳ', + 'ᅴ' => 'ᅴ', + 'ᅵ' => 'ᅵ', + '¢' => '¢', + '£' => '£', + '¬' => '¬', + ' ̄' => ' ̄', + '¦' => '¦', + '¥' => '¥', + '₩' => '₩', + '│' => '│', + '←' => '←', + '↑' => '↑', + '→' => '→', + '↓' => '↓', + '■' => '■', + '○' => '○', + '𝐀' => 'A', + '𝐁' => 'B', + '𝐂' => 'C', + '𝐃' => 'D', + '𝐄' => 'E', + '𝐅' => 'F', + '𝐆' => 'G', + '𝐇' => 'H', + '𝐈' => 'I', + '𝐉' => 'J', + '𝐊' => 'K', + '𝐋' => 'L', + '𝐌' => 'M', + '𝐍' => 'N', + '𝐎' => 'O', + '𝐏' => 'P', + '𝐐' => 'Q', + '𝐑' => 'R', + '𝐒' => 'S', + '𝐓' => 'T', + '𝐔' => 'U', + '𝐕' => 'V', + '𝐖' => 'W', + '𝐗' => 'X', + '𝐘' => 'Y', + '𝐙' => 'Z', + '𝐚' => 'a', + '𝐛' => 'b', + '𝐜' => 'c', + '𝐝' => 'd', + '𝐞' => 'e', + '𝐟' => 'f', + '𝐠' => 'g', + '𝐡' => 'h', + '𝐢' => 'i', + '𝐣' => 'j', + '𝐤' => 'k', + '𝐥' => 'l', + '𝐦' => 'm', + '𝐧' => 'n', + '𝐨' => 'o', + '𝐩' => 'p', + '𝐪' => 'q', + '𝐫' => 'r', + '𝐬' => 's', + '𝐭' => 't', + '𝐮' => 'u', + '𝐯' => 'v', + '𝐰' => 'w', + '𝐱' => 'x', + '𝐲' => 'y', + '𝐳' => 'z', + '𝐴' => 'A', + '𝐵' => 'B', + '𝐶' => 'C', + '𝐷' => 'D', + '𝐸' => 'E', + '𝐹' => 'F', + '𝐺' => 'G', + '𝐻' => 'H', + '𝐼' => 'I', + '𝐽' => 'J', + '𝐾' => 'K', + '𝐿' => 'L', + '𝑀' => 'M', + '𝑁' => 'N', + '𝑂' => 'O', + '𝑃' => 'P', + '𝑄' => 'Q', + '𝑅' => 'R', + '𝑆' => 'S', + '𝑇' => 'T', + '𝑈' => 'U', + '𝑉' => 'V', + '𝑊' => 'W', + '𝑋' => 'X', + '𝑌' => 'Y', + '𝑍' => 'Z', + '𝑎' => 'a', + '𝑏' => 'b', + '𝑐' => 'c', + '𝑑' => 'd', + '𝑒' => 'e', + '𝑓' => 'f', + '𝑔' => 'g', + '𝑖' => 'i', + '𝑗' => 'j', + '𝑘' => 'k', + '𝑙' => 'l', + '𝑚' => 'm', + '𝑛' => 'n', + '𝑜' => 'o', + '𝑝' => 'p', + '𝑞' => 'q', + '𝑟' => 'r', + '𝑠' => 's', + '𝑡' => 't', + '𝑢' => 'u', + '𝑣' => 'v', + '𝑤' => 'w', + '𝑥' => 'x', + '𝑦' => 'y', + '𝑧' => 'z', + '𝑨' => 'A', + '𝑩' => 'B', + '𝑪' => 'C', + '𝑫' => 'D', + '𝑬' => 'E', + '𝑭' => 'F', + '𝑮' => 'G', + '𝑯' => 'H', + '𝑰' => 'I', + '𝑱' => 'J', + '𝑲' => 'K', + '𝑳' => 'L', + '𝑴' => 'M', + '𝑵' => 'N', + '𝑶' => 'O', + '𝑷' => 'P', + '𝑸' => 'Q', + '𝑹' => 'R', + '𝑺' => 'S', + '𝑻' => 'T', + '𝑼' => 'U', + '𝑽' => 'V', + '𝑾' => 'W', + '𝑿' => 'X', + '𝒀' => 'Y', + '𝒁' => 'Z', + '𝒂' => 'a', + '𝒃' => 'b', + '𝒄' => 'c', + '𝒅' => 'd', + '𝒆' => 'e', + '𝒇' => 'f', + '𝒈' => 'g', + '𝒉' => 'h', + '𝒊' => 'i', + '𝒋' => 'j', + '𝒌' => 'k', + '𝒍' => 'l', + '𝒎' => 'm', + '𝒏' => 'n', + '𝒐' => 'o', + '𝒑' => 'p', + '𝒒' => 'q', + '𝒓' => 'r', + '𝒔' => 's', + '𝒕' => 't', + '𝒖' => 'u', + '𝒗' => 'v', + '𝒘' => 'w', + '𝒙' => 'x', + '𝒚' => 'y', + '𝒛' => 'z', + '𝒜' => 'A', + '𝒞' => 'C', + '𝒟' => 'D', + '𝒢' => 'G', + '𝒥' => 'J', + '𝒦' => 'K', + '𝒩' => 'N', + '𝒪' => 'O', + '𝒫' => 'P', + '𝒬' => 'Q', + '𝒮' => 'S', + '𝒯' => 'T', + '𝒰' => 'U', + '𝒱' => 'V', + '𝒲' => 'W', + '𝒳' => 'X', + '𝒴' => 'Y', + '𝒵' => 'Z', + '𝒶' => 'a', + '𝒷' => 'b', + '𝒸' => 'c', + '𝒹' => 'd', + '𝒻' => 'f', + '𝒽' => 'h', + '𝒾' => 'i', + '𝒿' => 'j', + '𝓀' => 'k', + '𝓁' => 'l', + '𝓂' => 'm', + '𝓃' => 'n', + '𝓅' => 'p', + '𝓆' => 'q', + '𝓇' => 'r', + '𝓈' => 's', + '𝓉' => 't', + '𝓊' => 'u', + '𝓋' => 'v', + '𝓌' => 'w', + '𝓍' => 'x', + '𝓎' => 'y', + '𝓏' => 'z', + '𝓐' => 'A', + '𝓑' => 'B', + '𝓒' => 'C', + '𝓓' => 'D', + '𝓔' => 'E', + '𝓕' => 'F', + '𝓖' => 'G', + '𝓗' => 'H', + '𝓘' => 'I', + '𝓙' => 'J', + '𝓚' => 'K', + '𝓛' => 'L', + '𝓜' => 'M', + '𝓝' => 'N', + '𝓞' => 'O', + '𝓟' => 'P', + '𝓠' => 'Q', + '𝓡' => 'R', + '𝓢' => 'S', + '𝓣' => 'T', + '𝓤' => 'U', + '𝓥' => 'V', + '𝓦' => 'W', + '𝓧' => 'X', + '𝓨' => 'Y', + '𝓩' => 'Z', + '𝓪' => 'a', + '𝓫' => 'b', + '𝓬' => 'c', + '𝓭' => 'd', + '𝓮' => 'e', + '𝓯' => 'f', + '𝓰' => 'g', + '𝓱' => 'h', + '𝓲' => 'i', + '𝓳' => 'j', + '𝓴' => 'k', + '𝓵' => 'l', + '𝓶' => 'm', + '𝓷' => 'n', + '𝓸' => 'o', + '𝓹' => 'p', + '𝓺' => 'q', + '𝓻' => 'r', + '𝓼' => 's', + '𝓽' => 't', + '𝓾' => 'u', + '𝓿' => 'v', + '𝔀' => 'w', + '𝔁' => 'x', + '𝔂' => 'y', + '𝔃' => 'z', + '𝔄' => 'A', + '𝔅' => 'B', + '𝔇' => 'D', + '𝔈' => 'E', + '𝔉' => 'F', + '𝔊' => 'G', + '𝔍' => 'J', + '𝔎' => 'K', + '𝔏' => 'L', + '𝔐' => 'M', + '𝔑' => 'N', + '𝔒' => 'O', + '𝔓' => 'P', + '𝔔' => 'Q', + '𝔖' => 'S', + '𝔗' => 'T', + '𝔘' => 'U', + '𝔙' => 'V', + '𝔚' => 'W', + '𝔛' => 'X', + '𝔜' => 'Y', + '𝔞' => 'a', + '𝔟' => 'b', + '𝔠' => 'c', + '𝔡' => 'd', + '𝔢' => 'e', + '𝔣' => 'f', + '𝔤' => 'g', + '𝔥' => 'h', + '𝔦' => 'i', + '𝔧' => 'j', + '𝔨' => 'k', + '𝔩' => 'l', + '𝔪' => 'm', + '𝔫' => 'n', + '𝔬' => 'o', + '𝔭' => 'p', + '𝔮' => 'q', + '𝔯' => 'r', + '𝔰' => 's', + '𝔱' => 't', + '𝔲' => 'u', + '𝔳' => 'v', + '𝔴' => 'w', + '𝔵' => 'x', + '𝔶' => 'y', + '𝔷' => 'z', + '𝔸' => 'A', + '𝔹' => 'B', + '𝔻' => 'D', + '𝔼' => 'E', + '𝔽' => 'F', + '𝔾' => 'G', + '𝕀' => 'I', + '𝕁' => 'J', + '𝕂' => 'K', + '𝕃' => 'L', + '𝕄' => 'M', + '𝕆' => 'O', + '𝕊' => 'S', + '𝕋' => 'T', + '𝕌' => 'U', + '𝕍' => 'V', + '𝕎' => 'W', + '𝕏' => 'X', + '𝕐' => 'Y', + '𝕒' => 'a', + '𝕓' => 'b', + '𝕔' => 'c', + '𝕕' => 'd', + '𝕖' => 'e', + '𝕗' => 'f', + '𝕘' => 'g', + '𝕙' => 'h', + '𝕚' => 'i', + '𝕛' => 'j', + '𝕜' => 'k', + '𝕝' => 'l', + '𝕞' => 'm', + '𝕟' => 'n', + '𝕠' => 'o', + '𝕡' => 'p', + '𝕢' => 'q', + '𝕣' => 'r', + '𝕤' => 's', + '𝕥' => 't', + '𝕦' => 'u', + '𝕧' => 'v', + '𝕨' => 'w', + '𝕩' => 'x', + '𝕪' => 'y', + '𝕫' => 'z', + '𝕬' => 'A', + '𝕭' => 'B', + '𝕮' => 'C', + '𝕯' => 'D', + '𝕰' => 'E', + '𝕱' => 'F', + '𝕲' => 'G', + '𝕳' => 'H', + '𝕴' => 'I', + '𝕵' => 'J', + '𝕶' => 'K', + '𝕷' => 'L', + '𝕸' => 'M', + '𝕹' => 'N', + '𝕺' => 'O', + '𝕻' => 'P', + '𝕼' => 'Q', + '𝕽' => 'R', + '𝕾' => 'S', + '𝕿' => 'T', + '𝖀' => 'U', + '𝖁' => 'V', + '𝖂' => 'W', + '𝖃' => 'X', + '𝖄' => 'Y', + '𝖅' => 'Z', + '𝖆' => 'a', + '𝖇' => 'b', + '𝖈' => 'c', + '𝖉' => 'd', + '𝖊' => 'e', + '𝖋' => 'f', + '𝖌' => 'g', + '𝖍' => 'h', + '𝖎' => 'i', + '𝖏' => 'j', + '𝖐' => 'k', + '𝖑' => 'l', + '𝖒' => 'm', + '𝖓' => 'n', + '𝖔' => 'o', + '𝖕' => 'p', + '𝖖' => 'q', + '𝖗' => 'r', + '𝖘' => 's', + '𝖙' => 't', + '𝖚' => 'u', + '𝖛' => 'v', + '𝖜' => 'w', + '𝖝' => 'x', + '𝖞' => 'y', + '𝖟' => 'z', + '𝖠' => 'A', + '𝖡' => 'B', + '𝖢' => 'C', + '𝖣' => 'D', + '𝖤' => 'E', + '𝖥' => 'F', + '𝖦' => 'G', + '𝖧' => 'H', + '𝖨' => 'I', + '𝖩' => 'J', + '𝖪' => 'K', + '𝖫' => 'L', + '𝖬' => 'M', + '𝖭' => 'N', + '𝖮' => 'O', + '𝖯' => 'P', + '𝖰' => 'Q', + '𝖱' => 'R', + '𝖲' => 'S', + '𝖳' => 'T', + '𝖴' => 'U', + '𝖵' => 'V', + '𝖶' => 'W', + '𝖷' => 'X', + '𝖸' => 'Y', + '𝖹' => 'Z', + '𝖺' => 'a', + '𝖻' => 'b', + '𝖼' => 'c', + '𝖽' => 'd', + '𝖾' => 'e', + '𝖿' => 'f', + '𝗀' => 'g', + '𝗁' => 'h', + '𝗂' => 'i', + '𝗃' => 'j', + '𝗄' => 'k', + '𝗅' => 'l', + '𝗆' => 'm', + '𝗇' => 'n', + '𝗈' => 'o', + '𝗉' => 'p', + '𝗊' => 'q', + '𝗋' => 'r', + '𝗌' => 's', + '𝗍' => 't', + '𝗎' => 'u', + '𝗏' => 'v', + '𝗐' => 'w', + '𝗑' => 'x', + '𝗒' => 'y', + '𝗓' => 'z', + '𝗔' => 'A', + '𝗕' => 'B', + '𝗖' => 'C', + '𝗗' => 'D', + '𝗘' => 'E', + '𝗙' => 'F', + '𝗚' => 'G', + '𝗛' => 'H', + '𝗜' => 'I', + '𝗝' => 'J', + '𝗞' => 'K', + '𝗟' => 'L', + '𝗠' => 'M', + '𝗡' => 'N', + '𝗢' => 'O', + '𝗣' => 'P', + '𝗤' => 'Q', + '𝗥' => 'R', + '𝗦' => 'S', + '𝗧' => 'T', + '𝗨' => 'U', + '𝗩' => 'V', + '𝗪' => 'W', + '𝗫' => 'X', + '𝗬' => 'Y', + '𝗭' => 'Z', + '𝗮' => 'a', + '𝗯' => 'b', + '𝗰' => 'c', + '𝗱' => 'd', + '𝗲' => 'e', + '𝗳' => 'f', + '𝗴' => 'g', + '𝗵' => 'h', + '𝗶' => 'i', + '𝗷' => 'j', + '𝗸' => 'k', + '𝗹' => 'l', + '𝗺' => 'm', + '𝗻' => 'n', + '𝗼' => 'o', + '𝗽' => 'p', + '𝗾' => 'q', + '𝗿' => 'r', + '𝘀' => 's', + '𝘁' => 't', + '𝘂' => 'u', + '𝘃' => 'v', + '𝘄' => 'w', + '𝘅' => 'x', + '𝘆' => 'y', + '𝘇' => 'z', + '𝘈' => 'A', + '𝘉' => 'B', + '𝘊' => 'C', + '𝘋' => 'D', + '𝘌' => 'E', + '𝘍' => 'F', + '𝘎' => 'G', + '𝘏' => 'H', + '𝘐' => 'I', + '𝘑' => 'J', + '𝘒' => 'K', + '𝘓' => 'L', + '𝘔' => 'M', + '𝘕' => 'N', + '𝘖' => 'O', + '𝘗' => 'P', + '𝘘' => 'Q', + '𝘙' => 'R', + '𝘚' => 'S', + '𝘛' => 'T', + '𝘜' => 'U', + '𝘝' => 'V', + '𝘞' => 'W', + '𝘟' => 'X', + '𝘠' => 'Y', + '𝘡' => 'Z', + '𝘢' => 'a', + '𝘣' => 'b', + '𝘤' => 'c', + '𝘥' => 'd', + '𝘦' => 'e', + '𝘧' => 'f', + '𝘨' => 'g', + '𝘩' => 'h', + '𝘪' => 'i', + '𝘫' => 'j', + '𝘬' => 'k', + '𝘭' => 'l', + '𝘮' => 'm', + '𝘯' => 'n', + '𝘰' => 'o', + '𝘱' => 'p', + '𝘲' => 'q', + '𝘳' => 'r', + '𝘴' => 's', + '𝘵' => 't', + '𝘶' => 'u', + '𝘷' => 'v', + '𝘸' => 'w', + '𝘹' => 'x', + '𝘺' => 'y', + '𝘻' => 'z', + '𝘼' => 'A', + '𝘽' => 'B', + '𝘾' => 'C', + '𝘿' => 'D', + '𝙀' => 'E', + '𝙁' => 'F', + '𝙂' => 'G', + '𝙃' => 'H', + '𝙄' => 'I', + '𝙅' => 'J', + '𝙆' => 'K', + '𝙇' => 'L', + '𝙈' => 'M', + '𝙉' => 'N', + '𝙊' => 'O', + '𝙋' => 'P', + '𝙌' => 'Q', + '𝙍' => 'R', + '𝙎' => 'S', + '𝙏' => 'T', + '𝙐' => 'U', + '𝙑' => 'V', + '𝙒' => 'W', + '𝙓' => 'X', + '𝙔' => 'Y', + '𝙕' => 'Z', + '𝙖' => 'a', + '𝙗' => 'b', + '𝙘' => 'c', + '𝙙' => 'd', + '𝙚' => 'e', + '𝙛' => 'f', + '𝙜' => 'g', + '𝙝' => 'h', + '𝙞' => 'i', + '𝙟' => 'j', + '𝙠' => 'k', + '𝙡' => 'l', + '𝙢' => 'm', + '𝙣' => 'n', + '𝙤' => 'o', + '𝙥' => 'p', + '𝙦' => 'q', + '𝙧' => 'r', + '𝙨' => 's', + '𝙩' => 't', + '𝙪' => 'u', + '𝙫' => 'v', + '𝙬' => 'w', + '𝙭' => 'x', + '𝙮' => 'y', + '𝙯' => 'z', + '𝙰' => 'A', + '𝙱' => 'B', + '𝙲' => 'C', + '𝙳' => 'D', + '𝙴' => 'E', + '𝙵' => 'F', + '𝙶' => 'G', + '𝙷' => 'H', + '𝙸' => 'I', + '𝙹' => 'J', + '𝙺' => 'K', + '𝙻' => 'L', + '𝙼' => 'M', + '𝙽' => 'N', + '𝙾' => 'O', + '𝙿' => 'P', + '𝚀' => 'Q', + '𝚁' => 'R', + '𝚂' => 'S', + '𝚃' => 'T', + '𝚄' => 'U', + '𝚅' => 'V', + '𝚆' => 'W', + '𝚇' => 'X', + '𝚈' => 'Y', + '𝚉' => 'Z', + '𝚊' => 'a', + '𝚋' => 'b', + '𝚌' => 'c', + '𝚍' => 'd', + '𝚎' => 'e', + '𝚏' => 'f', + '𝚐' => 'g', + '𝚑' => 'h', + '𝚒' => 'i', + '𝚓' => 'j', + '𝚔' => 'k', + '𝚕' => 'l', + '𝚖' => 'm', + '𝚗' => 'n', + '𝚘' => 'o', + '𝚙' => 'p', + '𝚚' => 'q', + '𝚛' => 'r', + '𝚜' => 's', + '𝚝' => 't', + '𝚞' => 'u', + '𝚟' => 'v', + '𝚠' => 'w', + '𝚡' => 'x', + '𝚢' => 'y', + '𝚣' => 'z', + '𝚤' => 'ı', + '𝚥' => 'ȷ', + '𝚨' => 'Α', + '𝚩' => 'Β', + '𝚪' => 'Γ', + '𝚫' => 'Δ', + '𝚬' => 'Ε', + '𝚭' => 'Ζ', + '𝚮' => 'Η', + '𝚯' => 'Θ', + '𝚰' => 'Ι', + '𝚱' => 'Κ', + '𝚲' => 'Λ', + '𝚳' => 'Μ', + '𝚴' => 'Ν', + '𝚵' => 'Ξ', + '𝚶' => 'Ο', + '𝚷' => 'Π', + '𝚸' => 'Ρ', + '𝚹' => 'Θ', + '𝚺' => 'Σ', + '𝚻' => 'Τ', + '𝚼' => 'Υ', + '𝚽' => 'Φ', + '𝚾' => 'Χ', + '𝚿' => 'Ψ', + '𝛀' => 'Ω', + '𝛁' => '∇', + '𝛂' => 'α', + '𝛃' => 'β', + '𝛄' => 'γ', + '𝛅' => 'δ', + '𝛆' => 'ε', + '𝛇' => 'ζ', + '𝛈' => 'η', + '𝛉' => 'θ', + '𝛊' => 'ι', + '𝛋' => 'κ', + '𝛌' => 'λ', + '𝛍' => 'μ', + '𝛎' => 'ν', + '𝛏' => 'ξ', + '𝛐' => 'ο', + '𝛑' => 'π', + '𝛒' => 'ρ', + '𝛓' => 'ς', + '𝛔' => 'σ', + '𝛕' => 'τ', + '𝛖' => 'υ', + '𝛗' => 'φ', + '𝛘' => 'χ', + '𝛙' => 'ψ', + '𝛚' => 'ω', + '𝛛' => '∂', + '𝛜' => 'ε', + '𝛝' => 'θ', + '𝛞' => 'κ', + '𝛟' => 'φ', + '𝛠' => 'ρ', + '𝛡' => 'π', + '𝛢' => 'Α', + '𝛣' => 'Β', + '𝛤' => 'Γ', + '𝛥' => 'Δ', + '𝛦' => 'Ε', + '𝛧' => 'Ζ', + '𝛨' => 'Η', + '𝛩' => 'Θ', + '𝛪' => 'Ι', + '𝛫' => 'Κ', + '𝛬' => 'Λ', + '𝛭' => 'Μ', + '𝛮' => 'Ν', + '𝛯' => 'Ξ', + '𝛰' => 'Ο', + '𝛱' => 'Π', + '𝛲' => 'Ρ', + '𝛳' => 'Θ', + '𝛴' => 'Σ', + '𝛵' => 'Τ', + '𝛶' => 'Υ', + '𝛷' => 'Φ', + '𝛸' => 'Χ', + '𝛹' => 'Ψ', + '𝛺' => 'Ω', + '𝛻' => '∇', + '𝛼' => 'α', + '𝛽' => 'β', + '𝛾' => 'γ', + '𝛿' => 'δ', + '𝜀' => 'ε', + '𝜁' => 'ζ', + '𝜂' => 'η', + '𝜃' => 'θ', + '𝜄' => 'ι', + '𝜅' => 'κ', + '𝜆' => 'λ', + '𝜇' => 'μ', + '𝜈' => 'ν', + '𝜉' => 'ξ', + '𝜊' => 'ο', + '𝜋' => 'π', + '𝜌' => 'ρ', + '𝜍' => 'ς', + '𝜎' => 'σ', + '𝜏' => 'τ', + '𝜐' => 'υ', + '𝜑' => 'φ', + '𝜒' => 'χ', + '𝜓' => 'ψ', + '𝜔' => 'ω', + '𝜕' => '∂', + '𝜖' => 'ε', + '𝜗' => 'θ', + '𝜘' => 'κ', + '𝜙' => 'φ', + '𝜚' => 'ρ', + '𝜛' => 'π', + '𝜜' => 'Α', + '𝜝' => 'Β', + '𝜞' => 'Γ', + '𝜟' => 'Δ', + '𝜠' => 'Ε', + '𝜡' => 'Ζ', + '𝜢' => 'Η', + '𝜣' => 'Θ', + '𝜤' => 'Ι', + '𝜥' => 'Κ', + '𝜦' => 'Λ', + '𝜧' => 'Μ', + '𝜨' => 'Ν', + '𝜩' => 'Ξ', + '𝜪' => 'Ο', + '𝜫' => 'Π', + '𝜬' => 'Ρ', + '𝜭' => 'Θ', + '𝜮' => 'Σ', + '𝜯' => 'Τ', + '𝜰' => 'Υ', + '𝜱' => 'Φ', + '𝜲' => 'Χ', + '𝜳' => 'Ψ', + '𝜴' => 'Ω', + '𝜵' => '∇', + '𝜶' => 'α', + '𝜷' => 'β', + '𝜸' => 'γ', + '𝜹' => 'δ', + '𝜺' => 'ε', + '𝜻' => 'ζ', + '𝜼' => 'η', + '𝜽' => 'θ', + '𝜾' => 'ι', + '𝜿' => 'κ', + '𝝀' => 'λ', + '𝝁' => 'μ', + '𝝂' => 'ν', + '𝝃' => 'ξ', + '𝝄' => 'ο', + '𝝅' => 'π', + '𝝆' => 'ρ', + '𝝇' => 'ς', + '𝝈' => 'σ', + '𝝉' => 'τ', + '𝝊' => 'υ', + '𝝋' => 'φ', + '𝝌' => 'χ', + '𝝍' => 'ψ', + '𝝎' => 'ω', + '𝝏' => '∂', + '𝝐' => 'ε', + '𝝑' => 'θ', + '𝝒' => 'κ', + '𝝓' => 'φ', + '𝝔' => 'ρ', + '𝝕' => 'π', + '𝝖' => 'Α', + '𝝗' => 'Β', + '𝝘' => 'Γ', + '𝝙' => 'Δ', + '𝝚' => 'Ε', + '𝝛' => 'Ζ', + '𝝜' => 'Η', + '𝝝' => 'Θ', + '𝝞' => 'Ι', + '𝝟' => 'Κ', + '𝝠' => 'Λ', + '𝝡' => 'Μ', + '𝝢' => 'Ν', + '𝝣' => 'Ξ', + '𝝤' => 'Ο', + '𝝥' => 'Π', + '𝝦' => 'Ρ', + '𝝧' => 'Θ', + '𝝨' => 'Σ', + '𝝩' => 'Τ', + '𝝪' => 'Υ', + '𝝫' => 'Φ', + '𝝬' => 'Χ', + '𝝭' => 'Ψ', + '𝝮' => 'Ω', + '𝝯' => '∇', + '𝝰' => 'α', + '𝝱' => 'β', + '𝝲' => 'γ', + '𝝳' => 'δ', + '𝝴' => 'ε', + '𝝵' => 'ζ', + '𝝶' => 'η', + '𝝷' => 'θ', + '𝝸' => 'ι', + '𝝹' => 'κ', + '𝝺' => 'λ', + '𝝻' => 'μ', + '𝝼' => 'ν', + '𝝽' => 'ξ', + '𝝾' => 'ο', + '𝝿' => 'π', + '𝞀' => 'ρ', + '𝞁' => 'ς', + '𝞂' => 'σ', + '𝞃' => 'τ', + '𝞄' => 'υ', + '𝞅' => 'φ', + '𝞆' => 'χ', + '𝞇' => 'ψ', + '𝞈' => 'ω', + '𝞉' => '∂', + '𝞊' => 'ε', + '𝞋' => 'θ', + '𝞌' => 'κ', + '𝞍' => 'φ', + '𝞎' => 'ρ', + '𝞏' => 'π', + '𝞐' => 'Α', + '𝞑' => 'Β', + '𝞒' => 'Γ', + '𝞓' => 'Δ', + '𝞔' => 'Ε', + '𝞕' => 'Ζ', + '𝞖' => 'Η', + '𝞗' => 'Θ', + '𝞘' => 'Ι', + '𝞙' => 'Κ', + '𝞚' => 'Λ', + '𝞛' => 'Μ', + '𝞜' => 'Ν', + '𝞝' => 'Ξ', + '𝞞' => 'Ο', + '𝞟' => 'Π', + '𝞠' => 'Ρ', + '𝞡' => 'Θ', + '𝞢' => 'Σ', + '𝞣' => 'Τ', + '𝞤' => 'Υ', + '𝞥' => 'Φ', + '𝞦' => 'Χ', + '𝞧' => 'Ψ', + '𝞨' => 'Ω', + '𝞩' => '∇', + '𝞪' => 'α', + '𝞫' => 'β', + '𝞬' => 'γ', + '𝞭' => 'δ', + '𝞮' => 'ε', + '𝞯' => 'ζ', + '𝞰' => 'η', + '𝞱' => 'θ', + '𝞲' => 'ι', + '𝞳' => 'κ', + '𝞴' => 'λ', + '𝞵' => 'μ', + '𝞶' => 'ν', + '𝞷' => 'ξ', + '𝞸' => 'ο', + '𝞹' => 'π', + '𝞺' => 'ρ', + '𝞻' => 'ς', + '𝞼' => 'σ', + '𝞽' => 'τ', + '𝞾' => 'υ', + '𝞿' => 'φ', + '𝟀' => 'χ', + '𝟁' => 'ψ', + '𝟂' => 'ω', + '𝟃' => '∂', + '𝟄' => 'ε', + '𝟅' => 'θ', + '𝟆' => 'κ', + '𝟇' => 'φ', + '𝟈' => 'ρ', + '𝟉' => 'π', + '𝟊' => 'Ϝ', + '𝟋' => 'ϝ', + '𝟎' => '0', + '𝟏' => '1', + '𝟐' => '2', + '𝟑' => '3', + '𝟒' => '4', + '𝟓' => '5', + '𝟔' => '6', + '𝟕' => '7', + '𝟖' => '8', + '𝟗' => '9', + '𝟘' => '0', + '𝟙' => '1', + '𝟚' => '2', + '𝟛' => '3', + '𝟜' => '4', + '𝟝' => '5', + '𝟞' => '6', + '𝟟' => '7', + '𝟠' => '8', + '𝟡' => '9', + '𝟢' => '0', + '𝟣' => '1', + '𝟤' => '2', + '𝟥' => '3', + '𝟦' => '4', + '𝟧' => '5', + '𝟨' => '6', + '𝟩' => '7', + '𝟪' => '8', + '𝟫' => '9', + '𝟬' => '0', + '𝟭' => '1', + '𝟮' => '2', + '𝟯' => '3', + '𝟰' => '4', + '𝟱' => '5', + '𝟲' => '6', + '𝟳' => '7', + '𝟴' => '8', + '𝟵' => '9', + '𝟶' => '0', + '𝟷' => '1', + '𝟸' => '2', + '𝟹' => '3', + '𝟺' => '4', + '𝟻' => '5', + '𝟼' => '6', + '𝟽' => '7', + '𝟾' => '8', + '𝟿' => '9', + '𞸀' => 'ا', + '𞸁' => 'ب', + '𞸂' => 'ج', + '𞸃' => 'د', + '𞸅' => 'و', + '𞸆' => 'ز', + '𞸇' => 'ح', + '𞸈' => 'ط', + '𞸉' => 'ي', + '𞸊' => 'ك', + '𞸋' => 'ل', + '𞸌' => 'م', + '𞸍' => 'ن', + '𞸎' => 'س', + '𞸏' => 'ع', + '𞸐' => 'ف', + '𞸑' => 'ص', + '𞸒' => 'ق', + '𞸓' => 'ر', + '𞸔' => 'ش', + '𞸕' => 'ت', + '𞸖' => 'ث', + '𞸗' => 'خ', + '𞸘' => 'ذ', + '𞸙' => 'ض', + '𞸚' => 'ظ', + '𞸛' => 'غ', + '𞸜' => 'ٮ', + '𞸝' => 'ں', + '𞸞' => 'ڡ', + '𞸟' => 'ٯ', + '𞸡' => 'ب', + '𞸢' => 'ج', + '𞸤' => 'ه', + '𞸧' => 'ح', + '𞸩' => 'ي', + '𞸪' => 'ك', + '𞸫' => 'ل', + '𞸬' => 'م', + '𞸭' => 'ن', + '𞸮' => 'س', + '𞸯' => 'ع', + '𞸰' => 'ف', + '𞸱' => 'ص', + '𞸲' => 'ق', + '𞸴' => 'ش', + '𞸵' => 'ت', + '𞸶' => 'ث', + '𞸷' => 'خ', + '𞸹' => 'ض', + '𞸻' => 'غ', + '𞹂' => 'ج', + '𞹇' => 'ح', + '𞹉' => 'ي', + '𞹋' => 'ل', + '𞹍' => 'ن', + '𞹎' => 'س', + '𞹏' => 'ع', + '𞹑' => 'ص', + '𞹒' => 'ق', + '𞹔' => 'ش', + '𞹗' => 'خ', + '𞹙' => 'ض', + '𞹛' => 'غ', + '𞹝' => 'ں', + '𞹟' => 'ٯ', + '𞹡' => 'ب', + '𞹢' => 'ج', + '𞹤' => 'ه', + '𞹧' => 'ح', + '𞹨' => 'ط', + '𞹩' => 'ي', + '𞹪' => 'ك', + '𞹬' => 'م', + '𞹭' => 'ن', + '𞹮' => 'س', + '𞹯' => 'ع', + '𞹰' => 'ف', + '𞹱' => 'ص', + '𞹲' => 'ق', + '𞹴' => 'ش', + '𞹵' => 'ت', + '𞹶' => 'ث', + '𞹷' => 'خ', + '𞹹' => 'ض', + '𞹺' => 'ظ', + '𞹻' => 'غ', + '𞹼' => 'ٮ', + '𞹾' => 'ڡ', + '𞺀' => 'ا', + '𞺁' => 'ب', + '𞺂' => 'ج', + '𞺃' => 'د', + '𞺄' => 'ه', + '𞺅' => 'و', + '𞺆' => 'ز', + '𞺇' => 'ح', + '𞺈' => 'ط', + '𞺉' => 'ي', + '𞺋' => 'ل', + '𞺌' => 'م', + '𞺍' => 'ن', + '𞺎' => 'س', + '𞺏' => 'ع', + '𞺐' => 'ف', + '𞺑' => 'ص', + '𞺒' => 'ق', + '𞺓' => 'ر', + '𞺔' => 'ش', + '𞺕' => 'ت', + '𞺖' => 'ث', + '𞺗' => 'خ', + '𞺘' => 'ذ', + '𞺙' => 'ض', + '𞺚' => 'ظ', + '𞺛' => 'غ', + '𞺡' => 'ب', + '𞺢' => 'ج', + '𞺣' => 'د', + '𞺥' => 'و', + '𞺦' => 'ز', + '𞺧' => 'ح', + '𞺨' => 'ط', + '𞺩' => 'ي', + '𞺫' => 'ل', + '𞺬' => 'م', + '𞺭' => 'ن', + '𞺮' => 'س', + '𞺯' => 'ع', + '𞺰' => 'ف', + '𞺱' => 'ص', + '𞺲' => 'ق', + '𞺳' => 'ر', + '𞺴' => 'ش', + '𞺵' => 'ت', + '𞺶' => 'ث', + '𞺷' => 'خ', + '𞺸' => 'ذ', + '𞺹' => 'ض', + '𞺺' => 'ظ', + '𞺻' => 'غ', + '🄀' => '0.', + '🄁' => '0,', + '🄂' => '1,', + '🄃' => '2,', + '🄄' => '3,', + '🄅' => '4,', + '🄆' => '5,', + '🄇' => '6,', + '🄈' => '7,', + '🄉' => '8,', + '🄊' => '9,', + '🄐' => '(A)', + '🄑' => '(B)', + '🄒' => '(C)', + '🄓' => '(D)', + '🄔' => '(E)', + '🄕' => '(F)', + '🄖' => '(G)', + '🄗' => '(H)', + '🄘' => '(I)', + '🄙' => '(J)', + '🄚' => '(K)', + '🄛' => '(L)', + '🄜' => '(M)', + '🄝' => '(N)', + '🄞' => '(O)', + '🄟' => '(P)', + '🄠' => '(Q)', + '🄡' => '(R)', + '🄢' => '(S)', + '🄣' => '(T)', + '🄤' => '(U)', + '🄥' => '(V)', + '🄦' => '(W)', + '🄧' => '(X)', + '🄨' => '(Y)', + '🄩' => '(Z)', + '🄪' => '〔S〕', + '🄫' => 'C', + '🄬' => 'R', + '🄭' => 'CD', + '🄮' => 'WZ', + '🄰' => 'A', + '🄱' => 'B', + '🄲' => 'C', + '🄳' => 'D', + '🄴' => 'E', + '🄵' => 'F', + '🄶' => 'G', + '🄷' => 'H', + '🄸' => 'I', + '🄹' => 'J', + '🄺' => 'K', + '🄻' => 'L', + '🄼' => 'M', + '🄽' => 'N', + '🄾' => 'O', + '🄿' => 'P', + '🅀' => 'Q', + '🅁' => 'R', + '🅂' => 'S', + '🅃' => 'T', + '🅄' => 'U', + '🅅' => 'V', + '🅆' => 'W', + '🅇' => 'X', + '🅈' => 'Y', + '🅉' => 'Z', + '🅊' => 'HV', + '🅋' => 'MV', + '🅌' => 'SD', + '🅍' => 'SS', + '🅎' => 'PPV', + '🅏' => 'WC', + '🅪' => 'MC', + '🅫' => 'MD', + '🅬' => 'MR', + '🆐' => 'DJ', + '🈀' => 'ほか', + '🈁' => 'ココ', + '🈂' => 'サ', + '🈐' => '手', + '🈑' => '字', + '🈒' => '双', + '🈓' => 'デ', + '🈔' => '二', + '🈕' => '多', + '🈖' => '解', + '🈗' => '天', + '🈘' => '交', + '🈙' => '映', + '🈚' => '無', + '🈛' => '料', + '🈜' => '前', + '🈝' => '後', + '🈞' => '再', + '🈟' => '新', + '🈠' => '初', + '🈡' => '終', + '🈢' => '生', + '🈣' => '販', + '🈤' => '声', + '🈥' => '吹', + '🈦' => '演', + '🈧' => '投', + '🈨' => '捕', + '🈩' => '一', + '🈪' => '三', + '🈫' => '遊', + '🈬' => '左', + '🈭' => '中', + '🈮' => '右', + '🈯' => '指', + '🈰' => '走', + '🈱' => '打', + '🈲' => '禁', + '🈳' => '空', + '🈴' => '合', + '🈵' => '満', + '🈶' => '有', + '🈷' => '月', + '🈸' => '申', + '🈹' => '割', + '🈺' => '営', + '🈻' => '配', + '🉀' => '〔本〕', + '🉁' => '〔三〕', + '🉂' => '〔二〕', + '🉃' => '〔安〕', + '🉄' => '〔点〕', + '🉅' => '〔打〕', + '🉆' => '〔盗〕', + '🉇' => '〔勝〕', + '🉈' => '〔敗〕', + '🉐' => '得', + '🉑' => '可', + '🯰' => '0', + '🯱' => '1', + '🯲' => '2', + '🯳' => '3', + '🯴' => '4', + '🯵' => '5', + '🯶' => '6', + '🯷' => '7', + '🯸' => '8', + '🯹' => '9', +); diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/bootstrap.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/bootstrap.php new file mode 100644 index 000000000..3608e5c05 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/bootstrap.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Normalizer as p; + +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} + +if (!function_exists('normalizer_is_normalized')) { + function normalizer_is_normalized($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::isNormalized($string, $form); } +} +if (!function_exists('normalizer_normalize')) { + function normalizer_normalize($string, $form = p\Normalizer::FORM_C) { return p\Normalizer::normalize($string, $form); } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php b/tests/integration/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php new file mode 100644 index 000000000..e36d1a947 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Intl\Normalizer as p; + +if (!function_exists('normalizer_is_normalized')) { + function normalizer_is_normalized(?string $string, ?int $form = p\Normalizer::FORM_C): bool { return p\Normalizer::isNormalized((string) $string, (int) $form); } +} +if (!function_exists('normalizer_normalize')) { + function normalizer_normalize(?string $string, ?int $form = p\Normalizer::FORM_C): string|false { return p\Normalizer::normalize((string) $string, (int) $form); } +} diff --git a/tests/integration/vendor/symfony/polyfill-intl-normalizer/composer.json b/tests/integration/vendor/symfony/polyfill-intl-normalizer/composer.json new file mode 100644 index 000000000..393edf701 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-intl-normalizer/composer.json @@ -0,0 +1,39 @@ +{ + "name": "symfony/polyfill-intl-normalizer", + "type": "library", + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "normalizer"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "suggest": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/LICENSE b/tests/integration/vendor/symfony/polyfill-mbstring/LICENSE index 39fa189d2..4cd8bdd30 100644 --- a/tests/integration/vendor/symfony/polyfill-mbstring/LICENSE +++ b/tests/integration/vendor/symfony/polyfill-mbstring/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016 Fabien Potencier +Copyright (c) 2015-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/Mbstring.php b/tests/integration/vendor/symfony/polyfill-mbstring/Mbstring.php index 934cfcff0..b65c54a6b 100644 --- a/tests/integration/vendor/symfony/polyfill-mbstring/Mbstring.php +++ b/tests/integration/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -20,7 +20,10 @@ * - mb_convert_variables - Convert character code in variable(s) * - mb_decode_mimeheader - Decode string in MIME header field * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference * - mb_convert_case - Perform case folding on a string + * - mb_detect_encoding - Detect character encoding * - mb_get_info - Get internal settings of mbstring * - mb_http_input - Detect HTTP input character encoding * - mb_http_output - Set/Get HTTP output character encoding @@ -32,6 +35,7 @@ * - mb_strlen - Get string length * - mb_strpos - Find position of first occurrence of string in a string * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_str_split - Convert a string to an array * - mb_strtolower - Make a string lowercase * - mb_strtoupper - Make a string uppercase * - mb_substitute_character - Set/Get substitution character @@ -41,14 +45,12 @@ * - mb_strrchr - Finds the last occurrence of a character in a string within another * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive - * - mb_strstr - Finds first occurrence of a string within anothers + * - mb_strstr - Finds first occurrence of a string within another * - mb_strwidth - Return width of string * - mb_substr_count - Count the number of substring occurrences * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) - * - mb_decode_numericentity - Decode HTML numeric string reference to character - * - mb_encode_numericentity - Encode character to HTML numeric string reference * - mb_ereg_* - Regular expression with multibyte support * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable * - mb_preferred_mime_name - Get MIME charset string @@ -65,19 +67,20 @@ */ final class Mbstring { - const MB_CASE_FOLD = PHP_INT_MAX; + public const MB_CASE_FOLD = \PHP_INT_MAX; + + private const CASE_FOLD = [ + ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], + ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], + ]; - private static $encodingList = array('ASCII', 'UTF-8'); + private static $encodingList = ['ASCII', 'UTF-8']; private static $language = 'neutral'; private static $internalEncoding = 'UTF-8'; - private static $caseFold = array( - array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), - array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'), - ); public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { - if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + if (\is_array($fromEncoding) || ($fromEncoding !== null && false !== strpos($fromEncoding, ','))) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); @@ -99,27 +102,25 @@ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null $fromEncoding = 'Windows-1252'; } if ('UTF-8' !== $fromEncoding) { - $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); + $s = \iconv($fromEncoding, 'UTF-8//IGNORE', $s); } - return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); } if ('HTML-ENTITIES' === $fromEncoding) { - $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); $fromEncoding = 'UTF-8'; } - return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + return \iconv($fromEncoding, $toEncoding.'//IGNORE', $s); } - public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) { - $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); - $ok = true; array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { - if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { $ok = false; } }); @@ -129,17 +130,148 @@ public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = nu public static function mb_decode_mimeheader($s) { - return iconv_mime_decode($s, 2, self::$internalEncoding); + return \iconv_mime_decode($s, 2, self::$internalEncoding); } public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) { - trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); + } + + public static function mb_decode_numericentity($s, $convmap, $encoding = null) + { + if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return null; + } + + if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { + return false; + } + + if (null !== $encoding && !is_scalar($encoding)) { + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return ''; // Instead of null (cf. mb_encode_numericentity). + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $cnt = floor(\count($convmap) / 4) * 4; + + for ($i = 0; $i < $cnt; $i += 4) { + // collector_decode_htmlnumericentity ignores $convmap[$i + 3] + $convmap[$i] += $convmap[$i + 2]; + $convmap[$i + 1] += $convmap[$i + 2]; + } + + $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { + $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; + for ($i = 0; $i < $cnt; $i += 4) { + if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { + return self::mb_chr($c - $convmap[$i + 2]); + } + } + + return $m[0]; + }, $s); + + if (null === $encoding) { + return $s; + } + + return \iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) + { + if (null !== $s && !is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return null; + } + + if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { + return false; + } + + if (null !== $encoding && !is_scalar($encoding)) { + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); + + return null; // Instead of '' (cf. mb_decode_numericentity). + } + + if (null !== $is_hex && !is_scalar($is_hex)) { + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING); + + return null; + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = \iconv($encoding, 'UTF-8//IGNORE', $s); + } + + static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; + + $cnt = floor(\count($convmap) / 4) * 4; + $i = 0; + $len = \strlen($s); + $result = ''; + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + $c = self::mb_ord($uchr); + + for ($j = 0; $j < $cnt; $j += 4) { + if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { + $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; + $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; + continue 2; + } + } + $result .= $uchr; + } + + if (null === $encoding) { + return $result; + } + + return \iconv('UTF-8', $encoding.'//IGNORE', $result); } public static function mb_convert_case($s, $mode, $encoding = null) { - if ('' === $s .= '') { + $s = (string) $s; + if ('' === $s) { return ''; } @@ -147,15 +279,21 @@ public static function mb_convert_case($s, $mode, $encoding = null) if ('UTF-8' === $encoding) { $encoding = null; + if (!preg_match('//u', $s)) { + $s = @\iconv('UTF-8', 'UTF-8//IGNORE', $s); + } } else { - $s = iconv($encoding, 'UTF-8//IGNORE', $s); + $s = \iconv($encoding, 'UTF-8//IGNORE', $s); } - if (MB_CASE_TITLE == $mode) { - $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); - $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); + if (\MB_CASE_TITLE == $mode) { + static $titleRegexp = null; + if (null === $titleRegexp) { + $titleRegexp = self::getData('titleCaseRegexp'); + } + $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); } else { - if (MB_CASE_UPPER == $mode) { + if (\MB_CASE_UPPER == $mode) { static $upper = null; if (null === $upper) { $upper = self::getData('upperCase'); @@ -163,7 +301,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) $map = $upper; } else { if (self::MB_CASE_FOLD === $mode) { - $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + $s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s); } static $lower = null; @@ -173,10 +311,10 @@ public static function mb_convert_case($s, $mode, $encoding = null) $map = $lower; } - static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $i = 0; - $len = strlen($s); + $len = \strlen($s); while ($i < $len) { $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; @@ -185,7 +323,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) if (isset($map[$uchr])) { $uchr = $map[$uchr]; - $nlen = strlen($uchr); + $nlen = \strlen($uchr); if ($nlen == $ulen) { $nlen = $i; @@ -195,7 +333,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) } else { $s = substr_replace($s, $uchr, $i - $ulen, $ulen); $len += $nlen - $ulen; - $i += $nlen - $ulen; + $i += $nlen - $ulen; } } } @@ -205,7 +343,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) return $s; } - return iconv('UTF-8', $encoding.'//IGNORE', $s); + return \iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_internal_encoding($encoding = null) @@ -214,15 +352,19 @@ public static function mb_internal_encoding($encoding = null) return self::$internalEncoding; } - $encoding = self::getEncoding($encoding); + $normalizedEncoding = self::getEncoding($encoding); - if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { - self::$internalEncoding = $encoding; + if ('UTF-8' === $normalizedEncoding || false !== @\iconv($normalizedEncoding, $normalizedEncoding, ' ')) { + self::$internalEncoding = $normalizedEncoding; return true; } - return false; + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); } public static function mb_language($lang = null) @@ -231,20 +373,24 @@ public static function mb_language($lang = null) return self::$language; } - switch ($lang = strtolower($lang)) { + switch ($normalizedLang = strtolower($lang)) { case 'uni': case 'neutral': - self::$language = $lang; + self::$language = $normalizedLang; return true; } - return false; + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); } public static function mb_list_encodings() { - return array('UTF-8'); + return ['UTF-8']; } public static function mb_encoding_aliases($encoding) @@ -252,7 +398,7 @@ public static function mb_encoding_aliases($encoding) switch (strtoupper($encoding)) { case 'UTF8': case 'UTF-8': - return array('utf8'); + return ['utf8']; } return false; @@ -267,7 +413,7 @@ public static function mb_check_encoding($var = null, $encoding = null) $encoding = self::$internalEncoding; } - return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + return self::mb_detect_encoding($var, [$encoding]) || false !== @\iconv($encoding, $encoding, $var); } public static function mb_detect_encoding($str, $encodingList = null, $strict = false) @@ -275,7 +421,7 @@ public static function mb_detect_encoding($str, $encodingList = null, $strict = if (null === $encodingList) { $encodingList = self::$encodingList; } else { - if (!is_array($encodingList)) { + if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); @@ -312,7 +458,7 @@ public static function mb_detect_order($encodingList = null) return self::$encodingList; } - if (!is_array($encodingList)) { + if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); @@ -323,6 +469,7 @@ public static function mb_detect_order($encodingList = null) if (strncmp($enc, 'ISO-8859-', 9)) { return false; } + // no break case 'ASCII': case 'UTF8': case 'UTF-8': @@ -336,73 +483,141 @@ public static function mb_detect_order($encodingList = null) public static function mb_strlen($s, $encoding = null) { - switch ($encoding = self::getEncoding($encoding)) { - case 'ASCII': - case 'CP850': - return strlen($s); + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return \strlen($s); } - return @iconv_strlen($s, $encoding); + return @\iconv_strlen($s, $encoding); } public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } - if ('' === $needle .= '') { - trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + $needle = (string) $needle; + if ('' === $needle) { + if (80000 > \PHP_VERSION_ID) { + trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING); - return false; + return false; + } + + return 0; } - return iconv_strpos($haystack, $needle, $offset, $encoding); + return \iconv_strpos($haystack, $needle, $offset, $encoding); } public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } if ($offset != (int) $offset) { $offset = 0; } elseif ($offset = (int) $offset) { if ($offset < 0) { - $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + if (0 > $offset += self::mb_strlen($needle)) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + } $offset = 0; } else { $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); } } - $pos = iconv_strrpos($haystack, $needle, $encoding); + $pos = '' !== $needle || 80000 > \PHP_VERSION_ID + ? \iconv_strrpos($haystack, $needle, $encoding) + : self::mb_strlen($haystack, $encoding); return false !== $pos ? $offset + $pos : false; } + public static function mb_str_split($string, $split_length = 1, $encoding = null) + { + if (null !== $string && !is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { + trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); + + return null; + } + + if (1 > $split_length = (int) $split_length) { + if (80000 > \PHP_VERSION_ID) { + trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); + return false; + } + + throw new \ValueError('Argument #2 ($length) must be greater than 0'); + } + + if (null === $encoding) { + $encoding = mb_internal_encoding(); + } + + if ('UTF-8' === $encoding = self::getEncoding($encoding)) { + $rx = '/('; + while (65535 < $split_length) { + $rx .= '.{65535}'; + $split_length -= 65535; + } + $rx .= '.{'.$split_length.'})/us'; + + return preg_split($rx, $string, null, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); + } + + $result = []; + $length = mb_strlen($string, $encoding); + + for ($i = 0; $i < $length; $i += $split_length) { + $result[] = mb_substr($string, $i, $split_length, $encoding); + } + + return $result; + } + public static function mb_strtolower($s, $encoding = null) { - return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding); } public static function mb_strtoupper($s, $encoding = null) { - return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding); } public static function mb_substitute_character($c = null) { + if (null === $c) { + return 'none'; + } if (0 === strcasecmp($c, 'none')) { return true; } + if (80000 > \PHP_VERSION_ID) { + return false; + } + if (\is_int($c) || 'long' === $c || 'entity' === $c) { + return false; + } - return null !== $c ? false : 'none'; + throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint'); } public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return (string) substr($s, $start, null === $length ? 2147483647 : $length); + } if ($start < 0) { - $start = iconv_strlen($s, $encoding) + $start; + $start = \iconv_strlen($s, $encoding) + $start; if ($start < 0) { $start = 0; } @@ -411,13 +626,13 @@ public static function mb_substr($s, $start, $length = null, $encoding = null) if (null === $length) { $length = 2147483647; } elseif ($length < 0) { - $length = iconv_strlen($s, $encoding) + $length - $start; + $length = \iconv_strlen($s, $encoding) + $length - $start; if ($length < 0) { return ''; } } - return iconv_substr($s, $start, $length, $encoding).''; + return (string) \iconv_substr($s, $start, $length, $encoding); } public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) @@ -438,8 +653,12 @@ public static function mb_stristr($haystack, $needle, $part = false, $encoding = public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) { $encoding = self::getEncoding($encoding); - $needle = self::mb_substr($needle, 0, 1, $encoding); - $pos = iconv_strrpos($haystack, $needle, $encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + $pos = strrpos($haystack, $needle); + } else { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = \iconv_strrpos($haystack, $needle, $encoding); + } return self::getSubpart($pos, $part, $haystack, $encoding); } @@ -475,7 +694,7 @@ public static function mb_strstr($haystack, $needle, $part = false, $encoding = public static function mb_get_info($type = 'all') { - $info = array( + $info = [ 'internal_encoding' => self::$internalEncoding, 'http_output' => 'pass', 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', @@ -490,7 +709,7 @@ public static function mb_get_info($type = 'all') 'detect_order' => self::$encodingList, 'substitute_character' => 'none', 'strict_detection' => 'Off', - ); + ]; if ('all' === $type) { return $info; @@ -517,12 +736,12 @@ public static function mb_strwidth($s, $encoding = null) $encoding = self::getEncoding($encoding); if ('UTF-8' !== $encoding) { - $s = iconv($encoding, 'UTF-8//IGNORE', $s); + $s = \iconv($encoding, 'UTF-8//IGNORE', $s); } $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); - return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + return ($wide << 1) + \iconv_strlen($s, 'UTF-8'); } public static function mb_substr_count($haystack, $needle, $encoding = null) @@ -538,13 +757,13 @@ public static function mb_output_handler($contents, $status) public static function mb_chr($code, $encoding = null) { if (0x80 > $code %= 0x200000) { - $s = chr($code); + $s = \chr($code); } elseif (0x800 > $code) { - $s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F); + $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); } elseif (0x10000 > $code) { - $s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); + $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } else { - $s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F); + $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { @@ -560,6 +779,10 @@ public static function mb_ord($s, $encoding = null) $s = mb_convert_encoding($s, 'UTF-8', $encoding); } + if (1 === \strlen($s)) { + return \ord($s); + } + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; if (0xF0 <= $code) { return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; @@ -586,15 +809,15 @@ private static function getSubpart($pos, $part, $haystack, $encoding) return self::mb_substr($haystack, $pos, null, $encoding); } - private static function html_encoding_callback($m) + private static function html_encoding_callback(array $m) { $i = 1; $entities = ''; - $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); while (isset($m[$i])) { if (0x80 > $m[$i]) { - $entities .= chr($m[$i++]); + $entities .= \chr($m[$i++]); continue; } if (0xF0 <= $m[$i]) { @@ -611,14 +834,9 @@ private static function html_encoding_callback($m) return $entities; } - private static function title_case_lower($s) + private static function title_case(array $s) { - return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); - } - - private static function title_case_upper($s) - { - return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); + return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8'); } private static function getData($file) @@ -636,11 +854,16 @@ private static function getEncoding($encoding) return self::$internalEncoding; } + if ('UTF-8' === $encoding) { + return 'UTF-8'; + } + $encoding = strtoupper($encoding); if ('8BIT' === $encoding || 'BINARY' === $encoding) { return 'CP850'; } + if ('UTF8' === $encoding) { return 'UTF-8'; } diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/README.md b/tests/integration/vendor/symfony/polyfill-mbstring/README.md index 342e8286d..4efb599d8 100644 --- a/tests/integration/vendor/symfony/polyfill-mbstring/README.md +++ b/tests/integration/vendor/symfony/polyfill-mbstring/README.md @@ -2,7 +2,7 @@ Symfony Polyfill / Mbstring =========================== This component provides a partial, native PHP implementation for the -[Mbstring](http://php.net/mbstring) extension. +[Mbstring](https://php.net/mbstring) extension. More information can be found in the [main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php index 3ca16416a..fac60b081 100644 --- a/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +++ b/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -1,6 +1,6 @@ 'a', 'B' => 'b', 'C' => 'c', @@ -81,7 +81,7 @@ 'Ī' => 'ī', 'Ĭ' => 'ĭ', 'Į' => 'į', - 'İ' => 'i', + 'İ' => 'i̇', 'IJ' => 'ij', 'Ĵ' => 'ĵ', 'Ķ' => 'ķ', @@ -510,6 +510,138 @@ 'Ⴥ' => 'ⴥ', 'Ⴧ' => 'ⴧ', 'Ⴭ' => 'ⴭ', + 'Ꭰ' => 'ꭰ', + 'Ꭱ' => 'ꭱ', + 'Ꭲ' => 'ꭲ', + 'Ꭳ' => 'ꭳ', + 'Ꭴ' => 'ꭴ', + 'Ꭵ' => 'ꭵ', + 'Ꭶ' => 'ꭶ', + 'Ꭷ' => 'ꭷ', + 'Ꭸ' => 'ꭸ', + 'Ꭹ' => 'ꭹ', + 'Ꭺ' => 'ꭺ', + 'Ꭻ' => 'ꭻ', + 'Ꭼ' => 'ꭼ', + 'Ꭽ' => 'ꭽ', + 'Ꭾ' => 'ꭾ', + 'Ꭿ' => 'ꭿ', + 'Ꮀ' => 'ꮀ', + 'Ꮁ' => 'ꮁ', + 'Ꮂ' => 'ꮂ', + 'Ꮃ' => 'ꮃ', + 'Ꮄ' => 'ꮄ', + 'Ꮅ' => 'ꮅ', + 'Ꮆ' => 'ꮆ', + 'Ꮇ' => 'ꮇ', + 'Ꮈ' => 'ꮈ', + 'Ꮉ' => 'ꮉ', + 'Ꮊ' => 'ꮊ', + 'Ꮋ' => 'ꮋ', + 'Ꮌ' => 'ꮌ', + 'Ꮍ' => 'ꮍ', + 'Ꮎ' => 'ꮎ', + 'Ꮏ' => 'ꮏ', + 'Ꮐ' => 'ꮐ', + 'Ꮑ' => 'ꮑ', + 'Ꮒ' => 'ꮒ', + 'Ꮓ' => 'ꮓ', + 'Ꮔ' => 'ꮔ', + 'Ꮕ' => 'ꮕ', + 'Ꮖ' => 'ꮖ', + 'Ꮗ' => 'ꮗ', + 'Ꮘ' => 'ꮘ', + 'Ꮙ' => 'ꮙ', + 'Ꮚ' => 'ꮚ', + 'Ꮛ' => 'ꮛ', + 'Ꮜ' => 'ꮜ', + 'Ꮝ' => 'ꮝ', + 'Ꮞ' => 'ꮞ', + 'Ꮟ' => 'ꮟ', + 'Ꮠ' => 'ꮠ', + 'Ꮡ' => 'ꮡ', + 'Ꮢ' => 'ꮢ', + 'Ꮣ' => 'ꮣ', + 'Ꮤ' => 'ꮤ', + 'Ꮥ' => 'ꮥ', + 'Ꮦ' => 'ꮦ', + 'Ꮧ' => 'ꮧ', + 'Ꮨ' => 'ꮨ', + 'Ꮩ' => 'ꮩ', + 'Ꮪ' => 'ꮪ', + 'Ꮫ' => 'ꮫ', + 'Ꮬ' => 'ꮬ', + 'Ꮭ' => 'ꮭ', + 'Ꮮ' => 'ꮮ', + 'Ꮯ' => 'ꮯ', + 'Ꮰ' => 'ꮰ', + 'Ꮱ' => 'ꮱ', + 'Ꮲ' => 'ꮲ', + 'Ꮳ' => 'ꮳ', + 'Ꮴ' => 'ꮴ', + 'Ꮵ' => 'ꮵ', + 'Ꮶ' => 'ꮶ', + 'Ꮷ' => 'ꮷ', + 'Ꮸ' => 'ꮸ', + 'Ꮹ' => 'ꮹ', + 'Ꮺ' => 'ꮺ', + 'Ꮻ' => 'ꮻ', + 'Ꮼ' => 'ꮼ', + 'Ꮽ' => 'ꮽ', + 'Ꮾ' => 'ꮾ', + 'Ꮿ' => 'ꮿ', + 'Ᏸ' => 'ᏸ', + 'Ᏹ' => 'ᏹ', + 'Ᏺ' => 'ᏺ', + 'Ᏻ' => 'ᏻ', + 'Ᏼ' => 'ᏼ', + 'Ᏽ' => 'ᏽ', + 'Ა' => 'ა', + 'Ბ' => 'ბ', + 'Გ' => 'გ', + 'Დ' => 'დ', + 'Ე' => 'ე', + 'Ვ' => 'ვ', + 'Ზ' => 'ზ', + 'Თ' => 'თ', + 'Ი' => 'ი', + 'Კ' => 'კ', + 'Ლ' => 'ლ', + 'Მ' => 'მ', + 'Ნ' => 'ნ', + 'Ო' => 'ო', + 'Პ' => 'პ', + 'Ჟ' => 'ჟ', + 'Რ' => 'რ', + 'Ს' => 'ს', + 'Ტ' => 'ტ', + 'Უ' => 'უ', + 'Ფ' => 'ფ', + 'Ქ' => 'ქ', + 'Ღ' => 'ღ', + 'Ყ' => 'ყ', + 'Შ' => 'შ', + 'Ჩ' => 'ჩ', + 'Ც' => 'ც', + 'Ძ' => 'ძ', + 'Წ' => 'წ', + 'Ჭ' => 'ჭ', + 'Ხ' => 'ხ', + 'Ჯ' => 'ჯ', + 'Ჰ' => 'ჰ', + 'Ჱ' => 'ჱ', + 'Ჲ' => 'ჲ', + 'Ჳ' => 'ჳ', + 'Ჴ' => 'ჴ', + 'Ჵ' => 'ჵ', + 'Ჶ' => 'ჶ', + 'Ჷ' => 'ჷ', + 'Ჸ' => 'ჸ', + 'Ჹ' => 'ჹ', + 'Ჺ' => 'ჺ', + 'Ჽ' => 'ჽ', + 'Ჾ' => 'ჾ', + 'Ჿ' => 'ჿ', 'Ḁ' => 'ḁ', 'Ḃ' => 'ḃ', 'Ḅ' => 'ḅ', @@ -993,8 +1125,24 @@ 'Ɜ' => 'ɜ', 'Ɡ' => 'ɡ', 'Ɬ' => 'ɬ', + 'Ɪ' => 'ɪ', 'Ʞ' => 'ʞ', 'Ʇ' => 'ʇ', + 'Ʝ' => 'ʝ', + 'Ꭓ' => 'ꭓ', + 'Ꞵ' => 'ꞵ', + 'Ꞷ' => 'ꞷ', + 'Ꞹ' => 'ꞹ', + 'Ꞻ' => 'ꞻ', + 'Ꞽ' => 'ꞽ', + 'Ꞿ' => 'ꞿ', + 'Ꟃ' => 'ꟃ', + 'Ꞔ' => 'ꞔ', + 'Ʂ' => 'ʂ', + 'Ᶎ' => 'ᶎ', + 'Ꟈ' => 'ꟈ', + 'Ꟊ' => 'ꟊ', + 'Ꟶ' => 'ꟶ', 'A' => 'a', 'B' => 'b', 'C' => 'c', @@ -1061,6 +1209,93 @@ '𐐥' => '𐑍', '𐐦' => '𐑎', '𐐧' => '𐑏', + '𐒰' => '𐓘', + '𐒱' => '𐓙', + '𐒲' => '𐓚', + '𐒳' => '𐓛', + '𐒴' => '𐓜', + '𐒵' => '𐓝', + '𐒶' => '𐓞', + '𐒷' => '𐓟', + '𐒸' => '𐓠', + '𐒹' => '𐓡', + '𐒺' => '𐓢', + '𐒻' => '𐓣', + '𐒼' => '𐓤', + '𐒽' => '𐓥', + '𐒾' => '𐓦', + '𐒿' => '𐓧', + '𐓀' => '𐓨', + '𐓁' => '𐓩', + '𐓂' => '𐓪', + '𐓃' => '𐓫', + '𐓄' => '𐓬', + '𐓅' => '𐓭', + '𐓆' => '𐓮', + '𐓇' => '𐓯', + '𐓈' => '𐓰', + '𐓉' => '𐓱', + '𐓊' => '𐓲', + '𐓋' => '𐓳', + '𐓌' => '𐓴', + '𐓍' => '𐓵', + '𐓎' => '𐓶', + '𐓏' => '𐓷', + '𐓐' => '𐓸', + '𐓑' => '𐓹', + '𐓒' => '𐓺', + '𐓓' => '𐓻', + '𐲀' => '𐳀', + '𐲁' => '𐳁', + '𐲂' => '𐳂', + '𐲃' => '𐳃', + '𐲄' => '𐳄', + '𐲅' => '𐳅', + '𐲆' => '𐳆', + '𐲇' => '𐳇', + '𐲈' => '𐳈', + '𐲉' => '𐳉', + '𐲊' => '𐳊', + '𐲋' => '𐳋', + '𐲌' => '𐳌', + '𐲍' => '𐳍', + '𐲎' => '𐳎', + '𐲏' => '𐳏', + '𐲐' => '𐳐', + '𐲑' => '𐳑', + '𐲒' => '𐳒', + '𐲓' => '𐳓', + '𐲔' => '𐳔', + '𐲕' => '𐳕', + '𐲖' => '𐳖', + '𐲗' => '𐳗', + '𐲘' => '𐳘', + '𐲙' => '𐳙', + '𐲚' => '𐳚', + '𐲛' => '𐳛', + '𐲜' => '𐳜', + '𐲝' => '𐳝', + '𐲞' => '𐳞', + '𐲟' => '𐳟', + '𐲠' => '𐳠', + '𐲡' => '𐳡', + '𐲢' => '𐳢', + '𐲣' => '𐳣', + '𐲤' => '𐳤', + '𐲥' => '𐳥', + '𐲦' => '𐳦', + '𐲧' => '𐳧', + '𐲨' => '𐳨', + '𐲩' => '𐳩', + '𐲪' => '𐳪', + '𐲫' => '𐳫', + '𐲬' => '𐳬', + '𐲭' => '𐳭', + '𐲮' => '𐳮', + '𐲯' => '𐳯', + '𐲰' => '𐳰', + '𐲱' => '𐳱', + '𐲲' => '𐳲', '𑢠' => '𑣀', '𑢡' => '𑣁', '𑢢' => '𑣂', @@ -1093,9 +1328,70 @@ '𑢽' => '𑣝', '𑢾' => '𑣞', '𑢿' => '𑣟', + '𖹀' => '𖹠', + '𖹁' => '𖹡', + '𖹂' => '𖹢', + '𖹃' => '𖹣', + '𖹄' => '𖹤', + '𖹅' => '𖹥', + '𖹆' => '𖹦', + '𖹇' => '𖹧', + '𖹈' => '𖹨', + '𖹉' => '𖹩', + '𖹊' => '𖹪', + '𖹋' => '𖹫', + '𖹌' => '𖹬', + '𖹍' => '𖹭', + '𖹎' => '𖹮', + '𖹏' => '𖹯', + '𖹐' => '𖹰', + '𖹑' => '𖹱', + '𖹒' => '𖹲', + '𖹓' => '𖹳', + '𖹔' => '𖹴', + '𖹕' => '𖹵', + '𖹖' => '𖹶', + '𖹗' => '𖹷', + '𖹘' => '𖹸', + '𖹙' => '𖹹', + '𖹚' => '𖹺', + '𖹛' => '𖹻', + '𖹜' => '𖹼', + '𖹝' => '𖹽', + '𖹞' => '𖹾', + '𖹟' => '𖹿', + '𞤀' => '𞤢', + '𞤁' => '𞤣', + '𞤂' => '𞤤', + '𞤃' => '𞤥', + '𞤄' => '𞤦', + '𞤅' => '𞤧', + '𞤆' => '𞤨', + '𞤇' => '𞤩', + '𞤈' => '𞤪', + '𞤉' => '𞤫', + '𞤊' => '𞤬', + '𞤋' => '𞤭', + '𞤌' => '𞤮', + '𞤍' => '𞤯', + '𞤎' => '𞤰', + '𞤏' => '𞤱', + '𞤐' => '𞤲', + '𞤑' => '𞤳', + '𞤒' => '𞤴', + '𞤓' => '𞤵', + '𞤔' => '𞤶', + '𞤕' => '𞤷', + '𞤖' => '𞤸', + '𞤗' => '𞤹', + '𞤘' => '𞤺', + '𞤙' => '𞤻', + '𞤚' => '𞤼', + '𞤛' => '𞤽', + '𞤜' => '𞤾', + '𞤝' => '𞤿', + '𞤞' => '𞥀', + '𞤟' => '𞥁', + '𞤠' => '𞥂', + '𞤡' => '𞥃', ); - -$result =& $data; -unset($data); - -return $result; diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php new file mode 100644 index 000000000..2a8f6e73b --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php @@ -0,0 +1,5 @@ + 'A', 'b' => 'B', 'c' => 'C', @@ -225,6 +225,7 @@ 'ɦ' => 'Ɦ', 'ɨ' => 'Ɨ', 'ɩ' => 'Ɩ', + 'ɪ' => 'Ɪ', 'ɫ' => 'Ɫ', 'ɬ' => 'Ɬ', 'ɯ' => 'Ɯ', @@ -233,6 +234,7 @@ 'ɵ' => 'Ɵ', 'ɽ' => 'Ɽ', 'ʀ' => 'Ʀ', + 'ʂ' => 'Ʂ', 'ʃ' => 'Ʃ', 'ʇ' => 'Ʇ', 'ʈ' => 'Ʈ', @@ -241,6 +243,7 @@ 'ʋ' => 'Ʋ', 'ʌ' => 'Ʌ', 'ʒ' => 'Ʒ', + 'ʝ' => 'Ʝ', 'ʞ' => 'Ʞ', 'ͅ' => 'Ι', 'ͱ' => 'Ͱ', @@ -493,8 +496,70 @@ 'ք' => 'Ք', 'օ' => 'Օ', 'ֆ' => 'Ֆ', + 'ა' => 'Ა', + 'ბ' => 'Ბ', + 'გ' => 'Გ', + 'დ' => 'Დ', + 'ე' => 'Ე', + 'ვ' => 'Ვ', + 'ზ' => 'Ზ', + 'თ' => 'Თ', + 'ი' => 'Ი', + 'კ' => 'Კ', + 'ლ' => 'Ლ', + 'მ' => 'Მ', + 'ნ' => 'Ნ', + 'ო' => 'Ო', + 'პ' => 'Პ', + 'ჟ' => 'Ჟ', + 'რ' => 'Რ', + 'ს' => 'Ს', + 'ტ' => 'Ტ', + 'უ' => 'Უ', + 'ფ' => 'Ფ', + 'ქ' => 'Ქ', + 'ღ' => 'Ღ', + 'ყ' => 'Ყ', + 'შ' => 'Შ', + 'ჩ' => 'Ჩ', + 'ც' => 'Ც', + 'ძ' => 'Ძ', + 'წ' => 'Წ', + 'ჭ' => 'Ჭ', + 'ხ' => 'Ხ', + 'ჯ' => 'Ჯ', + 'ჰ' => 'Ჰ', + 'ჱ' => 'Ჱ', + 'ჲ' => 'Ჲ', + 'ჳ' => 'Ჳ', + 'ჴ' => 'Ჴ', + 'ჵ' => 'Ჵ', + 'ჶ' => 'Ჶ', + 'ჷ' => 'Ჷ', + 'ჸ' => 'Ჸ', + 'ჹ' => 'Ჹ', + 'ჺ' => 'Ჺ', + 'ჽ' => 'Ჽ', + 'ჾ' => 'Ჾ', + 'ჿ' => 'Ჿ', + 'ᏸ' => 'Ᏸ', + 'ᏹ' => 'Ᏹ', + 'ᏺ' => 'Ᏺ', + 'ᏻ' => 'Ᏻ', + 'ᏼ' => 'Ᏼ', + 'ᏽ' => 'Ᏽ', + 'ᲀ' => 'В', + 'ᲁ' => 'Д', + 'ᲂ' => 'О', + 'ᲃ' => 'С', + 'ᲄ' => 'Т', + 'ᲅ' => 'Т', + 'ᲆ' => 'Ъ', + 'ᲇ' => 'Ѣ', + 'ᲈ' => 'Ꙋ', 'ᵹ' => 'Ᵹ', 'ᵽ' => 'Ᵽ', + 'ᶎ' => 'Ᶎ', 'ḁ' => 'Ḁ', 'ḃ' => 'Ḃ', 'ḅ' => 'Ḅ', @@ -681,41 +746,41 @@ 'ύ' => 'Ύ', 'ὼ' => 'Ὼ', 'ώ' => 'Ώ', - 'ᾀ' => 'ᾈ', - 'ᾁ' => 'ᾉ', - 'ᾂ' => 'ᾊ', - 'ᾃ' => 'ᾋ', - 'ᾄ' => 'ᾌ', - 'ᾅ' => 'ᾍ', - 'ᾆ' => 'ᾎ', - 'ᾇ' => 'ᾏ', - 'ᾐ' => 'ᾘ', - 'ᾑ' => 'ᾙ', - 'ᾒ' => 'ᾚ', - 'ᾓ' => 'ᾛ', - 'ᾔ' => 'ᾜ', - 'ᾕ' => 'ᾝ', - 'ᾖ' => 'ᾞ', - 'ᾗ' => 'ᾟ', - 'ᾠ' => 'ᾨ', - 'ᾡ' => 'ᾩ', - 'ᾢ' => 'ᾪ', - 'ᾣ' => 'ᾫ', - 'ᾤ' => 'ᾬ', - 'ᾥ' => 'ᾭ', - 'ᾦ' => 'ᾮ', - 'ᾧ' => 'ᾯ', + 'ᾀ' => 'ἈΙ', + 'ᾁ' => 'ἉΙ', + 'ᾂ' => 'ἊΙ', + 'ᾃ' => 'ἋΙ', + 'ᾄ' => 'ἌΙ', + 'ᾅ' => 'ἍΙ', + 'ᾆ' => 'ἎΙ', + 'ᾇ' => 'ἏΙ', + 'ᾐ' => 'ἨΙ', + 'ᾑ' => 'ἩΙ', + 'ᾒ' => 'ἪΙ', + 'ᾓ' => 'ἫΙ', + 'ᾔ' => 'ἬΙ', + 'ᾕ' => 'ἭΙ', + 'ᾖ' => 'ἮΙ', + 'ᾗ' => 'ἯΙ', + 'ᾠ' => 'ὨΙ', + 'ᾡ' => 'ὩΙ', + 'ᾢ' => 'ὪΙ', + 'ᾣ' => 'ὫΙ', + 'ᾤ' => 'ὬΙ', + 'ᾥ' => 'ὭΙ', + 'ᾦ' => 'ὮΙ', + 'ᾧ' => 'ὯΙ', 'ᾰ' => 'Ᾰ', 'ᾱ' => 'Ᾱ', - 'ᾳ' => 'ᾼ', + 'ᾳ' => 'ΑΙ', 'ι' => 'Ι', - 'ῃ' => 'ῌ', + 'ῃ' => 'ΗΙ', 'ῐ' => 'Ῐ', 'ῑ' => 'Ῑ', 'ῠ' => 'Ῠ', 'ῡ' => 'Ῡ', 'ῥ' => 'Ῥ', - 'ῳ' => 'ῼ', + 'ῳ' => 'ΩΙ', 'ⅎ' => 'Ⅎ', 'ⅰ' => 'Ⅰ', 'ⅱ' => 'Ⅱ', @@ -993,6 +1058,7 @@ 'ꞌ' => 'Ꞌ', 'ꞑ' => 'Ꞑ', 'ꞓ' => 'Ꞓ', + 'ꞔ' => 'Ꞔ', 'ꞗ' => 'Ꞗ', 'ꞙ' => 'Ꞙ', 'ꞛ' => 'Ꞛ', @@ -1003,6 +1069,97 @@ 'ꞥ' => 'Ꞥ', 'ꞧ' => 'Ꞧ', 'ꞩ' => 'Ꞩ', + 'ꞵ' => 'Ꞵ', + 'ꞷ' => 'Ꞷ', + 'ꞹ' => 'Ꞹ', + 'ꞻ' => 'Ꞻ', + 'ꞽ' => 'Ꞽ', + 'ꞿ' => 'Ꞿ', + 'ꟃ' => 'Ꟃ', + 'ꟈ' => 'Ꟈ', + 'ꟊ' => 'Ꟊ', + 'ꟶ' => 'Ꟶ', + 'ꭓ' => 'Ꭓ', + 'ꭰ' => 'Ꭰ', + 'ꭱ' => 'Ꭱ', + 'ꭲ' => 'Ꭲ', + 'ꭳ' => 'Ꭳ', + 'ꭴ' => 'Ꭴ', + 'ꭵ' => 'Ꭵ', + 'ꭶ' => 'Ꭶ', + 'ꭷ' => 'Ꭷ', + 'ꭸ' => 'Ꭸ', + 'ꭹ' => 'Ꭹ', + 'ꭺ' => 'Ꭺ', + 'ꭻ' => 'Ꭻ', + 'ꭼ' => 'Ꭼ', + 'ꭽ' => 'Ꭽ', + 'ꭾ' => 'Ꭾ', + 'ꭿ' => 'Ꭿ', + 'ꮀ' => 'Ꮀ', + 'ꮁ' => 'Ꮁ', + 'ꮂ' => 'Ꮂ', + 'ꮃ' => 'Ꮃ', + 'ꮄ' => 'Ꮄ', + 'ꮅ' => 'Ꮅ', + 'ꮆ' => 'Ꮆ', + 'ꮇ' => 'Ꮇ', + 'ꮈ' => 'Ꮈ', + 'ꮉ' => 'Ꮉ', + 'ꮊ' => 'Ꮊ', + 'ꮋ' => 'Ꮋ', + 'ꮌ' => 'Ꮌ', + 'ꮍ' => 'Ꮍ', + 'ꮎ' => 'Ꮎ', + 'ꮏ' => 'Ꮏ', + 'ꮐ' => 'Ꮐ', + 'ꮑ' => 'Ꮑ', + 'ꮒ' => 'Ꮒ', + 'ꮓ' => 'Ꮓ', + 'ꮔ' => 'Ꮔ', + 'ꮕ' => 'Ꮕ', + 'ꮖ' => 'Ꮖ', + 'ꮗ' => 'Ꮗ', + 'ꮘ' => 'Ꮘ', + 'ꮙ' => 'Ꮙ', + 'ꮚ' => 'Ꮚ', + 'ꮛ' => 'Ꮛ', + 'ꮜ' => 'Ꮜ', + 'ꮝ' => 'Ꮝ', + 'ꮞ' => 'Ꮞ', + 'ꮟ' => 'Ꮟ', + 'ꮠ' => 'Ꮠ', + 'ꮡ' => 'Ꮡ', + 'ꮢ' => 'Ꮢ', + 'ꮣ' => 'Ꮣ', + 'ꮤ' => 'Ꮤ', + 'ꮥ' => 'Ꮥ', + 'ꮦ' => 'Ꮦ', + 'ꮧ' => 'Ꮧ', + 'ꮨ' => 'Ꮨ', + 'ꮩ' => 'Ꮩ', + 'ꮪ' => 'Ꮪ', + 'ꮫ' => 'Ꮫ', + 'ꮬ' => 'Ꮬ', + 'ꮭ' => 'Ꮭ', + 'ꮮ' => 'Ꮮ', + 'ꮯ' => 'Ꮯ', + 'ꮰ' => 'Ꮰ', + 'ꮱ' => 'Ꮱ', + 'ꮲ' => 'Ꮲ', + 'ꮳ' => 'Ꮳ', + 'ꮴ' => 'Ꮴ', + 'ꮵ' => 'Ꮵ', + 'ꮶ' => 'Ꮶ', + 'ꮷ' => 'Ꮷ', + 'ꮸ' => 'Ꮸ', + 'ꮹ' => 'Ꮹ', + 'ꮺ' => 'Ꮺ', + 'ꮻ' => 'Ꮻ', + 'ꮼ' => 'Ꮼ', + 'ꮽ' => 'Ꮽ', + 'ꮾ' => 'Ꮾ', + 'ꮿ' => 'Ꮿ', 'a' => 'A', 'b' => 'B', 'c' => 'C', @@ -1069,6 +1226,93 @@ '𐑍' => '𐐥', '𐑎' => '𐐦', '𐑏' => '𐐧', + '𐓘' => '𐒰', + '𐓙' => '𐒱', + '𐓚' => '𐒲', + '𐓛' => '𐒳', + '𐓜' => '𐒴', + '𐓝' => '𐒵', + '𐓞' => '𐒶', + '𐓟' => '𐒷', + '𐓠' => '𐒸', + '𐓡' => '𐒹', + '𐓢' => '𐒺', + '𐓣' => '𐒻', + '𐓤' => '𐒼', + '𐓥' => '𐒽', + '𐓦' => '𐒾', + '𐓧' => '𐒿', + '𐓨' => '𐓀', + '𐓩' => '𐓁', + '𐓪' => '𐓂', + '𐓫' => '𐓃', + '𐓬' => '𐓄', + '𐓭' => '𐓅', + '𐓮' => '𐓆', + '𐓯' => '𐓇', + '𐓰' => '𐓈', + '𐓱' => '𐓉', + '𐓲' => '𐓊', + '𐓳' => '𐓋', + '𐓴' => '𐓌', + '𐓵' => '𐓍', + '𐓶' => '𐓎', + '𐓷' => '𐓏', + '𐓸' => '𐓐', + '𐓹' => '𐓑', + '𐓺' => '𐓒', + '𐓻' => '𐓓', + '𐳀' => '𐲀', + '𐳁' => '𐲁', + '𐳂' => '𐲂', + '𐳃' => '𐲃', + '𐳄' => '𐲄', + '𐳅' => '𐲅', + '𐳆' => '𐲆', + '𐳇' => '𐲇', + '𐳈' => '𐲈', + '𐳉' => '𐲉', + '𐳊' => '𐲊', + '𐳋' => '𐲋', + '𐳌' => '𐲌', + '𐳍' => '𐲍', + '𐳎' => '𐲎', + '𐳏' => '𐲏', + '𐳐' => '𐲐', + '𐳑' => '𐲑', + '𐳒' => '𐲒', + '𐳓' => '𐲓', + '𐳔' => '𐲔', + '𐳕' => '𐲕', + '𐳖' => '𐲖', + '𐳗' => '𐲗', + '𐳘' => '𐲘', + '𐳙' => '𐲙', + '𐳚' => '𐲚', + '𐳛' => '𐲛', + '𐳜' => '𐲜', + '𐳝' => '𐲝', + '𐳞' => '𐲞', + '𐳟' => '𐲟', + '𐳠' => '𐲠', + '𐳡' => '𐲡', + '𐳢' => '𐲢', + '𐳣' => '𐲣', + '𐳤' => '𐲤', + '𐳥' => '𐲥', + '𐳦' => '𐲦', + '𐳧' => '𐲧', + '𐳨' => '𐲨', + '𐳩' => '𐲩', + '𐳪' => '𐲪', + '𐳫' => '𐲫', + '𐳬' => '𐲬', + '𐳭' => '𐲭', + '𐳮' => '𐲮', + '𐳯' => '𐲯', + '𐳰' => '𐲰', + '𐳱' => '𐲱', + '𐳲' => '𐲲', '𑣀' => '𑢠', '𑣁' => '𑢡', '𑣂' => '𑢢', @@ -1101,9 +1345,145 @@ '𑣝' => '𑢽', '𑣞' => '𑢾', '𑣟' => '𑢿', + '𖹠' => '𖹀', + '𖹡' => '𖹁', + '𖹢' => '𖹂', + '𖹣' => '𖹃', + '𖹤' => '𖹄', + '𖹥' => '𖹅', + '𖹦' => '𖹆', + '𖹧' => '𖹇', + '𖹨' => '𖹈', + '𖹩' => '𖹉', + '𖹪' => '𖹊', + '𖹫' => '𖹋', + '𖹬' => '𖹌', + '𖹭' => '𖹍', + '𖹮' => '𖹎', + '𖹯' => '𖹏', + '𖹰' => '𖹐', + '𖹱' => '𖹑', + '𖹲' => '𖹒', + '𖹳' => '𖹓', + '𖹴' => '𖹔', + '𖹵' => '𖹕', + '𖹶' => '𖹖', + '𖹷' => '𖹗', + '𖹸' => '𖹘', + '𖹹' => '𖹙', + '𖹺' => '𖹚', + '𖹻' => '𖹛', + '𖹼' => '𖹜', + '𖹽' => '𖹝', + '𖹾' => '𖹞', + '𖹿' => '𖹟', + '𞤢' => '𞤀', + '𞤣' => '𞤁', + '𞤤' => '𞤂', + '𞤥' => '𞤃', + '𞤦' => '𞤄', + '𞤧' => '𞤅', + '𞤨' => '𞤆', + '𞤩' => '𞤇', + '𞤪' => '𞤈', + '𞤫' => '𞤉', + '𞤬' => '𞤊', + '𞤭' => '𞤋', + '𞤮' => '𞤌', + '𞤯' => '𞤍', + '𞤰' => '𞤎', + '𞤱' => '𞤏', + '𞤲' => '𞤐', + '𞤳' => '𞤑', + '𞤴' => '𞤒', + '𞤵' => '𞤓', + '𞤶' => '𞤔', + '𞤷' => '𞤕', + '𞤸' => '𞤖', + '𞤹' => '𞤗', + '𞤺' => '𞤘', + '𞤻' => '𞤙', + '𞤼' => '𞤚', + '𞤽' => '𞤛', + '𞤾' => '𞤜', + '𞤿' => '𞤝', + '𞥀' => '𞤞', + '𞥁' => '𞤟', + '𞥂' => '𞤠', + '𞥃' => '𞤡', + 'ß' => 'SS', + 'ff' => 'FF', + 'fi' => 'FI', + 'fl' => 'FL', + 'ffi' => 'FFI', + 'ffl' => 'FFL', + 'ſt' => 'ST', + 'st' => 'ST', + 'և' => 'ԵՒ', + 'ﬓ' => 'ՄՆ', + 'ﬔ' => 'ՄԵ', + 'ﬕ' => 'ՄԻ', + 'ﬖ' => 'ՎՆ', + 'ﬗ' => 'ՄԽ', + 'ʼn' => 'ʼN', + 'ΐ' => 'Ϊ́', + 'ΰ' => 'Ϋ́', + 'ǰ' => 'J̌', + 'ẖ' => 'H̱', + 'ẗ' => 'T̈', + 'ẘ' => 'W̊', + 'ẙ' => 'Y̊', + 'ẚ' => 'Aʾ', + 'ὐ' => 'Υ̓', + 'ὒ' => 'Υ̓̀', + 'ὔ' => 'Υ̓́', + 'ὖ' => 'Υ̓͂', + 'ᾶ' => 'Α͂', + 'ῆ' => 'Η͂', + 'ῒ' => 'Ϊ̀', + 'ΐ' => 'Ϊ́', + 'ῖ' => 'Ι͂', + 'ῗ' => 'Ϊ͂', + 'ῢ' => 'Ϋ̀', + 'ΰ' => 'Ϋ́', + 'ῤ' => 'Ρ̓', + 'ῦ' => 'Υ͂', + 'ῧ' => 'Ϋ͂', + 'ῶ' => 'Ω͂', + 'ᾈ' => 'ἈΙ', + 'ᾉ' => 'ἉΙ', + 'ᾊ' => 'ἊΙ', + 'ᾋ' => 'ἋΙ', + 'ᾌ' => 'ἌΙ', + 'ᾍ' => 'ἍΙ', + 'ᾎ' => 'ἎΙ', + 'ᾏ' => 'ἏΙ', + 'ᾘ' => 'ἨΙ', + 'ᾙ' => 'ἩΙ', + 'ᾚ' => 'ἪΙ', + 'ᾛ' => 'ἫΙ', + 'ᾜ' => 'ἬΙ', + 'ᾝ' => 'ἭΙ', + 'ᾞ' => 'ἮΙ', + 'ᾟ' => 'ἯΙ', + 'ᾨ' => 'ὨΙ', + 'ᾩ' => 'ὩΙ', + 'ᾪ' => 'ὪΙ', + 'ᾫ' => 'ὫΙ', + 'ᾬ' => 'ὬΙ', + 'ᾭ' => 'ὭΙ', + 'ᾮ' => 'ὮΙ', + 'ᾯ' => 'ὯΙ', + 'ᾼ' => 'ΑΙ', + 'ῌ' => 'ΗΙ', + 'ῼ' => 'ΩΙ', + 'ᾲ' => 'ᾺΙ', + 'ᾴ' => 'ΆΙ', + 'ῂ' => 'ῊΙ', + 'ῄ' => 'ΉΙ', + 'ῲ' => 'ῺΙ', + 'ῴ' => 'ΏΙ', + 'ᾷ' => 'Α͂Ι', + 'ῇ' => 'Η͂Ι', + 'ῷ' => 'Ω͂Ι', ); - -$result =& $data; -unset($data); - -return $result; diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap.php b/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap.php index 337229106..1fedd1f7c 100644 --- a/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap.php +++ b/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -11,46 +11,137 @@ use Symfony\Polyfill\Mbstring as p; -if (!function_exists('mb_strlen')) { - define('MB_CASE_UPPER', 0); - define('MB_CASE_LOWER', 1); - define('MB_CASE_TITLE', 2); +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; +} - function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } - function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } - function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } - function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } - function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } - function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language($language = null) { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } - function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } - function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } - function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } - function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } - function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } - function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } - function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } - function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } - function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } - function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } - function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } - function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } - function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } - function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } - function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } - function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } - function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; } +} +if (!function_exists('mb_strlen')) { + function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } - function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } - function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } - function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } - function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } - function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } - function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { - function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } - function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } - function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } + function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); } diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap80.php b/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap80.php new file mode 100644 index 000000000..82f5ac4d0 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-mbstring/bootstrap80.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { + function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { + function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; } +} +if (!function_exists('mb_strlen')) { + function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { + function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); } +} +if (!function_exists('mb_chr')) { + function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); +} diff --git a/tests/integration/vendor/symfony/polyfill-mbstring/composer.json b/tests/integration/vendor/symfony/polyfill-mbstring/composer.json index 24eefbde1..1fa21ca16 100644 --- a/tests/integration/vendor/symfony/polyfill-mbstring/composer.json +++ b/tests/integration/vendor/symfony/polyfill-mbstring/composer.json @@ -16,7 +16,10 @@ } ], "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, @@ -28,7 +31,11 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } } } diff --git a/tests/integration/vendor/symfony/polyfill-php73/LICENSE b/tests/integration/vendor/symfony/polyfill-php73/LICENSE new file mode 100644 index 000000000..3f853aaf3 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php73/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2019 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/polyfill-php73/Php73.php b/tests/integration/vendor/symfony/polyfill-php73/Php73.php new file mode 100644 index 000000000..65c35a6a1 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php73/Php73.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php73; + +/** + * @author Gabriel Caruso + * @author Ion Bazan + * + * @internal + */ +final class Php73 +{ + public static $startAt = 1533462603; + + /** + * @param bool $asNum + * + * @return array|float|int + */ + public static function hrtime($asNum = false) + { + $ns = microtime(false); + $s = substr($ns, 11) - self::$startAt; + $ns = 1E9 * (float) $ns; + + if ($asNum) { + $ns += $s * 1E9; + + return \PHP_INT_SIZE === 4 ? $ns : (int) $ns; + } + + return [$s, (int) $ns]; + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php73/README.md b/tests/integration/vendor/symfony/polyfill-php73/README.md new file mode 100644 index 000000000..b3ebbce51 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php73/README.md @@ -0,0 +1,18 @@ +Symfony Polyfill / Php73 +======================== + +This component provides functions added to PHP 7.3 core: + +- [`array_key_first`](https://php.net/array_key_first) +- [`array_key_last`](https://php.net/array_key_last) +- [`hrtime`](https://php.net/function.hrtime) +- [`is_countable`](https://php.net/is_countable) +- [`JsonException`](https://php.net/JsonException) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/tests/integration/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php b/tests/integration/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php new file mode 100644 index 000000000..f06d6c269 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (\PHP_VERSION_ID < 70300) { + class JsonException extends Exception + { + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php73/bootstrap.php b/tests/integration/vendor/symfony/polyfill-php73/bootstrap.php new file mode 100644 index 000000000..d6b215382 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php73/bootstrap.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php73 as p; + +if (\PHP_VERSION_ID >= 70300) { + return; +} + +if (!function_exists('is_countable')) { + function is_countable($value) { return is_array($value) || $value instanceof Countable || $value instanceof ResourceBundle || $value instanceof SimpleXmlElement; } +} +if (!function_exists('hrtime')) { + require_once __DIR__.'/Php73.php'; + p\Php73::$startAt = (int) microtime(true); + function hrtime($as_number = false) { return p\Php73::hrtime($as_number); } +} +if (!function_exists('array_key_first')) { + function array_key_first(array $array) { foreach ($array as $key => $value) { return $key; } } +} +if (!function_exists('array_key_last')) { + function array_key_last(array $array) { return key(array_slice($array, -1, 1, true)); } +} diff --git a/tests/integration/vendor/symfony/polyfill-php73/composer.json b/tests/integration/vendor/symfony/polyfill-php73/composer.json new file mode 100644 index 000000000..a7fe47875 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php73/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/polyfill-php73", + "type": "library", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php73\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php80/LICENSE b/tests/integration/vendor/symfony/polyfill-php80/LICENSE new file mode 100644 index 000000000..5593b1d84 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/polyfill-php80/Php80.php b/tests/integration/vendor/symfony/polyfill-php80/Php80.php new file mode 100644 index 000000000..362dd1a95 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/Php80.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Ion Bazan + * @author Nico Oelgart + * @author Nicolas Grekas + * + * @internal + */ +final class Php80 +{ + public static function fdiv(float $dividend, float $divisor): float + { + return @($dividend / $divisor); + } + + public static function get_debug_type($value): string + { + switch (true) { + case null === $value: return 'null'; + case \is_bool($value): return 'bool'; + case \is_string($value): return 'string'; + case \is_array($value): return 'array'; + case \is_int($value): return 'int'; + case \is_float($value): return 'float'; + case \is_object($value): break; + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; + default: + if (null === $type = @get_resource_type($value)) { + return 'unknown'; + } + + if ('Unknown' === $type) { + $type = 'closed'; + } + + return "resource ($type)"; + } + + $class = \get_class($value); + + if (false === strpos($class, '@')) { + return $class; + } + + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; + } + + public static function get_resource_id($res): int + { + if (!\is_resource($res) && null === @get_resource_type($res)) { + throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res))); + } + + return (int) $res; + } + + public static function preg_last_error_msg(): string + { + switch (preg_last_error()) { + case \PREG_INTERNAL_ERROR: + return 'Internal error'; + case \PREG_BAD_UTF8_ERROR: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + case \PREG_BAD_UTF8_OFFSET_ERROR: + return 'The offset did not correspond to the beginning of a valid UTF-8 code point'; + case \PREG_BACKTRACK_LIMIT_ERROR: + return 'Backtrack limit exhausted'; + case \PREG_RECURSION_LIMIT_ERROR: + return 'Recursion limit exhausted'; + case \PREG_JIT_STACKLIMIT_ERROR: + return 'JIT stack limit exhausted'; + case \PREG_NO_ERROR: + return 'No error'; + default: + return 'Unknown error'; + } + } + + public static function str_contains(string $haystack, string $needle): bool + { + return '' === $needle || false !== strpos($haystack, $needle); + } + + public static function str_starts_with(string $haystack, string $needle): bool + { + return 0 === strncmp($haystack, $needle, \strlen($needle)); + } + + public static function str_ends_with(string $haystack, string $needle): bool + { + if ('' === $needle || $needle === $haystack) { + return true; + } + + if ('' === $haystack) { + return false; + } + + $needleLength = \strlen($needle); + + return $needleLength <= \strlen($haystack) && 0 === substr_compare($haystack, $needle, -$needleLength); + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php80/PhpToken.php b/tests/integration/vendor/symfony/polyfill-php80/PhpToken.php new file mode 100644 index 000000000..fe6e69105 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/PhpToken.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php80; + +/** + * @author Fedonyuk Anton + * + * @internal + */ +class PhpToken implements \Stringable +{ + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $text; + + /** + * @var int + */ + public $line; + + /** + * @var int + */ + public $pos; + + public function __construct(int $id, string $text, int $line = -1, int $position = -1) + { + $this->id = $id; + $this->text = $text; + $this->line = $line; + $this->pos = $position; + } + + public function getTokenName(): ?string + { + if ('UNKNOWN' === $name = token_name($this->id)) { + $name = \strlen($this->text) > 1 || \ord($this->text) < 32 ? null : $this->text; + } + + return $name; + } + + /** + * @param int|string|array $kind + */ + public function is($kind): bool + { + foreach ((array) $kind as $value) { + if (\in_array($value, [$this->id, $this->text], true)) { + return true; + } + } + + return false; + } + + public function isIgnorable(): bool + { + return \in_array($this->id, [\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT, \T_OPEN_TAG], true); + } + + public function __toString(): string + { + return (string) $this->text; + } + + /** + * @return static[] + */ + public static function tokenize(string $code, int $flags = 0): array + { + $line = 1; + $position = 0; + $tokens = token_get_all($code, $flags); + foreach ($tokens as $index => $token) { + if (\is_string($token)) { + $id = \ord($token); + $text = $token; + } else { + [$id, $text, $line] = $token; + } + $tokens[$index] = new static($id, $text, $line, $position); + $position += \strlen($text); + } + + return $tokens; + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php80/README.md b/tests/integration/vendor/symfony/polyfill-php80/README.md new file mode 100644 index 000000000..10b8ee49a --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/README.md @@ -0,0 +1,24 @@ +Symfony Polyfill / Php80 +======================== + +This component provides features added to PHP 8.0 core: + +- `Stringable` interface +- [`fdiv`](https://php.net/fdiv) +- `ValueError` class +- `UnhandledMatchError` class +- `FILTER_VALIDATE_BOOL` constant +- [`get_debug_type`](https://php.net/get_debug_type) +- [`preg_last_error_msg`](https://php.net/preg_last_error_msg) +- [`str_contains`](https://php.net/str_contains) +- [`str_starts_with`](https://php.net/str_starts_with) +- [`str_ends_with`](https://php.net/str_ends_with) +- [`get_resource_id`](https://php.net/get_resource_id) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/tests/integration/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php b/tests/integration/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php new file mode 100644 index 000000000..7ea6d2772 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php @@ -0,0 +1,22 @@ +flags = $flags; + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php b/tests/integration/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php new file mode 100644 index 000000000..72f10812b --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php80 as p; + +if (\PHP_VERSION_ID >= 80000) { + return; +} + +if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { + define('FILTER_VALIDATE_BOOL', \FILTER_VALIDATE_BOOLEAN); +} + +if (!function_exists('fdiv')) { + function fdiv(float $num1, float $num2): float { return p\Php80::fdiv($num1, $num2); } +} +if (!function_exists('preg_last_error_msg')) { + function preg_last_error_msg(): string { return p\Php80::preg_last_error_msg(); } +} +if (!function_exists('str_contains')) { + function str_contains(?string $haystack, ?string $needle): bool { return p\Php80::str_contains($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_starts_with')) { + function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('str_ends_with')) { + function str_ends_with(?string $haystack, ?string $needle): bool { return p\Php80::str_ends_with($haystack ?? '', $needle ?? ''); } +} +if (!function_exists('get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } +} +if (!function_exists('get_resource_id')) { + function get_resource_id($resource): int { return p\Php80::get_resource_id($resource); } +} diff --git a/tests/integration/vendor/symfony/polyfill-php80/composer.json b/tests/integration/vendor/symfony/polyfill-php80/composer.json new file mode 100644 index 000000000..5fe679db3 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php80/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/polyfill-php80", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php81/LICENSE b/tests/integration/vendor/symfony/polyfill-php81/LICENSE new file mode 100644 index 000000000..efb17f98e --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php81/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/polyfill-php81/Php81.php b/tests/integration/vendor/symfony/polyfill-php81/Php81.php new file mode 100644 index 000000000..f0507b765 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php81/Php81.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php81; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class Php81 +{ + public static function array_is_list(array $array): bool + { + if ([] === $array || $array === array_values($array)) { + return true; + } + + $nextKey = -1; + + foreach ($array as $k => $v) { + if ($k !== ++$nextKey) { + return false; + } + } + + return true; + } +} diff --git a/tests/integration/vendor/symfony/polyfill-php81/README.md b/tests/integration/vendor/symfony/polyfill-php81/README.md new file mode 100644 index 000000000..5ef61be6a --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php81/README.md @@ -0,0 +1,16 @@ +Symfony Polyfill / Php81 +======================== + +This component provides features added to PHP 8.1 core: + +- [`array_is_list`](https://php.net/array_is_list) +- [`MYSQLI_REFRESH_REPLICA`](https://www.php.net/manual/en/mysqli.constants.php#constantmysqli-refresh-replica) constant +- [`ReturnTypeWillChange`](https://wiki.php.net/rfc/internal_method_return_types) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/tests/integration/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php b/tests/integration/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php new file mode 100644 index 000000000..f4cad34f6 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php @@ -0,0 +1,11 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php81 as p; + +if (\PHP_VERSION_ID >= 80100) { + return; +} + +if (defined('MYSQLI_REFRESH_SLAVE') && !defined('MYSQLI_REFRESH_REPLICA')) { + define('MYSQLI_REFRESH_REPLICA', 64); +} + +if (!function_exists('array_is_list')) { + function array_is_list(array $array): bool { return p\Php81::array_is_list($array); } +} + +if (!function_exists('enum_exists')) { + function enum_exists(string $enum, bool $autoload = true): bool { return $autoload && class_exists($enum) && false; } +} diff --git a/tests/integration/vendor/symfony/polyfill-php81/composer.json b/tests/integration/vendor/symfony/polyfill-php81/composer.json new file mode 100644 index 000000000..c39ccf477 --- /dev/null +++ b/tests/integration/vendor/symfony/polyfill-php81/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/polyfill-php81", + "type": "library", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.1" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php81\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + } +} diff --git a/tests/integration/vendor/symfony/console/.gitignore b/tests/integration/vendor/symfony/service-contracts/.gitignore similarity index 100% rename from tests/integration/vendor/symfony/console/.gitignore rename to tests/integration/vendor/symfony/service-contracts/.gitignore diff --git a/tests/integration/vendor/symfony/service-contracts/Attribute/Required.php b/tests/integration/vendor/symfony/service-contracts/Attribute/Required.php new file mode 100644 index 000000000..8ba6183f6 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/Attribute/Required.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service\Attribute; + +use Attribute; + +/** + * A required dependency. + * + * This attribute indicates that a property holds a required dependency. The annotated property or method should be + * considered during the instantiation process of the containing class. + * + * @author Alexander M. Turek + */ +#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY)] +final class Required +{ +} diff --git a/tests/integration/vendor/symfony/service-contracts/CHANGELOG.md b/tests/integration/vendor/symfony/service-contracts/CHANGELOG.md new file mode 100644 index 000000000..e9847779b --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/master/CHANGELOG.md diff --git a/tests/integration/vendor/symfony/service-contracts/LICENSE b/tests/integration/vendor/symfony/service-contracts/LICENSE new file mode 100644 index 000000000..69d925ba7 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2020 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/service-contracts/README.md b/tests/integration/vendor/symfony/service-contracts/README.md new file mode 100644 index 000000000..d033a439b --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/README.md @@ -0,0 +1,9 @@ +Symfony Service Contracts +========================= + +A set of abstractions extracted out of the Symfony components. + +Can be used to build on semantics that the Symfony components proved useful - and +that already have battle tested implementations. + +See https://github.com/symfony/contracts/blob/master/README.md for more information. diff --git a/tests/integration/vendor/symfony/service-contracts/ResetInterface.php b/tests/integration/vendor/symfony/service-contracts/ResetInterface.php new file mode 100644 index 000000000..1af1075ee --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/ResetInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +/** + * Provides a way to reset an object to its initial state. + * + * When calling the "reset()" method on an object, it should be put back to its + * initial state. This usually means clearing any internal buffers and forwarding + * the call to internal dependencies. All properties of the object should be put + * back to the same state it had when it was first ready to use. + * + * This method could be called, for example, to recycle objects that are used as + * services, so that they can be used to handle several requests in the same + * process loop (note that we advise making your services stateless instead of + * implementing this interface when possible.) + */ +interface ResetInterface +{ + public function reset(); +} diff --git a/tests/integration/vendor/symfony/service-contracts/ServiceLocatorTrait.php b/tests/integration/vendor/symfony/service-contracts/ServiceLocatorTrait.php new file mode 100644 index 000000000..1737f50e9 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/ServiceLocatorTrait.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(ContainerExceptionInterface::class); +class_exists(NotFoundExceptionInterface::class); + +/** + * A trait to help implement ServiceProviderInterface. + * + * @author Robin Chalas + * @author Nicolas Grekas + */ +trait ServiceLocatorTrait +{ + private $factories; + private $loading = []; + private $providedTypes; + + /** + * @param callable[] $factories + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * {@inheritdoc} + * + * @return bool + */ + public function has($id) + { + return isset($this->factories[$id]); + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + if (!isset($this->factories[$id])) { + throw $this->createNotFoundException($id); + } + + if (isset($this->loading[$id])) { + $ids = array_values($this->loading); + $ids = \array_slice($this->loading, array_search($id, $ids)); + $ids[] = $id; + + throw $this->createCircularReferenceException($id, $ids); + } + + $this->loading[$id] = $id; + try { + return $this->factories[$id]($this); + } finally { + unset($this->loading[$id]); + } + } + + /** + * {@inheritdoc} + */ + public function getProvidedServices(): array + { + if (null === $this->providedTypes) { + $this->providedTypes = []; + + foreach ($this->factories as $name => $factory) { + if (!\is_callable($factory)) { + $this->providedTypes[$name] = '?'; + } else { + $type = (new \ReflectionFunction($factory))->getReturnType(); + + $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?'; + } + } + } + + return $this->providedTypes; + } + + private function createNotFoundException(string $id): NotFoundExceptionInterface + { + if (!$alternatives = array_keys($this->factories)) { + $message = 'is empty...'; + } else { + $last = array_pop($alternatives); + if ($alternatives) { + $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); + } else { + $message = sprintf('only knows about the "%s" service.', $last); + } + } + + if ($this->loading) { + $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); + } else { + $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message); + } + + return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface { + }; + } + + private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface + { + return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface { + }; + } +} diff --git a/tests/integration/vendor/symfony/service-contracts/ServiceProviderInterface.php b/tests/integration/vendor/symfony/service-contracts/ServiceProviderInterface.php new file mode 100644 index 000000000..c60ad0bd4 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/ServiceProviderInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerInterface; + +/** + * A ServiceProviderInterface exposes the identifiers and the types of services provided by a container. + * + * @author Nicolas Grekas + * @author Mateusz Sip + */ +interface ServiceProviderInterface extends ContainerInterface +{ + /** + * Returns an associative array of service types keyed by the identifiers provided by the current container. + * + * Examples: + * + * * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface + * * ['foo' => '?'] means the container provides service name "foo" of unspecified type + * * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null + * + * @return string[] The provided service types, keyed by service names + */ + public function getProvidedServices(): array; +} diff --git a/tests/integration/vendor/symfony/service-contracts/ServiceSubscriberInterface.php b/tests/integration/vendor/symfony/service-contracts/ServiceSubscriberInterface.php new file mode 100644 index 000000000..8bb320f5b --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/ServiceSubscriberInterface.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +/** + * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. + * + * The getSubscribedServices method returns an array of service types required by such instances, + * optionally keyed by the service names used internally. Service types that start with an interrogation + * mark "?" are optional, while the other ones are mandatory service dependencies. + * + * The injected service locators SHOULD NOT allow access to any other services not specified by the method. + * + * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. + * This interface does not dictate any injection method for these service locators, although constructor + * injection is recommended. + * + * @author Nicolas Grekas + */ +interface ServiceSubscriberInterface +{ + /** + * Returns an array of service types required by such instances, optionally keyed by the service names used internally. + * + * For mandatory dependencies: + * + * * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name + * internally to fetch a service which must implement Psr\Log\LoggerInterface. + * * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name + * internally to fetch an iterable of Psr\Log\LoggerInterface instances. + * * ['Psr\Log\LoggerInterface'] is a shortcut for + * * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface'] + * + * otherwise: + * + * * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency + * * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency + * * ['?Psr\Log\LoggerInterface'] is a shortcut for + * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface'] + * + * @return array The required service types, optionally keyed by service names + */ + public static function getSubscribedServices(); +} diff --git a/tests/integration/vendor/symfony/service-contracts/ServiceSubscriberTrait.php b/tests/integration/vendor/symfony/service-contracts/ServiceSubscriberTrait.php new file mode 100644 index 000000000..82fb5ab36 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/ServiceSubscriberTrait.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service; + +use Psr\Container\ContainerInterface; + +/** + * Implementation of ServiceSubscriberInterface that determines subscribed services from + * private method return types. Service ids are available as "ClassName::methodName". + * + * @author Kevin Bond + */ +trait ServiceSubscriberTrait +{ + /** @var ContainerInterface */ + protected $container; + + public static function getSubscribedServices(): array + { + static $services; + + if (null !== $services) { + return $services; + } + + $services = \is_callable(['parent', __FUNCTION__]) ? parent::getSubscribedServices() : []; + + foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { + if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { + continue; + } + + if (self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !$returnType->isBuiltin()) { + $services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $type); + } + } + + return $services; + } + + /** + * @required + */ + public function setContainer(ContainerInterface $container) + { + $this->container = $container; + + if (\is_callable(['parent', __FUNCTION__])) { + return parent::setContainer($container); + } + + return null; + } +} diff --git a/tests/integration/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php b/tests/integration/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php new file mode 100644 index 000000000..5ed914952 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Service\Test; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\ServiceLocatorTrait; + +abstract class ServiceLocatorTest extends TestCase +{ + protected function getServiceLocator(array $factories) + { + return new class($factories) implements ContainerInterface { + use ServiceLocatorTrait; + }; + } + + public function testHas() + { + $locator = $this->getServiceLocator([ + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + function () { return 'dummy'; }, + ]); + + $this->assertTrue($locator->has('foo')); + $this->assertTrue($locator->has('bar')); + $this->assertFalse($locator->has('dummy')); + } + + public function testGet() + { + $locator = $this->getServiceLocator([ + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + ]); + + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('baz', $locator->get('bar')); + } + + public function testGetDoesNotMemoize() + { + $i = 0; + $locator = $this->getServiceLocator([ + 'foo' => function () use (&$i) { + ++$i; + + return 'bar'; + }, + ]); + + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame(2, $i); + } + + public function testThrowsOnUndefinedInternalService() + { + if (!$this->getExpectedException()) { + $this->expectException('Psr\Container\NotFoundExceptionInterface'); + $this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.'); + } + $locator = $this->getServiceLocator([ + 'foo' => function () use (&$locator) { return $locator->get('bar'); }, + ]); + + $locator->get('foo'); + } + + public function testThrowsOnCircularReference() + { + $this->expectException('Psr\Container\ContainerExceptionInterface'); + $this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".'); + $locator = $this->getServiceLocator([ + 'foo' => function () use (&$locator) { return $locator->get('bar'); }, + 'bar' => function () use (&$locator) { return $locator->get('baz'); }, + 'baz' => function () use (&$locator) { return $locator->get('bar'); }, + ]); + + $locator->get('foo'); + } +} diff --git a/tests/integration/vendor/symfony/service-contracts/composer.json b/tests/integration/vendor/symfony/service-contracts/composer.json new file mode 100644 index 000000000..47244fbb1 --- /dev/null +++ b/tests/integration/vendor/symfony/service-contracts/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/service-contracts", + "type": "library", + "description": "Generic abstractions related to writing services", + "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "psr/container": "^1.0" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\Service\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/tests/integration/vendor/symfony/string/AbstractString.php b/tests/integration/vendor/symfony/string/AbstractString.php new file mode 100644 index 000000000..cf21fef1f --- /dev/null +++ b/tests/integration/vendor/symfony/string/AbstractString.php @@ -0,0 +1,795 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\Exception\RuntimeException; + +/** + * Represents a string of abstract characters. + * + * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). + * This class is the abstract type to use as a type-hint when the logic you want to + * implement doesn't care about the exact variant it deals with. + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +abstract class AbstractString implements \Stringable, \JsonSerializable +{ + public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER; + public const PREG_SET_ORDER = \PREG_SET_ORDER; + public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE; + public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL; + + public const PREG_SPLIT = 0; + public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY; + public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE; + public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE; + + protected $string = ''; + protected $ignoreCase = false; + + abstract public function __construct(string $string = ''); + + /** + * Unwraps instances of AbstractString back to strings. + * + * @return string[]|array + */ + public static function unwrap(array $values): array + { + foreach ($values as $k => $v) { + if ($v instanceof self) { + $values[$k] = $v->__toString(); + } elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) { + $values[$k] = $v; + } + } + + return $values; + } + + /** + * Wraps (and normalizes) strings in instances of AbstractString. + * + * @return static[]|array + */ + public static function wrap(array $values): array + { + $i = 0; + $keys = null; + + foreach ($values as $k => $v) { + if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) { + $keys = $keys ?? array_keys($values); + $keys[$i] = $j; + } + + if (\is_string($v)) { + $values[$k] = new static($v); + } elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) { + $values[$k] = $v; + } + + ++$i; + } + + return null !== $keys ? array_combine($keys, $values) : $values; + } + + /** + * @param string|string[] $needle + * + * @return static + */ + public function after($needle, bool $includeNeedle = false, int $offset = 0): self + { + $str = clone $this; + $i = \PHP_INT_MAX; + + foreach ((array) $needle as $n) { + $n = (string) $n; + $j = $this->indexOf($n, $offset); + + if (null !== $j && $j < $i) { + $i = $j; + $str->string = $n; + } + } + + if (\PHP_INT_MAX === $i) { + return $str; + } + + if (!$includeNeedle) { + $i += $str->length(); + } + + return $this->slice($i); + } + + /** + * @param string|string[] $needle + * + * @return static + */ + public function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self + { + $str = clone $this; + $i = null; + + foreach ((array) $needle as $n) { + $n = (string) $n; + $j = $this->indexOfLast($n, $offset); + + if (null !== $j && $j >= $i) { + $i = $offset = $j; + $str->string = $n; + } + } + + if (null === $i) { + return $str; + } + + if (!$includeNeedle) { + $i += $str->length(); + } + + return $this->slice($i); + } + + /** + * @return static + */ + abstract public function append(string ...$suffix): self; + + /** + * @param string|string[] $needle + * + * @return static + */ + public function before($needle, bool $includeNeedle = false, int $offset = 0): self + { + $str = clone $this; + $i = \PHP_INT_MAX; + + foreach ((array) $needle as $n) { + $n = (string) $n; + $j = $this->indexOf($n, $offset); + + if (null !== $j && $j < $i) { + $i = $j; + $str->string = $n; + } + } + + if (\PHP_INT_MAX === $i) { + return $str; + } + + if ($includeNeedle) { + $i += $str->length(); + } + + return $this->slice(0, $i); + } + + /** + * @param string|string[] $needle + * + * @return static + */ + public function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self + { + $str = clone $this; + $i = null; + + foreach ((array) $needle as $n) { + $n = (string) $n; + $j = $this->indexOfLast($n, $offset); + + if (null !== $j && $j >= $i) { + $i = $offset = $j; + $str->string = $n; + } + } + + if (null === $i) { + return $str; + } + + if ($includeNeedle) { + $i += $str->length(); + } + + return $this->slice(0, $i); + } + + /** + * @return int[] + */ + public function bytesAt(int $offset): array + { + $str = $this->slice($offset, 1); + + return '' === $str->string ? [] : array_values(unpack('C*', $str->string)); + } + + /** + * @return static + */ + abstract public function camel(): self; + + /** + * @return static[] + */ + abstract public function chunk(int $length = 1): array; + + /** + * @return static + */ + public function collapseWhitespace(): self + { + $str = clone $this; + $str->string = trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $str->string)); + + return $str; + } + + /** + * @param string|string[] $needle + */ + public function containsAny($needle): bool + { + return null !== $this->indexOf($needle); + } + + /** + * @param string|string[] $suffix + */ + public function endsWith($suffix): bool + { + if (!\is_array($suffix) && !$suffix instanceof \Traversable) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + foreach ($suffix as $s) { + if ($this->endsWith((string) $s)) { + return true; + } + } + + return false; + } + + /** + * @return static + */ + public function ensureEnd(string $suffix): self + { + if (!$this->endsWith($suffix)) { + return $this->append($suffix); + } + + $suffix = preg_quote($suffix); + $regex = '{('.$suffix.')(?:'.$suffix.')++$}D'; + + return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1'); + } + + /** + * @return static + */ + public function ensureStart(string $prefix): self + { + $prefix = new static($prefix); + + if (!$this->startsWith($prefix)) { + return $this->prepend($prefix); + } + + $str = clone $this; + $i = $prefixLen = $prefix->length(); + + while ($this->indexOf($prefix, $i) === $i) { + $str = $str->slice($prefixLen); + $i += $prefixLen; + } + + return $str; + } + + /** + * @param string|string[] $string + */ + public function equalsTo($string): bool + { + if (!\is_array($string) && !$string instanceof \Traversable) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + foreach ($string as $s) { + if ($this->equalsTo((string) $s)) { + return true; + } + } + + return false; + } + + /** + * @return static + */ + abstract public function folded(): self; + + /** + * @return static + */ + public function ignoreCase(): self + { + $str = clone $this; + $str->ignoreCase = true; + + return $str; + } + + /** + * @param string|string[] $needle + */ + public function indexOf($needle, int $offset = 0): ?int + { + if (!\is_array($needle) && !$needle instanceof \Traversable) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + $i = \PHP_INT_MAX; + + foreach ($needle as $n) { + $j = $this->indexOf((string) $n, $offset); + + if (null !== $j && $j < $i) { + $i = $j; + } + } + + return \PHP_INT_MAX === $i ? null : $i; + } + + /** + * @param string|string[] $needle + */ + public function indexOfLast($needle, int $offset = 0): ?int + { + if (!\is_array($needle) && !$needle instanceof \Traversable) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + $i = null; + + foreach ($needle as $n) { + $j = $this->indexOfLast((string) $n, $offset); + + if (null !== $j && $j >= $i) { + $i = $offset = $j; + } + } + + return $i; + } + + public function isEmpty(): bool + { + return '' === $this->string; + } + + /** + * @return static + */ + abstract public function join(array $strings, string $lastGlue = null): self; + + public function jsonSerialize(): string + { + return $this->string; + } + + abstract public function length(): int; + + /** + * @return static + */ + abstract public function lower(): self; + + /** + * Matches the string using a regular expression. + * + * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression. + * + * @return array All matches in a multi-dimensional array ordered according to flags + */ + abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array; + + /** + * @return static + */ + abstract public function padBoth(int $length, string $padStr = ' '): self; + + /** + * @return static + */ + abstract public function padEnd(int $length, string $padStr = ' '): self; + + /** + * @return static + */ + abstract public function padStart(int $length, string $padStr = ' '): self; + + /** + * @return static + */ + abstract public function prepend(string ...$prefix): self; + + /** + * @return static + */ + public function repeat(int $multiplier): self + { + if (0 > $multiplier) { + throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier)); + } + + $str = clone $this; + $str->string = str_repeat($str->string, $multiplier); + + return $str; + } + + /** + * @return static + */ + abstract public function replace(string $from, string $to): self; + + /** + * @param string|callable $to + * + * @return static + */ + abstract public function replaceMatches(string $fromRegexp, $to): self; + + /** + * @return static + */ + abstract public function reverse(): self; + + /** + * @return static + */ + abstract public function slice(int $start = 0, int $length = null): self; + + /** + * @return static + */ + abstract public function snake(): self; + + /** + * @return static + */ + abstract public function splice(string $replacement, int $start = 0, int $length = null): self; + + /** + * @return static[] + */ + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (null === $flags) { + throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.'); + } + + if ($this->ignoreCase) { + $delimiter .= 'i'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Splitting failed with '.$k.'.'); + } + } + + throw new RuntimeException('Splitting failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + $str = clone $this; + + if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) { + foreach ($chunks as &$chunk) { + $str->string = $chunk[0]; + $chunk[0] = clone $str; + } + } else { + foreach ($chunks as &$chunk) { + $str->string = $chunk; + $chunk = clone $str; + } + } + + return $chunks; + } + + /** + * @param string|string[] $prefix + */ + public function startsWith($prefix): bool + { + if (!\is_array($prefix) && !$prefix instanceof \Traversable) { + throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); + } + + foreach ($prefix as $prefix) { + if ($this->startsWith((string) $prefix)) { + return true; + } + } + + return false; + } + + /** + * @return static + */ + abstract public function title(bool $allWords = false): self; + + public function toByteString(string $toEncoding = null): ByteString + { + $b = new ByteString(); + + $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding; + + if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') { + $b->string = $this->string; + + return $b; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + try { + $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8'); + } catch (InvalidArgumentException $e) { + if (!\function_exists('iconv')) { + throw $e; + } + + $b->string = iconv('UTF-8', $toEncoding, $this->string); + } + } finally { + restore_error_handler(); + } + + return $b; + } + + public function toCodePointString(): CodePointString + { + return new CodePointString($this->string); + } + + public function toString(): string + { + return $this->string; + } + + public function toUnicodeString(): UnicodeString + { + return new UnicodeString($this->string); + } + + /** + * @return static + */ + abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; + + /** + * @return static + */ + abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; + + /** + * @param string|string[] $prefix + * + * @return static + */ + public function trimPrefix($prefix): self + { + if (\is_array($prefix) || $prefix instanceof \Traversable) { + foreach ($prefix as $s) { + $t = $this->trimPrefix($s); + + if ($t->string !== $this->string) { + return $t; + } + } + + return clone $this; + } + + $str = clone $this; + + if ($prefix instanceof self) { + $prefix = $prefix->string; + } else { + $prefix = (string) $prefix; + } + + if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) { + $str->string = substr($this->string, \strlen($prefix)); + } + + return $str; + } + + /** + * @return static + */ + abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; + + /** + * @param string|string[] $suffix + * + * @return static + */ + public function trimSuffix($suffix): self + { + if (\is_array($suffix) || $suffix instanceof \Traversable) { + foreach ($suffix as $s) { + $t = $this->trimSuffix($s); + + if ($t->string !== $this->string) { + return $t; + } + } + + return clone $this; + } + + $str = clone $this; + + if ($suffix instanceof self) { + $suffix = $suffix->string; + } else { + $suffix = (string) $suffix; + } + + if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) { + $str->string = substr($this->string, 0, -\strlen($suffix)); + } + + return $str; + } + + /** + * @return static + */ + public function truncate(int $length, string $ellipsis = '', bool $cut = true): self + { + $stringLength = $this->length(); + + if ($stringLength <= $length) { + return clone $this; + } + + $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0; + + if ($length < $ellipsisLength) { + $ellipsisLength = 0; + } + + if (!$cut) { + if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) { + return clone $this; + } + + $length += $ellipsisLength; + } + + $str = $this->slice(0, $length - $ellipsisLength); + + return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str; + } + + /** + * @return static + */ + abstract public function upper(): self; + + /** + * Returns the printable length on a terminal. + */ + abstract public function width(bool $ignoreAnsiDecoration = true): int; + + /** + * @return static + */ + public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self + { + $lines = '' !== $break ? $this->split($break) : [clone $this]; + $chars = []; + $mask = ''; + + if (1 === \count($lines) && '' === $lines[0]->string) { + return $lines[0]; + } + + foreach ($lines as $i => $line) { + if ($i) { + $chars[] = $break; + $mask .= '#'; + } + + foreach ($line->chunk() as $char) { + $chars[] = $char->string; + $mask .= ' ' === $char->string ? ' ' : '?'; + } + } + + $string = ''; + $j = 0; + $b = $i = -1; + $mask = wordwrap($mask, $width, '#', $cut); + + while (false !== $b = strpos($mask, '#', $b + 1)) { + for (++$i; $i < $b; ++$i) { + $string .= $chars[$j]; + unset($chars[$j++]); + } + + if ($break === $chars[$j] || ' ' === $chars[$j]) { + unset($chars[$j++]); + } + + $string .= $break; + } + + $str = clone $this; + $str->string = $string.implode('', $chars); + + return $str; + } + + public function __sleep(): array + { + return ['string']; + } + + public function __clone() + { + $this->ignoreCase = false; + } + + public function __toString(): string + { + return $this->string; + } +} diff --git a/tests/integration/vendor/symfony/string/AbstractUnicodeString.php b/tests/integration/vendor/symfony/string/AbstractUnicodeString.php new file mode 100644 index 000000000..db810cb6d --- /dev/null +++ b/tests/integration/vendor/symfony/string/AbstractUnicodeString.php @@ -0,0 +1,620 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\Exception\RuntimeException; + +/** + * Represents a string of abstract Unicode characters. + * + * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). + * This class is the abstract type to use as a type-hint when the logic you want to + * implement is Unicode-aware but doesn't care about code points vs grapheme clusters. + * + * @author Nicolas Grekas + * + * @throws ExceptionInterface + */ +abstract class AbstractUnicodeString extends AbstractString +{ + public const NFC = \Normalizer::NFC; + public const NFD = \Normalizer::NFD; + public const NFKC = \Normalizer::NFKC; + public const NFKD = \Normalizer::NFKD; + + // all ASCII letters sorted by typical frequency of occurrence + private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; + + // the subset of folded case mappings that is not in lower case mappings + private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'İ', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ']; + private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'i̇', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ']; + + // the subset of upper case mappings that map one code point to many code points + private const UPPER_FROM = ['ß', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'և', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ', 'ʼn', 'ΐ', 'ΰ', 'ǰ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾶ', 'ῆ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῶ']; + private const UPPER_TO = ['SS', 'FF', 'FI', 'FL', 'FFI', 'FFL', 'ST', 'ST', 'ԵՒ', 'ՄՆ', 'ՄԵ', 'ՄԻ', 'ՎՆ', 'ՄԽ', 'ʼN', 'Ϊ́', 'Ϋ́', 'J̌', 'H̱', 'T̈', 'W̊', 'Y̊', 'Aʾ', 'Υ̓', 'Υ̓̀', 'Υ̓́', 'Υ̓͂', 'Α͂', 'Η͂', 'Ϊ̀', 'Ϊ́', 'Ι͂', 'Ϊ͂', 'Ϋ̀', 'Ϋ́', 'Ρ̓', 'Υ͂', 'Ϋ͂', 'Ω͂']; + + // the subset of https://github.com/unicode-org/cldr/blob/master/common/transforms/Latin-ASCII.xml that is not in NFKD + private const TRANSLIT_FROM = ['Æ', 'Ð', 'Ø', 'Þ', 'ß', 'æ', 'ð', 'ø', 'þ', 'Đ', 'đ', 'Ħ', 'ħ', 'ı', 'ĸ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'ʼn', 'Ŋ', 'ŋ', 'Œ', 'œ', 'Ŧ', 'ŧ', 'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƈ', 'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'ƕ', 'Ɩ', 'Ɨ', 'Ƙ', 'ƙ', 'ƚ', 'Ɲ', 'ƞ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'ƫ', 'Ƭ', 'ƭ', 'Ʈ', 'Ʋ', 'Ƴ', 'ƴ', 'Ƶ', 'ƶ', 'DŽ', 'Dž', 'dž', 'Ǥ', 'ǥ', 'ȡ', 'Ȥ', 'ȥ', 'ȴ', 'ȵ', 'ȶ', 'ȷ', 'ȸ', 'ȹ', 'Ⱥ', 'Ȼ', 'ȼ', 'Ƚ', 'Ⱦ', 'ȿ', 'ɀ', 'Ƀ', 'Ʉ', 'Ɇ', 'ɇ', 'Ɉ', 'ɉ', 'Ɍ', 'ɍ', 'Ɏ', 'ɏ', 'ɓ', 'ɕ', 'ɖ', 'ɗ', 'ɛ', 'ɟ', 'ɠ', 'ɡ', 'ɢ', 'ɦ', 'ɧ', 'ɨ', 'ɪ', 'ɫ', 'ɬ', 'ɭ', 'ɱ', 'ɲ', 'ɳ', 'ɴ', 'ɶ', 'ɼ', 'ɽ', 'ɾ', 'ʀ', 'ʂ', 'ʈ', 'ʉ', 'ʋ', 'ʏ', 'ʐ', 'ʑ', 'ʙ', 'ʛ', 'ʜ', 'ʝ', 'ʟ', 'ʠ', 'ʣ', 'ʥ', 'ʦ', 'ʪ', 'ʫ', 'ᴀ', 'ᴁ', 'ᴃ', 'ᴄ', 'ᴅ', 'ᴆ', 'ᴇ', 'ᴊ', 'ᴋ', 'ᴌ', 'ᴍ', 'ᴏ', 'ᴘ', 'ᴛ', 'ᴜ', 'ᴠ', 'ᴡ', 'ᴢ', 'ᵫ', 'ᵬ', 'ᵭ', 'ᵮ', 'ᵯ', 'ᵰ', 'ᵱ', 'ᵲ', 'ᵳ', 'ᵴ', 'ᵵ', 'ᵶ', 'ᵺ', 'ᵻ', 'ᵽ', 'ᵾ', 'ᶀ', 'ᶁ', 'ᶂ', 'ᶃ', 'ᶄ', 'ᶅ', 'ᶆ', 'ᶇ', 'ᶈ', 'ᶉ', 'ᶊ', 'ᶌ', 'ᶍ', 'ᶎ', 'ᶏ', 'ᶑ', 'ᶒ', 'ᶓ', 'ᶖ', 'ᶙ', 'ẚ', 'ẜ', 'ẝ', 'ẞ', 'Ỻ', 'ỻ', 'Ỽ', 'ỽ', 'Ỿ', 'ỿ', '©', '®', '₠', '₢', '₣', '₤', '₧', '₺', '₹', 'ℌ', '℞', '㎧', '㎮', '㏆', '㏗', '㏞', '㏟', '¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '〇', '‘', '’', '‚', '‛', '“', '”', '„', '‟', '′', '″', '〝', '〞', '«', '»', '‹', '›', '‐', '‑', '‒', '–', '—', '―', '︱', '︲', '﹘', '‖', '⁄', '⁅', '⁆', '⁎', '、', '。', '〈', '〉', '《', '》', '〔', '〕', '〘', '〙', '〚', '〛', '︑', '︒', '︹', '︺', '︽', '︾', '︿', '﹀', '﹑', '﹝', '﹞', '⦅', '⦆', '。', '、', '×', '÷', '−', '∕', '∖', '∣', '∥', '≪', '≫', '⦅', '⦆']; + private const TRANSLIT_TO = ['AE', 'D', 'O', 'TH', 'ss', 'ae', 'd', 'o', 'th', 'D', 'd', 'H', 'h', 'i', 'q', 'L', 'l', 'L', 'l', '\'n', 'N', 'n', 'OE', 'oe', 'T', 't', 'b', 'B', 'B', 'b', 'C', 'c', 'D', 'D', 'D', 'd', 'E', 'F', 'f', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'N', 'n', 'OI', 'oi', 'P', 'p', 't', 'T', 't', 'T', 'V', 'Y', 'y', 'Z', 'z', 'DZ', 'Dz', 'dz', 'G', 'g', 'd', 'Z', 'z', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', 'z', 'B', 'U', 'E', 'e', 'J', 'j', 'R', 'r', 'Y', 'y', 'b', 'c', 'd', 'd', 'e', 'j', 'g', 'g', 'G', 'h', 'h', 'i', 'I', 'l', 'l', 'l', 'm', 'n', 'n', 'N', 'OE', 'r', 'r', 'r', 'R', 's', 't', 'u', 'v', 'Y', 'z', 'z', 'B', 'G', 'H', 'j', 'L', 'q', 'dz', 'dz', 'ts', 'ls', 'lz', 'A', 'AE', 'B', 'C', 'D', 'D', 'E', 'J', 'K', 'L', 'M', 'O', 'P', 'T', 'U', 'V', 'W', 'Z', 'ue', 'b', 'd', 'f', 'm', 'n', 'p', 'r', 'r', 's', 't', 'z', 'th', 'I', 'p', 'U', 'b', 'd', 'f', 'g', 'k', 'l', 'm', 'n', 'p', 'r', 's', 'v', 'x', 'z', 'a', 'd', 'e', 'e', 'i', 'u', 'a', 's', 's', 'SS', 'LL', 'll', 'V', 'v', 'Y', 'y', '(C)', '(R)', 'CE', 'Cr', 'Fr.', 'L.', 'Pts', 'TL', 'Rs', 'x', 'Rx', 'm/s', 'rad/s', 'C/kg', 'pH', 'V/m', 'A/m', ' 1/4', ' 1/2', ' 3/4', ' 1/3', ' 2/3', ' 1/5', ' 2/5', ' 3/5', ' 4/5', ' 1/6', ' 5/6', ' 1/8', ' 3/8', ' 5/8', ' 7/8', ' 1/', '0', '\'', '\'', ',', '\'', '"', '"', ',,', '"', '\'', '"', '"', '"', '<<', '>>', '<', '>', '-', '-', '-', '-', '-', '-', '-', '-', '-', '||', '/', '[', ']', '*', ',', '.', '<', '>', '<<', '>>', '[', ']', '[', ']', '[', ']', ',', '.', '[', ']', '<<', '>>', '<', '>', ',', '[', ']', '((', '))', '.', ',', '*', '/', '-', '/', '\\', '|', '||', '<<', '>>', '((', '))']; + + private static $transliterators = []; + private static $tableZero; + private static $tableWide; + + /** + * @return static + */ + public static function fromCodePoints(int ...$codes): self + { + $string = ''; + + foreach ($codes as $code) { + if (0x80 > $code %= 0x200000) { + $string .= \chr($code); + } elseif (0x800 > $code) { + $string .= \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $string .= \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } else { + $string .= \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } + } + + return new static($string); + } + + /** + * Generic UTF-8 to ASCII transliteration. + * + * Install the intl extension for best results. + * + * @param string[]|\Transliterator[]|\Closure[] $rules See "*-Latin" rules from Transliterator::listIDs() + */ + public function ascii(array $rules = []): self + { + $str = clone $this; + $s = $str->string; + $str->string = ''; + + array_unshift($rules, 'nfd'); + $rules[] = 'latin-ascii'; + + if (\function_exists('transliterator_transliterate')) { + $rules[] = 'any-latin/bgn'; + } + + $rules[] = 'nfkd'; + $rules[] = '[:nonspacing mark:] remove'; + + while (\strlen($s) - 1 > $i = strspn($s, self::ASCII)) { + if (0 < --$i) { + $str->string .= substr($s, 0, $i); + $s = substr($s, $i); + } + + if (!$rule = array_shift($rules)) { + $rules = []; // An empty rule interrupts the next ones + } + + if ($rule instanceof \Transliterator) { + $s = $rule->transliterate($s); + } elseif ($rule instanceof \Closure) { + $s = $rule($s); + } elseif ($rule) { + if ('nfd' === $rule = strtolower($rule)) { + normalizer_is_normalized($s, self::NFD) ?: $s = normalizer_normalize($s, self::NFD); + } elseif ('nfkd' === $rule) { + normalizer_is_normalized($s, self::NFKD) ?: $s = normalizer_normalize($s, self::NFKD); + } elseif ('[:nonspacing mark:] remove' === $rule) { + $s = preg_replace('/\p{Mn}++/u', '', $s); + } elseif ('latin-ascii' === $rule) { + $s = str_replace(self::TRANSLIT_FROM, self::TRANSLIT_TO, $s); + } elseif ('de-ascii' === $rule) { + $s = preg_replace("/([AUO])\u{0308}(?=\p{Ll})/u", '$1e', $s); + $s = str_replace(["a\u{0308}", "o\u{0308}", "u\u{0308}", "A\u{0308}", "O\u{0308}", "U\u{0308}"], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], $s); + } elseif (\function_exists('transliterator_transliterate')) { + if (null === $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule)) { + if ('any-latin/bgn' === $rule) { + $rule = 'any-latin'; + $transliterator = self::$transliterators[$rule] ?? self::$transliterators[$rule] = \Transliterator::create($rule); + } + + if (null === $transliterator) { + throw new InvalidArgumentException(sprintf('Unknown transliteration rule "%s".', $rule)); + } + + self::$transliterators['any-latin/bgn'] = $transliterator; + } + + $s = $transliterator->transliterate($s); + } + } elseif (!\function_exists('iconv')) { + $s = preg_replace('/[^\x00-\x7F]/u', '?', $s); + } else { + $s = @preg_replace_callback('/[^\x00-\x7F]/u', static function ($c) { + $c = (string) iconv('UTF-8', 'ASCII//TRANSLIT', $c[0]); + + if ('' === $c && '' === iconv('UTF-8', 'ASCII//TRANSLIT', '²')) { + throw new \LogicException(sprintf('"%s" requires a translit-able iconv implementation, try installing "gnu-libiconv" if you\'re using Alpine Linux.', static::class)); + } + + return 1 < \strlen($c) ? ltrim($c, '\'`"^~') : ('' !== $c ? $c : '?'); + }, $s); + } + } + + $str->string .= $s; + + return $str; + } + + public function camel(): parent + { + $str = clone $this; + $str->string = str_replace(' ', '', preg_replace_callback('/\b./u', static function ($m) use (&$i) { + return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : mb_strtolower($m[0], 'UTF-8')) : mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); + }, preg_replace('/[^\pL0-9]++/u', ' ', $this->string))); + + return $str; + } + + /** + * @return int[] + */ + public function codePointsAt(int $offset): array + { + $str = $this->slice($offset, 1); + + if ('' === $str->string) { + return []; + } + + $codePoints = []; + + foreach (preg_split('//u', $str->string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { + $codePoints[] = mb_ord($c, 'UTF-8'); + } + + return $codePoints; + } + + public function folded(bool $compat = true): parent + { + $str = clone $this; + + if (!$compat || \PHP_VERSION_ID < 70300 || !\defined('Normalizer::NFKC_CF')) { + $str->string = normalizer_normalize($str->string, $compat ? \Normalizer::NFKC : \Normalizer::NFC); + $str->string = mb_strtolower(str_replace(self::FOLD_FROM, self::FOLD_TO, $this->string), 'UTF-8'); + } else { + $str->string = normalizer_normalize($str->string, \Normalizer::NFKC_CF); + } + + return $str; + } + + public function join(array $strings, string $lastGlue = null): parent + { + $str = clone $this; + + $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; + $str->string = implode($this->string, $strings).$tail; + + if (!preg_match('//u', $str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function lower(): parent + { + $str = clone $this; + $str->string = mb_strtolower(str_replace('İ', 'i̇', $str->string), 'UTF-8'); + + return $str; + } + + public function match(string $regexp, int $flags = 0, int $offset = 0): array + { + $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; + + if ($this->ignoreCase) { + $regexp .= 'i'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + return $matches; + } + + /** + * @return static + */ + public function normalize(int $form = self::NFC): self + { + if (!\in_array($form, [self::NFC, self::NFD, self::NFKC, self::NFKD])) { + throw new InvalidArgumentException('Unsupported normalization form.'); + } + + $str = clone $this; + normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); + + return $str; + } + + public function padBoth(int $length, string $padStr = ' '): parent + { + if ('' === $padStr || !preg_match('//u', $padStr)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $pad = clone $this; + $pad->string = $padStr; + + return $this->pad($length, $pad, \STR_PAD_BOTH); + } + + public function padEnd(int $length, string $padStr = ' '): parent + { + if ('' === $padStr || !preg_match('//u', $padStr)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $pad = clone $this; + $pad->string = $padStr; + + return $this->pad($length, $pad, \STR_PAD_RIGHT); + } + + public function padStart(int $length, string $padStr = ' '): parent + { + if ('' === $padStr || !preg_match('//u', $padStr)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $pad = clone $this; + $pad->string = $padStr; + + return $this->pad($length, $pad, \STR_PAD_LEFT); + } + + public function replaceMatches(string $fromRegexp, $to): parent + { + if ($this->ignoreCase) { + $fromRegexp .= 'i'; + } + + if (\is_array($to) || $to instanceof \Closure) { + if (!\is_callable($to)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s::replaceMatches()" must be callable, array given.', static::class)); + } + + $replace = 'preg_replace_callback'; + $to = static function (array $m) use ($to): string { + $to = $to($m); + + if ('' !== $to && (!\is_string($to) || !preg_match('//u', $to))) { + throw new InvalidArgumentException('Replace callback must return a valid UTF-8 string.'); + } + + return $to; + }; + } elseif ('' !== $to && !preg_match('//u', $to)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } else { + $replace = 'preg_replace'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (null === $string = $replace($fromRegexp.'u', $to, $this->string)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + $str = clone $this; + $str->string = $string; + + return $str; + } + + public function reverse(): parent + { + $str = clone $this; + $str->string = implode('', array_reverse(preg_split('/(\X)/u', $str->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY))); + + return $str; + } + + public function snake(): parent + { + $str = $this->camel()->title(); + $str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8'); + + return $str; + } + + public function title(bool $allWords = false): parent + { + $str = clone $this; + + $limit = $allWords ? -1 : 1; + + $str->string = preg_replace_callback('/\b./u', static function (array $m): string { + return mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); + }, $str->string, $limit); + + return $str; + } + + public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent + { + if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { + throw new InvalidArgumentException('Invalid UTF-8 chars.'); + } + $chars = preg_quote($chars); + + $str = clone $this; + $str->string = preg_replace("{^[$chars]++|[$chars]++$}uD", '', $str->string); + + return $str; + } + + public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent + { + if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { + throw new InvalidArgumentException('Invalid UTF-8 chars.'); + } + $chars = preg_quote($chars); + + $str = clone $this; + $str->string = preg_replace("{[$chars]++$}uD", '', $str->string); + + return $str; + } + + public function trimPrefix($prefix): parent + { + if (!$this->ignoreCase) { + return parent::trimPrefix($prefix); + } + + $str = clone $this; + + if ($prefix instanceof \Traversable) { + $prefix = iterator_to_array($prefix, false); + } elseif ($prefix instanceof parent) { + $prefix = $prefix->string; + } + + $prefix = implode('|', array_map('preg_quote', (array) $prefix)); + $str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string); + + return $str; + } + + public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent + { + if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { + throw new InvalidArgumentException('Invalid UTF-8 chars.'); + } + $chars = preg_quote($chars); + + $str = clone $this; + $str->string = preg_replace("{^[$chars]++}uD", '', $str->string); + + return $str; + } + + public function trimSuffix($suffix): parent + { + if (!$this->ignoreCase) { + return parent::trimSuffix($suffix); + } + + $str = clone $this; + + if ($suffix instanceof \Traversable) { + $suffix = iterator_to_array($suffix, false); + } elseif ($suffix instanceof parent) { + $suffix = $suffix->string; + } + + $suffix = implode('|', array_map('preg_quote', (array) $suffix)); + $str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string); + + return $str; + } + + public function upper(): parent + { + $str = clone $this; + $str->string = mb_strtoupper($str->string, 'UTF-8'); + + if (\PHP_VERSION_ID < 70300) { + $str->string = str_replace(self::UPPER_FROM, self::UPPER_TO, $str->string); + } + + return $str; + } + + public function width(bool $ignoreAnsiDecoration = true): int + { + $width = 0; + $s = str_replace(["\x00", "\x05", "\x07"], '', $this->string); + + if (false !== strpos($s, "\r")) { + $s = str_replace(["\r\n", "\r"], "\n", $s); + } + + if (!$ignoreAnsiDecoration) { + $s = preg_replace('/[\p{Cc}\x7F]++/u', '', $s); + } + + foreach (explode("\n", $s) as $s) { + if ($ignoreAnsiDecoration) { + $s = preg_replace('/(?:\x1B(?: + \[ [\x30-\x3F]*+ [\x20-\x2F]*+ [0x40-\x7E] + | [P\]X^_] .*? \x1B\\\\ + | [\x41-\x7E] + )|[\p{Cc}\x7F]++)/xu', '', $s); + } + + // Non printable characters have been dropped, so wcswidth cannot logically return -1. + $width += $this->wcswidth($s); + } + + return $width; + } + + /** + * @return static + */ + private function pad(int $len, self $pad, int $type): parent + { + $sLen = $this->length(); + + if ($len <= $sLen) { + return clone $this; + } + + $padLen = $pad->length(); + $freeLen = $len - $sLen; + $len = $freeLen % $padLen; + + switch ($type) { + case \STR_PAD_RIGHT: + return $this->append(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + case \STR_PAD_LEFT: + return $this->prepend(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + case \STR_PAD_BOTH: + $freeLen /= 2; + + $rightLen = ceil($freeLen); + $len = $rightLen % $padLen; + $str = $this->append(str_repeat($pad->string, intdiv($rightLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + $leftLen = floor($freeLen); + $len = $leftLen % $padLen; + + return $str->prepend(str_repeat($pad->string, intdiv($leftLen, $padLen)).($len ? $pad->slice(0, $len) : '')); + + default: + throw new InvalidArgumentException('Invalid padding type.'); + } + } + + /** + * Based on https://github.com/jquast/wcwidth, a Python implementation of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c. + */ + private function wcswidth(string $string): int + { + $width = 0; + + foreach (preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { + $codePoint = mb_ord($c, 'UTF-8'); + + if (0 === $codePoint // NULL + || 0x034F === $codePoint // COMBINING GRAPHEME JOINER + || (0x200B <= $codePoint && 0x200F >= $codePoint) // ZERO WIDTH SPACE to RIGHT-TO-LEFT MARK + || 0x2028 === $codePoint // LINE SEPARATOR + || 0x2029 === $codePoint // PARAGRAPH SEPARATOR + || (0x202A <= $codePoint && 0x202E >= $codePoint) // LEFT-TO-RIGHT EMBEDDING to RIGHT-TO-LEFT OVERRIDE + || (0x2060 <= $codePoint && 0x2063 >= $codePoint) // WORD JOINER to INVISIBLE SEPARATOR + ) { + continue; + } + + // Non printable characters + if (32 > $codePoint // C0 control characters + || (0x07F <= $codePoint && 0x0A0 > $codePoint) // C1 control characters and DEL + ) { + return -1; + } + + if (null === self::$tableZero) { + self::$tableZero = require __DIR__.'/Resources/data/wcswidth_table_zero.php'; + } + + if ($codePoint >= self::$tableZero[0][0] && $codePoint <= self::$tableZero[$ubound = \count(self::$tableZero) - 1][1]) { + $lbound = 0; + while ($ubound >= $lbound) { + $mid = floor(($lbound + $ubound) / 2); + + if ($codePoint > self::$tableZero[$mid][1]) { + $lbound = $mid + 1; + } elseif ($codePoint < self::$tableZero[$mid][0]) { + $ubound = $mid - 1; + } else { + continue 2; + } + } + } + + if (null === self::$tableWide) { + self::$tableWide = require __DIR__.'/Resources/data/wcswidth_table_wide.php'; + } + + if ($codePoint >= self::$tableWide[0][0] && $codePoint <= self::$tableWide[$ubound = \count(self::$tableWide) - 1][1]) { + $lbound = 0; + while ($ubound >= $lbound) { + $mid = floor(($lbound + $ubound) / 2); + + if ($codePoint > self::$tableWide[$mid][1]) { + $lbound = $mid + 1; + } elseif ($codePoint < self::$tableWide[$mid][0]) { + $ubound = $mid - 1; + } else { + $width += 2; + + continue 2; + } + } + } + + ++$width; + } + + return $width; + } +} diff --git a/tests/integration/vendor/symfony/string/ByteString.php b/tests/integration/vendor/symfony/string/ByteString.php new file mode 100644 index 000000000..bbf8614cf --- /dev/null +++ b/tests/integration/vendor/symfony/string/ByteString.php @@ -0,0 +1,506 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; +use Symfony\Component\String\Exception\RuntimeException; + +/** + * Represents a binary-safe string of bytes. + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +class ByteString extends AbstractString +{ + private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + + public function __construct(string $string = '') + { + $this->string = $string; + } + + /* + * The following method was derived from code of the Hack Standard Library (v4.40 - 2020-05-03) + * + * https://github.com/hhvm/hsl/blob/80a42c02f036f72a42f0415e80d6b847f4bf62d5/src/random/private.php#L16 + * + * Code subject to the MIT license (https://github.com/hhvm/hsl/blob/master/LICENSE). + * + * Copyright (c) 2004-2020, Facebook, Inc. (https://www.facebook.com/) + */ + + public static function fromRandom(int $length = 16, string $alphabet = null): self + { + if ($length <= 0) { + throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length)); + } + + $alphabet = $alphabet ?? self::ALPHABET_ALPHANUMERIC; + $alphabetSize = \strlen($alphabet); + $bits = (int) ceil(log($alphabetSize, 2.0)); + if ($bits <= 0 || $bits > 56) { + throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.'); + } + + $ret = ''; + while ($length > 0) { + $urandomLength = (int) ceil(2 * $length * $bits / 8.0); + $data = random_bytes($urandomLength); + $unpackedData = 0; + $unpackedBits = 0; + for ($i = 0; $i < $urandomLength && $length > 0; ++$i) { + // Unpack 8 bits + $unpackedData = ($unpackedData << 8) | \ord($data[$i]); + $unpackedBits += 8; + + // While we have enough bits to select a character from the alphabet, keep + // consuming the random data + for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) { + $index = ($unpackedData & ((1 << $bits) - 1)); + $unpackedData >>= $bits; + // Unfortunately, the alphabet size is not necessarily a power of two. + // Worst case, it is 2^k + 1, which means we need (k+1) bits and we + // have around a 50% chance of missing as k gets larger + if ($index < $alphabetSize) { + $ret .= $alphabet[$index]; + --$length; + } + } + } + } + + return new static($ret); + } + + public function bytesAt(int $offset): array + { + $str = $this->string[$offset] ?? ''; + + return '' === $str ? [] : [\ord($str)]; + } + + public function append(string ...$suffix): parent + { + $str = clone $this; + $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); + + return $str; + } + + public function camel(): parent + { + $str = clone $this; + $str->string = lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string)))); + + return $str; + } + + public function chunk(int $length = 1): array + { + if (1 > $length) { + throw new InvalidArgumentException('The chunk length must be greater than zero.'); + } + + if ('' === $this->string) { + return []; + } + + $str = clone $this; + $chunks = []; + + foreach (str_split($this->string, $length) as $chunk) { + $str->string = $chunk; + $chunks[] = clone $str; + } + + return $chunks; + } + + public function endsWith($suffix): bool + { + if ($suffix instanceof parent) { + $suffix = $suffix->string; + } elseif (\is_array($suffix) || $suffix instanceof \Traversable) { + return parent::endsWith($suffix); + } else { + $suffix = (string) $suffix; + } + + return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase); + } + + public function equalsTo($string): bool + { + if ($string instanceof parent) { + $string = $string->string; + } elseif (\is_array($string) || $string instanceof \Traversable) { + return parent::equalsTo($string); + } else { + $string = (string) $string; + } + + if ('' !== $string && $this->ignoreCase) { + return 0 === strcasecmp($string, $this->string); + } + + return $string === $this->string; + } + + public function folded(): parent + { + $str = clone $this; + $str->string = strtolower($str->string); + + return $str; + } + + public function indexOf($needle, int $offset = 0): ?int + { + if ($needle instanceof parent) { + $needle = $needle->string; + } elseif (\is_array($needle) || $needle instanceof \Traversable) { + return parent::indexOf($needle, $offset); + } else { + $needle = (string) $needle; + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset); + + return false === $i ? null : $i; + } + + public function indexOfLast($needle, int $offset = 0): ?int + { + if ($needle instanceof parent) { + $needle = $needle->string; + } elseif (\is_array($needle) || $needle instanceof \Traversable) { + return parent::indexOfLast($needle, $offset); + } else { + $needle = (string) $needle; + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset); + + return false === $i ? null : $i; + } + + public function isUtf8(): bool + { + return '' === $this->string || preg_match('//u', $this->string); + } + + public function join(array $strings, string $lastGlue = null): parent + { + $str = clone $this; + + $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; + $str->string = implode($this->string, $strings).$tail; + + return $str; + } + + public function length(): int + { + return \strlen($this->string); + } + + public function lower(): parent + { + $str = clone $this; + $str->string = strtolower($str->string); + + return $str; + } + + public function match(string $regexp, int $flags = 0, int $offset = 0): array + { + $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; + + if ($this->ignoreCase) { + $regexp .= 'i'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + return $matches; + } + + public function padBoth(int $length, string $padStr = ' '): parent + { + $str = clone $this; + $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH); + + return $str; + } + + public function padEnd(int $length, string $padStr = ' '): parent + { + $str = clone $this; + $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT); + + return $str; + } + + public function padStart(int $length, string $padStr = ' '): parent + { + $str = clone $this; + $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT); + + return $str; + } + + public function prepend(string ...$prefix): parent + { + $str = clone $this; + $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string; + + return $str; + } + + public function replace(string $from, string $to): parent + { + $str = clone $this; + + if ('' !== $from) { + $str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string); + } + + return $str; + } + + public function replaceMatches(string $fromRegexp, $to): parent + { + if ($this->ignoreCase) { + $fromRegexp .= 'i'; + } + + if (\is_array($to)) { + if (!\is_callable($to)) { + throw new \TypeError(sprintf('Argument 2 passed to "%s::replaceMatches()" must be callable, array given.', static::class)); + } + + $replace = 'preg_replace_callback'; + } else { + $replace = $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace'; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + if (null === $string = $replace($fromRegexp, $to, $this->string)) { + $lastError = preg_last_error(); + + foreach (get_defined_constants(true)['pcre'] as $k => $v) { + if ($lastError === $v && '_ERROR' === substr($k, -6)) { + throw new RuntimeException('Matching failed with '.$k.'.'); + } + } + + throw new RuntimeException('Matching failed with unknown error code.'); + } + } finally { + restore_error_handler(); + } + + $str = clone $this; + $str->string = $string; + + return $str; + } + + public function reverse(): parent + { + $str = clone $this; + $str->string = strrev($str->string); + + return $str; + } + + public function slice(int $start = 0, int $length = null): parent + { + $str = clone $this; + $str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX); + + return $str; + } + + public function snake(): parent + { + $str = $this->camel()->title(); + $str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string)); + + return $str; + } + + public function splice(string $replacement, int $start = 0, int $length = null): parent + { + $str = clone $this; + $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $limit = $limit ?? \PHP_INT_MAX) { + throw new InvalidArgumentException('Split limit must be a positive integer.'); + } + + if ('' === $delimiter) { + throw new InvalidArgumentException('Split delimiter is empty.'); + } + + if (null !== $flags) { + return parent::split($delimiter, $limit, $flags); + } + + $str = clone $this; + $chunks = $this->ignoreCase + ? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit) + : explode($delimiter, $this->string, $limit); + + foreach ($chunks as &$chunk) { + $str->string = $chunk; + $chunk = clone $str; + } + + return $chunks; + } + + public function startsWith($prefix): bool + { + if ($prefix instanceof parent) { + $prefix = $prefix->string; + } elseif (!\is_string($prefix)) { + return parent::startsWith($prefix); + } + + return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix))); + } + + public function title(bool $allWords = false): parent + { + $str = clone $this; + $str->string = $allWords ? ucwords($str->string) : ucfirst($str->string); + + return $str; + } + + public function toUnicodeString(string $fromEncoding = null): UnicodeString + { + return new UnicodeString($this->toCodePointString($fromEncoding)->string); + } + + public function toCodePointString(string $fromEncoding = null): CodePointString + { + $u = new CodePointString(); + + if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) { + $u->string = $this->string; + + return $u; + } + + set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); }); + + try { + try { + $validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true); + } catch (InvalidArgumentException $e) { + if (!\function_exists('iconv')) { + throw $e; + } + + $u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string); + + return $u; + } + } finally { + restore_error_handler(); + } + + if (!$validEncoding) { + throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252')); + } + + $u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252'); + + return $u; + } + + public function trim(string $chars = " \t\n\r\0\x0B\x0C"): parent + { + $str = clone $this; + $str->string = trim($str->string, $chars); + + return $str; + } + + public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): parent + { + $str = clone $this; + $str->string = rtrim($str->string, $chars); + + return $str; + } + + public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): parent + { + $str = clone $this; + $str->string = ltrim($str->string, $chars); + + return $str; + } + + public function upper(): parent + { + $str = clone $this; + $str->string = strtoupper($str->string); + + return $str; + } + + public function width(bool $ignoreAnsiDecoration = true): int + { + $string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string); + + return (new CodePointString($string))->width($ignoreAnsiDecoration); + } +} diff --git a/tests/integration/vendor/symfony/string/CHANGELOG.md b/tests/integration/vendor/symfony/string/CHANGELOG.md new file mode 100644 index 000000000..53af36400 --- /dev/null +++ b/tests/integration/vendor/symfony/string/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG +========= + +5.4 +--- + + * Add `trimSuffix()` and `trimPrefix()` methods + +5.3 +--- + + * Made `AsciiSlugger` fallback to parent locale's symbolsMap + +5.2.0 +----- + + * added a `FrenchInflector` class + +5.1.0 +----- + + * added the `AbstractString::reverse()` method + * made `AbstractString::width()` follow POSIX.1-2001 + * added `LazyString` which provides memoizing stringable objects + * The component is not marked as `@experimental` anymore + * added the `s()` helper method to get either an `UnicodeString` or `ByteString` instance, + depending of the input string UTF-8 compliancy + * added `$cut` parameter to `Symfony\Component\String\AbstractString::truncate()` + * added `AbstractString::containsAny()` + * allow passing a string of custom characters to `ByteString::fromRandom()` + +5.0.0 +----- + + * added the component as experimental diff --git a/tests/integration/vendor/symfony/string/CodePointString.php b/tests/integration/vendor/symfony/string/CodePointString.php new file mode 100644 index 000000000..8ab920941 --- /dev/null +++ b/tests/integration/vendor/symfony/string/CodePointString.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; + +/** + * Represents a string of Unicode code points encoded as UTF-8. + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +class CodePointString extends AbstractUnicodeString +{ + public function __construct(string $string = '') + { + if ('' !== $string && !preg_match('//u', $string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $this->string = $string; + } + + public function append(string ...$suffix): AbstractString + { + $str = clone $this; + $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); + + if (!preg_match('//u', $str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function chunk(int $length = 1): array + { + if (1 > $length) { + throw new InvalidArgumentException('The chunk length must be greater than zero.'); + } + + if ('' === $this->string) { + return []; + } + + $rx = '/('; + while (65535 < $length) { + $rx .= '.{65535}'; + $length -= 65535; + } + $rx .= '.{'.$length.'})/us'; + + $str = clone $this; + $chunks = []; + + foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { + $str->string = $chunk; + $chunks[] = clone $str; + } + + return $chunks; + } + + public function codePointsAt(int $offset): array + { + $str = $offset ? $this->slice($offset, 1) : $this; + + return '' === $str->string ? [] : [mb_ord($str->string, 'UTF-8')]; + } + + public function endsWith($suffix): bool + { + if ($suffix instanceof AbstractString) { + $suffix = $suffix->string; + } elseif (\is_array($suffix) || $suffix instanceof \Traversable) { + return parent::endsWith($suffix); + } else { + $suffix = (string) $suffix; + } + + if ('' === $suffix || !preg_match('//u', $suffix)) { + return false; + } + + if ($this->ignoreCase) { + return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string); + } + + return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix)); + } + + public function equalsTo($string): bool + { + if ($string instanceof AbstractString) { + $string = $string->string; + } elseif (\is_array($string) || $string instanceof \Traversable) { + return parent::equalsTo($string); + } else { + $string = (string) $string; + } + + if ('' !== $string && $this->ignoreCase) { + return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); + } + + return $string === $this->string; + } + + public function indexOf($needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (\is_array($needle) || $needle instanceof \Traversable) { + return parent::indexOf($needle, $offset); + } else { + $needle = (string) $needle; + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? mb_stripos($this->string, $needle, $offset, 'UTF-8') : mb_strpos($this->string, $needle, $offset, 'UTF-8'); + + return false === $i ? null : $i; + } + + public function indexOfLast($needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (\is_array($needle) || $needle instanceof \Traversable) { + return parent::indexOfLast($needle, $offset); + } else { + $needle = (string) $needle; + } + + if ('' === $needle) { + return null; + } + + $i = $this->ignoreCase ? mb_strripos($this->string, $needle, $offset, 'UTF-8') : mb_strrpos($this->string, $needle, $offset, 'UTF-8'); + + return false === $i ? null : $i; + } + + public function length(): int + { + return mb_strlen($this->string, 'UTF-8'); + } + + public function prepend(string ...$prefix): AbstractString + { + $str = clone $this; + $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; + + if (!preg_match('//u', $str->string)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function replace(string $from, string $to): AbstractString + { + $str = clone $this; + + if ('' === $from || !preg_match('//u', $from)) { + return $str; + } + + if ('' !== $to && !preg_match('//u', $to)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + if ($this->ignoreCase) { + $str->string = implode($to, preg_split('{'.preg_quote($from).'}iuD', $this->string)); + } else { + $str->string = str_replace($from, $to, $this->string); + } + + return $str; + } + + public function slice(int $start = 0, int $length = null): AbstractString + { + $str = clone $this; + $str->string = mb_substr($this->string, $start, $length, 'UTF-8'); + + return $str; + } + + public function splice(string $replacement, int $start = 0, int $length = null): AbstractString + { + if (!preg_match('//u', $replacement)) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + $str = clone $this; + $start = $start ? \strlen(mb_substr($this->string, 0, $start, 'UTF-8')) : 0; + $length = $length ? \strlen(mb_substr($this->string, $start, $length, 'UTF-8')) : $length; + $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $limit = $limit ?? \PHP_INT_MAX) { + throw new InvalidArgumentException('Split limit must be a positive integer.'); + } + + if ('' === $delimiter) { + throw new InvalidArgumentException('Split delimiter is empty.'); + } + + if (null !== $flags) { + return parent::split($delimiter.'u', $limit, $flags); + } + + if (!preg_match('//u', $delimiter)) { + throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); + } + + $str = clone $this; + $chunks = $this->ignoreCase + ? preg_split('{'.preg_quote($delimiter).'}iuD', $this->string, $limit) + : explode($delimiter, $this->string, $limit); + + foreach ($chunks as &$chunk) { + $str->string = $chunk; + $chunk = clone $str; + } + + return $chunks; + } + + public function startsWith($prefix): bool + { + if ($prefix instanceof AbstractString) { + $prefix = $prefix->string; + } elseif (\is_array($prefix) || $prefix instanceof \Traversable) { + return parent::startsWith($prefix); + } else { + $prefix = (string) $prefix; + } + + if ('' === $prefix || !preg_match('//u', $prefix)) { + return false; + } + + if ($this->ignoreCase) { + return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8'); + } + + return 0 === strncmp($this->string, $prefix, \strlen($prefix)); + } +} diff --git a/tests/integration/vendor/symfony/string/Exception/ExceptionInterface.php b/tests/integration/vendor/symfony/string/Exception/ExceptionInterface.php new file mode 100644 index 000000000..361978656 --- /dev/null +++ b/tests/integration/vendor/symfony/string/Exception/ExceptionInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Exception; + +interface ExceptionInterface extends \Throwable +{ +} diff --git a/tests/integration/vendor/symfony/string/Exception/InvalidArgumentException.php b/tests/integration/vendor/symfony/string/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..6aa586bcf --- /dev/null +++ b/tests/integration/vendor/symfony/string/Exception/InvalidArgumentException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/tests/integration/vendor/symfony/string/Exception/RuntimeException.php b/tests/integration/vendor/symfony/string/Exception/RuntimeException.php new file mode 100644 index 000000000..77cb091f9 --- /dev/null +++ b/tests/integration/vendor/symfony/string/Exception/RuntimeException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Exception; + +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/tests/integration/vendor/symfony/string/Inflector/EnglishInflector.php b/tests/integration/vendor/symfony/string/Inflector/EnglishInflector.php new file mode 100644 index 000000000..9f2fac675 --- /dev/null +++ b/tests/integration/vendor/symfony/string/Inflector/EnglishInflector.php @@ -0,0 +1,511 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Inflector; + +final class EnglishInflector implements InflectorInterface +{ + /** + * Map English plural to singular suffixes. + * + * @see http://english-zone.com/spelling/plurals.html + */ + private const PLURAL_MAP = [ + // First entry: plural suffix, reversed + // Second entry: length of plural suffix + // Third entry: Whether the suffix may succeed a vocal + // Fourth entry: Whether the suffix may succeed a consonant + // Fifth entry: singular suffix, normal + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['a', 1, true, true, ['on', 'um']], + + // nebulae (nebula) + ['ea', 2, true, true, 'a'], + + // services (service) + ['secivres', 8, true, true, 'service'], + + // mice (mouse), lice (louse) + ['eci', 3, false, true, 'ouse'], + + // geese (goose) + ['esee', 4, false, true, 'oose'], + + // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) + ['i', 1, true, true, 'us'], + + // men (man), women (woman) + ['nem', 3, true, true, 'man'], + + // children (child) + ['nerdlihc', 8, true, true, 'child'], + + // oxen (ox) + ['nexo', 4, false, false, 'ox'], + + // indices (index), appendices (appendix), prices (price) + ['seci', 4, false, true, ['ex', 'ix', 'ice']], + + // selfies (selfie) + ['seifles', 7, true, true, 'selfie'], + + // zombies (zombie) + ['seibmoz', 7, true, true, 'zombie'], + + // movies (movie) + ['seivom', 6, true, true, 'movie'], + + // conspectuses (conspectus), prospectuses (prospectus) + ['sesutcep', 8, true, true, 'pectus'], + + // feet (foot) + ['teef', 4, true, true, 'foot'], + + // geese (goose) + ['eseeg', 5, true, true, 'goose'], + + // teeth (tooth) + ['hteet', 5, true, true, 'tooth'], + + // news (news) + ['swen', 4, true, true, 'news'], + + // series (series) + ['seires', 6, true, true, 'series'], + + // babies (baby) + ['sei', 3, false, true, 'y'], + + // accesses (access), addresses (address), kisses (kiss) + ['sess', 4, true, false, 'ss'], + + // analyses (analysis), ellipses (ellipsis), fungi (fungus), + // neuroses (neurosis), theses (thesis), emphases (emphasis), + // oases (oasis), crises (crisis), houses (house), bases (base), + // atlases (atlas) + ['ses', 3, true, true, ['s', 'se', 'sis']], + + // objectives (objective), alternative (alternatives) + ['sevit', 5, true, true, 'tive'], + + // drives (drive) + ['sevird', 6, false, true, 'drive'], + + // lives (life), wives (wife) + ['sevi', 4, false, true, 'ife'], + + // moves (move) + ['sevom', 5, true, true, 'move'], + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff) + ['sev', 3, true, true, ['f', 've', 'ff']], + + // axes (axis), axes (ax), axes (axe) + ['sexa', 4, false, false, ['ax', 'axe', 'axis']], + + // indexes (index), matrixes (matrix) + ['sex', 3, true, false, 'x'], + + // quizzes (quiz) + ['sezz', 4, true, false, 'z'], + + // bureaus (bureau) + ['suae', 4, false, true, 'eau'], + + // fees (fee), trees (tree), employees (employee) + ['see', 3, true, true, 'ee'], + + // edges (edge) + ['segd', 4, true, true, 'dge'], + + // roses (rose), garages (garage), cassettes (cassette), + // waltzes (waltz), heroes (hero), bushes (bush), arches (arch), + // shoes (shoe) + ['se', 2, true, true, ['', 'e']], + + // tags (tag) + ['s', 1, true, true, ''], + + // chateaux (chateau) + ['xuae', 4, false, true, 'eau'], + + // people (person) + ['elpoep', 6, true, true, 'person'], + ]; + + /** + * Map English singular to plural suffixes. + * + * @see http://english-zone.com/spelling/plurals.html + */ + private const SINGULAR_MAP = [ + // First entry: singular suffix, reversed + // Second entry: length of singular suffix + // Third entry: Whether the suffix may succeed a vocal + // Fourth entry: Whether the suffix may succeed a consonant + // Fifth entry: plural suffix, normal + + // criterion (criteria) + ['airetirc', 8, false, false, 'criterion'], + + // nebulae (nebula) + ['aluben', 6, false, false, 'nebulae'], + + // children (child) + ['dlihc', 5, true, true, 'children'], + + // prices (price) + ['eci', 3, false, true, 'ices'], + + // services (service) + ['ecivres', 7, true, true, 'services'], + + // lives (life), wives (wife) + ['efi', 3, false, true, 'ives'], + + // selfies (selfie) + ['eifles', 6, true, true, 'selfies'], + + // movies (movie) + ['eivom', 5, true, true, 'movies'], + + // lice (louse) + ['esuol', 5, false, true, 'lice'], + + // mice (mouse) + ['esuom', 5, false, true, 'mice'], + + // geese (goose) + ['esoo', 4, false, true, 'eese'], + + // houses (house), bases (base) + ['es', 2, true, true, 'ses'], + + // geese (goose) + ['esoog', 5, true, true, 'geese'], + + // caves (cave) + ['ev', 2, true, true, 'ves'], + + // drives (drive) + ['evird', 5, false, true, 'drives'], + + // objectives (objective), alternative (alternatives) + ['evit', 4, true, true, 'tives'], + + // moves (move) + ['evom', 4, true, true, 'moves'], + + // staves (staff) + ['ffats', 5, true, true, 'staves'], + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) + ['ff', 2, true, true, 'ffs'], + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) + ['f', 1, true, true, ['fs', 'ves']], + + // arches (arch) + ['hc', 2, true, true, 'ches'], + + // bushes (bush) + ['hs', 2, true, true, 'shes'], + + // teeth (tooth) + ['htoot', 5, true, true, 'teeth'], + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['mu', 2, true, true, 'a'], + + // men (man), women (woman) + ['nam', 3, true, true, 'men'], + + // people (person) + ['nosrep', 6, true, true, ['persons', 'people']], + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['noi', 3, true, true, 'ions'], + + // coupon (coupons) + ['nop', 3, true, true, 'pons'], + + // seasons (season), treasons (treason), poisons (poison), lessons (lesson) + ['nos', 3, true, true, 'sons'], + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + ['no', 2, true, true, 'a'], + + // echoes (echo) + ['ohce', 4, true, true, 'echoes'], + + // heroes (hero) + ['oreh', 4, true, true, 'heroes'], + + // atlases (atlas) + ['salta', 5, true, true, 'atlases'], + + // irises (iris) + ['siri', 4, true, true, 'irises'], + + // analyses (analysis), ellipses (ellipsis), neuroses (neurosis) + // theses (thesis), emphases (emphasis), oases (oasis), + // crises (crisis) + ['sis', 3, true, true, 'ses'], + + // accesses (access), addresses (address), kisses (kiss) + ['ss', 2, true, false, 'sses'], + + // syllabi (syllabus) + ['suballys', 8, true, true, 'syllabi'], + + // buses (bus) + ['sub', 3, true, true, 'buses'], + + // circuses (circus) + ['suc', 3, true, true, 'cuses'], + + // conspectuses (conspectus), prospectuses (prospectus) + ['sutcep', 6, true, true, 'pectuses'], + + // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) + ['su', 2, true, true, 'i'], + + // news (news) + ['swen', 4, true, true, 'news'], + + // feet (foot) + ['toof', 4, true, true, 'feet'], + + // chateaux (chateau), bureaus (bureau) + ['uae', 3, false, true, ['eaus', 'eaux']], + + // oxen (ox) + ['xo', 2, false, false, 'oxen'], + + // hoaxes (hoax) + ['xaoh', 4, true, false, 'hoaxes'], + + // indices (index) + ['xedni', 5, false, true, ['indicies', 'indexes']], + + // boxes (box) + ['xo', 2, false, true, 'oxes'], + + // indexes (index), matrixes (matrix) + ['x', 1, true, false, ['cies', 'xes']], + + // appendices (appendix) + ['xi', 2, false, true, 'ices'], + + // babies (baby) + ['y', 1, false, true, 'ies'], + + // quizzes (quiz) + ['ziuq', 4, true, false, 'quizzes'], + + // waltzes (waltz) + ['z', 1, true, true, 'zes'], + ]; + + /** + * A list of words which should not be inflected, reversed. + */ + private const UNINFLECTED = [ + '', + + // data + 'atad', + + // deer + 'reed', + + // feedback + 'kcabdeef', + + // fish + 'hsif', + + // info + 'ofni', + + // moose + 'esoom', + + // series + 'seires', + + // sheep + 'peehs', + + // species + 'seiceps', + ]; + + /** + * {@inheritdoc} + */ + public function singularize(string $plural): array + { + $pluralRev = strrev($plural); + $lowerPluralRev = strtolower($pluralRev); + $pluralLength = \strlen($lowerPluralRev); + + // Check if the word is one which is not inflected, return early if so + if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) { + return [$plural]; + } + + // The outer loop iterates over the entries of the plural table + // The inner loop $j iterates over the characters of the plural suffix + // in the plural table to compare them with the characters of the actual + // given plural suffix + foreach (self::PLURAL_MAP as $map) { + $suffix = $map[0]; + $suffixLength = $map[1]; + $j = 0; + + // Compare characters in the plural table and of the suffix of the + // given plural one by one + while ($suffix[$j] === $lowerPluralRev[$j]) { + // Let $j point to the next character + ++$j; + + // Successfully compared the last character + // Add an entry with the singular suffix to the singular array + if ($j === $suffixLength) { + // Is there any character preceding the suffix in the plural string? + if ($j < $pluralLength) { + $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]); + + if (!$map[2] && $nextIsVocal) { + // suffix may not succeed a vocal but next char is one + break; + } + + if (!$map[3] && !$nextIsVocal) { + // suffix may not succeed a consonant but next char is one + break; + } + } + + $newBase = substr($plural, 0, $pluralLength - $suffixLength); + $newSuffix = $map[4]; + + // Check whether the first character in the plural suffix + // is uppercased. If yes, uppercase the first character in + // the singular suffix too + $firstUpper = ctype_upper($pluralRev[$j - 1]); + + if (\is_array($newSuffix)) { + $singulars = []; + + foreach ($newSuffix as $newSuffixEntry) { + $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); + } + + return $singulars; + } + + return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; + } + + // Suffix is longer than word + if ($j === $pluralLength) { + break; + } + } + } + + // Assume that plural and singular is identical + return [$plural]; + } + + /** + * {@inheritdoc} + */ + public function pluralize(string $singular): array + { + $singularRev = strrev($singular); + $lowerSingularRev = strtolower($singularRev); + $singularLength = \strlen($lowerSingularRev); + + // Check if the word is one which is not inflected, return early if so + if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) { + return [$singular]; + } + + // The outer loop iterates over the entries of the singular table + // The inner loop $j iterates over the characters of the singular suffix + // in the singular table to compare them with the characters of the actual + // given singular suffix + foreach (self::SINGULAR_MAP as $map) { + $suffix = $map[0]; + $suffixLength = $map[1]; + $j = 0; + + // Compare characters in the singular table and of the suffix of the + // given plural one by one + + while ($suffix[$j] === $lowerSingularRev[$j]) { + // Let $j point to the next character + ++$j; + + // Successfully compared the last character + // Add an entry with the plural suffix to the plural array + if ($j === $suffixLength) { + // Is there any character preceding the suffix in the plural string? + if ($j < $singularLength) { + $nextIsVocal = false !== strpos('aeiou', $lowerSingularRev[$j]); + + if (!$map[2] && $nextIsVocal) { + // suffix may not succeed a vocal but next char is one + break; + } + + if (!$map[3] && !$nextIsVocal) { + // suffix may not succeed a consonant but next char is one + break; + } + } + + $newBase = substr($singular, 0, $singularLength - $suffixLength); + $newSuffix = $map[4]; + + // Check whether the first character in the singular suffix + // is uppercased. If yes, uppercase the first character in + // the singular suffix too + $firstUpper = ctype_upper($singularRev[$j - 1]); + + if (\is_array($newSuffix)) { + $plurals = []; + + foreach ($newSuffix as $newSuffixEntry) { + $plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); + } + + return $plurals; + } + + return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; + } + + // Suffix is longer than word + if ($j === $singularLength) { + break; + } + } + } + + // Assume that plural is singular with a trailing `s` + return [$singular.'s']; + } +} diff --git a/tests/integration/vendor/symfony/string/Inflector/FrenchInflector.php b/tests/integration/vendor/symfony/string/Inflector/FrenchInflector.php new file mode 100644 index 000000000..42f6125aa --- /dev/null +++ b/tests/integration/vendor/symfony/string/Inflector/FrenchInflector.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Inflector; + +/** + * French inflector. + * + * This class does only inflect nouns; not adjectives nor composed words like "soixante-dix". + */ +final class FrenchInflector implements InflectorInterface +{ + /** + * A list of all rules for pluralise. + * + * @see https://la-conjugaison.nouvelobs.com/regles/grammaire/le-pluriel-des-noms-121.php + */ + private const PLURALIZE_REGEXP = [ + // First entry: regexp + // Second entry: replacement + + // Words finishing with "s", "x" or "z" are invariables + // Les mots finissant par "s", "x" ou "z" sont invariables + ['/(s|x|z)$/i', '\1'], + + // Words finishing with "eau" are pluralized with a "x" + // Les mots finissant par "eau" prennent tous un "x" au pluriel + ['/(eau)$/i', '\1x'], + + // Words finishing with "au" are pluralized with a "x" excepted "landau" + // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" + ['/^(landau)$/i', '\1s'], + ['/(au)$/i', '\1x'], + + // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" + // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" + ['/^(pneu|bleu|émeu)$/i', '\1s'], + ['/(eu)$/i', '\1x'], + + // Words finishing with "al" are pluralized with a "aux" excepted + // Les mots finissant en "al" se terminent en "aux" sauf + ['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'], + ['/al$/i', '\1aux'], + + // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux + ['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'], + + // Bijou, caillou, chou, genou, hibou, joujou et pou qui prennent un x au pluriel + ['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'], + + // Invariable words + ['/^(cinquante|soixante|mille)$/i', '\1'], + + // French titles + ['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'], + ['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'], + ]; + + /** + * A list of all rules for singularize. + */ + private const SINGULARIZE_REGEXP = [ + // First entry: regexp + // Second entry: replacement + + // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux + ['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'], + + // Words finishing with "eau" are pluralized with a "x" + // Les mots finissant par "eau" prennent tous un "x" au pluriel + ['/(eau)x$/i', '\1'], + + // Words finishing with "al" are pluralized with a "aux" expected + // Les mots finissant en "al" se terminent en "aux" sauf + ['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'], + + // Words finishing with "au" are pluralized with a "x" excepted "landau" + // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" + ['/(au)x$/i', '\1'], + + // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" + // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" + ['/(eu)x$/i', '\1'], + + // Words finishing with "ou" are pluralized with a "s" excepted bijou, caillou, chou, genou, hibou, joujou, pou + // Les mots finissant par "ou" prennent un "s" sauf bijou, caillou, chou, genou, hibou, joujou, pou + ['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'], + + // French titles + ['/^mes(dame|demoiselle)s$/', 'ma\1'], + ['/^Mes(dame|demoiselle)s$/', 'Ma\1'], + ['/^mes(sieur|seigneur)s$/', 'mon\1'], + ['/^Mes(sieur|seigneur)s$/', 'Mon\1'], + + //Default rule + ['/s$/i', ''], + ]; + + /** + * A list of words which should not be inflected. + * This list is only used by singularize. + */ + private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i'; + + /** + * {@inheritdoc} + */ + public function singularize(string $plural): array + { + if ($this->isInflectedWord($plural)) { + return [$plural]; + } + + foreach (self::SINGULARIZE_REGEXP as $rule) { + [$regexp, $replace] = $rule; + + if (1 === preg_match($regexp, $plural)) { + return [preg_replace($regexp, $replace, $plural)]; + } + } + + return [$plural]; + } + + /** + * {@inheritdoc} + */ + public function pluralize(string $singular): array + { + if ($this->isInflectedWord($singular)) { + return [$singular]; + } + + foreach (self::PLURALIZE_REGEXP as $rule) { + [$regexp, $replace] = $rule; + + if (1 === preg_match($regexp, $singular)) { + return [preg_replace($regexp, $replace, $singular)]; + } + } + + return [$singular.'s']; + } + + private function isInflectedWord(string $word): bool + { + return 1 === preg_match(self::UNINFLECTED, $word); + } +} diff --git a/tests/integration/vendor/symfony/string/Inflector/InflectorInterface.php b/tests/integration/vendor/symfony/string/Inflector/InflectorInterface.php new file mode 100644 index 000000000..67f283404 --- /dev/null +++ b/tests/integration/vendor/symfony/string/Inflector/InflectorInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Inflector; + +interface InflectorInterface +{ + /** + * Returns the singular forms of a string. + * + * If the method can't determine the form with certainty, several possible singulars are returned. + * + * @return string[] + */ + public function singularize(string $plural): array; + + /** + * Returns the plural forms of a string. + * + * If the method can't determine the form with certainty, several possible plurals are returned. + * + * @return string[] + */ + public function pluralize(string $singular): array; +} diff --git a/tests/integration/vendor/symfony/string/LICENSE b/tests/integration/vendor/symfony/string/LICENSE new file mode 100644 index 000000000..9c907a46a --- /dev/null +++ b/tests/integration/vendor/symfony/string/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-2022 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/string/LazyString.php b/tests/integration/vendor/symfony/string/LazyString.php new file mode 100644 index 000000000..b3801db77 --- /dev/null +++ b/tests/integration/vendor/symfony/string/LazyString.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +/** + * A string whose value is computed lazily by a callback. + * + * @author Nicolas Grekas + */ +class LazyString implements \Stringable, \JsonSerializable +{ + private $value; + + /** + * @param callable|array $callback A callable or a [Closure, method] lazy-callable + * + * @return static + */ + public static function fromCallable($callback, ...$arguments): self + { + if (!\is_callable($callback) && !(\is_array($callback) && isset($callback[0]) && $callback[0] instanceof \Closure && 2 >= \count($callback))) { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a callable or a [Closure, method] lazy-callable, "%s" given.', __METHOD__, get_debug_type($callback))); + } + + $lazyString = new static(); + $lazyString->value = static function () use (&$callback, &$arguments, &$value): string { + if (null !== $arguments) { + if (!\is_callable($callback)) { + $callback[0] = $callback[0](); + $callback[1] = $callback[1] ?? '__invoke'; + } + $value = $callback(...$arguments); + $callback = self::getPrettyName($callback); + $arguments = null; + } + + return $value ?? ''; + }; + + return $lazyString; + } + + /** + * @param string|int|float|bool|\Stringable $value + * + * @return static + */ + public static function fromStringable($value): self + { + if (!self::isStringable($value)) { + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a scalar or a stringable object, "%s" given.', __METHOD__, get_debug_type($value))); + } + + if (\is_object($value)) { + return static::fromCallable([$value, '__toString']); + } + + $lazyString = new static(); + $lazyString->value = (string) $value; + + return $lazyString; + } + + /** + * Tells whether the provided value can be cast to string. + */ + final public static function isStringable($value): bool + { + return \is_string($value) || $value instanceof self || (\is_object($value) ? method_exists($value, '__toString') : is_scalar($value)); + } + + /** + * Casts scalars and stringable objects to strings. + * + * @param object|string|int|float|bool $value + * + * @throws \TypeError When the provided value is not stringable + */ + final public static function resolve($value): string + { + return $value; + } + + /** + * @return string + */ + public function __toString() + { + if (\is_string($this->value)) { + return $this->value; + } + + try { + return $this->value = ($this->value)(); + } catch (\Throwable $e) { + if (\TypeError::class === \get_class($e) && __FILE__ === $e->getFile()) { + $type = explode(', ', $e->getMessage()); + $type = substr(array_pop($type), 0, -\strlen(' returned')); + $r = new \ReflectionFunction($this->value); + $callback = $r->getStaticVariables()['callback']; + + $e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type)); + } + + if (\PHP_VERSION_ID < 70400) { + // leverage the ErrorHandler component with graceful fallback when it's not available + return trigger_error($e, \E_USER_ERROR); + } + + throw $e; + } + } + + public function __sleep(): array + { + $this->__toString(); + + return ['value']; + } + + public function jsonSerialize(): string + { + return $this->__toString(); + } + + private function __construct() + { + } + + private static function getPrettyName(callable $callback): string + { + if (\is_string($callback)) { + return $callback; + } + + if (\is_array($callback)) { + $class = \is_object($callback[0]) ? get_debug_type($callback[0]) : $callback[0]; + $method = $callback[1]; + } elseif ($callback instanceof \Closure) { + $r = new \ReflectionFunction($callback); + + if (false !== strpos($r->name, '{closure}') || !$class = $r->getClosureScopeClass()) { + return $r->name; + } + + $class = $class->name; + $method = $r->name; + } else { + $class = get_debug_type($callback); + $method = '__invoke'; + } + + return $class.'::'.$method; + } +} diff --git a/tests/integration/vendor/symfony/string/README.md b/tests/integration/vendor/symfony/string/README.md new file mode 100644 index 000000000..9c7e1e190 --- /dev/null +++ b/tests/integration/vendor/symfony/string/README.md @@ -0,0 +1,14 @@ +String Component +================ + +The String component provides an object-oriented API to strings and deals +with bytes, UTF-8 code points and grapheme clusters in a unified way. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/string.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/tests/integration/vendor/symfony/string/Resources/data/wcswidth_table_wide.php b/tests/integration/vendor/symfony/string/Resources/data/wcswidth_table_wide.php new file mode 100644 index 000000000..43c802d05 --- /dev/null +++ b/tests/integration/vendor/symfony/string/Resources/data/wcswidth_table_wide.php @@ -0,0 +1,1135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +if (!\function_exists(u::class)) { + function u(?string $string = ''): UnicodeString + { + return new UnicodeString($string ?? ''); + } +} + +if (!\function_exists(b::class)) { + function b(?string $string = ''): ByteString + { + return new ByteString($string ?? ''); + } +} + +if (!\function_exists(s::class)) { + /** + * @return UnicodeString|ByteString + */ + function s(?string $string = ''): AbstractString + { + $string = $string ?? ''; + + return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string); + } +} diff --git a/tests/integration/vendor/symfony/string/Slugger/AsciiSlugger.php b/tests/integration/vendor/symfony/string/Slugger/AsciiSlugger.php new file mode 100644 index 000000000..5aecfeb5f --- /dev/null +++ b/tests/integration/vendor/symfony/string/Slugger/AsciiSlugger.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Slugger; + +use Symfony\Component\String\AbstractUnicodeString; +use Symfony\Component\String\UnicodeString; +use Symfony\Contracts\Translation\LocaleAwareInterface; + +if (!interface_exists(LocaleAwareInterface::class)) { + throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".'); +} + +/** + * @author Titouan Galopin + */ +class AsciiSlugger implements SluggerInterface, LocaleAwareInterface +{ + private const LOCALE_TO_TRANSLITERATOR_ID = [ + 'am' => 'Amharic-Latin', + 'ar' => 'Arabic-Latin', + 'az' => 'Azerbaijani-Latin', + 'be' => 'Belarusian-Latin', + 'bg' => 'Bulgarian-Latin', + 'bn' => 'Bengali-Latin', + 'de' => 'de-ASCII', + 'el' => 'Greek-Latin', + 'fa' => 'Persian-Latin', + 'he' => 'Hebrew-Latin', + 'hy' => 'Armenian-Latin', + 'ka' => 'Georgian-Latin', + 'kk' => 'Kazakh-Latin', + 'ky' => 'Kirghiz-Latin', + 'ko' => 'Korean-Latin', + 'mk' => 'Macedonian-Latin', + 'mn' => 'Mongolian-Latin', + 'or' => 'Oriya-Latin', + 'ps' => 'Pashto-Latin', + 'ru' => 'Russian-Latin', + 'sr' => 'Serbian-Latin', + 'sr_Cyrl' => 'Serbian-Latin', + 'th' => 'Thai-Latin', + 'tk' => 'Turkmen-Latin', + 'uk' => 'Ukrainian-Latin', + 'uz' => 'Uzbek-Latin', + 'zh' => 'Han-Latin', + ]; + + private $defaultLocale; + private $symbolsMap = [ + 'en' => ['@' => 'at', '&' => 'and'], + ]; + + /** + * Cache of transliterators per locale. + * + * @var \Transliterator[] + */ + private $transliterators = []; + + /** + * @param array|\Closure|null $symbolsMap + */ + public function __construct(string $defaultLocale = null, $symbolsMap = null) + { + if (null !== $symbolsMap && !\is_array($symbolsMap) && !$symbolsMap instanceof \Closure) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be array, Closure or null, "%s" given.', __METHOD__, \gettype($symbolsMap))); + } + + $this->defaultLocale = $defaultLocale; + $this->symbolsMap = $symbolsMap ?? $this->symbolsMap; + } + + /** + * {@inheritdoc} + */ + public function setLocale($locale) + { + $this->defaultLocale = $locale; + } + + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->defaultLocale; + } + + /** + * {@inheritdoc} + */ + public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString + { + $locale = $locale ?? $this->defaultLocale; + + $transliterator = []; + if ($locale && ('de' === $locale || 0 === strpos($locale, 'de_'))) { + // Use the shortcut for German in UnicodeString::ascii() if possible (faster and no requirement on intl) + $transliterator = ['de-ASCII']; + } elseif (\function_exists('transliterator_transliterate') && $locale) { + $transliterator = (array) $this->createTransliterator($locale); + } + + if ($this->symbolsMap instanceof \Closure) { + // If the symbols map is passed as a closure, there is no need to fallback to the parent locale + // as the closure can just provide substitutions for all locales of interest. + $symbolsMap = $this->symbolsMap; + array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) { + return $symbolsMap($s, $locale); + }); + } + + $unicodeString = (new UnicodeString($string))->ascii($transliterator); + + if (\is_array($this->symbolsMap)) { + $map = null; + if (isset($this->symbolsMap[$locale])) { + $map = $this->symbolsMap[$locale]; + } else { + $parent = self::getParentLocale($locale); + if ($parent && isset($this->symbolsMap[$parent])) { + $map = $this->symbolsMap[$parent]; + } + } + if ($map) { + foreach ($map as $char => $replace) { + $unicodeString = $unicodeString->replace($char, ' '.$replace.' '); + } + } + } + + return $unicodeString + ->replaceMatches('/[^A-Za-z0-9]++/', $separator) + ->trim($separator) + ; + } + + private function createTransliterator(string $locale): ?\Transliterator + { + if (\array_key_exists($locale, $this->transliterators)) { + return $this->transliterators[$locale]; + } + + // Exact locale supported, cache and return + if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) { + return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); + } + + // Locale not supported and no parent, fallback to any-latin + if (!$parent = self::getParentLocale($locale)) { + return $this->transliterators[$locale] = null; + } + + // Try to use the parent locale (ie. try "de" for "de_AT") and cache both locales + if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) { + $transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); + } + + return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null; + } + + private static function getParentLocale(?string $locale): ?string + { + if (!$locale) { + return null; + } + if (false === $str = strrchr($locale, '_')) { + // no parent locale + return null; + } + + return substr($locale, 0, -\strlen($str)); + } +} diff --git a/tests/integration/vendor/symfony/string/Slugger/SluggerInterface.php b/tests/integration/vendor/symfony/string/Slugger/SluggerInterface.php new file mode 100644 index 000000000..c679ed933 --- /dev/null +++ b/tests/integration/vendor/symfony/string/Slugger/SluggerInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Slugger; + +use Symfony\Component\String\AbstractUnicodeString; + +/** + * Creates a URL-friendly slug from a given string. + * + * @author Titouan Galopin + */ +interface SluggerInterface +{ + /** + * Creates a slug for the given string and locale, using appropriate transliteration when needed. + */ + public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString; +} diff --git a/tests/integration/vendor/symfony/string/UnicodeString.php b/tests/integration/vendor/symfony/string/UnicodeString.php new file mode 100644 index 000000000..9b906c6fc --- /dev/null +++ b/tests/integration/vendor/symfony/string/UnicodeString.php @@ -0,0 +1,377 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String; + +use Symfony\Component\String\Exception\ExceptionInterface; +use Symfony\Component\String\Exception\InvalidArgumentException; + +/** + * Represents a string of Unicode grapheme clusters encoded as UTF-8. + * + * A letter followed by combining characters (accents typically) form what Unicode defines + * as a grapheme cluster: a character as humans mean it in written texts. This class knows + * about the concept and won't split a letter apart from its combining accents. It also + * ensures all string comparisons happen on their canonically-composed representation, + * ignoring e.g. the order in which accents are listed when a letter has many of them. + * + * @see https://unicode.org/reports/tr15/ + * + * @author Nicolas Grekas + * @author Hugo Hamon + * + * @throws ExceptionInterface + */ +class UnicodeString extends AbstractUnicodeString +{ + public function __construct(string $string = '') + { + $this->string = normalizer_is_normalized($string) ? $string : normalizer_normalize($string); + + if (false === $this->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + } + + public function append(string ...$suffix): AbstractString + { + $str = clone $this; + $str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix)); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function chunk(int $length = 1): array + { + if (1 > $length) { + throw new InvalidArgumentException('The chunk length must be greater than zero.'); + } + + if ('' === $this->string) { + return []; + } + + $rx = '/('; + while (65535 < $length) { + $rx .= '\X{65535}'; + $length -= 65535; + } + $rx .= '\X{'.$length.'})/u'; + + $str = clone $this; + $chunks = []; + + foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { + $str->string = $chunk; + $chunks[] = clone $str; + } + + return $chunks; + } + + public function endsWith($suffix): bool + { + if ($suffix instanceof AbstractString) { + $suffix = $suffix->string; + } elseif (\is_array($suffix) || $suffix instanceof \Traversable) { + return parent::endsWith($suffix); + } else { + $suffix = (string) $suffix; + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form); + + if ('' === $suffix || false === $suffix) { + return false; + } + + if ($this->ignoreCase) { + return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8'); + } + + return $suffix === grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)); + } + + public function equalsTo($string): bool + { + if ($string instanceof AbstractString) { + $string = $string->string; + } elseif (\is_array($string) || $string instanceof \Traversable) { + return parent::equalsTo($string); + } else { + $string = (string) $string; + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form); + + if ('' !== $string && false !== $string && $this->ignoreCase) { + return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); + } + + return $string === $this->string; + } + + public function indexOf($needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (\is_array($needle) || $needle instanceof \Traversable) { + return parent::indexOf($needle, $offset); + } else { + $needle = (string) $needle; + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); + + if ('' === $needle || false === $needle) { + return null; + } + + try { + $i = $this->ignoreCase ? grapheme_stripos($this->string, $needle, $offset) : grapheme_strpos($this->string, $needle, $offset); + } catch (\ValueError $e) { + return null; + } + + return false === $i ? null : $i; + } + + public function indexOfLast($needle, int $offset = 0): ?int + { + if ($needle instanceof AbstractString) { + $needle = $needle->string; + } elseif (\is_array($needle) || $needle instanceof \Traversable) { + return parent::indexOfLast($needle, $offset); + } else { + $needle = (string) $needle; + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); + + if ('' === $needle || false === $needle) { + return null; + } + + $string = $this->string; + + if (0 > $offset) { + // workaround https://bugs.php.net/74264 + if (0 > $offset += grapheme_strlen($needle)) { + $string = grapheme_substr($string, 0, $offset); + } + $offset = 0; + } + + $i = $this->ignoreCase ? grapheme_strripos($string, $needle, $offset) : grapheme_strrpos($string, $needle, $offset); + + return false === $i ? null : $i; + } + + public function join(array $strings, string $lastGlue = null): AbstractString + { + $str = parent::join($strings, $lastGlue); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + return $str; + } + + public function length(): int + { + return grapheme_strlen($this->string); + } + + /** + * @return static + */ + public function normalize(int $form = self::NFC): parent + { + $str = clone $this; + + if (\in_array($form, [self::NFC, self::NFKC], true)) { + normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); + } elseif (!\in_array($form, [self::NFD, self::NFKD], true)) { + throw new InvalidArgumentException('Unsupported normalization form.'); + } elseif (!normalizer_is_normalized($str->string, $form)) { + $str->string = normalizer_normalize($str->string, $form); + $str->ignoreCase = null; + } + + return $str; + } + + public function prepend(string ...$prefix): AbstractString + { + $str = clone $this; + $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function replace(string $from, string $to): AbstractString + { + $str = clone $this; + normalizer_is_normalized($from) ?: $from = normalizer_normalize($from); + + if ('' !== $from && false !== $from) { + $tail = $str->string; + $result = ''; + $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; + + while ('' !== $tail && false !== $i = $indexOf($tail, $from)) { + $slice = grapheme_substr($tail, 0, $i); + $result .= $slice.$to; + $tail = substr($tail, \strlen($slice) + \strlen($from)); + } + + $str->string = $result.$tail; + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + } + + return $str; + } + + public function replaceMatches(string $fromRegexp, $to): AbstractString + { + $str = parent::replaceMatches($fromRegexp, $to); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + return $str; + } + + public function slice(int $start = 0, int $length = null): AbstractString + { + $str = clone $this; + + if (\PHP_VERSION_ID < 80000 && 0 > $start && grapheme_strlen($this->string) < -$start) { + $start = 0; + } + $str->string = (string) grapheme_substr($this->string, $start, $length ?? 2147483647); + + return $str; + } + + public function splice(string $replacement, int $start = 0, int $length = null): AbstractString + { + $str = clone $this; + + if (\PHP_VERSION_ID < 80000 && 0 > $start && grapheme_strlen($this->string) < -$start) { + $start = 0; + } + $start = $start ? \strlen(grapheme_substr($this->string, 0, $start)) : 0; + $length = $length ? \strlen(grapheme_substr($this->string, $start, $length ?? 2147483647)) : $length; + $str->string = substr_replace($this->string, $replacement, $start, $length ?? 2147483647); + normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); + + if (false === $str->string) { + throw new InvalidArgumentException('Invalid UTF-8 string.'); + } + + return $str; + } + + public function split(string $delimiter, int $limit = null, int $flags = null): array + { + if (1 > $limit = $limit ?? 2147483647) { + throw new InvalidArgumentException('Split limit must be a positive integer.'); + } + + if ('' === $delimiter) { + throw new InvalidArgumentException('Split delimiter is empty.'); + } + + if (null !== $flags) { + return parent::split($delimiter.'u', $limit, $flags); + } + + normalizer_is_normalized($delimiter) ?: $delimiter = normalizer_normalize($delimiter); + + if (false === $delimiter) { + throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); + } + + $str = clone $this; + $tail = $this->string; + $chunks = []; + $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; + + while (1 < $limit && false !== $i = $indexOf($tail, $delimiter)) { + $str->string = grapheme_substr($tail, 0, $i); + $chunks[] = clone $str; + $tail = substr($tail, \strlen($str->string) + \strlen($delimiter)); + --$limit; + } + + $str->string = $tail; + $chunks[] = clone $str; + + return $chunks; + } + + public function startsWith($prefix): bool + { + if ($prefix instanceof AbstractString) { + $prefix = $prefix->string; + } elseif (\is_array($prefix) || $prefix instanceof \Traversable) { + return parent::startsWith($prefix); + } else { + $prefix = (string) $prefix; + } + + $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; + normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form); + + if ('' === $prefix || false === $prefix) { + return false; + } + + if ($this->ignoreCase) { + return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8'); + } + + return $prefix === grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES); + } + + public function __wakeup() + { + if (!\is_string($this->string)) { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); + } + + public function __clone() + { + if (null === $this->ignoreCase) { + normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); + } + + $this->ignoreCase = false; + } +} diff --git a/tests/integration/vendor/symfony/string/composer.json b/tests/integration/vendor/symfony/string/composer.json new file mode 100644 index 000000000..2b88fd529 --- /dev/null +++ b/tests/integration/vendor/symfony/string/composer.json @@ -0,0 +1,43 @@ +{ + "name": "symfony/string", + "type": "library", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "keywords": ["string", "utf8", "utf-8", "grapheme", "i18n", "unicode"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\String\\": "" }, + "files": [ "Resources/functions.php" ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/tests/integration/vendor/symfony/debug/.gitignore b/tests/integration/vendor/symfony/translation-contracts/.gitignore similarity index 100% rename from tests/integration/vendor/symfony/debug/.gitignore rename to tests/integration/vendor/symfony/translation-contracts/.gitignore diff --git a/tests/integration/vendor/symfony/translation-contracts/CHANGELOG.md b/tests/integration/vendor/symfony/translation-contracts/CHANGELOG.md new file mode 100644 index 000000000..7932e2613 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/CHANGELOG.md @@ -0,0 +1,5 @@ +CHANGELOG +========= + +The changelog is maintained for all Symfony contracts at the following URL: +https://github.com/symfony/contracts/blob/main/CHANGELOG.md diff --git a/tests/integration/vendor/symfony/translation-contracts/LICENSE b/tests/integration/vendor/symfony/translation-contracts/LICENSE new file mode 100644 index 000000000..235841453 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2021 Fabien Potencier + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/tests/integration/vendor/symfony/translation-contracts/LocaleAwareInterface.php b/tests/integration/vendor/symfony/translation-contracts/LocaleAwareInterface.php new file mode 100644 index 000000000..693f92ba9 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/LocaleAwareInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation; + +interface LocaleAwareInterface +{ + /** + * Sets the current locale. + * + * @param string $locale The locale + * + * @throws \InvalidArgumentException If the locale contains invalid characters + */ + public function setLocale(string $locale); + + /** + * Returns the current locale. + * + * @return string + */ + public function getLocale(); +} diff --git a/tests/integration/vendor/symfony/translation-contracts/README.md b/tests/integration/vendor/symfony/translation-contracts/README.md new file mode 100644 index 000000000..42e5c5175 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/README.md @@ -0,0 +1,9 @@ +Symfony Translation Contracts +============================= + +A set of abstractions extracted out of the Symfony components. + +Can be used to build on semantics that the Symfony components proved useful - and +that already have battle tested implementations. + +See https://github.com/symfony/contracts/blob/main/README.md for more information. diff --git a/tests/integration/vendor/symfony/translation-contracts/Test/TranslatorTest.php b/tests/integration/vendor/symfony/translation-contracts/Test/TranslatorTest.php new file mode 100644 index 000000000..890367657 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/Test/TranslatorTest.php @@ -0,0 +1,390 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation\Test; + +use PHPUnit\Framework\TestCase; +use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorTrait; + +/** + * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms + * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms. + * + * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms. + * The mozilla code is also interesting to check for. + * + * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199 + * + * The goal to cover all languages is to far fetched so this test case is smaller. + * + * @author Clemens Tolboom clemens@build2be.nl + */ +class TranslatorTest extends TestCase +{ + private $defaultLocale; + + protected function setUp(): void + { + $this->defaultLocale = \Locale::getDefault(); + \Locale::setDefault('en'); + } + + protected function tearDown(): void + { + \Locale::setDefault($this->defaultLocale); + } + + /** + * @return TranslatorInterface + */ + public function getTranslator() + { + return new class() implements TranslatorInterface { + use TranslatorTrait; + }; + } + + /** + * @dataProvider getTransTests + */ + public function testTrans($expected, $id, $parameters) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($id, $parameters)); + } + + /** + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithExplicitLocale($expected, $id, $number) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number])); + } + + /** + * @requires extension intl + * + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithDefaultLocale($expected, $id, $number) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number])); + } + + /** + * @dataProvider getTransChoiceTests + */ + public function testTransChoiceWithEnUsPosix($expected, $id, $number) + { + $translator = $this->getTranslator(); + $translator->setLocale('en_US_POSIX'); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number])); + } + + public function testGetSetLocale() + { + $translator = $this->getTranslator(); + + $this->assertEquals('en', $translator->getLocale()); + } + + /** + * @requires extension intl + */ + public function testGetLocaleReturnsDefaultLocaleIfNotSet() + { + $translator = $this->getTranslator(); + + \Locale::setDefault('pt_BR'); + $this->assertEquals('pt_BR', $translator->getLocale()); + + \Locale::setDefault('en'); + $this->assertEquals('en', $translator->getLocale()); + } + + public function getTransTests() + { + return [ + ['Symfony is great!', 'Symfony is great!', []], + ['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']], + ]; + } + + public function getTransChoiceTests() + { + return [ + ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0], + ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1], + ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10], + ['There are 0 apples', 'There is 1 apple|There are %count% apples', 0], + ['There is 1 apple', 'There is 1 apple|There are %count% apples', 1], + ['There are 10 apples', 'There is 1 apple|There are %count% apples', 10], + // custom validation messages may be coded with a fixed value + ['There are 2 apples', 'There are 2 apples', 2], + ]; + } + + /** + * @dataProvider getInternal + */ + public function testInterval($expected, $number, $interval) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number])); + } + + public function getInternal() + { + return [ + ['foo', 3, '{1,2, 3 ,4}'], + ['bar', 10, '{1,2, 3 ,4}'], + ['bar', 3, '[1,2]'], + ['foo', 1, '[1,2]'], + ['foo', 2, '[1,2]'], + ['bar', 1, ']1,2['], + ['bar', 2, ']1,2['], + ['foo', log(0), '[-Inf,2['], + ['foo', -log(0), '[-2,+Inf]'], + ]; + } + + /** + * @dataProvider getChooseTests + */ + public function testChoose($expected, $id, $number, $locale = null) + { + $translator = $this->getTranslator(); + + $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number], null, $locale)); + } + + public function testReturnMessageIfExactlyOneStandardRuleIsGiven() + { + $translator = $this->getTranslator(); + + $this->assertEquals('There are two apples', $translator->trans('There are two apples', ['%count%' => 2])); + } + + /** + * @dataProvider getNonMatchingMessages + */ + public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number) + { + $this->expectException(\InvalidArgumentException::class); + $translator = $this->getTranslator(); + + $translator->trans($id, ['%count%' => $number]); + } + + public function getNonMatchingMessages() + { + return [ + ['{0} There are no apples|{1} There is one apple', 2], + ['{1} There is one apple|]1,Inf] There are %count% apples', 0], + ['{1} There is one apple|]2,Inf] There are %count% apples', 2], + ['{0} There are no apples|There is one apple', 2], + ]; + } + + public function getChooseTests() + { + return [ + ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0], + ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0], + ['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0], + + ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1], + + ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10], + ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10], + ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10], + + ['There are 0 apples', 'There is one apple|There are %count% apples', 0], + ['There is one apple', 'There is one apple|There are %count% apples', 1], + ['There are 10 apples', 'There is one apple|There are %count% apples', 10], + + ['There are 0 apples', 'one: There is one apple|more: There are %count% apples', 0], + ['There is one apple', 'one: There is one apple|more: There are %count% apples', 1], + ['There are 10 apples', 'one: There is one apple|more: There are %count% apples', 10], + + ['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0], + ['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1], + ['There are 10 apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10], + + ['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0], + ['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1], + + // Indexed only tests which are Gettext PoFile* compatible strings. + ['There are 0 apples', 'There is one apple|There are %count% apples', 0], + ['There is one apple', 'There is one apple|There are %count% apples', 1], + ['There are 2 apples', 'There is one apple|There are %count% apples', 2], + + // Tests for float numbers + ['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7], + ['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1], + ['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7], + ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0], + ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0], + ['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0], + + // Test texts with new-lines + // with double-quotes and \n in id & double-quotes and actual newlines in text + ["This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 0], + // with double-quotes and \n in id and single-quotes and actual newlines in text + ["This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 1], + ["This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 5], + // with double-quotes and id split accros lines + ['This is a text with a + new-line in it. Selector = 1.', '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 1], + // with single-quotes and id split accros lines + ['This is a text with a + new-line in it. Selector > 1.', '{0}This is a text with a + new-line in it. Selector = 0.|{1}This is a text with a + new-line in it. Selector = 1.|[1,Inf]This is a text with a + new-line in it. Selector > 1.', 5], + // with single-quotes and \n in text + ['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0], + // with double-quotes and id split accros lines + ["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1], + // esacape pipe + ['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0], + // Empty plural set (2 plural forms) from a .PO file + ['', '|', 1], + // Empty plural set (3 plural forms) from a .PO file + ['', '||', 1], + + // Floating values + ['1.5 liters', '%count% liter|%count% liters', 1.5], + ['1.5 litre', '%count% litre|%count% litres', 1.5, 'fr'], + + // Negative values + ['-1 degree', '%count% degree|%count% degrees', -1], + ['-1 degré', '%count% degré|%count% degrés', -1], + ['-1.5 degrees', '%count% degree|%count% degrees', -1.5], + ['-1.5 degré', '%count% degré|%count% degrés', -1.5, 'fr'], + ['-2 degrees', '%count% degree|%count% degrees', -2], + ['-2 degrés', '%count% degré|%count% degrés', -2], + ]; + } + + /** + * @dataProvider failingLangcodes + */ + public function testFailedLangcodes($nplural, $langCodes) + { + $matrix = $this->generateTestData($langCodes); + $this->validateMatrix($nplural, $matrix, false); + } + + /** + * @dataProvider successLangcodes + */ + public function testLangcodes($nplural, $langCodes) + { + $matrix = $this->generateTestData($langCodes); + $this->validateMatrix($nplural, $matrix); + } + + /** + * This array should contain all currently known langcodes. + * + * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete. + * + * @return array + */ + public function successLangcodes() + { + return [ + ['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']], + ['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM', 'en_US_POSIX']], + ['3', ['be', 'bs', 'cs', 'hr']], + ['4', ['cy', 'mt', 'sl']], + ['6', ['ar']], + ]; + } + + /** + * This array should be at least empty within the near future. + * + * This both depends on a complete list trying to add above as understanding + * the plural rules of the current failing languages. + * + * @return array with nplural together with langcodes + */ + public function failingLangcodes() + { + return [ + ['1', ['fa']], + ['2', ['jbo']], + ['3', ['cbs']], + ['4', ['gd', 'kw']], + ['5', ['ga']], + ]; + } + + /** + * We validate only on the plural coverage. Thus the real rules is not tested. + * + * @param string $nplural Plural expected + * @param array $matrix Containing langcodes and their plural index values + * @param bool $expectSuccess + */ + protected function validateMatrix($nplural, $matrix, $expectSuccess = true) + { + foreach ($matrix as $langCode => $data) { + $indexes = array_flip($data); + if ($expectSuccess) { + $this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms."); + } else { + $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms."); + } + } + } + + protected function generateTestData($langCodes) + { + $translator = new class() { + use TranslatorTrait { + getPluralizationRule as public; + } + }; + + $matrix = []; + foreach ($langCodes as $langCode) { + for ($count = 0; $count < 200; ++$count) { + $plural = $translator->getPluralizationRule($count, $langCode); + $matrix[$langCode][$count] = $plural; + } + } + + return $matrix; + } +} diff --git a/tests/integration/vendor/symfony/translation-contracts/TranslatableInterface.php b/tests/integration/vendor/symfony/translation-contracts/TranslatableInterface.php new file mode 100644 index 000000000..47fd6fa02 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/TranslatableInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation; + +/** + * @author Nicolas Grekas + */ +interface TranslatableInterface +{ + public function trans(TranslatorInterface $translator, string $locale = null): string; +} diff --git a/tests/integration/vendor/symfony/translation-contracts/TranslatorInterface.php b/tests/integration/vendor/symfony/translation-contracts/TranslatorInterface.php new file mode 100644 index 000000000..77b7a9c58 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/TranslatorInterface.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation; + +/** + * @author Fabien Potencier + * + * @method string getLocale() Returns the default locale + */ +interface TranslatorInterface +{ + /** + * Translates the given message. + * + * When a number is provided as a parameter named "%count%", the message is parsed for plural + * forms and a translation is chosen according to this number using the following rules: + * + * Given a message with different plural translations separated by a + * pipe (|), this method returns the correct portion of the message based + * on the given number, locale and the pluralization rules in the message + * itself. + * + * The message supports two different types of pluralization rules: + * + * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples + * indexed: There is one apple|There are %count% apples + * + * The indexed solution can also contain labels (e.g. one: There is one apple). + * This is purely for making the translations more clear - it does not + * affect the functionality. + * + * The two methods can also be mixed: + * {0} There are no apples|one: There is one apple|more: There are %count% apples + * + * An interval can represent a finite set of numbers: + * {1,2,3,4} + * + * An interval can represent numbers between two numbers: + * [1, +Inf] + * ]-1,2[ + * + * The left delimiter can be [ (inclusive) or ] (exclusive). + * The right delimiter can be [ (exclusive) or ] (inclusive). + * Beside numbers, you can use -Inf and +Inf for the infinite. + * + * @see https://en.wikipedia.org/wiki/ISO_31-11 + * + * @param string $id The message id (may also be an object that can be cast to string) + * @param array $parameters An array of parameters for the message + * @param string|null $domain The domain for the message or null to use the default + * @param string|null $locale The locale or null to use the default + * + * @return string + * + * @throws \InvalidArgumentException If the locale contains invalid characters + */ + public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null); +} diff --git a/tests/integration/vendor/symfony/translation-contracts/TranslatorTrait.php b/tests/integration/vendor/symfony/translation-contracts/TranslatorTrait.php new file mode 100644 index 000000000..405ce8d70 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/TranslatorTrait.php @@ -0,0 +1,262 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Contracts\Translation; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * A trait to help implement TranslatorInterface and LocaleAwareInterface. + * + * @author Fabien Potencier + */ +trait TranslatorTrait +{ + private $locale; + + /** + * {@inheritdoc} + */ + public function setLocale(string $locale) + { + $this->locale = $locale; + } + + /** + * {@inheritdoc} + * + * @return string + */ + public function getLocale() + { + return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en'); + } + + /** + * {@inheritdoc} + */ + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + if (null === $id || '' === $id) { + return ''; + } + + if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) { + return strtr($id, $parameters); + } + + $number = (float) $parameters['%count%']; + $locale = $locale ?: $this->getLocale(); + + $parts = []; + if (preg_match('/^\|++$/', $id)) { + $parts = explode('|', $id); + } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) { + $parts = $matches[0]; + } + + $intervalRegexp = <<<'EOF' +/^(?P + ({\s* + (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) + \s*}) + + | + + (?P[\[\]]) + \s* + (?P-Inf|\-?\d+(\.\d+)?) + \s*,\s* + (?P\+?Inf|\-?\d+(\.\d+)?) + \s* + (?P[\[\]]) +)\s*(?P.*?)$/xs +EOF; + + $standardRules = []; + foreach ($parts as $part) { + $part = trim(str_replace('||', '|', $part)); + + // try to match an explicit rule, then fallback to the standard ones + if (preg_match($intervalRegexp, $part, $matches)) { + if ($matches[2]) { + foreach (explode(',', $matches[3]) as $n) { + if ($number == $n) { + return strtr($matches['message'], $parameters); + } + } + } else { + $leftNumber = '-Inf' === $matches['left'] ? -\INF : (float) $matches['left']; + $rightNumber = is_numeric($matches['right']) ? (float) $matches['right'] : \INF; + + if (('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber) + && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber) + ) { + return strtr($matches['message'], $parameters); + } + } + } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) { + $standardRules[] = $matches[1]; + } else { + $standardRules[] = $part; + } + } + + $position = $this->getPluralizationRule($number, $locale); + + if (!isset($standardRules[$position])) { + // when there's exactly one rule given, and that rule is a standard + // rule, use this rule + if (1 === \count($parts) && isset($standardRules[0])) { + return strtr($standardRules[0], $parameters); + } + + $message = sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number); + + if (class_exists(InvalidArgumentException::class)) { + throw new InvalidArgumentException($message); + } + + throw new \InvalidArgumentException($message); + } + + return strtr($standardRules[$position], $parameters); + } + + /** + * Returns the plural position to use for the given locale and number. + * + * The plural rules are derived from code of the Zend Framework (2010-09-25), + * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd). + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + private function getPluralizationRule(float $number, string $locale): int + { + $number = abs($number); + + switch ('pt_BR' !== $locale && 'en_US_POSIX' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) { + case 'af': + case 'bn': + case 'bg': + case 'ca': + case 'da': + case 'de': + case 'el': + case 'en': + case 'en_US_POSIX': + case 'eo': + case 'es': + case 'et': + case 'eu': + case 'fa': + case 'fi': + case 'fo': + case 'fur': + case 'fy': + case 'gl': + case 'gu': + case 'ha': + case 'he': + case 'hu': + case 'is': + case 'it': + case 'ku': + case 'lb': + case 'ml': + case 'mn': + case 'mr': + case 'nah': + case 'nb': + case 'ne': + case 'nl': + case 'nn': + case 'no': + case 'oc': + case 'om': + case 'or': + case 'pa': + case 'pap': + case 'ps': + case 'pt': + case 'so': + case 'sq': + case 'sv': + case 'sw': + case 'ta': + case 'te': + case 'tk': + case 'ur': + case 'zu': + return (1 == $number) ? 0 : 1; + + case 'am': + case 'bh': + case 'fil': + case 'fr': + case 'gun': + case 'hi': + case 'hy': + case 'ln': + case 'mg': + case 'nso': + case 'pt_BR': + case 'ti': + case 'wa': + return ($number < 2) ? 0 : 1; + + case 'be': + case 'bs': + case 'hr': + case 'ru': + case 'sh': + case 'sr': + case 'uk': + return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); + + case 'cs': + case 'sk': + return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2); + + case 'ga': + return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2); + + case 'lt': + return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); + + case 'sl': + return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3)); + + case 'mk': + return (1 == $number % 10) ? 0 : 1; + + case 'mt': + return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3)); + + case 'lv': + return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2); + + case 'pl': + return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2); + + case 'cy': + return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3)); + + case 'ro': + return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2); + + case 'ar': + return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); + + default: + return 0; + } + } +} diff --git a/tests/integration/vendor/symfony/translation-contracts/composer.json b/tests/integration/vendor/symfony/translation-contracts/composer.json new file mode 100644 index 000000000..65fe243a4 --- /dev/null +++ b/tests/integration/vendor/symfony/translation-contracts/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/translation-contracts", + "type": "library", + "description": "Generic abstractions related to translation", + "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "autoload": { + "psr-4": { "Symfony\\Contracts\\Translation\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/tests/integration/vendor/symfony/translation/.gitignore b/tests/integration/vendor/symfony/translation/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/tests/integration/vendor/symfony/translation/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/tests/integration/vendor/symfony/translation/CHANGELOG.md b/tests/integration/vendor/symfony/translation/CHANGELOG.md index 349faceb0..160b5e694 100644 --- a/tests/integration/vendor/symfony/translation/CHANGELOG.md +++ b/tests/integration/vendor/symfony/translation/CHANGELOG.md @@ -1,6 +1,103 @@ CHANGELOG ========= +5.4 +--- + + * Add `github` format & autodetection to render errors as annotations when + running the XLIFF linter command in a Github Actions environment. + * Translation providers are not experimental anymore + +5.3 +--- + + * Add `translation:pull` and `translation:push` commands to manage translations with third-party providers + * Add `TranslatorBagInterface::getCatalogues` method + * Add support to load XLIFF string in `XliffFileLoader` + +5.2.0 +----- + + * added support for calling `trans` with ICU formatted messages + * added `PseudoLocalizationTranslator` + * added `TranslatableMessage` objects that represent a message that can be translated + * added the `t()` function to easily create `TranslatableMessage` objects + * Added support for extracting messages from `TranslatableMessage` objects + +5.1.0 +----- + + * added support for `name` attribute on `unit` element from xliff2 to be used as a translation key instead of always the `source` element + +5.0.0 +----- + + * removed support for using `null` as the locale in `Translator` + * removed `TranslatorInterface` + * removed `MessageSelector` + * removed `ChoiceMessageFormatterInterface` + * removed `PluralizationRule` + * removed `Interval` + * removed `transChoice()` methods, use the trans() method instead with a %count% parameter + * removed `FileDumper::setBackup()` and `TranslationWriter::disableBackup()` + * removed `MessageFormatter::choiceFormat()` + * added argument `$filename` to `PhpExtractor::parseTokens()` + * removed support for implicit STDIN usage in the `lint:xliff` command, use `lint:xliff -` (append a dash) instead to make it explicit. + +4.4.0 +----- + + * deprecated support for using `null` as the locale in `Translator` + * deprecated accepting STDIN implicitly when using the `lint:xliff` command, use `lint:xliff -` (append a dash) instead to make it explicit. + * Marked the `TranslationDataCollector` class as `@final`. + +4.3.0 +----- + + * Improved Xliff 1.2 loader to load the original file's metadata + * Added `TranslatorPathsPass` + +4.2.0 +----- + + * Started using ICU parent locales as fallback locales. + * allow using the ICU message format using domains with the "+intl-icu" suffix + * deprecated `Translator::transChoice()` in favor of using `Translator::trans()` with a `%count%` parameter + * deprecated `TranslatorInterface` in favor of `Symfony\Contracts\Translation\TranslatorInterface` + * deprecated `MessageSelector`, `Interval` and `PluralizationRules`; use `IdentityTranslator` instead + * Added `IntlFormatter` and `IntlFormatterInterface` + * added support for multiple files and directories in `XliffLintCommand` + * Marked `Translator::getFallbackLocales()` and `TranslationDataCollector::getFallbackLocales()` as internal + +4.1.0 +----- + + * The `FileDumper::setBackup()` method is deprecated. + * The `TranslationWriter::disableBackup()` method is deprecated. + * The `XliffFileDumper` will write "name" on the "unit" node when dumping XLIFF 2.0. + +4.0.0 +----- + + * removed the backup feature of the `FileDumper` class + * removed `TranslationWriter::writeTranslations()` method + * removed support for passing `MessageSelector` instances to the constructor of the `Translator` class + +3.4.0 +----- + + * Added `TranslationDumperPass` + * Added `TranslationExtractorPass` + * Added `TranslatorPass` + * Added `TranslationReader` and `TranslationReaderInterface` + * Added `` section to the Xliff 2.0 dumper. + * Improved Xliff 2.0 loader to load `` section. + * Added `TranslationWriterInterface` + * Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write` + * added support for adding custom message formatter and decoupling the default one. + * Added `PhpExtractor` + * Added `PhpStringTokenParser` + 3.2.0 ----- diff --git a/tests/integration/vendor/symfony/translation/Catalogue/AbstractOperation.php b/tests/integration/vendor/symfony/translation/Catalogue/AbstractOperation.php index 8ea921aa4..98d42e5b6 100644 --- a/tests/integration/vendor/symfony/translation/Catalogue/AbstractOperation.php +++ b/tests/integration/vendor/symfony/translation/Catalogue/AbstractOperation.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Translation\Catalogue; -use Symfony\Component\Translation\MessageCatalogue; -use Symfony\Component\Translation\MessageCatalogueInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\LogicException; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; /** * Base catalogues binary operation class. @@ -26,23 +26,16 @@ */ abstract class AbstractOperation implements OperationInterface { - /** - * @var MessageCatalogueInterface The source catalogue - */ - protected $source; + public const OBSOLETE_BATCH = 'obsolete'; + public const NEW_BATCH = 'new'; + public const ALL_BATCH = 'all'; - /** - * @var MessageCatalogueInterface The target catalogue - */ + protected $source; protected $target; - - /** - * @var MessageCatalogue The result catalogue - */ protected $result; /** - * @var null|array The domains affected by this operation + * @var array|null The domains affected by this operation */ private $domains; @@ -50,30 +43,26 @@ abstract class AbstractOperation implements OperationInterface * This array stores 'all', 'new' and 'obsolete' messages for all valid domains. * * The data structure of this array is as follows: - * ```php - * array( - * 'domain 1' => array( - * 'all' => array(...), - * 'new' => array(...), - * 'obsolete' => array(...) - * ), - * 'domain 2' => array( - * 'all' => array(...), - * 'new' => array(...), - * 'obsolete' => array(...) - * ), - * ... - * ) - * ``` + * + * [ + * 'domain 1' => [ + * 'all' => [...], + * 'new' => [...], + * 'obsolete' => [...] + * ], + * 'domain 2' => [ + * 'all' => [...], + * 'new' => [...], + * 'obsolete' => [...] + * ], + * ... + * ] * * @var array The array that stores 'all', 'new' and 'obsolete' messages */ protected $messages; /** - * @param MessageCatalogueInterface $source The source catalogue - * @param MessageCatalogueInterface $target The target catalogue - * * @throws LogicException */ public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target) @@ -85,8 +74,7 @@ public function __construct(MessageCatalogueInterface $source, MessageCatalogueI $this->source = $source; $this->target = $target; $this->result = new MessageCatalogue($source->getLocale()); - $this->domains = null; - $this->messages = array(); + $this->messages = []; } /** @@ -95,7 +83,18 @@ public function __construct(MessageCatalogueInterface $source, MessageCatalogueI public function getDomains() { if (null === $this->domains) { - $this->domains = array_values(array_unique(array_merge($this->source->getDomains(), $this->target->getDomains()))); + $domains = []; + foreach ([$this->source, $this->target] as $catalogue) { + foreach ($catalogue->getDomains() as $domain) { + $domains[$domain] = $domain; + + if ($catalogue->all($domainIcu = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX)) { + $domains[$domainIcu] = $domainIcu; + } + } + } + + $this->domains = array_values($domains); } return $this->domains; @@ -104,49 +103,49 @@ public function getDomains() /** * {@inheritdoc} */ - public function getMessages($domain) + public function getMessages(string $domain) { - if (!in_array($domain, $this->getDomains())) { - throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain)); + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } - if (!isset($this->messages[$domain]['all'])) { + if (!isset($this->messages[$domain][self::ALL_BATCH])) { $this->processDomain($domain); } - return $this->messages[$domain]['all']; + return $this->messages[$domain][self::ALL_BATCH]; } /** * {@inheritdoc} */ - public function getNewMessages($domain) + public function getNewMessages(string $domain) { - if (!in_array($domain, $this->getDomains())) { - throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain)); + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } - if (!isset($this->messages[$domain]['new'])) { + if (!isset($this->messages[$domain][self::NEW_BATCH])) { $this->processDomain($domain); } - return $this->messages[$domain]['new']; + return $this->messages[$domain][self::NEW_BATCH]; } /** * {@inheritdoc} */ - public function getObsoleteMessages($domain) + public function getObsoleteMessages(string $domain) { - if (!in_array($domain, $this->getDomains())) { - throw new InvalidArgumentException(sprintf('Invalid domain: %s.', $domain)); + if (!\in_array($domain, $this->getDomains())) { + throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } - if (!isset($this->messages[$domain]['obsolete'])) { + if (!isset($this->messages[$domain][self::OBSOLETE_BATCH])) { $this->processDomain($domain); } - return $this->messages[$domain]['obsolete']; + return $this->messages[$domain][self::OBSOLETE_BATCH]; } /** @@ -163,11 +162,42 @@ public function getResult() return $this->result; } + /** + * @param self::*_BATCH $batch + */ + public function moveMessagesToIntlDomainsIfPossible(string $batch = self::ALL_BATCH): void + { + // If MessageFormatter class does not exists, intl domains are not supported. + if (!class_exists(\MessageFormatter::class)) { + return; + } + + foreach ($this->getDomains() as $domain) { + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + switch ($batch) { + case self::OBSOLETE_BATCH: $messages = $this->getObsoleteMessages($domain); break; + case self::NEW_BATCH: $messages = $this->getNewMessages($domain); break; + case self::ALL_BATCH: $messages = $this->getMessages($domain); break; + default: throw new \InvalidArgumentException(sprintf('$batch argument must be one of ["%s", "%s", "%s"].', self::ALL_BATCH, self::NEW_BATCH, self::OBSOLETE_BATCH)); + } + + if (!$messages || (!$this->source->all($intlDomain) && $this->source->all($domain))) { + continue; + } + + $result = $this->getResult(); + $allIntlMessages = $result->all($intlDomain); + $currentMessages = array_diff_key($messages, $result->all($domain)); + $result->replace($currentMessages, $domain); + $result->replace($allIntlMessages + $messages, $intlDomain); + } + } + /** * Performs operation on source and target catalogues for the given domain and * stores the results. * * @param string $domain The domain which the operation will be performed for */ - abstract protected function processDomain($domain); + abstract protected function processDomain(string $domain); } diff --git a/tests/integration/vendor/symfony/translation/Catalogue/MergeOperation.php b/tests/integration/vendor/symfony/translation/Catalogue/MergeOperation.php index 6db3f801f..87db2fb03 100644 --- a/tests/integration/vendor/symfony/translation/Catalogue/MergeOperation.php +++ b/tests/integration/vendor/symfony/translation/Catalogue/MergeOperation.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Translation\Catalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; + /** * Merge operation between two catalogues as follows: * all = source ∪ target = {x: x ∈ source ∨ x ∈ target} @@ -25,19 +27,21 @@ class MergeOperation extends AbstractOperation /** * {@inheritdoc} */ - protected function processDomain($domain) + protected function processDomain(string $domain) { - $this->messages[$domain] = array( - 'all' => array(), - 'new' => array(), - 'obsolete' => array(), - ); + $this->messages[$domain] = [ + 'all' => [], + 'new' => [], + 'obsolete' => [], + ]; + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; foreach ($this->source->all($domain) as $id => $message) { $this->messages[$domain]['all'][$id] = $message; - $this->result->add(array($id => $message), $domain); - if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) { - $this->result->setMetadata($id, $keyMetadata, $domain); + $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); } } @@ -45,9 +49,10 @@ protected function processDomain($domain) if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; - $this->result->add(array($id => $message), $domain); - if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) { - $this->result->setMetadata($id, $keyMetadata, $domain); + $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); } } } diff --git a/tests/integration/vendor/symfony/translation/Catalogue/OperationInterface.php b/tests/integration/vendor/symfony/translation/Catalogue/OperationInterface.php index 87d888efb..9ffac88d2 100644 --- a/tests/integration/vendor/symfony/translation/Catalogue/OperationInterface.php +++ b/tests/integration/vendor/symfony/translation/Catalogue/OperationInterface.php @@ -44,29 +44,23 @@ public function getDomains(); /** * Returns all valid messages ('all') after operation. * - * @param string $domain - * * @return array */ - public function getMessages($domain); + public function getMessages(string $domain); /** * Returns new messages ('new') after operation. * - * @param string $domain - * * @return array */ - public function getNewMessages($domain); + public function getNewMessages(string $domain); /** * Returns obsolete messages ('obsolete') after operation. * - * @param string $domain - * * @return array */ - public function getObsoleteMessages($domain); + public function getObsoleteMessages(string $domain); /** * Returns resulting catalogue ('result'). diff --git a/tests/integration/vendor/symfony/translation/Catalogue/TargetOperation.php b/tests/integration/vendor/symfony/translation/Catalogue/TargetOperation.php index f3b0a29df..682b5752f 100644 --- a/tests/integration/vendor/symfony/translation/Catalogue/TargetOperation.php +++ b/tests/integration/vendor/symfony/translation/Catalogue/TargetOperation.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Translation\Catalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; + /** * Target operation between two catalogues: * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target} @@ -26,29 +28,31 @@ class TargetOperation extends AbstractOperation /** * {@inheritdoc} */ - protected function processDomain($domain) + protected function processDomain(string $domain) { - $this->messages[$domain] = array( - 'all' => array(), - 'new' => array(), - 'obsolete' => array(), - ); + $this->messages[$domain] = [ + 'all' => [], + 'new' => [], + 'obsolete' => [], + ]; + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``, // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} // - // For 'new' messages, the code can't be simplied as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));`` + // For 'new' messages, the code can't be simplified as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));`` // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback} // - // For 'obsolete' messages, the code can't be simplifed as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))`` + // For 'obsolete' messages, the code can't be simplified as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))`` // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} foreach ($this->source->all($domain) as $id => $message) { if ($this->target->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; - $this->result->add(array($id => $message), $domain); - if (null !== $keyMetadata = $this->source->getMetadata($id, $domain)) { - $this->result->setMetadata($id, $keyMetadata, $domain); + $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); } } else { $this->messages[$domain]['obsolete'][$id] = $message; @@ -59,9 +63,10 @@ protected function processDomain($domain) if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; - $this->result->add(array($id => $message), $domain); - if (null !== $keyMetadata = $this->target->getMetadata($id, $domain)) { - $this->result->setMetadata($id, $keyMetadata, $domain); + $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; + $this->result->add([$id => $message], $d); + if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { + $this->result->setMetadata($id, $keyMetadata, $d); } } } diff --git a/tests/integration/vendor/symfony/translation/Command/TranslationPullCommand.php b/tests/integration/vendor/symfony/translation/Command/TranslationPullCommand.php new file mode 100644 index 000000000..52da595c6 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Command/TranslationPullCommand.php @@ -0,0 +1,188 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Translation\Catalogue\TargetOperation; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Provider\TranslationProviderCollection; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\Writer\TranslationWriterInterface; + +/** + * @author Mathieu Santostefano + */ +final class TranslationPullCommand extends Command +{ + use TranslationTrait; + + protected static $defaultName = 'translation:pull'; + protected static $defaultDescription = 'Pull translations from a given provider.'; + + private $providerCollection; + private $writer; + private $reader; + private $defaultLocale; + private $transPaths; + private $enabledLocales; + + public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = []) + { + $this->providerCollection = $providerCollection; + $this->writer = $writer; + $this->reader = $reader; + $this->defaultLocale = $defaultLocale; + $this->transPaths = $transPaths; + $this->enabledLocales = $enabledLocales; + + parent::__construct(); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('provider')) { + $suggestions->suggestValues($this->providerCollection->keys()); + + return; + } + + if ($input->mustSuggestOptionValuesFor('domains')) { + $provider = $this->providerCollection->get($input->getArgument('provider')); + + if ($provider && method_exists($provider, 'getDomains')) { + $domains = $provider->getDomains(); + $suggestions->suggestValues($domains); + } + + return; + } + + if ($input->mustSuggestOptionValuesFor('locales')) { + $suggestions->suggestValues($this->enabledLocales); + + return; + } + + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(['php', 'xlf', 'xlf12', 'xlf20', 'po', 'mo', 'yml', 'yaml', 'ts', 'csv', 'json', 'ini', 'res']); + } + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $keys = $this->providerCollection->keys(); + $defaultProvider = 1 === \count($keys) ? $keys[0] : null; + + $this + ->setDefinition([ + new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to pull translations from.', $defaultProvider), + new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with provider ones (it will delete not synchronized messages).'), + new InputOption('intl-icu', null, InputOption::VALUE_NONE, 'Associated to --force option, it will write messages in "%domain%+intl-icu.%locale%.xlf" files.'), + new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to pull.'), + new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to pull.'), + new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format.', 'xlf12'), + ]) + ->setHelp(<<<'EOF' +The %command.name% command pulls translations from the given provider. Only +new translations are pulled, existing ones are not overwritten. + +You can overwrite existing translations (and remove the missing ones on local side) by using the --force flag: + + php %command.full_name% --force provider + +Full example: + + php %command.full_name% provider --force --domains=messages --domains=validators --locales=en + +This command pulls all translations associated with the messages and validators domains for the en locale. +Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case. +Local translations for others domains and locales are ignored. +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $provider = $this->providerCollection->get($input->getArgument('provider')); + $force = $input->getOption('force'); + $intlIcu = $input->getOption('intl-icu'); + $locales = $input->getOption('locales') ?: $this->enabledLocales; + $domains = $input->getOption('domains'); + $format = $input->getOption('format'); + $xliffVersion = '1.2'; + + if ($intlIcu && !$force) { + $io->note('--intl-icu option only has an effect when used with --force. Here, it will be ignored.'); + } + + switch ($format) { + case 'xlf20': $xliffVersion = '2.0'; + // no break + case 'xlf12': $format = 'xlf'; + } + + $writeOptions = [ + 'path' => end($this->transPaths), + 'xliff_version' => $xliffVersion, + 'default_locale' => $this->defaultLocale, + ]; + + if (!$domains) { + $domains = $provider->getDomains(); + } + + $providerTranslations = $provider->read($domains, $locales); + + if ($force) { + foreach ($providerTranslations->getCatalogues() as $catalogue) { + $operation = new TargetOperation((new MessageCatalogue($catalogue->getLocale())), $catalogue); + if ($intlIcu) { + $operation->moveMessagesToIntlDomainsIfPossible(); + } + $this->writer->write($operation->getResult(), $format, $writeOptions); + } + + $io->success(sprintf('Local translations has been updated from "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } + + $localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths); + + // Append pulled translations to local ones. + $localTranslations->addBag($providerTranslations->diff($localTranslations)); + + foreach ($localTranslations->getCatalogues() as $catalogue) { + $this->writer->write($catalogue, $format, $writeOptions); + } + + $io->success(sprintf('New translations from "%s" has been written locally (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } +} diff --git a/tests/integration/vendor/symfony/translation/Command/TranslationPushCommand.php b/tests/integration/vendor/symfony/translation/Command/TranslationPushCommand.php new file mode 100644 index 000000000..56c6f5b1c --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Command/TranslationPushCommand.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Translation\Provider\TranslationProviderCollection; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\TranslatorBag; + +/** + * @author Mathieu Santostefano + */ +final class TranslationPushCommand extends Command +{ + use TranslationTrait; + + protected static $defaultName = 'translation:push'; + protected static $defaultDescription = 'Push translations to a given provider.'; + + private $providers; + private $reader; + private $transPaths; + private $enabledLocales; + + public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = []) + { + $this->providers = $providers; + $this->reader = $reader; + $this->transPaths = $transPaths; + $this->enabledLocales = $enabledLocales; + + parent::__construct(); + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestArgumentValuesFor('provider')) { + $suggestions->suggestValues($this->providers->keys()); + + return; + } + + if ($input->mustSuggestOptionValuesFor('domains')) { + $provider = $this->providers->get($input->getArgument('provider')); + + if ($provider && method_exists($provider, 'getDomains')) { + $domains = $provider->getDomains(); + $suggestions->suggestValues($domains); + } + + return; + } + + if ($input->mustSuggestOptionValuesFor('locales')) { + $suggestions->suggestValues($this->enabledLocales); + } + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $keys = $this->providers->keys(); + $defaultProvider = 1 === \count($keys) ? $keys[0] : null; + + $this + ->setDefinition([ + new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to push translations to.', $defaultProvider), + new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with local ones (it will delete not synchronized messages).'), + new InputOption('delete-missing', null, InputOption::VALUE_NONE, 'Delete translations available on provider but not locally.'), + new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to push.'), + new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to push.', $this->enabledLocales), + ]) + ->setHelp(<<<'EOF' +The %command.name% command pushes translations to the given provider. Only new +translations are pushed, existing ones are not overwritten. + +You can overwrite existing translations by using the --force flag: + + php %command.full_name% --force provider + +You can delete provider translations which are not present locally by using the --delete-missing flag: + + php %command.full_name% --delete-missing provider + +Full example: + + php %command.full_name% provider --force --delete-missing --domains=messages --domains=validators --locales=en + +This command pushes all translations associated with the messages and validators domains for the en locale. +Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case. +Provider translations for others domains and locales are ignored. +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $provider = $this->providers->get($input->getArgument('provider')); + + if (!$this->enabledLocales) { + throw new InvalidArgumentException(sprintf('You must define "framework.translator.enabled_locales" or "framework.translator.providers.%s.locales" config key in order to work with translation providers.', parse_url($provider, \PHP_URL_SCHEME))); + } + + $io = new SymfonyStyle($input, $output); + $domains = $input->getOption('domains'); + $locales = $input->getOption('locales'); + $force = $input->getOption('force'); + $deleteMissing = $input->getOption('delete-missing'); + + $localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths); + + if (!$domains) { + $domains = $this->getDomainsFromTranslatorBag($localTranslations); + } + + if (!$deleteMissing && $force) { + $provider->write($localTranslations); + + $io->success(sprintf('All local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } + + $providerTranslations = $provider->read($domains, $locales); + + if ($deleteMissing) { + $provider->delete($providerTranslations->diff($localTranslations)); + + $io->success(sprintf('Missing translations on "%s" has been deleted (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + // Read provider translations again, after missing translations deletion, + // to avoid push freshly deleted translations. + $providerTranslations = $provider->read($domains, $locales); + } + + $translationsToWrite = $localTranslations->diff($providerTranslations); + + if ($force) { + $translationsToWrite->addBag($localTranslations->intersect($providerTranslations)); + } + + $provider->write($translationsToWrite); + + $io->success(sprintf('%s local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', $force ? 'All' : 'New', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); + + return 0; + } + + private function getDomainsFromTranslatorBag(TranslatorBag $translatorBag): array + { + $domains = []; + + foreach ($translatorBag->getCatalogues() as $catalogue) { + $domains += $catalogue->getDomains(); + } + + return array_unique($domains); + } +} diff --git a/tests/integration/vendor/symfony/translation/Command/TranslationTrait.php b/tests/integration/vendor/symfony/translation/Command/TranslationTrait.php new file mode 100644 index 000000000..eafaffd3f --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Command/TranslationTrait.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Command; + +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; +use Symfony\Component\Translation\TranslatorBag; + +/** + * @internal + */ +trait TranslationTrait +{ + private function readLocalTranslations(array $locales, array $domains, array $transPaths): TranslatorBag + { + $bag = new TranslatorBag(); + + foreach ($locales as $locale) { + $catalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + $this->reader->read($path, $catalogue); + } + + if ($domains) { + foreach ($domains as $domain) { + $bag->addCatalogue($this->filterCatalogue($catalogue, $domain)); + } + } else { + $bag->addCatalogue($catalogue); + } + } + + return $bag; + } + + private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue + { + $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); + + // extract intl-icu messages only + $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; + if ($intlMessages = $catalogue->all($intlDomain)) { + $filteredCatalogue->add($intlMessages, $intlDomain); + } + + // extract all messages and subtract intl-icu messages + if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { + $filteredCatalogue->add($messages, $domain); + } + foreach ($catalogue->getResources() as $resource) { + $filteredCatalogue->addResource($resource); + } + + if ($metadata = $catalogue->getMetadata('', $intlDomain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $intlDomain); + } + } + + if ($metadata = $catalogue->getMetadata('', $domain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $domain); + } + } + + return $filteredCatalogue; + } +} diff --git a/tests/integration/vendor/symfony/translation/Command/XliffLintCommand.php b/tests/integration/vendor/symfony/translation/Command/XliffLintCommand.php new file mode 100644 index 000000000..fb2b5f31c --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Command/XliffLintCommand.php @@ -0,0 +1,286 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Command; + +use Symfony\Component\Console\CI\GithubActionReporter; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Completion\CompletionInput; +use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Util\XliffUtils; + +/** + * Validates XLIFF files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + * @author Javier Eguiluz + */ +class XliffLintCommand extends Command +{ + protected static $defaultName = 'lint:xliff'; + protected static $defaultDescription = 'Lint an XLIFF file and outputs encountered errors'; + + private $format; + private $displayCorrectFiles; + private $directoryIteratorProvider; + private $isReadableProvider; + private $requireStrictFileNames; + + public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = true) + { + parent::__construct($name); + + $this->directoryIteratorProvider = $directoryIteratorProvider; + $this->isReadableProvider = $isReadableProvider; + $this->requireStrictFileNames = $requireStrictFileNames; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDescription(self::$defaultDescription) + ->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format') + ->setHelp(<<%command.name% command lints an XLIFF file and outputs to STDOUT +the first encountered syntax error. + +You can validates XLIFF contents passed from STDIN: + + cat filename | php %command.full_name% - + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $filenames = (array) $input->getArgument('filename'); + $this->format = $input->getOption('format') ?? (GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt'); + $this->displayCorrectFiles = $output->isVerbose(); + + if (['-'] === $filenames) { + return $this->display($io, [$this->validate(file_get_contents('php://stdin'))]); + } + + if (!$filenames) { + throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); + } + + $filesInfo = []; + foreach ($filenames as $filename) { + if (!$this->isReadable($filename)) { + throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); + } + + foreach ($this->getFiles($filename) as $file) { + $filesInfo[] = $this->validate(file_get_contents($file), $file); + } + } + + return $this->display($io, $filesInfo); + } + + private function validate(string $content, string $file = null): array + { + $errors = []; + + // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input + if ('' === trim($content)) { + return ['file' => $file, 'valid' => true]; + } + + $internal = libxml_use_internal_errors(true); + + $document = new \DOMDocument(); + $document->loadXML($content); + + if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) { + $normalizedLocalePattern = sprintf('(%s|%s)', preg_quote($targetLanguage, '/'), preg_quote(str_replace('-', '_', $targetLanguage), '/')); + // strict file names require translation files to be named '____.locale.xlf' + // otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed + // also, the regexp matching must be case-insensitive, as defined for 'target-language' values + // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language + $expectedFilenamePattern = $this->requireStrictFileNames ? sprintf('/^.*\.(?i:%s)\.(?:xlf|xliff)/', $normalizedLocalePattern) : sprintf('/^(?:.*\.(?i:%s)|(?i:%s)\..*)\.(?:xlf|xliff)/', $normalizedLocalePattern, $normalizedLocalePattern); + + if (0 === preg_match($expectedFilenamePattern, basename($file))) { + $errors[] = [ + 'line' => -1, + 'column' => -1, + 'message' => sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', basename($file), $targetLanguage), + ]; + } + } + + foreach (XliffUtils::validateSchema($document) as $xmlError) { + $errors[] = [ + 'line' => $xmlError['line'], + 'column' => $xmlError['column'], + 'message' => $xmlError['message'], + ]; + } + + libxml_clear_errors(); + libxml_use_internal_errors($internal); + + return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors]; + } + + private function display(SymfonyStyle $io, array $files) + { + switch ($this->format) { + case 'txt': + return $this->displayTxt($io, $files); + case 'json': + return $this->displayJson($io, $files); + case 'github': + return $this->displayTxt($io, $files, true); + default: + throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); + } + } + + private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false) + { + $countFiles = \count($filesInfo); + $erroredFiles = 0; + $githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($io) : null; + + foreach ($filesInfo as $info) { + if ($info['valid'] && $this->displayCorrectFiles) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$erroredFiles; + $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + $io->listing(array_map(function ($error) use ($info, $githubReporter) { + // general document errors have a '-1' line number + $line = -1 === $error['line'] ? null : $error['line']; + + if ($githubReporter) { + $githubReporter->error($error['message'], $info['file'], $line, null !== $line ? $error['column'] : null); + } + + return null === $line ? $error['message'] : sprintf('Line %d, Column %d: %s', $line, $error['column'], $error['message']); + }, $info['messages'])); + } + } + + if (0 === $erroredFiles) { + $io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles)); + } else { + $io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles)); + } + + return min($erroredFiles, 1); + } + + private function displayJson(SymfonyStyle $io, array $filesInfo) + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + if (!$v['valid']) { + ++$errors; + } + }); + + $io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); + + return min($errors, 1); + } + + private function getFiles(string $fileOrDirectory) + { + if (is_file($fileOrDirectory)) { + yield new \SplFileInfo($fileOrDirectory); + + return; + } + + foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { + if (!\in_array($file->getExtension(), ['xlf', 'xliff'])) { + continue; + } + + yield $file; + } + } + + private function getDirectoryIterator(string $directory) + { + $default = function ($directory) { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + }; + + if (null !== $this->directoryIteratorProvider) { + return ($this->directoryIteratorProvider)($directory, $default); + } + + return $default($directory); + } + + private function isReadable(string $fileOrDirectory) + { + $default = function ($fileOrDirectory) { + return is_readable($fileOrDirectory); + }; + + if (null !== $this->isReadableProvider) { + return ($this->isReadableProvider)($fileOrDirectory, $default); + } + + return $default($fileOrDirectory); + } + + private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string + { + foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? [] as $attribute) { + if ('target-language' === $attribute->nodeName) { + return $attribute->nodeValue; + } + } + + return null; + } + + public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void + { + if ($input->mustSuggestOptionValuesFor('format')) { + $suggestions->suggestValues(['txt', 'json', 'github']); + } + } +} diff --git a/tests/integration/vendor/symfony/translation/DataCollector/TranslationDataCollector.php b/tests/integration/vendor/symfony/translation/DataCollector/TranslationDataCollector.php index 6b5e165aa..379130a44 100644 --- a/tests/integration/vendor/symfony/translation/DataCollector/TranslationDataCollector.php +++ b/tests/integration/vendor/symfony/translation/DataCollector/TranslationDataCollector.php @@ -16,20 +16,17 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; use Symfony\Component\Translation\DataCollectorTranslator; +use Symfony\Component\VarDumper\Cloner\Data; /** * @author Abdellatif Ait boudad + * + * @final */ class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface { - /** - * @var DataCollectorTranslator - */ private $translator; - /** - * @param DataCollectorTranslator $translator - */ public function __construct(DataCollectorTranslator $translator) { $this->translator = $translator; @@ -42,71 +39,87 @@ public function lateCollect() { $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages()); - $this->data = $this->computeCount($messages); + $this->data += $this->computeCount($messages); $this->data['messages'] = $messages; + + $this->data = $this->cloneVar($this->data); } /** * {@inheritdoc} */ - public function collect(Request $request, Response $response, \Exception $exception = null) + public function collect(Request $request, Response $response, \Throwable $exception = null) { + $this->data['locale'] = $this->translator->getLocale(); + $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); } /** - * @return array + * {@inheritdoc} */ - public function getMessages() + public function reset() { - return isset($this->data['messages']) ? $this->data['messages'] : array(); + $this->data = []; } /** - * @return int + * @return array|Data */ - public function getCountMissings() + public function getMessages() { - return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0; + return $this->data['messages'] ?? []; } - /** - * @return int - */ - public function getCountFallbacks() + public function getCountMissings(): int + { + return $this->data[DataCollectorTranslator::MESSAGE_MISSING] ?? 0; + } + + public function getCountFallbacks(): int + { + return $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] ?? 0; + } + + public function getCountDefines(): int + { + return $this->data[DataCollectorTranslator::MESSAGE_DEFINED] ?? 0; + } + + public function getLocale() { - return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0; + return !empty($this->data['locale']) ? $this->data['locale'] : null; } /** - * @return int + * @internal */ - public function getCountDefines() + public function getFallbackLocales() { - return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0; + return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : []; } /** * {@inheritdoc} */ - public function getName() + public function getName(): string { return 'translation'; } - private function sanitizeCollectedMessages($messages) + private function sanitizeCollectedMessages(array $messages) { - $result = array(); + $result = []; foreach ($messages as $key => $message) { $messageId = $message['locale'].$message['domain'].$message['id']; if (!isset($result[$messageId])) { $message['count'] = 1; - $message['parameters'] = !empty($message['parameters']) ? array($this->cloneVar($message['parameters'])) : array(); + $message['parameters'] = !empty($message['parameters']) ? [$message['parameters']] : []; $messages[$key]['translation'] = $this->sanitizeString($message['translation']); $result[$messageId] = $message; } else { if (!empty($message['parameters'])) { - $result[$messageId]['parameters'][] = $this->cloneVar($message['parameters']); + $result[$messageId]['parameters'][] = $message['parameters']; } ++$result[$messageId]['count']; @@ -118,13 +131,13 @@ private function sanitizeCollectedMessages($messages) return $result; } - private function computeCount($messages) + private function computeCount(array $messages) { - $count = array( + $count = [ DataCollectorTranslator::MESSAGE_DEFINED => 0, DataCollectorTranslator::MESSAGE_MISSING => 0, DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0, - ); + ]; foreach ($messages as $message) { ++$count[$message['state']]; @@ -133,7 +146,7 @@ private function computeCount($messages) return $count; } - private function sanitizeString($string, $length = 80) + private function sanitizeString(string $string, int $length = 80) { $string = trim(preg_replace('/\s+/', ' ', $string)); @@ -141,7 +154,7 @@ private function sanitizeString($string, $length = 80) if (mb_strlen($string, $encoding) > $length) { return mb_substr($string, 0, $length - 3, $encoding).'...'; } - } elseif (strlen($string) > $length) { + } elseif (\strlen($string) > $length) { return substr($string, 0, $length - 3).'...'; } diff --git a/tests/integration/vendor/symfony/translation/DataCollectorTranslator.php b/tests/integration/vendor/symfony/translation/DataCollectorTranslator.php index cdfde100b..ea5a2dd5e 100644 --- a/tests/integration/vendor/symfony/translation/DataCollectorTranslator.php +++ b/tests/integration/vendor/symfony/translation/DataCollectorTranslator.php @@ -11,34 +11,30 @@ namespace Symfony\Component\Translation; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad */ -class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface +class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface, WarmableInterface { - const MESSAGE_DEFINED = 0; - const MESSAGE_MISSING = 1; - const MESSAGE_EQUALS_FALLBACK = 2; + public const MESSAGE_DEFINED = 0; + public const MESSAGE_MISSING = 1; + public const MESSAGE_EQUALS_FALLBACK = 2; - /** - * @var TranslatorInterface|TranslatorBagInterface - */ private $translator; + private $messages = []; /** - * @var array - */ - private $messages = array(); - - /** - * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface + * @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator */ public function __construct(TranslatorInterface $translator) { - if (!$translator instanceof TranslatorBagInterface) { - throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator))); + if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', get_debug_type($translator))); } $this->translator = $translator; @@ -47,9 +43,9 @@ public function __construct(TranslatorInterface $translator) /** * {@inheritdoc} */ - public function trans($id, array $parameters = array(), $domain = null, $locale = null) + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) { - $trans = $this->translator->trans($id, $parameters, $domain, $locale); + $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); $this->collectMessage($locale, $domain, $id, $trans, $parameters); return $trans; @@ -58,58 +54,69 @@ public function trans($id, array $parameters = array(), $domain = null, $locale /** * {@inheritdoc} */ - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + public function setLocale(string $locale) { - $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); - $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number); + $this->translator->setLocale($locale); + } - return $trans; + /** + * {@inheritdoc} + */ + public function getLocale() + { + return $this->translator->getLocale(); } /** * {@inheritdoc} */ - public function setLocale($locale) + public function getCatalogue(string $locale = null) { - $this->translator->setLocale($locale); + return $this->translator->getCatalogue($locale); } /** * {@inheritdoc} */ - public function getLocale() + public function getCatalogues(): array { - return $this->translator->getLocale(); + return $this->translator->getCatalogues(); } /** * {@inheritdoc} + * + * @return string[] */ - public function getCatalogue($locale = null) + public function warmUp(string $cacheDir) { - return $this->translator->getCatalogue($locale); + if ($this->translator instanceof WarmableInterface) { + return (array) $this->translator->warmUp($cacheDir); + } + + return []; } /** * Gets the fallback locales. * - * @return array $locales The fallback locales + * @return array */ public function getFallbackLocales() { - if ($this->translator instanceof Translator) { + if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) { return $this->translator->getFallbackLocales(); } - return array(); + return []; } /** * Passes through all unknown calls onto the translator object. */ - public function __call($method, $args) + public function __call(string $method, array $args) { - return call_user_func_array(array($this->translator, $method), $args); + return $this->translator->{$method}(...$args); } /** @@ -120,23 +127,15 @@ public function getCollectedMessages() return $this->messages; } - /** - * @param string|null $locale - * @param string|null $domain - * @param string $id - * @param string $translation - * @param array|null $parameters - * @param int|null $number - */ - private function collectMessage($locale, $domain, $id, $translation, $parameters = array(), $number = null) + private function collectMessage(?string $locale, ?string $domain, string $id, string $translation, ?array $parameters = []) { if (null === $domain) { $domain = 'messages'; } - $id = (string) $id; $catalogue = $this->translator->getCatalogue($locale); $locale = $catalogue->getLocale(); + $fallbackLocale = null; if ($catalogue->defines($id, $domain)) { $state = self::MESSAGE_DEFINED; } elseif ($catalogue->has($id, $domain)) { @@ -145,24 +144,24 @@ private function collectMessage($locale, $domain, $id, $translation, $parameters $fallbackCatalogue = $catalogue->getFallbackCatalogue(); while ($fallbackCatalogue) { if ($fallbackCatalogue->defines($id, $domain)) { - $locale = $fallbackCatalogue->getLocale(); + $fallbackLocale = $fallbackCatalogue->getLocale(); break; } - $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); } } else { $state = self::MESSAGE_MISSING; } - $this->messages[] = array( + $this->messages[] = [ 'locale' => $locale, + 'fallbackLocale' => $fallbackLocale, 'domain' => $domain, 'id' => $id, 'translation' => $translation, 'parameters' => $parameters, - 'transChoiceNumber' => $number, 'state' => $state, - ); + 'transChoiceNumber' => isset($parameters['%count%']) && is_numeric($parameters['%count%']) ? $parameters['%count%'] : null, + ]; } } diff --git a/tests/integration/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php new file mode 100644 index 000000000..6d78342bc --- /dev/null +++ b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds tagged translation.formatter services to translation writer. + */ +class TranslationDumperPass implements CompilerPassInterface +{ + private $writerServiceId; + private $dumperTag; + + public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper') + { + if (1 < \func_num_args()) { + trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->writerServiceId = $writerServiceId; + $this->dumperTag = $dumperTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->writerServiceId)) { + return; + } + + $definition = $container->getDefinition($this->writerServiceId); + + foreach ($container->findTaggedServiceIds($this->dumperTag, true) as $id => $attributes) { + $definition->addMethodCall('addDumper', [$attributes[0]['alias'], new Reference($id)]); + } + } +} diff --git a/tests/integration/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php new file mode 100644 index 000000000..fab6b20ae --- /dev/null +++ b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds tagged translation.extractor services to translation extractor. + */ +class TranslationExtractorPass implements CompilerPassInterface +{ + private $extractorServiceId; + private $extractorTag; + + public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->extractorServiceId = $extractorServiceId; + $this->extractorTag = $extractorTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->extractorServiceId)) { + return; + } + + $definition = $container->getDefinition($this->extractorServiceId); + + foreach ($container->findTaggedServiceIds($this->extractorTag, true) as $id => $attributes) { + if (!isset($attributes[0]['alias'])) { + throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id)); + } + + $definition->addMethodCall('addExtractor', [$attributes[0]['alias'], new Reference($id)]); + } + } +} diff --git a/tests/integration/vendor/symfony/translation/DependencyInjection/TranslatorPass.php b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslatorPass.php new file mode 100644 index 000000000..e6a4b362f --- /dev/null +++ b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslatorPass.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class TranslatorPass implements CompilerPassInterface +{ + private $translatorServiceId; + private $readerServiceId; + private $loaderTag; + private $debugCommandServiceId; + private $updateCommandServiceId; + + public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_extract') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->translatorServiceId = $translatorServiceId; + $this->readerServiceId = $readerServiceId; + $this->loaderTag = $loaderTag; + $this->debugCommandServiceId = $debugCommandServiceId; + $this->updateCommandServiceId = $updateCommandServiceId; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->translatorServiceId)) { + return; + } + + $loaders = []; + $loaderRefs = []; + foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) { + $loaderRefs[$id] = new Reference($id); + $loaders[$id][] = $attributes[0]['alias']; + if (isset($attributes[0]['legacy-alias'])) { + $loaders[$id][] = $attributes[0]['legacy-alias']; + } + } + + if ($container->hasDefinition($this->readerServiceId)) { + $definition = $container->getDefinition($this->readerServiceId); + foreach ($loaders as $id => $formats) { + foreach ($formats as $format) { + $definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]); + } + } + } + + $container + ->findDefinition($this->translatorServiceId) + ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs)) + ->replaceArgument(3, $loaders) + ; + + if (!$container->hasParameter('twig.default_path')) { + return; + } + + $paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(1)); + if ($container->hasDefinition($this->debugCommandServiceId)) { + $definition = $container->getDefinition($this->debugCommandServiceId); + $definition->replaceArgument(4, $container->getParameter('twig.default_path')); + + if (\count($definition->getArguments()) > 6) { + $definition->replaceArgument(6, $paths); + } + } + if ($container->hasDefinition($this->updateCommandServiceId)) { + $definition = $container->getDefinition($this->updateCommandServiceId); + $definition->replaceArgument(5, $container->getParameter('twig.default_path')); + + if (\count($definition->getArguments()) > 7) { + $definition->replaceArgument(7, $paths); + } + } + } +} diff --git a/tests/integration/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php new file mode 100644 index 000000000..18a71c45a --- /dev/null +++ b/tests/integration/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; + +/** + * @author Yonel Ceruto + */ +class TranslatorPathsPass extends AbstractRecursivePass +{ + private $translatorServiceId; + private $debugCommandServiceId; + private $updateCommandServiceId; + private $resolverServiceId; + private $level = 0; + + /** + * @var array + */ + private $paths = []; + + /** + * @var array + */ + private $definitions = []; + + /** + * @var array> + */ + private $controllers = []; + + public function __construct(string $translatorServiceId = 'translator', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_extract', string $resolverServiceId = 'argument_resolver.service') + { + if (0 < \func_num_args()) { + trigger_deprecation('symfony/translation', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); + } + + $this->translatorServiceId = $translatorServiceId; + $this->debugCommandServiceId = $debugCommandServiceId; + $this->updateCommandServiceId = $updateCommandServiceId; + $this->resolverServiceId = $resolverServiceId; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->translatorServiceId)) { + return; + } + + foreach ($this->findControllerArguments($container) as $controller => $argument) { + $id = substr($controller, 0, strpos($controller, ':') ?: \strlen($controller)); + if ($container->hasDefinition($id)) { + [$locatorRef] = $argument->getValues(); + $this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = true; + } + } + + try { + parent::process($container); + + $paths = []; + foreach ($this->paths as $class => $_) { + if (($r = $container->getReflectionClass($class)) && !$r->isInterface()) { + $paths[] = $r->getFileName(); + foreach ($r->getTraits() as $trait) { + $paths[] = $trait->getFileName(); + } + } + } + if ($paths) { + if ($container->hasDefinition($this->debugCommandServiceId)) { + $definition = $container->getDefinition($this->debugCommandServiceId); + $definition->replaceArgument(6, array_merge($definition->getArgument(6), $paths)); + } + if ($container->hasDefinition($this->updateCommandServiceId)) { + $definition = $container->getDefinition($this->updateCommandServiceId); + $definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths)); + } + } + } finally { + $this->level = 0; + $this->paths = []; + $this->definitions = []; + } + } + + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Reference) { + if ((string) $value === $this->translatorServiceId) { + for ($i = $this->level - 1; $i >= 0; --$i) { + $class = $this->definitions[$i]->getClass(); + + if (ServiceLocator::class === $class) { + if (!isset($this->controllers[$this->currentId])) { + continue; + } + foreach ($this->controllers[$this->currentId] as $class => $_) { + $this->paths[$class] = true; + } + } else { + $this->paths[$class] = true; + } + + break; + } + } + + return $value; + } + + if ($value instanceof Definition) { + $this->definitions[$this->level++] = $value; + $value = parent::processValue($value, $isRoot); + unset($this->definitions[--$this->level]); + + return $value; + } + + return parent::processValue($value, $isRoot); + } + + private function findControllerArguments(ContainerBuilder $container): array + { + if ($container->hasDefinition($this->resolverServiceId)) { + $argument = $container->getDefinition($this->resolverServiceId)->getArgument(0); + if ($argument instanceof Reference) { + $argument = $container->getDefinition($argument); + } + + return $argument->getArgument(0); + } + + if ($container->hasDefinition('debug.'.$this->resolverServiceId)) { + $argument = $container->getDefinition('debug.'.$this->resolverServiceId)->getArgument(0); + if ($argument instanceof Reference) { + $argument = $container->getDefinition($argument); + } + $argument = $argument->getArgument(0); + if ($argument instanceof Reference) { + $argument = $container->getDefinition($argument); + } + + return $argument->getArgument(0); + } + + return []; + } +} diff --git a/tests/integration/vendor/symfony/translation/Dumper/CsvFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/CsvFileDumper.php index fe5dccb42..0c8589af8 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/CsvFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/CsvFileDumper.php @@ -26,12 +26,12 @@ class CsvFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { - $handle = fopen('php://memory', 'rb+'); + $handle = fopen('php://memory', 'r+'); foreach ($messages->all($domain) as $source => $target) { - fputcsv($handle, array($source, $target), $this->delimiter, $this->enclosure); + fputcsv($handle, [$source, $target], $this->delimiter, $this->enclosure); } rewind($handle); @@ -43,11 +43,8 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti /** * Sets the delimiter and escape character for CSV. - * - * @param string $delimiter delimiter character - * @param string $enclosure enclosure character */ - public function setCsvControl($delimiter = ';', $enclosure = '"') + public function setCsvControl(string $delimiter = ';', string $enclosure = '"') { $this->delimiter = $delimiter; $this->enclosure = $enclosure; diff --git a/tests/integration/vendor/symfony/translation/Dumper/DumperInterface.php b/tests/integration/vendor/symfony/translation/Dumper/DumperInterface.php index cebc65ed8..7cdaef515 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/DumperInterface.php +++ b/tests/integration/vendor/symfony/translation/Dumper/DumperInterface.php @@ -24,8 +24,7 @@ interface DumperInterface /** * Dumps the message catalogue. * - * @param MessageCatalogue $messages The message catalogue - * @param array $options Options that are used by the dumper + * @param array $options Options that are used by the dumper */ - public function dump(MessageCatalogue $messages, $options = array()); + public function dump(MessageCatalogue $messages, array $options = []); } diff --git a/tests/integration/vendor/symfony/translation/Dumper/FileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/FileDumper.php index a0b207fc3..0e1084519 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/FileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/FileDumper.php @@ -11,12 +11,12 @@ namespace Symfony\Component\Translation\Dumper; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; /** * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s). - * Performs backup of already existing files. * * Options: * - path (mandatory): the directory where the files should be saved @@ -32,58 +32,54 @@ abstract class FileDumper implements DumperInterface */ protected $relativePathTemplate = '%domain%.%locale%.%extension%'; - /** - * Make file backup before the dump. - * - * @var bool - */ - private $backup = true; - /** * Sets the template for the relative paths to files. * * @param string $relativePathTemplate A template for the relative paths to files */ - public function setRelativePathTemplate($relativePathTemplate) + public function setRelativePathTemplate(string $relativePathTemplate) { $this->relativePathTemplate = $relativePathTemplate; } - /** - * Sets backup flag. - * - * @param bool - */ - public function setBackup($backup) - { - $this->backup = $backup; - } - /** * {@inheritdoc} */ - public function dump(MessageCatalogue $messages, $options = array()) + public function dump(MessageCatalogue $messages, array $options = []) { - if (!array_key_exists('path', $options)) { + if (!\array_key_exists('path', $options)) { throw new InvalidArgumentException('The file dumper needs a path option.'); } // save a file for each domain foreach ($messages->getDomains() as $domain) { - // backup $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale()); - if (file_exists($fullpath)) { - if ($this->backup) { - @trigger_error('Creating a backup while dumping a message catalogue is deprecated since version 3.1 and will be removed in 4.0. Use TranslationWriter::disableBackup() to disable the backup.', E_USER_DEPRECATED); - copy($fullpath, $fullpath.'~'); - } - } else { - $directory = dirname($fullpath); + if (!file_exists($fullpath)) { + $directory = \dirname($fullpath); if (!file_exists($directory) && !@mkdir($directory, 0777, true)) { throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory)); } } - // save file + + $intlDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX; + $intlMessages = $messages->all($intlDomain); + + if ($intlMessages) { + $intlPath = $options['path'].'/'.$this->getRelativePath($intlDomain, $messages->getLocale()); + file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options)); + + $messages->replace([], $intlDomain); + + try { + if ($messages->all($domain)) { + file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); + } + continue; + } finally { + $messages->replace($intlMessages, $intlDomain); + } + } + file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); } } @@ -91,35 +87,26 @@ public function dump(MessageCatalogue $messages, $options = array()) /** * Transforms a domain of a message catalogue to its string representation. * - * @param MessageCatalogue $messages - * @param string $domain - * @param array $options - * - * @return string representation + * @return string */ - abstract public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()); + abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []); /** * Gets the file extension of the dumper. * - * @return string file extension + * @return string */ abstract protected function getExtension(); /** * Gets the relative file path using the template. - * - * @param string $domain The domain - * @param string $locale The locale - * - * @return string The relative file path */ - private function getRelativePath($domain, $locale) + private function getRelativePath(string $domain, string $locale): string { - return strtr($this->relativePathTemplate, array( + return strtr($this->relativePathTemplate, [ '%domain%' => $domain, '%locale%' => $locale, '%extension%' => $this->getExtension(), - )); + ]); } } diff --git a/tests/integration/vendor/symfony/translation/Dumper/IcuResFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/IcuResFileDumper.php index 77ce27b2a..cdc59913b 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/IcuResFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/IcuResFileDumper.php @@ -28,12 +28,12 @@ class IcuResFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { $data = $indexes = $resources = ''; foreach ($messages->all($domain) as $source => $target) { - $indexes .= pack('v', strlen($data) + 28); + $indexes .= pack('v', \strlen($data) + 28); $data .= $source."\0"; } @@ -44,7 +44,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti foreach ($messages->all($domain) as $source => $target) { $resources .= pack('V', $this->getPosition($data)); - $data .= pack('V', strlen($target)) + $data .= pack('V', \strlen($target)) .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8') .$this->writePadding($data) ; @@ -52,7 +52,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti $resOffset = $this->getPosition($data); - $data .= pack('v', count($messages)) + $data .= pack('v', \count($messages->all($domain))) .$indexes .$this->writePadding($data) .$resources @@ -63,11 +63,11 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti $root = pack('V7', $resOffset + (2 << 28), // Resource Offset + Resource Type 6, // Index length - $keyTop, // Index keys top - $bundleTop, // Index resources top - $bundleTop, // Index bundle top - count($messages), // Index max table length - 0 // Index attributes + $keyTop, // Index keys top + $bundleTop, // Index resources top + $bundleTop, // Index bundle top + \count($messages->all($domain)), // Index max table length + 0 // Index attributes ); $header = pack('vC2v4C12@32', @@ -82,18 +82,16 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti return $header.$root.$data; } - private function writePadding($data) + private function writePadding(string $data): ?string { - $padding = strlen($data) % 4; + $padding = \strlen($data) % 4; - if ($padding) { - return str_repeat("\xAA", 4 - $padding); - } + return $padding ? str_repeat("\xAA", 4 - $padding) : null; } - private function getPosition($data) + private function getPosition(string $data) { - return (strlen($data) + 28) / 4; + return (\strlen($data) + 28) / 4; } /** diff --git a/tests/integration/vendor/symfony/translation/Dumper/IniFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/IniFileDumper.php index 9ed375403..93c900a4a 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/IniFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/IniFileDumper.php @@ -23,7 +23,7 @@ class IniFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { $output = ''; diff --git a/tests/integration/vendor/symfony/translation/Dumper/JsonFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/JsonFileDumper.php index 08b538e1f..34c0b5694 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/JsonFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/JsonFileDumper.php @@ -23,13 +23,9 @@ class JsonFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { - if (isset($options['json_encoding'])) { - $flags = $options['json_encoding']; - } else { - $flags = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; - } + $flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT; return json_encode($messages->all($domain), $flags); } diff --git a/tests/integration/vendor/symfony/translation/Dumper/MoFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/MoFileDumper.php index 00a33d253..54d0da875 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/MoFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/MoFileDumper.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Translation\Dumper; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Loader\MoFileLoader; +use Symfony\Component\Translation\MessageCatalogue; /** * MoFileDumper generates a gettext formatted string representation of a message catalogue. @@ -24,20 +24,20 @@ class MoFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { $sources = $targets = $sourceOffsets = $targetOffsets = ''; - $offsets = array(); + $offsets = []; $size = 0; foreach ($messages->all($domain) as $source => $target) { - $offsets[] = array_map('strlen', array($sources, $source, $targets, $target)); + $offsets[] = array_map('strlen', [$sources, $source, $targets, $target]); $sources .= "\0".$source; $targets .= "\0".$target; ++$size; } - $header = array( + $header = [ 'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC, 'formatRevision' => 0, 'count' => $size, @@ -45,9 +45,9 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti 'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size), 'sizeHashes' => 0, 'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size), - ); + ]; - $sourcesSize = strlen($sources); + $sourcesSize = \strlen($sources); $sourcesStart = $header['offsetHashes'] + 1; foreach ($offsets as $offset) { @@ -57,7 +57,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize); } - $output = implode(array_map(array($this, 'writeLong'), $header)) + $output = implode('', array_map([$this, 'writeLong'], $header)) .$sourceOffsets .$targetOffsets .$sources @@ -75,7 +75,7 @@ protected function getExtension() return 'mo'; } - private function writeLong($str) + private function writeLong($str): string { return pack('V*', $str); } diff --git a/tests/integration/vendor/symfony/translation/Dumper/PhpFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/PhpFileDumper.php index c7c37aac9..6163b5297 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/PhpFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/PhpFileDumper.php @@ -23,7 +23,7 @@ class PhpFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { return "all($domain), true).";\n"; } diff --git a/tests/integration/vendor/symfony/translation/Dumper/PoFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/PoFileDumper.php index ed4418b14..0d822818c 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/PoFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/PoFileDumper.php @@ -23,7 +23,7 @@ class PoFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { $output = 'msgid ""'."\n"; $output .= 'msgstr ""'."\n"; @@ -39,13 +39,78 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti } else { $newLine = true; } - $output .= sprintf('msgid "%s"'."\n", $this->escape($source)); - $output .= sprintf('msgstr "%s"', $this->escape($target)); + $metadata = $messages->getMetadata($source, $domain); + + if (isset($metadata['comments'])) { + $output .= $this->formatComments($metadata['comments']); + } + if (isset($metadata['flags'])) { + $output .= $this->formatComments(implode(',', (array) $metadata['flags']), ','); + } + if (isset($metadata['sources'])) { + $output .= $this->formatComments(implode(' ', (array) $metadata['sources']), ':'); + } + + $sourceRules = $this->getStandardRules($source); + $targetRules = $this->getStandardRules($target); + if (2 == \count($sourceRules) && [] !== $targetRules) { + $output .= sprintf('msgid "%s"'."\n", $this->escape($sourceRules[0])); + $output .= sprintf('msgid_plural "%s"'."\n", $this->escape($sourceRules[1])); + foreach ($targetRules as $i => $targetRule) { + $output .= sprintf('msgstr[%d] "%s"'."\n", $i, $this->escape($targetRule)); + } + } else { + $output .= sprintf('msgid "%s"'."\n", $this->escape($source)); + $output .= sprintf('msgstr "%s"'."\n", $this->escape($target)); + } } return $output; } + private function getStandardRules(string $id) + { + // Partly copied from TranslatorTrait::trans. + $parts = []; + if (preg_match('/^\|++$/', $id)) { + $parts = explode('|', $id); + } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) { + $parts = $matches[0]; + } + + $intervalRegexp = <<<'EOF' +/^(?P + ({\s* + (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) + \s*}) + + | + + (?P[\[\]]) + \s* + (?P-Inf|\-?\d+(\.\d+)?) + \s*,\s* + (?P\+?Inf|\-?\d+(\.\d+)?) + \s* + (?P[\[\]]) +)\s*(?P.*?)$/xs +EOF; + + $standardRules = []; + foreach ($parts as $part) { + $part = trim(str_replace('||', '|', $part)); + + if (preg_match($intervalRegexp, $part)) { + // Explicit rule is not a standard rule. + return []; + } else { + $standardRules[] = $part; + } + } + + return $standardRules; + } + /** * {@inheritdoc} */ @@ -54,8 +119,19 @@ protected function getExtension() return 'po'; } - private function escape($str) + private function escape(string $str): string { return addcslashes($str, "\0..\37\42\134"); } + + private function formatComments($comments, string $prefix = ''): ?string + { + $output = null; + + foreach ((array) $comments as $comment) { + $output .= sprintf('#%s %s'."\n", $prefix, $comment); + } + + return $output; + } } diff --git a/tests/integration/vendor/symfony/translation/Dumper/QtFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/QtFileDumper.php index a9073f26d..406e9f006 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/QtFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/QtFileDumper.php @@ -23,7 +23,7 @@ class QtFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; @@ -33,6 +33,17 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti foreach ($messages->all($domain) as $source => $target) { $message = $context->appendChild($dom->createElement('message')); + $metadata = $messages->getMetadata($source, $domain); + if (isset($metadata['sources'])) { + foreach ((array) $metadata['sources'] as $location) { + $loc = explode(':', $location, 2); + $location = $message->appendChild($dom->createElement('location')); + $location->setAttribute('filename', $loc[0]); + if (isset($loc[1])) { + $location->setAttribute('line', $loc[1]); + } + } + } $message->appendChild($dom->createElement('source', $source)); $message->appendChild($dom->createElement('translation', $target)); } diff --git a/tests/integration/vendor/symfony/translation/Dumper/XliffFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/XliffFileDumper.php index 47b05c81b..f7dbdcddf 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/XliffFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/XliffFileDumper.php @@ -11,8 +11,8 @@ namespace Symfony\Component\Translation\Dumper; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\MessageCatalogue; /** * XliffFileDumper generates xliff files from a message catalogue. @@ -24,14 +24,14 @@ class XliffFileDumper extends FileDumper /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { $xliffVersion = '1.2'; - if (array_key_exists('xliff_version', $options)) { + if (\array_key_exists('xliff_version', $options)) { $xliffVersion = $options['xliff_version']; } - if (array_key_exists('default_locale', $options)) { + if (\array_key_exists('default_locale', $options)) { $defaultLocale = $options['default_locale']; } else { $defaultLocale = \Locale::getDefault(); @@ -41,7 +41,7 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti return $this->dumpXliff1($defaultLocale, $messages, $domain, $options); } if ('2.0' === $xliffVersion) { - return $this->dumpXliff2($defaultLocale, $messages, $domain, $options); + return $this->dumpXliff2($defaultLocale, $messages, $domain); } throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); @@ -55,10 +55,10 @@ protected function getExtension() return 'xlf'; } - private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, array $options = array()) + private function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, ?string $domain, array $options = []) { - $toolInfo = array('tool-id' => 'symfony', 'tool-name' => 'Symfony'); - if (array_key_exists('tool_info', $options)) { + $toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony']; + if (\array_key_exists('tool_info', $options)) { $toolInfo = array_merge($toolInfo, $options['tool_info']); } @@ -85,7 +85,7 @@ private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, foreach ($messages->all($domain) as $source => $target) { $translation = $dom->createElement('trans-unit'); - $translation->setAttribute('id', md5($source)); + $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._')); $translation->setAttribute('resname', $source); $s = $translation->appendChild($dom->createElement('source')); @@ -129,7 +129,7 @@ private function dumpXliff1($defaultLocale, MessageCatalogue $messages, $domain, return $dom->saveXML(); } - private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, array $options = array()) + private function dumpXliff2(string $defaultLocale, MessageCatalogue $messages, ?string $domain) { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; @@ -141,11 +141,37 @@ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale())); $xliffFile = $xliff->appendChild($dom->createElement('file')); - $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale()); + if (str_ends_with($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX)) { + $xliffFile->setAttribute('id', substr($domain, 0, -\strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)).'.'.$messages->getLocale()); + } else { + $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale()); + } foreach ($messages->all($domain) as $source => $target) { $translation = $dom->createElement('unit'); - $translation->setAttribute('id', md5($source)); + $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._')); + + if (\strlen($source) <= 80) { + $translation->setAttribute('name', $source); + } + + $metadata = $messages->getMetadata($source, $domain); + + // Add notes section + if ($this->hasMetadataArrayInfo('notes', $metadata)) { + $notesElement = $dom->createElement('notes'); + foreach ($metadata['notes'] as $note) { + $n = $dom->createElement('note'); + $n->appendChild($dom->createTextNode($note['content'] ?? '')); + unset($note['content']); + + foreach ($note as $name => $value) { + $n->setAttribute($name, $value); + } + $notesElement->appendChild($n); + } + $translation->appendChild($notesElement); + } $segment = $translation->appendChild($dom->createElement('segment')); @@ -156,7 +182,6 @@ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); $targetElement = $dom->createElement('target'); - $metadata = $messages->getMetadata($source, $domain); if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { foreach ($metadata['target-attributes'] as $name => $value) { $targetElement->setAttribute($name, $value); @@ -171,14 +196,8 @@ private function dumpXliff2($defaultLocale, MessageCatalogue $messages, $domain, return $dom->saveXML(); } - /** - * @param string $key - * @param array|null $metadata - * - * @return bool - */ - private function hasMetadataArrayInfo($key, $metadata = null) + private function hasMetadataArrayInfo(string $key, array $metadata = null): bool { - return null !== $metadata && array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || is_array($metadata[$key])); + return is_iterable($metadata[$key] ?? null); } } diff --git a/tests/integration/vendor/symfony/translation/Dumper/YamlFileDumper.php b/tests/integration/vendor/symfony/translation/Dumper/YamlFileDumper.php index fdd113b10..0b21e8c83 100644 --- a/tests/integration/vendor/symfony/translation/Dumper/YamlFileDumper.php +++ b/tests/integration/vendor/symfony/translation/Dumper/YamlFileDumper.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Translation\Dumper; +use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Util\ArrayConverter; use Symfony\Component\Yaml\Yaml; -use Symfony\Component\Translation\Exception\LogicException; /** * YamlFileDumper generates yaml files from a message catalogue. @@ -23,12 +23,19 @@ */ class YamlFileDumper extends FileDumper { + private $extension; + + public function __construct(string $extension = 'yml') + { + $this->extension = $extension; + } + /** * {@inheritdoc} */ - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []) { - if (!class_exists('Symfony\Component\Yaml\Yaml')) { + if (!class_exists(Yaml::class)) { throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.'); } @@ -50,6 +57,6 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti */ protected function getExtension() { - return 'yml'; + return $this->extension; } } diff --git a/tests/integration/vendor/symfony/translation/Exception/ExceptionInterface.php b/tests/integration/vendor/symfony/translation/Exception/ExceptionInterface.php index c85fb93ca..8f9c54ef7 100644 --- a/tests/integration/vendor/symfony/translation/Exception/ExceptionInterface.php +++ b/tests/integration/vendor/symfony/translation/Exception/ExceptionInterface.php @@ -16,6 +16,6 @@ * * @author Fabien Potencier */ -interface ExceptionInterface +interface ExceptionInterface extends \Throwable { } diff --git a/tests/integration/vendor/symfony/translation/Exception/IncompleteDsnException.php b/tests/integration/vendor/symfony/translation/Exception/IncompleteDsnException.php new file mode 100644 index 000000000..cb0ce027e --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Exception/IncompleteDsnException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +class IncompleteDsnException extends InvalidArgumentException +{ + public function __construct(string $message, string $dsn = null, \Throwable $previous = null) + { + if ($dsn) { + $message = sprintf('Invalid "%s" provider DSN: ', $dsn).$message; + } + + parent::__construct($message, 0, $previous); + } +} diff --git a/tests/integration/vendor/symfony/translation/Exception/MissingRequiredOptionException.php b/tests/integration/vendor/symfony/translation/Exception/MissingRequiredOptionException.php new file mode 100644 index 000000000..2b5f80806 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Exception/MissingRequiredOptionException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * @author Oskar Stark + */ +class MissingRequiredOptionException extends IncompleteDsnException +{ + public function __construct(string $option, string $dsn = null, \Throwable $previous = null) + { + $message = sprintf('The option "%s" is required but missing.', $option); + + parent::__construct($message, $dsn, $previous); + } +} diff --git a/tests/integration/vendor/symfony/translation/Exception/ProviderException.php b/tests/integration/vendor/symfony/translation/Exception/ProviderException.php new file mode 100644 index 000000000..571920da3 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Exception/ProviderException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +use Symfony\Contracts\HttpClient\ResponseInterface; + +/** + * @author Fabien Potencier + */ +class ProviderException extends RuntimeException implements ProviderExceptionInterface +{ + private $response; + private $debug; + + public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null) + { + $this->response = $response; + $this->debug = $response->getInfo('debug') ?? ''; + + parent::__construct($message, $code, $previous); + } + + public function getResponse(): ResponseInterface + { + return $this->response; + } + + public function getDebug(): string + { + return $this->debug; + } +} diff --git a/tests/integration/vendor/symfony/translation/Exception/ProviderExceptionInterface.php b/tests/integration/vendor/symfony/translation/Exception/ProviderExceptionInterface.php new file mode 100644 index 000000000..922e82726 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Exception/ProviderExceptionInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +/** + * @author Fabien Potencier + */ +interface ProviderExceptionInterface extends ExceptionInterface +{ + /* + * Returns debug info coming from the Symfony\Contracts\HttpClient\ResponseInterface + */ + public function getDebug(): string; +} diff --git a/tests/integration/vendor/symfony/translation/Exception/UnsupportedSchemeException.php b/tests/integration/vendor/symfony/translation/Exception/UnsupportedSchemeException.php new file mode 100644 index 000000000..7fbaa8f04 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Exception/UnsupportedSchemeException.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Exception; + +use Symfony\Component\Translation\Bridge; +use Symfony\Component\Translation\Provider\Dsn; + +class UnsupportedSchemeException extends LogicException +{ + private const SCHEME_TO_PACKAGE_MAP = [ + 'crowdin' => [ + 'class' => Bridge\Crowdin\CrowdinProviderFactory::class, + 'package' => 'symfony/crowdin-translation-provider', + ], + 'loco' => [ + 'class' => Bridge\Loco\LocoProviderFactory::class, + 'package' => 'symfony/loco-translation-provider', + ], + 'lokalise' => [ + 'class' => Bridge\Lokalise\LokaliseProviderFactory::class, + 'package' => 'symfony/lokalise-translation-provider', + ], + ]; + + public function __construct(Dsn $dsn, string $name = null, array $supported = []) + { + $provider = $dsn->getScheme(); + if (false !== $pos = strpos($provider, '+')) { + $provider = substr($provider, 0, $pos); + } + $package = self::SCHEME_TO_PACKAGE_MAP[$provider] ?? null; + if ($package && !class_exists($package['class'])) { + parent::__construct(sprintf('Unable to synchronize translations via "%s" as the provider is not installed; try running "composer require %s".', $provider, $package['package'])); + + return; + } + + $message = sprintf('The "%s" scheme is not supported', $dsn->getScheme()); + if ($name && $supported) { + $message .= sprintf('; supported schemes for translation provider "%s" are: "%s"', $name, implode('", "', $supported)); + } + + parent::__construct($message.'.'); + } +} diff --git a/tests/integration/vendor/symfony/translation/Extractor/AbstractFileExtractor.php b/tests/integration/vendor/symfony/translation/Extractor/AbstractFileExtractor.php index 4b6b155df..3bc878348 100644 --- a/tests/integration/vendor/symfony/translation/Extractor/AbstractFileExtractor.php +++ b/tests/integration/vendor/symfony/translation/Extractor/AbstractFileExtractor.php @@ -21,21 +21,21 @@ abstract class AbstractFileExtractor { /** - * @param string|array $resource files, a file or a directory + * @param string|iterable $resource Files, a file or a directory * - * @return array + * @return iterable */ protected function extractFiles($resource) { - if (is_array($resource) || $resource instanceof \Traversable) { - $files = array(); + if (is_iterable($resource)) { + $files = []; foreach ($resource as $file) { if ($this->canBeExtracted($file)) { $files[] = $this->toSplFileInfo($file); } } } elseif (is_file($resource)) { - $files = $this->canBeExtracted($resource) ? array($this->toSplFileInfo($resource)) : array(); + $files = $this->canBeExtracted($resource) ? [$this->toSplFileInfo($resource)] : []; } else { $files = $this->extractFromDirectory($resource); } @@ -43,24 +43,17 @@ protected function extractFiles($resource) return $files; } - /** - * @param string $file - * - * @return \SplFileInfo - */ - private function toSplFileInfo($file) + private function toSplFileInfo(string $file): \SplFileInfo { - return ($file instanceof \SplFileInfo) ? $file : new \SplFileInfo($file); + return new \SplFileInfo($file); } /** - * @param string $file - * * @return bool * * @throws InvalidArgumentException */ - protected function isFile($file) + protected function isFile(string $file) { if (!is_file($file)) { throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file)); @@ -70,16 +63,14 @@ protected function isFile($file) } /** - * @param string $file - * * @return bool */ - abstract protected function canBeExtracted($file); + abstract protected function canBeExtracted(string $file); /** - * @param string|array $resource files, a file or a directory + * @param string|array $resource Files, a file or a directory * - * @return array files to be extracted + * @return iterable */ abstract protected function extractFromDirectory($resource); } diff --git a/tests/integration/vendor/symfony/translation/Extractor/ChainExtractor.php b/tests/integration/vendor/symfony/translation/Extractor/ChainExtractor.php index 50e3c8457..95dcf157c 100644 --- a/tests/integration/vendor/symfony/translation/Extractor/ChainExtractor.php +++ b/tests/integration/vendor/symfony/translation/Extractor/ChainExtractor.php @@ -25,15 +25,12 @@ class ChainExtractor implements ExtractorInterface * * @var ExtractorInterface[] */ - private $extractors = array(); + private $extractors = []; /** * Adds a loader to the translation extractor. - * - * @param string $format The format of the loader - * @param ExtractorInterface $extractor The loader */ - public function addExtractor($format, ExtractorInterface $extractor) + public function addExtractor(string $format, ExtractorInterface $extractor) { $this->extractors[$format] = $extractor; } @@ -41,7 +38,7 @@ public function addExtractor($format, ExtractorInterface $extractor) /** * {@inheritdoc} */ - public function setPrefix($prefix) + public function setPrefix(string $prefix) { foreach ($this->extractors as $extractor) { $extractor->setPrefix($prefix); diff --git a/tests/integration/vendor/symfony/translation/Extractor/ExtractorInterface.php b/tests/integration/vendor/symfony/translation/Extractor/ExtractorInterface.php index 438f80b32..e1db8a9c8 100644 --- a/tests/integration/vendor/symfony/translation/Extractor/ExtractorInterface.php +++ b/tests/integration/vendor/symfony/translation/Extractor/ExtractorInterface.php @@ -24,15 +24,12 @@ interface ExtractorInterface /** * Extracts translation messages from files, a file or a directory to the catalogue. * - * @param string|array $resource files, a file or a directory - * @param MessageCatalogue $catalogue The catalogue + * @param string|iterable $resource Files, a file or a directory */ public function extract($resource, MessageCatalogue $catalogue); /** * Sets the prefix that should be used for new found messages. - * - * @param string $prefix The prefix */ - public function setPrefix($prefix); + public function setPrefix(string $prefix); } diff --git a/tests/integration/vendor/symfony/translation/Extractor/PhpExtractor.php b/tests/integration/vendor/symfony/translation/Extractor/PhpExtractor.php new file mode 100644 index 000000000..38c08d548 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Extractor/PhpExtractor.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * PhpExtractor extracts translation messages from a PHP template. + * + * @author Michel Salib + */ +class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface +{ + public const MESSAGE_TOKEN = 300; + public const METHOD_ARGUMENTS_TOKEN = 1000; + public const DOMAIN_TOKEN = 1001; + + /** + * Prefix for new found message. + */ + private $prefix = ''; + + /** + * The sequence that captures translation messages. + */ + protected $sequences = [ + [ + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ], + [ + 'new', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 'new', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ], + [ + 'new', + '\\', + 'Symfony', + '\\', + 'Component', + '\\', + 'Translation', + '\\', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 'new', + '\Symfony\Component\Translation\TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 'new', + '\\', + 'Symfony', + '\\', + 'Component', + '\\', + 'Translation', + '\\', + 'TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ], + [ + 'new', + '\Symfony\Component\Translation\TranslatableMessage', + '(', + self::MESSAGE_TOKEN, + ], + [ + 't', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ], + [ + 't', + '(', + self::MESSAGE_TOKEN, + ], + ]; + + /** + * {@inheritdoc} + */ + public function extract($resource, MessageCatalogue $catalog) + { + $files = $this->extractFiles($resource); + foreach ($files as $file) { + $this->parseTokens(token_get_all(file_get_contents($file)), $catalog, $file); + + gc_mem_caches(); + } + } + + /** + * {@inheritdoc} + */ + public function setPrefix(string $prefix) + { + $this->prefix = $prefix; + } + + /** + * Normalizes a token. + * + * @param mixed $token + * + * @return string|null + */ + protected function normalizeToken($token) + { + if (isset($token[1]) && 'b"' !== $token) { + return $token[1]; + } + + return $token; + } + + /** + * Seeks to a non-whitespace token. + */ + private function seekToNextRelevantToken(\Iterator $tokenIterator) + { + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (\T_WHITESPACE !== $t[0]) { + break; + } + } + } + + private function skipMethodArgument(\Iterator $tokenIterator) + { + $openBraces = 0; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + + if ('[' === $t[0] || '(' === $t[0]) { + ++$openBraces; + } + + if (']' === $t[0] || ')' === $t[0]) { + --$openBraces; + } + + if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) { + break; + } + } + } + + /** + * Extracts the message from the iterator while the tokens + * match allowed message tokens. + */ + private function getValue(\Iterator $tokenIterator) + { + $message = ''; + $docToken = ''; + $docPart = ''; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if ('.' === $t) { + // Concatenate with next token + continue; + } + if (!isset($t[1])) { + break; + } + + switch ($t[0]) { + case \T_START_HEREDOC: + $docToken = $t[1]; + break; + case \T_ENCAPSED_AND_WHITESPACE: + case \T_CONSTANT_ENCAPSED_STRING: + if ('' === $docToken) { + $message .= PhpStringTokenParser::parse($t[1]); + } else { + $docPart = $t[1]; + } + break; + case \T_END_HEREDOC: + if ($indentation = strspn($t[1], ' ')) { + $docPartWithLineBreaks = $docPart; + $docPart = ''; + + foreach (preg_split('~(\r\n|\n|\r)~', $docPartWithLineBreaks, -1, \PREG_SPLIT_DELIM_CAPTURE) as $str) { + if (\in_array($str, ["\r\n", "\n", "\r"], true)) { + $docPart .= $str; + } else { + $docPart .= substr($str, $indentation); + } + } + } + + $message .= PhpStringTokenParser::parseDocString($docToken, $docPart); + $docToken = ''; + $docPart = ''; + break; + case \T_WHITESPACE: + break; + default: + break 2; + } + } + + return $message; + } + + /** + * Extracts trans message from PHP tokens. + */ + protected function parseTokens(array $tokens, MessageCatalogue $catalog, string $filename) + { + $tokenIterator = new \ArrayIterator($tokens); + + for ($key = 0; $key < $tokenIterator->count(); ++$key) { + foreach ($this->sequences as $sequence) { + $message = ''; + $domain = 'messages'; + $tokenIterator->seek($key); + + foreach ($sequence as $sequenceKey => $item) { + $this->seekToNextRelevantToken($tokenIterator); + + if ($this->normalizeToken($tokenIterator->current()) === $item) { + $tokenIterator->next(); + continue; + } elseif (self::MESSAGE_TOKEN === $item) { + $message = $this->getValue($tokenIterator); + + if (\count($sequence) === ($sequenceKey + 1)) { + break; + } + } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) { + $this->skipMethodArgument($tokenIterator); + } elseif (self::DOMAIN_TOKEN === $item) { + $domainToken = $this->getValue($tokenIterator); + if ('' !== $domainToken) { + $domain = $domainToken; + } + + break; + } else { + break; + } + } + + if ($message) { + $catalog->set($message, $this->prefix.$message, $domain); + $metadata = $catalog->getMetadata($message, $domain) ?? []; + $normalizedFilename = preg_replace('{[\\\\/]+}', '/', $filename); + $metadata['sources'][] = $normalizedFilename.':'.$tokens[$key][2]; + $catalog->setMetadata($message, $metadata, $domain); + break; + } + } + } + } + + /** + * @return bool + * + * @throws \InvalidArgumentException + */ + protected function canBeExtracted(string $file) + { + return $this->isFile($file) && 'php' === pathinfo($file, \PATHINFO_EXTENSION); + } + + /** + * {@inheritdoc} + */ + protected function extractFromDirectory($directory) + { + if (!class_exists(Finder::class)) { + throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class)); + } + + $finder = new Finder(); + + return $finder->files()->name('*.php')->in($directory); + } +} diff --git a/tests/integration/vendor/symfony/translation/Extractor/PhpStringTokenParser.php b/tests/integration/vendor/symfony/translation/Extractor/PhpStringTokenParser.php new file mode 100644 index 000000000..d114cc738 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Extractor/PhpStringTokenParser.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +/* + * The following is derived from code at http://github.com/nikic/PHP-Parser + * + * Copyright (c) 2011 by Nikita Popov + * + * Some rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * The names of the contributors may not be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +class PhpStringTokenParser +{ + protected static $replacements = [ + '\\' => '\\', + '$' => '$', + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'f' => "\f", + 'v' => "\v", + 'e' => "\x1B", + ]; + + /** + * Parses a string token. + * + * @param string $str String token content + * + * @return string + */ + public static function parse(string $str) + { + $bLength = 0; + if ('b' === $str[0]) { + $bLength = 1; + } + + if ('\'' === $str[$bLength]) { + return str_replace( + ['\\\\', '\\\''], + ['\\', '\''], + substr($str, $bLength + 1, -1) + ); + } else { + return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"'); + } + } + + /** + * Parses escape sequences in strings (all string types apart from single quoted). + * + * @param string $str String without quotes + * @param string|null $quote Quote type + * + * @return string + */ + public static function parseEscapeSequences(string $str, string $quote = null) + { + if (null !== $quote) { + $str = str_replace('\\'.$quote, $quote, $str); + } + + return preg_replace_callback( + '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', + [__CLASS__, 'parseCallback'], + $str + ); + } + + private static function parseCallback(array $matches): string + { + $str = $matches[1]; + + if (isset(self::$replacements[$str])) { + return self::$replacements[$str]; + } elseif ('x' === $str[0] || 'X' === $str[0]) { + return \chr(hexdec($str)); + } else { + return \chr(octdec($str)); + } + } + + /** + * Parses a constant doc string. + * + * @param string $startToken Doc string start token content (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\LogicException; + +/** + * @author Guilherme Blanco + * @author Abdellatif Ait boudad + */ +class IntlFormatter implements IntlFormatterInterface +{ + private $hasMessageFormatter; + private $cache = []; + + /** + * {@inheritdoc} + */ + public function formatIntl(string $message, string $locale, array $parameters = []): string + { + // MessageFormatter constructor throws an exception if the message is empty + if ('' === $message) { + return ''; + } + + if (!$formatter = $this->cache[$locale][$message] ?? null) { + if (!($this->hasMessageFormatter ?? $this->hasMessageFormatter = class_exists(\MessageFormatter::class))) { + throw new LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.'); + } + try { + $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message); + } catch (\IntlException $e) { + throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): ', intl_get_error_code()).intl_get_error_message(), 0, $e); + } + } + + foreach ($parameters as $key => $value) { + if (\in_array($key[0] ?? null, ['%', '{'], true)) { + unset($parameters[$key]); + $parameters[trim($key, '%{ }')] = $value; + } + } + + if (false === $message = $formatter->format($parameters)) { + throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): ', $formatter->getErrorCode()).$formatter->getErrorMessage()); + } + + return $message; + } +} diff --git a/tests/integration/vendor/symfony/translation/Formatter/IntlFormatterInterface.php b/tests/integration/vendor/symfony/translation/Formatter/IntlFormatterInterface.php new file mode 100644 index 000000000..02fc6acbd --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Formatter/IntlFormatterInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * Formats ICU message patterns. + * + * @author Nicolas Grekas + */ +interface IntlFormatterInterface +{ + /** + * Formats a localized message using rules defined by ICU MessageFormat. + * + * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html#details + */ + public function formatIntl(string $message, string $locale, array $parameters = []): string; +} diff --git a/tests/integration/vendor/symfony/translation/Formatter/MessageFormatter.php b/tests/integration/vendor/symfony/translation/Formatter/MessageFormatter.php new file mode 100644 index 000000000..040796483 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Formatter/MessageFormatter.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +use Symfony\Component\Translation\IdentityTranslator; +use Symfony\Contracts\Translation\TranslatorInterface; + +// Help opcache.preload discover always-needed symbols +class_exists(IntlFormatter::class); + +/** + * @author Abdellatif Ait boudad + */ +class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterface +{ + private $translator; + private $intlFormatter; + + /** + * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization + */ + public function __construct(TranslatorInterface $translator = null, IntlFormatterInterface $intlFormatter = null) + { + $this->translator = $translator ?? new IdentityTranslator(); + $this->intlFormatter = $intlFormatter ?? new IntlFormatter(); + } + + /** + * {@inheritdoc} + */ + public function format(string $message, string $locale, array $parameters = []) + { + if ($this->translator instanceof TranslatorInterface) { + return $this->translator->trans($message, $parameters, null, $locale); + } + + return strtr($message, $parameters); + } + + /** + * {@inheritdoc} + */ + public function formatIntl(string $message, string $locale, array $parameters = []): string + { + return $this->intlFormatter->formatIntl($message, $locale, $parameters); + } +} diff --git a/tests/integration/vendor/symfony/translation/Formatter/MessageFormatterInterface.php b/tests/integration/vendor/symfony/translation/Formatter/MessageFormatterInterface.php new file mode 100644 index 000000000..b85dbfd11 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Formatter/MessageFormatterInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Formatter; + +/** + * @author Guilherme Blanco + * @author Abdellatif Ait boudad + */ +interface MessageFormatterInterface +{ + /** + * Formats a localized message pattern with given arguments. + * + * @param string $message The message (may also be an object that can be cast to string) + * @param string $locale The message locale + * @param array $parameters An array of parameters for the message + * + * @return string + */ + public function format(string $message, string $locale, array $parameters = []); +} diff --git a/tests/integration/vendor/symfony/translation/IdentityTranslator.php b/tests/integration/vendor/symfony/translation/IdentityTranslator.php index 46a046365..46875edf2 100644 --- a/tests/integration/vendor/symfony/translation/IdentityTranslator.php +++ b/tests/integration/vendor/symfony/translation/IdentityTranslator.php @@ -11,55 +11,16 @@ namespace Symfony\Component\Translation; +use Symfony\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorTrait; + /** * IdentityTranslator does not translate anything. * * @author Fabien Potencier */ -class IdentityTranslator implements TranslatorInterface +class IdentityTranslator implements TranslatorInterface, LocaleAwareInterface { - private $selector; - private $locale; - - /** - * Constructor. - * - * @param MessageSelector|null $selector The message selector for pluralization - */ - public function __construct(MessageSelector $selector = null) - { - $this->selector = $selector ?: new MessageSelector(); - } - - /** - * {@inheritdoc} - */ - public function setLocale($locale) - { - $this->locale = $locale; - } - - /** - * {@inheritdoc} - */ - public function getLocale() - { - return $this->locale ?: \Locale::getDefault(); - } - - /** - * {@inheritdoc} - */ - public function trans($id, array $parameters = array(), $domain = null, $locale = null) - { - return strtr((string) $id, $parameters); - } - - /** - * {@inheritdoc} - */ - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) - { - return strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters); - } + use TranslatorTrait; } diff --git a/tests/integration/vendor/symfony/translation/Interval.php b/tests/integration/vendor/symfony/translation/Interval.php deleted file mode 100644 index 9e2cae648..000000000 --- a/tests/integration/vendor/symfony/translation/Interval.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Translation; - -use Symfony\Component\Translation\Exception\InvalidArgumentException; - -/** - * Tests if a given number belongs to a given math interval. - * - * An interval can represent a finite set of numbers: - * - * {1,2,3,4} - * - * An interval can represent numbers between two numbers: - * - * [1, +Inf] - * ]-1,2[ - * - * The left delimiter can be [ (inclusive) or ] (exclusive). - * The right delimiter can be [ (exclusive) or ] (inclusive). - * Beside numbers, you can use -Inf and +Inf for the infinite. - * - * @author Fabien Potencier - * - * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation - */ -class Interval -{ - /** - * Tests if the given number is in the math interval. - * - * @param int $number A number - * @param string $interval An interval - * - * @return bool - * - * @throws InvalidArgumentException - */ - public static function test($number, $interval) - { - $interval = trim($interval); - - if (!preg_match('/^'.self::getIntervalRegexp().'$/x', $interval, $matches)) { - throw new InvalidArgumentException(sprintf('"%s" is not a valid interval.', $interval)); - } - - if ($matches[1]) { - foreach (explode(',', $matches[2]) as $n) { - if ($number == $n) { - return true; - } - } - } else { - $leftNumber = self::convertNumber($matches['left']); - $rightNumber = self::convertNumber($matches['right']); - - return - ('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber) - && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber) - ; - } - - return false; - } - - /** - * Returns a Regexp that matches valid intervals. - * - * @return string A Regexp (without the delimiters) - */ - public static function getIntervalRegexp() - { - return <<[\[\]]) - \s* - (?P-Inf|\-?\d+(\.\d+)?) - \s*,\s* - (?P\+?Inf|\-?\d+(\.\d+)?) - \s* - (?P[\[\]]) -EOF; - } - - private static function convertNumber($number) - { - if ('-Inf' === $number) { - return log(0); - } elseif ('+Inf' === $number || 'Inf' === $number) { - return -log(0); - } - - return (float) $number; - } -} diff --git a/tests/integration/vendor/symfony/translation/LICENSE b/tests/integration/vendor/symfony/translation/LICENSE index 12a74531e..88bf75bb4 100644 --- a/tests/integration/vendor/symfony/translation/LICENSE +++ b/tests/integration/vendor/symfony/translation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2016 Fabien Potencier +Copyright (c) 2004-2022 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tests/integration/vendor/symfony/translation/Loader/ArrayLoader.php b/tests/integration/vendor/symfony/translation/Loader/ArrayLoader.php index 9a595b7da..0758da8b5 100644 --- a/tests/integration/vendor/symfony/translation/Loader/ArrayLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/ArrayLoader.php @@ -23,9 +23,9 @@ class ArrayLoader implements LoaderInterface /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + public function load($resource, string $locale, string $domain = 'messages') { - $this->flatten($resource); + $resource = $this->flatten($resource); $catalogue = new MessageCatalogue($locale); $catalogue->add($resource, $domain); @@ -36,31 +36,23 @@ public function load($resource, $locale, $domain = 'messages') * Flattens an nested array of translations. * * The scheme used is: - * 'key' => array('key2' => array('key3' => 'value')) + * 'key' => ['key2' => ['key3' => 'value']] * Becomes: * 'key.key2.key3' => 'value' - * - * This function takes an array by reference and will modify it - * - * @param array &$messages The array that will be flattened - * @param array $subnode Current subnode being parsed, used internally for recursive calls - * @param string $path Current path being parsed, used internally for recursive calls */ - private function flatten(array &$messages, array $subnode = null, $path = null) + private function flatten(array $messages): array { - if (null === $subnode) { - $subnode = &$messages; - } - foreach ($subnode as $key => $value) { - if (is_array($value)) { - $nodePath = $path ? $path.'.'.$key : $key; - $this->flatten($messages, $value, $nodePath); - if (null === $path) { - unset($messages[$key]); + $result = []; + foreach ($messages as $key => $value) { + if (\is_array($value)) { + foreach ($this->flatten($value) as $k => $v) { + $result[$key.'.'.$k] = $v; } - } elseif (null !== $path) { - $messages[$path.'.'.$key] = $value; + } else { + $result[$key] = $value; } } + + return $result; } } diff --git a/tests/integration/vendor/symfony/translation/Loader/CsvFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/CsvFileLoader.php index f1d3443f4..8d5d4db9a 100644 --- a/tests/integration/vendor/symfony/translation/Loader/CsvFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/CsvFileLoader.php @@ -27,9 +27,9 @@ class CsvFileLoader extends FileLoader /** * {@inheritdoc} */ - protected function loadResource($resource) + protected function loadResource(string $resource) { - $messages = array(); + $messages = []; try { $file = new \SplFileObject($resource, 'rb'); @@ -41,7 +41,11 @@ protected function loadResource($resource) $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape); foreach ($file as $data) { - if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === count($data)) { + if (false === $data) { + continue; + } + + if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === \count($data)) { $messages[$data[0]] = $data[1]; } } @@ -51,12 +55,8 @@ protected function loadResource($resource) /** * Sets the delimiter, enclosure, and escape character for CSV. - * - * @param string $delimiter delimiter character - * @param string $enclosure enclosure character - * @param string $escape escape character */ - public function setCsvControl($delimiter = ';', $enclosure = '"', $escape = '\\') + public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '\\') { $this->delimiter = $delimiter; $this->enclosure = $enclosure; diff --git a/tests/integration/vendor/symfony/translation/Loader/FileLoader.php b/tests/integration/vendor/symfony/translation/Loader/FileLoader.php index a7f24f41a..4725ea6d6 100644 --- a/tests/integration/vendor/symfony/translation/Loader/FileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/FileLoader.php @@ -11,9 +11,9 @@ namespace Symfony\Component\Translation\Loader; +use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; /** * @author Abdellatif Ait boudad @@ -23,7 +23,7 @@ abstract class FileLoader extends ArrayLoader /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + public function load($resource, string $locale, string $domain = 'messages') { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); @@ -37,29 +37,27 @@ public function load($resource, $locale, $domain = 'messages') // empty resource if (null === $messages) { - $messages = array(); + $messages = []; } // not an array - if (!is_array($messages)) { + if (!\is_array($messages)) { throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); } $catalogue = parent::load($messages, $locale, $domain); - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } return $catalogue; } - /* - * @param string $resource - * + /** * @return array * - * @throws InvalidResourceException If stream content has an invalid format. + * @throws InvalidResourceException if stream content has an invalid format */ - abstract protected function loadResource($resource); + abstract protected function loadResource(string $resource); } diff --git a/tests/integration/vendor/symfony/translation/Loader/IcuDatFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/IcuDatFileLoader.php index 71ba90a39..2a1aecc60 100644 --- a/tests/integration/vendor/symfony/translation/Loader/IcuDatFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/IcuDatFileLoader.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\MessageCatalogue; /** * IcuResFileLoader loads translations from a resource bundle. @@ -26,7 +26,7 @@ class IcuDatFileLoader extends IcuResFileLoader /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + public function load($resource, string $locale, string $domain = 'messages') { if (!stream_is_local($resource.'.dat')) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); @@ -39,12 +39,11 @@ public function load($resource, $locale, $domain = 'messages') try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { - // HHVM compatibility: constructor throws on invalid resource $rb = null; } if (!$rb) { - throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource)); + throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } @@ -53,7 +52,7 @@ public function load($resource, $locale, $domain = 'messages') $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource.'.dat')); } diff --git a/tests/integration/vendor/symfony/translation/Loader/IcuResFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/IcuResFileLoader.php index 2f8037fb1..7e3391ecb 100644 --- a/tests/integration/vendor/symfony/translation/Loader/IcuResFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/IcuResFileLoader.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Translation\MessageCatalogue; /** * IcuResFileLoader loads translations from a resource bundle. @@ -26,7 +26,7 @@ class IcuResFileLoader implements LoaderInterface /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + public function load($resource, string $locale, string $domain = 'messages') { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); @@ -39,12 +39,11 @@ public function load($resource, $locale, $domain = 'messages') try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { - // HHVM compatibility: constructor throws on invalid resource $rb = null; } if (!$rb) { - throw new InvalidResourceException(sprintf('Cannot load resource "%s"', $resource)); + throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } @@ -53,7 +52,7 @@ public function load($resource, $locale, $domain = 'messages') $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); - if (class_exists('Symfony\Component\Config\Resource\DirectoryResource')) { + if (class_exists(DirectoryResource::class)) { $catalogue->addResource(new DirectoryResource($resource)); } @@ -70,13 +69,13 @@ public function load($resource, $locale, $domain = 'messages') * * This function takes an array by reference and will modify it * - * @param \ResourceBundle $rb the ResourceBundle that will be flattened - * @param array $messages used internally for recursive calls - * @param string $path current path being parsed, used internally for recursive calls + * @param \ResourceBundle $rb The ResourceBundle that will be flattened + * @param array $messages Used internally for recursive calls + * @param string $path Current path being parsed, used internally for recursive calls * - * @return array the flattened ResourceBundle + * @return array */ - protected function flatten(\ResourceBundle $rb, array &$messages = array(), $path = null) + protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null) { foreach ($rb as $key => $value) { $nodePath = $path ? $path.'.'.$key : $key; diff --git a/tests/integration/vendor/symfony/translation/Loader/IniFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/IniFileLoader.php index 11d9b272e..7398f7778 100644 --- a/tests/integration/vendor/symfony/translation/Loader/IniFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/IniFileLoader.php @@ -21,7 +21,7 @@ class IniFileLoader extends FileLoader /** * {@inheritdoc} */ - protected function loadResource($resource) + protected function loadResource(string $resource) { return parse_ini_file($resource, true); } diff --git a/tests/integration/vendor/symfony/translation/Loader/JsonFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/JsonFileLoader.php index ce4e91ff4..5aefba072 100644 --- a/tests/integration/vendor/symfony/translation/Loader/JsonFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/JsonFileLoader.php @@ -23,14 +23,14 @@ class JsonFileLoader extends FileLoader /** * {@inheritdoc} */ - protected function loadResource($resource) + protected function loadResource(string $resource) { - $messages = array(); + $messages = []; if ($data = file_get_contents($resource)) { $messages = json_decode($data, true); if (0 < $errorCode = json_last_error()) { - throw new InvalidResourceException(sprintf('Error parsing JSON - %s', $this->getJSONErrorMessage($errorCode))); + throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode)); } } @@ -39,23 +39,19 @@ protected function loadResource($resource) /** * Translates JSON_ERROR_* constant into meaningful message. - * - * @param int $errorCode Error code returned by json_last_error() call - * - * @return string Message string */ - private function getJSONErrorMessage($errorCode) + private function getJSONErrorMessage(int $errorCode): string { switch ($errorCode) { - case JSON_ERROR_DEPTH: + case \JSON_ERROR_DEPTH: return 'Maximum stack depth exceeded'; - case JSON_ERROR_STATE_MISMATCH: + case \JSON_ERROR_STATE_MISMATCH: return 'Underflow or the modes mismatch'; - case JSON_ERROR_CTRL_CHAR: + case \JSON_ERROR_CTRL_CHAR: return 'Unexpected control character found'; - case JSON_ERROR_SYNTAX: + case \JSON_ERROR_SYNTAX: return 'Syntax error, malformed JSON'; - case JSON_ERROR_UTF8: + case \JSON_ERROR_UTF8: return 'Malformed UTF-8 characters, possibly incorrectly encoded'; default: return 'Unknown error'; diff --git a/tests/integration/vendor/symfony/translation/Loader/LoaderInterface.php b/tests/integration/vendor/symfony/translation/Loader/LoaderInterface.php index 6b65fe380..96b0c0d85 100644 --- a/tests/integration/vendor/symfony/translation/Loader/LoaderInterface.php +++ b/tests/integration/vendor/symfony/translation/Loader/LoaderInterface.php @@ -11,9 +11,9 @@ namespace Symfony\Component\Translation\Loader; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\MessageCatalogue; /** * LoaderInterface is the interface implemented by all translation loaders. @@ -29,10 +29,10 @@ interface LoaderInterface * @param string $locale A locale * @param string $domain The domain * - * @return MessageCatalogue A MessageCatalogue instance + * @return MessageCatalogue * * @throws NotFoundResourceException when the resource cannot be found * @throws InvalidResourceException when the resource cannot be loaded */ - public function load($resource, $locale, $domain = 'messages'); + public function load($resource, string $locale, string $domain = 'messages'); } diff --git a/tests/integration/vendor/symfony/translation/Loader/MoFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/MoFileLoader.php index 025fcd89c..7fa2f2dcb 100644 --- a/tests/integration/vendor/symfony/translation/Loader/MoFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/MoFileLoader.php @@ -19,27 +19,21 @@ class MoFileLoader extends FileLoader { /** - * Magic used for validating the format of a MO file as well as + * Magic used for validating the format of an MO file as well as * detecting if the machine used to create that file was little endian. - * - * @var float */ - const MO_LITTLE_ENDIAN_MAGIC = 0x950412de; + public const MO_LITTLE_ENDIAN_MAGIC = 0x950412DE; /** - * Magic used for validating the format of a MO file as well as + * Magic used for validating the format of an MO file as well as * detecting if the machine used to create that file was big endian. - * - * @var float */ - const MO_BIG_ENDIAN_MAGIC = 0xde120495; + public const MO_BIG_ENDIAN_MAGIC = 0xDE120495; /** - * The size of the header of a MO file in bytes. - * - * @var int Number of bytes + * The size of the header of an MO file in bytes. */ - const MO_HEADER_SIZE = 28; + public const MO_HEADER_SIZE = 28; /** * Parses machine object (MO) format, independent of the machine's endian it @@ -47,7 +41,7 @@ class MoFileLoader extends FileLoader * * {@inheritdoc} */ - protected function loadResource($resource) + protected function loadResource(string $resource) { $stream = fopen($resource, 'r'); @@ -59,9 +53,9 @@ protected function loadResource($resource) $magic = unpack('V1', fread($stream, 4)); $magic = hexdec(substr(dechex(current($magic)), -8)); - if ($magic == self::MO_LITTLE_ENDIAN_MAGIC) { + if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) { $isBigEndian = false; - } elseif ($magic == self::MO_BIG_ENDIAN_MAGIC) { + } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) { $isBigEndian = true; } else { throw new InvalidResourceException('MO stream content has an invalid format.'); @@ -77,7 +71,7 @@ protected function loadResource($resource) // offsetHashes $this->readLong($stream, $isBigEndian); - $messages = array(); + $messages = []; for ($i = 0; $i < $count; ++$i) { $pluralId = null; @@ -95,8 +89,8 @@ protected function loadResource($resource) fseek($stream, $offset); $singularId = fread($stream, $length); - if (strpos($singularId, "\000") !== false) { - list($singularId, $pluralId) = explode("\000", $singularId); + if (str_contains($singularId, "\000")) { + [$singularId, $pluralId] = explode("\000", $singularId); } fseek($stream, $offsetTranslated + $i * 8); @@ -110,24 +104,19 @@ protected function loadResource($resource) fseek($stream, $offset); $translated = fread($stream, $length); - if (strpos($translated, "\000") !== false) { + if (str_contains($translated, "\000")) { $translated = explode("\000", $translated); } - $ids = array('singular' => $singularId, 'plural' => $pluralId); + $ids = ['singular' => $singularId, 'plural' => $pluralId]; $item = compact('ids', 'translated'); - if (is_array($item['translated'])) { - $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]); + if (!empty($item['ids']['singular'])) { + $id = $item['ids']['singular']; if (isset($item['ids']['plural'])) { - $plurals = array(); - foreach ($item['translated'] as $plural => $translated) { - $plurals[] = sprintf('{%d} %s', $plural, $translated); - } - $messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals)); + $id .= '|'.$item['ids']['plural']; } - } elseif (!empty($item['ids']['singular'])) { - $messages[$item['ids']['singular']] = stripcslashes($item['translated']); + $messages[$id] = stripcslashes(implode('|', (array) $item['translated'])); } } @@ -137,14 +126,11 @@ protected function loadResource($resource) } /** - * Reads an unsigned long from stream respecting endianess. + * Reads an unsigned long from stream respecting endianness. * * @param resource $stream - * @param bool $isBigEndian - * - * @return int */ - private function readLong($stream, $isBigEndian) + private function readLong($stream, bool $isBigEndian): int { $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4)); $result = current($result); diff --git a/tests/integration/vendor/symfony/translation/Loader/PhpFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/PhpFileLoader.php index a0050e8db..85f10902b 100644 --- a/tests/integration/vendor/symfony/translation/Loader/PhpFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/PhpFileLoader.php @@ -18,11 +18,25 @@ */ class PhpFileLoader extends FileLoader { + private static $cache = []; + /** * {@inheritdoc} */ - protected function loadResource($resource) + protected function loadResource(string $resource) { - return require $resource; + if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) { + self::$cache = null; + } + + if (null === self::$cache) { + return require $resource; + } + + if (isset(self::$cache[$resource])) { + return self::$cache[$resource]; + } + + return self::$cache[$resource] = require $resource; } } diff --git a/tests/integration/vendor/symfony/translation/Loader/PoFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/PoFileLoader.php index 40f5464bf..ee143e203 100644 --- a/tests/integration/vendor/symfony/translation/Loader/PoFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/PoFileLoader.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Translation\Loader; /** - * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) + * @copyright Copyright (c) 2010, Union of RAD https://github.com/UnionOfRAD/lithium * @copyright Copyright (c) 2012, Clemens Tolboom */ class PoFileLoader extends FileLoader @@ -20,7 +20,7 @@ class PoFileLoader extends FileLoader /** * Parses portable object (PO) format. * - * From http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files + * From https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files * we should be able to parse files having: * * white-space @@ -60,57 +60,57 @@ class PoFileLoader extends FileLoader * * {@inheritdoc} */ - protected function loadResource($resource) + protected function loadResource(string $resource) { $stream = fopen($resource, 'r'); - $defaults = array( - 'ids' => array(), + $defaults = [ + 'ids' => [], 'translated' => null, - ); + ]; - $messages = array(); + $messages = []; $item = $defaults; - $flags = array(); + $flags = []; while ($line = fgets($stream)) { $line = trim($line); - if ($line === '') { + if ('' === $line) { // Whitespace indicated current item is done - if (!in_array('fuzzy', $flags)) { + if (!\in_array('fuzzy', $flags)) { $this->addMessage($messages, $item); } $item = $defaults; - $flags = array(); - } elseif (substr($line, 0, 2) === '#,') { + $flags = []; + } elseif ('#,' === substr($line, 0, 2)) { $flags = array_map('trim', explode(',', substr($line, 2))); - } elseif (substr($line, 0, 7) === 'msgid "') { + } elseif ('msgid "' === substr($line, 0, 7)) { // We start a new msg so save previous // TODO: this fails when comments or contexts are added $this->addMessage($messages, $item); $item = $defaults; $item['ids']['singular'] = substr($line, 7, -1); - } elseif (substr($line, 0, 8) === 'msgstr "') { + } elseif ('msgstr "' === substr($line, 0, 8)) { $item['translated'] = substr($line, 8, -1); - } elseif ($line[0] === '"') { + } elseif ('"' === $line[0]) { $continues = isset($item['translated']) ? 'translated' : 'ids'; - if (is_array($item[$continues])) { + if (\is_array($item[$continues])) { end($item[$continues]); $item[$continues][key($item[$continues])] .= substr($line, 1, -1); } else { $item[$continues] .= substr($line, 1, -1); } - } elseif (substr($line, 0, 14) === 'msgid_plural "') { + } elseif ('msgid_plural "' === substr($line, 0, 14)) { $item['ids']['plural'] = substr($line, 14, -1); - } elseif (substr($line, 0, 7) === 'msgstr[') { + } elseif ('msgstr[' === substr($line, 0, 7)) { $size = strpos($line, ']'); $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1); } } // save last item - if (!in_array('fuzzy', $flags)) { + if (!\in_array('fuzzy', $flags)) { $this->addMessage($messages, $item); } fclose($stream); @@ -123,29 +123,27 @@ protected function loadResource($resource) * * A .po file could contain by error missing plural indexes. We need to * fix these before saving them. - * - * @param array $messages - * @param array $item */ private function addMessage(array &$messages, array $item) { - if (is_array($item['translated'])) { - $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]); + if (!empty($item['ids']['singular'])) { + $id = stripcslashes($item['ids']['singular']); if (isset($item['ids']['plural'])) { - $plurals = $item['translated']; - // PO are by definition indexed so sort by index. - ksort($plurals); - // Make sure every index is filled. - end($plurals); - $count = key($plurals); - // Fill missing spots with '-'. - $empties = array_fill(0, $count + 1, '-'); - $plurals += $empties; - ksort($plurals); - $messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals)); + $id .= '|'.stripcslashes($item['ids']['plural']); } - } elseif (!empty($item['ids']['singular'])) { - $messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']); + + $translated = (array) $item['translated']; + // PO are by definition indexed so sort by index. + ksort($translated); + // Make sure every index is filled. + end($translated); + $count = key($translated); + // Fill missing spots with '-'. + $empties = array_fill(0, $count + 1, '-'); + $translated += $empties; + ksort($translated); + + $messages[$id] = stripcslashes(implode('|', $translated)); } } } diff --git a/tests/integration/vendor/symfony/translation/Loader/QtFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/QtFileLoader.php index 657bd6eb5..9cf2fe976 100644 --- a/tests/integration/vendor/symfony/translation/Loader/QtFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/QtFileLoader.php @@ -11,11 +11,12 @@ namespace Symfony\Component\Translation\Loader; +use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\XmlUtils; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; /** * QtFileLoader loads translations from QT Translations XML files. @@ -27,8 +28,12 @@ class QtFileLoader implements LoaderInterface /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + public function load($resource, string $locale, string $domain = 'messages') { + if (!class_exists(XmlUtils::class)) { + throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.'); + } + if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } @@ -50,7 +55,7 @@ public function load($resource, $locale, $domain = 'messages') $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]'); $catalogue = new MessageCatalogue($locale); - if ($nodes->length == 1) { + if (1 == $nodes->length) { $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message'); foreach ($translations as $translation) { $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue; @@ -65,7 +70,7 @@ public function load($resource, $locale, $domain = 'messages') $translation = $translation->nextSibling; } - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } } diff --git a/tests/integration/vendor/symfony/translation/Loader/XliffFileLoader.php b/tests/integration/vendor/symfony/translation/Loader/XliffFileLoader.php index e3cab6543..5c9794a54 100644 --- a/tests/integration/vendor/symfony/translation/Loader/XliffFileLoader.php +++ b/tests/integration/vendor/symfony/translation/Loader/XliffFileLoader.php @@ -11,12 +11,15 @@ namespace Symfony\Component\Translation\Loader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; use Symfony\Component\Config\Util\XmlUtils; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; -use Symfony\Component\Translation\Exception\InvalidArgumentException; -use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Translation\Exception\RuntimeException; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Util\XliffUtils; /** * XliffFileLoader loads translations from XLIFF files. @@ -28,36 +31,53 @@ class XliffFileLoader implements LoaderInterface /** * {@inheritdoc} */ - public function load($resource, $locale, $domain = 'messages') + public function load($resource, string $locale, string $domain = 'messages') { - if (!stream_is_local($resource)) { - throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + if (!class_exists(XmlUtils::class)) { + throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.'); + } + + if (!$this->isXmlString($resource)) { + if (!stream_is_local($resource)) { + throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); + } + + if (!file_exists($resource)) { + throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + } + + if (!is_file($resource)) { + throw new InvalidResourceException(sprintf('This is neither a file nor an XLIFF string "%s".', $resource)); + } } - if (!file_exists($resource)) { - throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); + try { + if ($this->isXmlString($resource)) { + $dom = XmlUtils::parse($resource); + } else { + $dom = XmlUtils::loadFile($resource); + } + } catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) { + throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e); + } + + if ($errors = XliffUtils::validateSchema($dom)) { + throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors)); } $catalogue = new MessageCatalogue($locale); - $this->extract($resource, $catalogue, $domain); + $this->extract($dom, $catalogue, $domain); - if (class_exists('Symfony\Component\Config\Resource\FileResource')) { + if (is_file($resource) && class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } return $catalogue; } - private function extract($resource, MessageCatalogue $catalogue, $domain) + private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) { - try { - $dom = XmlUtils::loadFile($resource); - } catch (\InvalidArgumentException $e) { - throw new InvalidResourceException(sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e); - } - - $xliffVersion = $this->getVersionNumber($dom); - $this->validateSchema($xliffVersion, $dom, $this->getSchema($xliffVersion)); + $xliffVersion = XliffUtils::getVersionNumber($dom); if ('1.2' === $xliffVersion) { $this->extractXliff1($dom, $catalogue, $domain); @@ -70,93 +90,107 @@ private function extract($resource, MessageCatalogue $catalogue, $domain) /** * Extract messages and metadata from DOMDocument into a MessageCatalogue. - * - * @param \DOMDocument $dom Source to extract messages and metadata - * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata - * @param string $domain The domain */ - private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, $domain) + private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) { $xml = simplexml_import_dom($dom); - $encoding = strtoupper($dom->encoding); + $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; - $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2'); - foreach ($xml->xpath('//xliff:trans-unit') as $translation) { - $attributes = $translation->attributes(); + $namespace = 'urn:oasis:names:tc:xliff:document:1.2'; + $xml->registerXPathNamespace('xliff', $namespace); - if (!(isset($attributes['resname']) || isset($translation->source))) { - continue; - } + foreach ($xml->xpath('//xliff:file') as $file) { + $fileAttributes = $file->attributes(); - $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; - // If the xlf file has another encoding specified, try to convert it because - // simple_xml will always return utf-8 encoded values - $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $source), $encoding); + $file->registerXPathNamespace('xliff', $namespace); - $catalogue->set((string) $source, $target, $domain); + foreach ($file->xpath('.//xliff:trans-unit') as $translation) { + $attributes = $translation->attributes(); - $metadata = array(); - if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { - $metadata['notes'] = $notes; - } + if (!(isset($attributes['resname']) || isset($translation->source))) { + continue; + } - if (isset($translation->target) && $translation->target->attributes()) { - $metadata['target-attributes'] = array(); - foreach ($translation->target->attributes() as $key => $value) { - $metadata['target-attributes'][$key] = (string) $value; + $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding); + + $catalogue->set((string) $source, $target, $domain); + + $metadata = [ + 'source' => (string) $translation->source, + 'file' => [ + 'original' => (string) $fileAttributes['original'], + ], + ]; + if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { + $metadata['notes'] = $notes; } - } - if (isset($attributes['id'])) { - $metadata['id'] = (string) $attributes['id']; - } + if (isset($translation->target) && $translation->target->attributes()) { + $metadata['target-attributes'] = []; + foreach ($translation->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; + } + } + + if (isset($attributes['id'])) { + $metadata['id'] = (string) $attributes['id']; + } - $catalogue->setMetadata((string) $source, $metadata, $domain); + $catalogue->setMetadata((string) $source, $metadata, $domain); + } } } - /** - * @param \DOMDocument $dom - * @param MessageCatalogue $catalogue - * @param string $domain - */ - private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, $domain) + private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain) { $xml = simplexml_import_dom($dom); - $encoding = strtoupper($dom->encoding); + $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0'); - foreach ($xml->xpath('//xliff:unit/xliff:segment') as $segment) { - $source = $segment->source; + foreach ($xml->xpath('//xliff:unit') as $unit) { + foreach ($unit->segment as $segment) { + $attributes = $unit->attributes(); + $source = $attributes['name'] ?? $segment->source; - // If the xlf file has another encoding specified, try to convert it because - // simple_xml will always return utf-8 encoded values - $target = $this->utf8ToCharset((string) (isset($segment->target) ? $segment->target : $source), $encoding); + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) ($segment->target ?? $segment->source), $encoding); - $catalogue->set((string) $source, $target, $domain); + $catalogue->set((string) $source, $target, $domain); - $metadata = array(); - if (isset($segment->target) && $segment->target->attributes()) { - $metadata['target-attributes'] = array(); - foreach ($segment->target->attributes() as $key => $value) { - $metadata['target-attributes'][$key] = (string) $value; + $metadata = []; + if (isset($segment->target) && $segment->target->attributes()) { + $metadata['target-attributes'] = []; + foreach ($segment->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; + } } - } - $catalogue->setMetadata((string) $source, $metadata, $domain); + if (isset($unit->notes)) { + $metadata['notes'] = []; + foreach ($unit->notes->note as $noteNode) { + $note = []; + foreach ($noteNode->attributes() as $key => $value) { + $note[$key] = (string) $value; + } + $note['content'] = (string) $noteNode; + $metadata['notes'][] = $note; + } + } + + $catalogue->setMetadata((string) $source, $metadata, $domain); + } } } /** * Convert a UTF8 string to the specified encoding. - * - * @param string $content String to decode - * @param string $encoding Target encoding - * - * @return string */ - private function utf8ToCharset($content, $encoding = null) + private function utf8ToCharset(string $content, string $encoding = null): string { if ('UTF-8' !== $encoding && !empty($encoding)) { return mb_convert_encoding($content, $encoding, 'UTF-8'); @@ -165,145 +199,9 @@ private function utf8ToCharset($content, $encoding = null) return $content; } - /** - * Validates and parses the given file into a DOMDocument. - * - * @param string $file - * @param \DOMDocument $dom - * @param string $schema source of the schema - * - * @throws InvalidResourceException - */ - private function validateSchema($file, \DOMDocument $dom, $schema) + private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array { - $internalErrors = libxml_use_internal_errors(true); - - $disableEntities = libxml_disable_entity_loader(false); - - if (!@$dom->schemaValidateSource($schema)) { - libxml_disable_entity_loader($disableEntities); - - throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s', $file, implode("\n", $this->getXmlErrors($internalErrors)))); - } - - libxml_disable_entity_loader($disableEntities); - - $dom->normalizeDocument(); - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - } - - private function getSchema($xliffVersion) - { - if ('1.2' === $xliffVersion) { - $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-1.2-strict.xsd'); - $xmlUri = 'http://www.w3.org/2001/xml.xsd'; - } elseif ('2.0' === $xliffVersion) { - $schemaSource = file_get_contents(__DIR__.'/schema/dic/xliff-core/xliff-core-2.0.xsd'); - $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd'; - } else { - throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion)); - } - - return $this->fixXmlLocation($schemaSource, $xmlUri); - } - - /** - * Internally changes the URI of a dependent xsd to be loaded locally. - * - * @param string $schemaSource Current content of schema file - * @param string $xmlUri External URI of XML to convert to local - * - * @return string - */ - private function fixXmlLocation($schemaSource, $xmlUri) - { - $newPath = str_replace('\\', '/', __DIR__).'/schema/dic/xliff-core/xml.xsd'; - $parts = explode('/', $newPath); - if (0 === stripos($newPath, 'phar://')) { - $tmpfile = tempnam(sys_get_temp_dir(), 'sf2'); - if ($tmpfile) { - copy($newPath, $tmpfile); - $parts = explode('/', str_replace('\\', '/', $tmpfile)); - } - } - - $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; - $newPath = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts)); - - return str_replace($xmlUri, $newPath, $schemaSource); - } - - /** - * Returns the XML errors of the internal XML parser. - * - * @param bool $internalErrors - * - * @return array An array of errors - */ - private function getXmlErrors($internalErrors) - { - $errors = array(); - foreach (libxml_get_errors() as $error) { - $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', - LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', - $error->code, - trim($error->message), - $error->file ?: 'n/a', - $error->line, - $error->column - ); - } - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - - return $errors; - } - - /** - * Gets xliff file version based on the root "version" attribute. - * Defaults to 1.2 for backwards compatibility. - * - * @param \DOMDocument $dom - * - * @throws InvalidArgumentException - * - * @return string - */ - private function getVersionNumber(\DOMDocument $dom) - { - /** @var \DOMNode $xliff */ - foreach ($dom->getElementsByTagName('xliff') as $xliff) { - $version = $xliff->attributes->getNamedItem('version'); - if ($version) { - return $version->nodeValue; - } - - $namespace = $xliff->attributes->getNamedItem('xmlns'); - if ($namespace) { - if (substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34) !== 0) { - throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s"', $namespace)); - } - - return substr($namespace, 34); - } - } - - // Falls back to v1.2 - return '1.2'; - } - - /** - * @param \SimpleXMLElement|null $noteElement - * @param string|null $encoding - * - * @return array - */ - private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $encoding = null) - { - $notes = array(); + $notes = []; if (null === $noteElement) { return $notes; @@ -312,7 +210,7 @@ private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $enco /** @var \SimpleXMLElement $xmlNote */ foreach ($noteElement as $xmlNote) { $noteAttributes = $xmlNote->attributes(); - $note = array('content' => $this->utf8ToCharset((string) $xmlNote, $encoding)); + $note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)]; if (isset($noteAttributes['priority'])) { $note['priority'] = (int) $noteAttributes['priority']; } @@ -326,4 +224,9 @@ private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $enco return $notes; } + + private function isXmlString(string $resource): bool + { + return 0 === strpos($resource, 'yamlParser) { - if (!class_exists('Symfony\Component\Yaml\Parser')) { + if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); } @@ -39,11 +40,15 @@ protected function loadResource($resource) } try { - $messages = $this->yamlParser->parse(file_get_contents($resource)); + $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { - throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e); + throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML: ', $resource).$e->getMessage(), 0, $e); + } + + if (null !== $messages && !\is_array($messages)) { + throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); } - return $messages; + return $messages ?: []; } } diff --git a/tests/integration/vendor/symfony/translation/LoggingTranslator.php b/tests/integration/vendor/symfony/translation/LoggingTranslator.php index 469b3d133..6ccd48289 100644 --- a/tests/integration/vendor/symfony/translation/LoggingTranslator.php +++ b/tests/integration/vendor/symfony/translation/LoggingTranslator.php @@ -13,30 +13,24 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad */ -class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface +class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface { - /** - * @var TranslatorInterface|TranslatorBagInterface - */ private $translator; - - /** - * @var LoggerInterface - */ private $logger; /** - * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface - * @param LoggerInterface $logger + * @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator The translator must implement TranslatorBagInterface */ public function __construct(TranslatorInterface $translator, LoggerInterface $logger) { - if (!$translator instanceof TranslatorBagInterface) { - throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator))); + if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { + throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', get_debug_type($translator))); } $this->translator = $translator; @@ -46,9 +40,9 @@ public function __construct(TranslatorInterface $translator, LoggerInterface $lo /** * {@inheritdoc} */ - public function trans($id, array $parameters = array(), $domain = null, $locale = null) + public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) { - $trans = $this->translator->trans($id, $parameters, $domain, $locale); + $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); $this->log($id, $domain, $locale); return $trans; @@ -57,83 +51,81 @@ public function trans($id, array $parameters = array(), $domain = null, $locale /** * {@inheritdoc} */ - public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + public function setLocale(string $locale) { - $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); - $this->log($id, $domain, $locale); + $prev = $this->translator->getLocale(); + $this->translator->setLocale($locale); + if ($prev === $locale) { + return; + } - return $trans; + $this->logger->debug(sprintf('The locale of the translator has changed from "%s" to "%s".', $prev, $locale)); } /** * {@inheritdoc} */ - public function setLocale($locale) + public function getLocale() { - $this->translator->setLocale($locale); + return $this->translator->getLocale(); } /** * {@inheritdoc} */ - public function getLocale() + public function getCatalogue(string $locale = null) { - return $this->translator->getLocale(); + return $this->translator->getCatalogue($locale); } /** * {@inheritdoc} */ - public function getCatalogue($locale = null) + public function getCatalogues(): array { - return $this->translator->getCatalogue($locale); + return $this->translator->getCatalogues(); } /** * Gets the fallback locales. * - * @return array $locales The fallback locales + * @return array */ public function getFallbackLocales() { - if ($this->translator instanceof Translator) { + if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) { return $this->translator->getFallbackLocales(); } - return array(); + return []; } /** * Passes through all unknown calls onto the translator object. */ - public function __call($method, $args) + public function __call(string $method, array $args) { - return call_user_func_array(array($this->translator, $method), $args); + return $this->translator->{$method}(...$args); } /** * Logs for missing translations. - * - * @param string $id - * @param string|null $domain - * @param string|null $locale */ - private function log($id, $domain, $locale) + private function log(string $id, ?string $domain, ?string $locale) { if (null === $domain) { $domain = 'messages'; } - $id = (string) $id; $catalogue = $this->translator->getCatalogue($locale); if ($catalogue->defines($id, $domain)) { return; } if ($catalogue->has($id, $domain)) { - $this->logger->debug('Translation use fallback catalogue.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale())); + $this->logger->debug('Translation use fallback catalogue.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]); } else { - $this->logger->warning('Translation not found.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale())); + $this->logger->warning('Translation not found.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]); } } } diff --git a/tests/integration/vendor/symfony/translation/MessageCatalogue.php b/tests/integration/vendor/symfony/translation/MessageCatalogue.php index c82b73e19..ff49b5a97 100644 --- a/tests/integration/vendor/symfony/translation/MessageCatalogue.php +++ b/tests/integration/vendor/symfony/translation/MessageCatalogue.php @@ -15,26 +15,21 @@ use Symfony\Component\Translation\Exception\LogicException; /** - * MessageCatalogue. - * * @author Fabien Potencier */ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface { - private $messages = array(); - private $metadata = array(); - private $resources = array(); + private $messages = []; + private $metadata = []; + private $resources = []; private $locale; private $fallbackCatalogue; private $parent; /** - * Constructor. - * - * @param string $locale The locale - * @param array $messages An array of messages classified by domain + * @param array $messages An array of messages classified by domain */ - public function __construct($locale, array $messages = array()) + public function __construct(string $locale, array $messages = []) { $this->locale = $locale; $this->messages = $messages; @@ -53,35 +48,60 @@ public function getLocale() */ public function getDomains() { - return array_keys($this->messages); + $domains = []; + + foreach ($this->messages as $domain => $messages) { + if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)); + } + $domains[$domain] = $domain; + } + + return array_values($domains); } /** * {@inheritdoc} */ - public function all($domain = null) + public function all(string $domain = null) { - if (null === $domain) { - return $this->messages; + if (null !== $domain) { + // skip messages merge if intl-icu requested explicitly + if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + return $this->messages[$domain] ?? []; + } + + return ($this->messages[$domain.self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []); } - return isset($this->messages[$domain]) ? $this->messages[$domain] : array(); + $allMessages = []; + + foreach ($this->messages as $domain => $messages) { + if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)); + $allMessages[$domain] = $messages + ($allMessages[$domain] ?? []); + } else { + $allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages; + } + } + + return $allMessages; } /** * {@inheritdoc} */ - public function set($id, $translation, $domain = 'messages') + public function set(string $id, string $translation, string $domain = 'messages') { - $this->add(array($id => $translation), $domain); + $this->add([$id => $translation], $domain); } /** * {@inheritdoc} */ - public function has($id, $domain = 'messages') + public function has(string $id, string $domain = 'messages') { - if (isset($this->messages[$domain][$id])) { + if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) { return true; } @@ -95,16 +115,20 @@ public function has($id, $domain = 'messages') /** * {@inheritdoc} */ - public function defines($id, $domain = 'messages') + public function defines(string $id, string $domain = 'messages') { - return isset($this->messages[$domain][$id]); + return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]); } /** * {@inheritdoc} */ - public function get($id, $domain = 'messages') + public function get(string $id, string $domain = 'messages') { + if (isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) { + return $this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]; + } + if (isset($this->messages[$domain][$id])) { return $this->messages[$domain][$id]; } @@ -119,9 +143,9 @@ public function get($id, $domain = 'messages') /** * {@inheritdoc} */ - public function replace($messages, $domain = 'messages') + public function replace(array $messages, string $domain = 'messages') { - $this->messages[$domain] = array(); + unset($this->messages[$domain], $this->messages[$domain.self::INTL_DOMAIN_SUFFIX]); $this->add($messages, $domain); } @@ -129,12 +153,21 @@ public function replace($messages, $domain = 'messages') /** * {@inheritdoc} */ - public function add($messages, $domain = 'messages') + public function add(array $messages, string $domain = 'messages') { if (!isset($this->messages[$domain])) { - $this->messages[$domain] = $messages; - } else { - $this->messages[$domain] = array_replace($this->messages[$domain], $messages); + $this->messages[$domain] = []; + } + $intlDomain = $domain; + if (!str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { + $intlDomain .= self::INTL_DOMAIN_SUFFIX; + } + foreach ($messages as $id => $message) { + if (isset($this->messages[$intlDomain]) && \array_key_exists($id, $this->messages[$intlDomain])) { + $this->messages[$intlDomain][$id] = $message; + } else { + $this->messages[$domain][$id] = $message; + } } } @@ -144,10 +177,14 @@ public function add($messages, $domain = 'messages') public function addCatalogue(MessageCatalogueInterface $catalogue) { if ($catalogue->getLocale() !== $this->locale) { - throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale)); + throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s".', $catalogue->getLocale(), $this->locale)); } foreach ($catalogue->all() as $domain => $messages) { + if ($intlMessages = $catalogue->all($domain.self::INTL_DOMAIN_SUFFIX)) { + $this->add($intlMessages, $domain.self::INTL_DOMAIN_SUFFIX); + $messages = array_diff_key($messages, $intlMessages); + } $this->add($messages, $domain); } @@ -220,7 +257,7 @@ public function addResource(ResourceInterface $resource) /** * {@inheritdoc} */ - public function getMetadata($key = '', $domain = 'messages') + public function getMetadata(string $key = '', string $domain = 'messages') { if ('' == $domain) { return $this->metadata; @@ -235,12 +272,14 @@ public function getMetadata($key = '', $domain = 'messages') return $this->metadata[$domain][$key]; } } + + return null; } /** * {@inheritdoc} */ - public function setMetadata($key, $value, $domain = 'messages') + public function setMetadata(string $key, $value, string $domain = 'messages') { $this->metadata[$domain][$key] = $value; } @@ -248,10 +287,10 @@ public function setMetadata($key, $value, $domain = 'messages') /** * {@inheritdoc} */ - public function deleteMetadata($key = '', $domain = 'messages') + public function deleteMetadata(string $key = '', string $domain = 'messages') { if ('' == $domain) { - $this->metadata = array(); + $this->metadata = []; } elseif ('' == $key) { unset($this->metadata[$domain]); } else { diff --git a/tests/integration/vendor/symfony/translation/MessageCatalogueInterface.php b/tests/integration/vendor/symfony/translation/MessageCatalogueInterface.php index b1b516dc2..cf7746c23 100644 --- a/tests/integration/vendor/symfony/translation/MessageCatalogueInterface.php +++ b/tests/integration/vendor/symfony/translation/MessageCatalogueInterface.php @@ -20,17 +20,19 @@ */ interface MessageCatalogueInterface { + public const INTL_DOMAIN_SUFFIX = '+intl-icu'; + /** * Gets the catalogue locale. * - * @return string The locale + * @return string */ public function getLocale(); /** * Gets the domains. * - * @return array An array of domains + * @return array */ public function getDomains(); @@ -41,9 +43,9 @@ public function getDomains(); * * @param string $domain The domain name * - * @return array An array of messages + * @return array */ - public function all($domain = null); + public function all(string $domain = null); /** * Sets a message translation. @@ -52,7 +54,7 @@ public function all($domain = null); * @param string $translation The messages translation * @param string $domain The domain name */ - public function set($id, $translation, $domain = 'messages'); + public function set(string $id, string $translation, string $domain = 'messages'); /** * Checks if a message has a translation. @@ -60,9 +62,9 @@ public function set($id, $translation, $domain = 'messages'); * @param string $id The message id * @param string $domain The domain name * - * @return bool true if the message has a translation, false otherwise + * @return bool */ - public function has($id, $domain = 'messages'); + public function has(string $id, string $domain = 'messages'); /** * Checks if a message has a translation (it does not take into account the fallback mechanism). @@ -70,9 +72,9 @@ public function has($id, $domain = 'messages'); * @param string $id The message id * @param string $domain The domain name * - * @return bool true if the message has a translation, false otherwise + * @return bool */ - public function defines($id, $domain = 'messages'); + public function defines(string $id, string $domain = 'messages'); /** * Gets a message translation. @@ -80,9 +82,9 @@ public function defines($id, $domain = 'messages'); * @param string $id The message id * @param string $domain The domain name * - * @return string The message translation + * @return string */ - public function get($id, $domain = 'messages'); + public function get(string $id, string $domain = 'messages'); /** * Sets translations for a given domain. @@ -90,7 +92,7 @@ public function get($id, $domain = 'messages'); * @param array $messages An array of translations * @param string $domain The domain name */ - public function replace($messages, $domain = 'messages'); + public function replace(array $messages, string $domain = 'messages'); /** * Adds translations for a given domain. @@ -98,45 +100,39 @@ public function replace($messages, $domain = 'messages'); * @param array $messages An array of translations * @param string $domain The domain name */ - public function add($messages, $domain = 'messages'); + public function add(array $messages, string $domain = 'messages'); /** * Merges translations from the given Catalogue into the current one. * * The two catalogues must have the same locale. - * - * @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance */ - public function addCatalogue(MessageCatalogueInterface $catalogue); + public function addCatalogue(self $catalogue); /** * Merges translations from the given Catalogue into the current one * only when the translation does not exist. * * This is used to provide default translations when they do not exist for the current locale. - * - * @param MessageCatalogueInterface $catalogue A MessageCatalogueInterface instance */ - public function addFallbackCatalogue(MessageCatalogueInterface $catalogue); + public function addFallbackCatalogue(self $catalogue); /** * Gets the fallback catalogue. * - * @return MessageCatalogueInterface|null A MessageCatalogueInterface instance or null when no fallback has been set + * @return self|null */ public function getFallbackCatalogue(); /** * Returns an array of resources loaded to build this collection. * - * @return ResourceInterface[] An array of resources + * @return ResourceInterface[] */ public function getResources(); /** * Adds a resource for this collection. - * - * @param ResourceInterface $resource A resource instance */ public function addResource(ResourceInterface $resource); } diff --git a/tests/integration/vendor/symfony/translation/MessageSelector.php b/tests/integration/vendor/symfony/translation/MessageSelector.php deleted file mode 100644 index c6134191b..000000000 --- a/tests/integration/vendor/symfony/translation/MessageSelector.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Translation; - -use Symfony\Component\Translation\Exception\InvalidArgumentException; - -/** - * MessageSelector. - * - * @author Fabien Potencier - * @author Bernhard Schussek - */ -class MessageSelector -{ - /** - * Given a message with different plural translations separated by a - * pipe (|), this method returns the correct portion of the message based - * on the given number, locale and the pluralization rules in the message - * itself. - * - * The message supports two different types of pluralization rules: - * - * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples - * indexed: There is one apple|There are %count% apples - * - * The indexed solution can also contain labels (e.g. one: There is one apple). - * This is purely for making the translations more clear - it does not - * affect the functionality. - * - * The two methods can also be mixed: - * {0} There are no apples|one: There is one apple|more: There are %count% apples - * - * @param string $message The message being translated - * @param int $number The number of items represented for the message - * @param string $locale The locale to use for choosing - * - * @return string - * - * @throws InvalidArgumentException - */ - public function choose($message, $number, $locale) - { - preg_match_all('/(?:\|\||[^\|])++/', $message, $parts); - $explicitRules = array(); - $standardRules = array(); - foreach ($parts[0] as $part) { - $part = trim(str_replace('||', '|', $part)); - - if (preg_match('/^(?P'.Interval::getIntervalRegexp().')\s*(?P.*?)$/xs', $part, $matches)) { - $explicitRules[$matches['interval']] = $matches['message']; - } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) { - $standardRules[] = $matches[1]; - } else { - $standardRules[] = $part; - } - } - - // try to match an explicit rule, then fallback to the standard ones - foreach ($explicitRules as $interval => $m) { - if (Interval::test($number, $interval)) { - return $m; - } - } - - $position = PluralizationRules::get($number, $locale); - - if (!isset($standardRules[$position])) { - // when there's exactly one rule given, and that rule is a standard - // rule, use this rule - if (1 === count($parts[0]) && isset($standardRules[0])) { - return $standardRules[0]; - } - - throw new InvalidArgumentException(sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $message, $locale, $number)); - } - - return $standardRules[$position]; - } -} diff --git a/tests/integration/vendor/symfony/translation/MetadataAwareInterface.php b/tests/integration/vendor/symfony/translation/MetadataAwareInterface.php index e93c6fbc7..2216eed94 100644 --- a/tests/integration/vendor/symfony/translation/MetadataAwareInterface.php +++ b/tests/integration/vendor/symfony/translation/MetadataAwareInterface.php @@ -25,30 +25,22 @@ interface MetadataAwareInterface * domain and then by key. Passing an empty key will return an array with all * metadata for the given domain. * - * @param string $key The key - * @param string $domain The domain name - * * @return mixed The value that was set or an array with the domains/keys or null */ - public function getMetadata($key = '', $domain = 'messages'); + public function getMetadata(string $key = '', string $domain = 'messages'); /** * Adds metadata to a message domain. * - * @param string $key The key - * @param mixed $value The value - * @param string $domain The domain name + * @param mixed $value */ - public function setMetadata($key, $value, $domain = 'messages'); + public function setMetadata(string $key, $value, string $domain = 'messages'); /** * Deletes metadata for the given key and domain. * * Passing an empty domain will delete all metadata. Passing an empty key will * delete all metadata for the given domain. - * - * @param string $key The key - * @param string $domain The domain name */ - public function deleteMetadata($key = '', $domain = 'messages'); + public function deleteMetadata(string $key = '', string $domain = 'messages'); } diff --git a/tests/integration/vendor/symfony/translation/PluralizationRules.php b/tests/integration/vendor/symfony/translation/PluralizationRules.php deleted file mode 100644 index ef2be7097..000000000 --- a/tests/integration/vendor/symfony/translation/PluralizationRules.php +++ /dev/null @@ -1,209 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Translation; - -/** - * Returns the plural rules for a given locale. - * - * @author Fabien Potencier - */ -class PluralizationRules -{ - private static $rules = array(); - - /** - * Returns the plural position to use for the given locale and number. - * - * @param int $number The number - * @param string $locale The locale - * - * @return int The plural position - */ - public static function get($number, $locale) - { - if ('pt_BR' === $locale) { - // temporary set a locale for brazilian - $locale = 'xbr'; - } - - if (strlen($locale) > 3) { - $locale = substr($locale, 0, -strlen(strrchr($locale, '_'))); - } - - if (isset(self::$rules[$locale])) { - $return = call_user_func(self::$rules[$locale], $number); - - if (!is_int($return) || $return < 0) { - return 0; - } - - return $return; - } - - /* - * The plural rules are derived from code of the Zend Framework (2010-09-25), - * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd). - * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) - */ - switch ($locale) { - case 'az': - case 'bo': - case 'dz': - case 'id': - case 'ja': - case 'jv': - case 'ka': - case 'km': - case 'kn': - case 'ko': - case 'ms': - case 'th': - case 'tr': - case 'vi': - case 'zh': - return 0; - break; - - case 'af': - case 'bn': - case 'bg': - case 'ca': - case 'da': - case 'de': - case 'el': - case 'en': - case 'eo': - case 'es': - case 'et': - case 'eu': - case 'fa': - case 'fi': - case 'fo': - case 'fur': - case 'fy': - case 'gl': - case 'gu': - case 'ha': - case 'he': - case 'hu': - case 'is': - case 'it': - case 'ku': - case 'lb': - case 'ml': - case 'mn': - case 'mr': - case 'nah': - case 'nb': - case 'ne': - case 'nl': - case 'nn': - case 'no': - case 'om': - case 'or': - case 'pa': - case 'pap': - case 'ps': - case 'pt': - case 'so': - case 'sq': - case 'sv': - case 'sw': - case 'ta': - case 'te': - case 'tk': - case 'ur': - case 'zu': - return ($number == 1) ? 0 : 1; - - case 'am': - case 'bh': - case 'fil': - case 'fr': - case 'gun': - case 'hi': - case 'hy': - case 'ln': - case 'mg': - case 'nso': - case 'xbr': - case 'ti': - case 'wa': - return (($number == 0) || ($number == 1)) ? 0 : 1; - - case 'be': - case 'bs': - case 'hr': - case 'ru': - case 'sr': - case 'uk': - return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); - - case 'cs': - case 'sk': - return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2); - - case 'ga': - return ($number == 1) ? 0 : (($number == 2) ? 1 : 2); - - case 'lt': - return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); - - case 'sl': - return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3)); - - case 'mk': - return ($number % 10 == 1) ? 0 : 1; - - case 'mt': - return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3)); - - case 'lv': - return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2); - - case 'pl': - return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2); - - case 'cy': - return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3)); - - case 'ro': - return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2); - - case 'ar': - return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); - - default: - return 0; - } - } - - /** - * Overrides the default plural rule for a given locale. - * - * @param callable $rule A PHP callable - * @param string $locale The locale - */ - public static function set(callable $rule, $locale) - { - if ('pt_BR' === $locale) { - // temporary set a locale for brazilian - $locale = 'xbr'; - } - - if (strlen($locale) > 3) { - $locale = substr($locale, 0, -strlen(strrchr($locale, '_'))); - } - - self::$rules[$locale] = $rule; - } -} diff --git a/tests/integration/vendor/symfony/translation/Provider/AbstractProviderFactory.php b/tests/integration/vendor/symfony/translation/Provider/AbstractProviderFactory.php new file mode 100644 index 000000000..17442fde8 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/AbstractProviderFactory.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\IncompleteDsnException; + +abstract class AbstractProviderFactory implements ProviderFactoryInterface +{ + public function supports(Dsn $dsn): bool + { + return \in_array($dsn->getScheme(), $this->getSupportedSchemes(), true); + } + + /** + * @return string[] + */ + abstract protected function getSupportedSchemes(): array; + + protected function getUser(Dsn $dsn): string + { + if (null === $user = $dsn->getUser()) { + throw new IncompleteDsnException('User is not set.', $dsn->getOriginalDsn()); + } + + return $user; + } + + protected function getPassword(Dsn $dsn): string + { + if (null === $password = $dsn->getPassword()) { + throw new IncompleteDsnException('Password is not set.', $dsn->getOriginalDsn()); + } + + return $password; + } +} diff --git a/tests/integration/vendor/symfony/translation/Provider/Dsn.php b/tests/integration/vendor/symfony/translation/Provider/Dsn.php new file mode 100644 index 000000000..820cabfb3 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/Dsn.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Exception\MissingRequiredOptionException; + +/** + * @author Fabien Potencier + * @author Oskar Stark + */ +final class Dsn +{ + private $scheme; + private $host; + private $user; + private $password; + private $port; + private $path; + private $options; + private $originalDsn; + + public function __construct(string $dsn) + { + $this->originalDsn = $dsn; + + if (false === $parsedDsn = parse_url($dsn)) { + throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN is invalid.', $dsn)); + } + + if (!isset($parsedDsn['scheme'])) { + throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN must contain a scheme.', $dsn)); + } + $this->scheme = $parsedDsn['scheme']; + + if (!isset($parsedDsn['host'])) { + throw new InvalidArgumentException(sprintf('The "%s" translation provider DSN must contain a host (use "default" by default).', $dsn)); + } + $this->host = $parsedDsn['host']; + + $this->user = '' !== ($parsedDsn['user'] ?? '') ? urldecode($parsedDsn['user']) : null; + $this->password = '' !== ($parsedDsn['pass'] ?? '') ? urldecode($parsedDsn['pass']) : null; + $this->port = $parsedDsn['port'] ?? null; + $this->path = $parsedDsn['path'] ?? null; + parse_str($parsedDsn['query'] ?? '', $this->options); + } + + public function getScheme(): string + { + return $this->scheme; + } + + public function getHost(): string + { + return $this->host; + } + + public function getUser(): ?string + { + return $this->user; + } + + public function getPassword(): ?string + { + return $this->password; + } + + public function getPort(int $default = null): ?int + { + return $this->port ?? $default; + } + + public function getOption(string $key, $default = null) + { + return $this->options[$key] ?? $default; + } + + public function getRequiredOption(string $key) + { + if (!\array_key_exists($key, $this->options) || '' === trim($this->options[$key])) { + throw new MissingRequiredOptionException($key); + } + + return $this->options[$key]; + } + + public function getOptions(): array + { + return $this->options; + } + + public function getPath(): ?string + { + return $this->path; + } + + public function getOriginalDsn(): string + { + return $this->originalDsn; + } +} diff --git a/tests/integration/vendor/symfony/translation/Provider/FilteringProvider.php b/tests/integration/vendor/symfony/translation/Provider/FilteringProvider.php new file mode 100644 index 000000000..5f970a2e5 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/FilteringProvider.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\TranslatorBag; +use Symfony\Component\Translation\TranslatorBagInterface; + +/** + * Filters domains and locales between the Translator config values and those specific to each provider. + * + * @author Mathieu Santostefano + */ +class FilteringProvider implements ProviderInterface +{ + private $provider; + private $locales; + private $domains; + + public function __construct(ProviderInterface $provider, array $locales, array $domains = []) + { + $this->provider = $provider; + $this->locales = $locales; + $this->domains = $domains; + } + + public function __toString(): string + { + return (string) $this->provider; + } + + /** + * {@inheritdoc} + */ + public function write(TranslatorBagInterface $translatorBag): void + { + $this->provider->write($translatorBag); + } + + public function read(array $domains, array $locales): TranslatorBag + { + $domains = !$this->domains ? $domains : array_intersect($this->domains, $domains); + $locales = array_intersect($this->locales, $locales); + + return $this->provider->read($domains, $locales); + } + + public function delete(TranslatorBagInterface $translatorBag): void + { + $this->provider->delete($translatorBag); + } + + public function getDomains(): array + { + return $this->domains; + } +} diff --git a/tests/integration/vendor/symfony/translation/Provider/NullProvider.php b/tests/integration/vendor/symfony/translation/Provider/NullProvider.php new file mode 100644 index 000000000..f00392ea0 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/NullProvider.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\TranslatorBag; +use Symfony\Component\Translation\TranslatorBagInterface; + +/** + * @author Mathieu Santostefano + */ +class NullProvider implements ProviderInterface +{ + public function __toString(): string + { + return 'null'; + } + + public function write(TranslatorBagInterface $translatorBag, bool $override = false): void + { + } + + public function read(array $domains, array $locales): TranslatorBag + { + return new TranslatorBag(); + } + + public function delete(TranslatorBagInterface $translatorBag): void + { + } +} diff --git a/tests/integration/vendor/symfony/translation/Provider/NullProviderFactory.php b/tests/integration/vendor/symfony/translation/Provider/NullProviderFactory.php new file mode 100644 index 000000000..f350f1602 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/NullProviderFactory.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; + +/** + * @author Mathieu Santostefano + */ +final class NullProviderFactory extends AbstractProviderFactory +{ + public function create(Dsn $dsn): ProviderInterface + { + if ('null' === $dsn->getScheme()) { + return new NullProvider(); + } + + throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes()); + } + + protected function getSupportedSchemes(): array + { + return ['null']; + } +} diff --git a/tests/integration/vendor/symfony/translation/Provider/ProviderFactoryInterface.php b/tests/integration/vendor/symfony/translation/Provider/ProviderFactoryInterface.php new file mode 100644 index 000000000..3fd4494b4 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/ProviderFactoryInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\IncompleteDsnException; +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; + +interface ProviderFactoryInterface +{ + /** + * @throws UnsupportedSchemeException + * @throws IncompleteDsnException + */ + public function create(Dsn $dsn): ProviderInterface; + + public function supports(Dsn $dsn): bool; +} diff --git a/tests/integration/vendor/symfony/translation/Provider/ProviderInterface.php b/tests/integration/vendor/symfony/translation/Provider/ProviderInterface.php new file mode 100644 index 000000000..a32193f29 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/ProviderInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\TranslatorBag; +use Symfony\Component\Translation\TranslatorBagInterface; + +interface ProviderInterface +{ + public function __toString(): string; + + /** + * Translations available in the TranslatorBag only must be created. + * Translations available in both the TranslatorBag and on the provider + * must be overwritten. + * Translations available on the provider only must be kept. + */ + public function write(TranslatorBagInterface $translatorBag): void; + + public function read(array $domains, array $locales): TranslatorBag; + + public function delete(TranslatorBagInterface $translatorBag): void; +} diff --git a/tests/integration/vendor/symfony/translation/Provider/TranslationProviderCollection.php b/tests/integration/vendor/symfony/translation/Provider/TranslationProviderCollection.php new file mode 100644 index 000000000..61ac641cd --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/TranslationProviderCollection.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\InvalidArgumentException; + +/** + * @author Mathieu Santostefano + */ +final class TranslationProviderCollection +{ + /** + * @var array + */ + private $providers; + + /** + * @param array $providers + */ + public function __construct(iterable $providers) + { + $this->providers = \is_array($providers) ? $providers : iterator_to_array($providers); + } + + public function __toString(): string + { + return '['.implode(',', array_keys($this->providers)).']'; + } + + public function has(string $name): bool + { + return isset($this->providers[$name]); + } + + public function get(string $name): ProviderInterface + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('Provider "%s" not found. Available: "%s".', $name, (string) $this)); + } + + return $this->providers[$name]; + } + + public function keys(): array + { + return array_keys($this->providers); + } +} diff --git a/tests/integration/vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php b/tests/integration/vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php new file mode 100644 index 000000000..81db3a5f1 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Provider/TranslationProviderCollectionFactory.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Provider; + +use Symfony\Component\Translation\Exception\UnsupportedSchemeException; + +/** + * @author Mathieu Santostefano + */ +class TranslationProviderCollectionFactory +{ + private $factories; + private $enabledLocales; + + /** + * @param iterable $factories + */ + public function __construct(iterable $factories, array $enabledLocales) + { + $this->factories = $factories; + $this->enabledLocales = $enabledLocales; + } + + public function fromConfig(array $config): TranslationProviderCollection + { + $providers = []; + foreach ($config as $name => $currentConfig) { + $providers[$name] = $this->fromDsnObject( + new Dsn($currentConfig['dsn']), + !$currentConfig['locales'] ? $this->enabledLocales : $currentConfig['locales'], + !$currentConfig['domains'] ? [] : $currentConfig['domains'] + ); + } + + return new TranslationProviderCollection($providers); + } + + public function fromDsnObject(Dsn $dsn, array $locales, array $domains = []): ProviderInterface + { + foreach ($this->factories as $factory) { + if ($factory->supports($dsn)) { + return new FilteringProvider($factory->create($dsn), $locales, $domains); + } + } + + throw new UnsupportedSchemeException($dsn); + } +} diff --git a/tests/integration/vendor/symfony/translation/PseudoLocalizationTranslator.php b/tests/integration/vendor/symfony/translation/PseudoLocalizationTranslator.php new file mode 100644 index 000000000..e004986a5 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/PseudoLocalizationTranslator.php @@ -0,0 +1,368 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * This translator should only be used in a development environment. + */ +final class PseudoLocalizationTranslator implements TranslatorInterface +{ + private const EXPANSION_CHARACTER = '~'; + + private $translator; + private $accents; + private $expansionFactor; + private $brackets; + private $parseHTML; + + /** + * @var string[] + */ + private $localizableHTMLAttributes; + + /** + * Available options: + * * accents: + * type: boolean + * default: true + * description: replace ASCII characters of the translated string with accented versions or similar characters + * example: if true, "foo" => "ƒöö". + * + * * expansion_factor: + * type: float + * default: 1 + * validation: it must be greater than or equal to 1 + * description: expand the translated string by the given factor with spaces and tildes + * example: if 2, "foo" => "~foo ~" + * + * * brackets: + * type: boolean + * default: true + * description: wrap the translated string with brackets + * example: if true, "foo" => "[foo]" + * + * * parse_html: + * type: boolean + * default: false + * description: parse the translated string as HTML - looking for HTML tags has a performance impact but allows to preserve them from alterations - it also allows to compute the visible translated string length which is useful to correctly expand ot when it contains HTML + * warning: unclosed tags are unsupported, they will be fixed (closed) by the parser - eg, "foo
bar" => "foo
bar
" + * + * * localizable_html_attributes: + * type: string[] + * default: [] + * description: the list of HTML attributes whose values can be altered - it is only useful when the "parse_html" option is set to true + * example: if ["title"], and with the "accents" option set to true, "Profile" => "Þŕöƒîļé" - if "title" was not in the "localizable_html_attributes" list, the title attribute data would be left unchanged. + */ + public function __construct(TranslatorInterface $translator, array $options = []) + { + $this->translator = $translator; + $this->accents = $options['accents'] ?? true; + + if (1.0 > ($this->expansionFactor = $options['expansion_factor'] ?? 1.0)) { + throw new \InvalidArgumentException('The expansion factor must be greater than or equal to 1.'); + } + + $this->brackets = $options['brackets'] ?? true; + + $this->parseHTML = $options['parse_html'] ?? false; + if ($this->parseHTML && !$this->accents && 1.0 === $this->expansionFactor) { + $this->parseHTML = false; + } + + $this->localizableHTMLAttributes = $options['localizable_html_attributes'] ?? []; + } + + /** + * {@inheritdoc} + */ + public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string + { + $trans = ''; + $visibleText = ''; + + foreach ($this->getParts($this->translator->trans($id, $parameters, $domain, $locale)) as [$visible, $localizable, $text]) { + if ($visible) { + $visibleText .= $text; + } + + if (!$localizable) { + $trans .= $text; + + continue; + } + + $this->addAccents($trans, $text); + } + + $this->expand($trans, $visibleText); + + $this->addBrackets($trans); + + return $trans; + } + + public function getLocale(): string + { + return $this->translator->getLocale(); + } + + private function getParts(string $originalTrans): array + { + if (!$this->parseHTML) { + return [[true, true, $originalTrans]]; + } + + $html = mb_encode_numericentity($originalTrans, [0x80, 0xFFFF, 0, 0xFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8'); + + $useInternalErrors = libxml_use_internal_errors(true); + + $dom = new \DOMDocument(); + $dom->loadHTML(''.$html.''); + + libxml_clear_errors(); + libxml_use_internal_errors($useInternalErrors); + + return $this->parseNode($dom->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)); + } + + private function parseNode(\DOMNode $node): array + { + $parts = []; + + foreach ($node->childNodes as $childNode) { + if (!$childNode instanceof \DOMElement) { + $parts[] = [true, true, $childNode->nodeValue]; + + continue; + } + + $parts[] = [false, false, '<'.$childNode->tagName]; + + /** @var \DOMAttr $attribute */ + foreach ($childNode->attributes as $attribute) { + $parts[] = [false, false, ' '.$attribute->nodeName.'="']; + + $localizableAttribute = \in_array($attribute->nodeName, $this->localizableHTMLAttributes, true); + foreach (preg_split('/(&(?:amp|quot|#039|lt|gt);+)/', htmlspecialchars($attribute->nodeValue, \ENT_QUOTES, 'UTF-8'), -1, \PREG_SPLIT_DELIM_CAPTURE) as $i => $match) { + if ('' === $match) { + continue; + } + + $parts[] = [false, $localizableAttribute && 0 === $i % 2, $match]; + } + + $parts[] = [false, false, '"']; + } + + $parts[] = [false, false, '>']; + + $parts = array_merge($parts, $this->parseNode($childNode, $parts)); + + $parts[] = [false, false, 'tagName.'>']; + } + + return $parts; + } + + private function addAccents(string &$trans, string $text): void + { + $trans .= $this->accents ? strtr($text, [ + ' ' => ' ', + '!' => '¡', + '"' => '″', + '#' => '♯', + '$' => '€', + '%' => '‰', + '&' => '⅋', + '\'' => '´', + '(' => '{', + ')' => '}', + '*' => '⁎', + '+' => '⁺', + ',' => '،', + '-' => '‐', + '.' => '·', + '/' => '⁄', + '0' => '⓪', + '1' => '①', + '2' => '②', + '3' => '③', + '4' => '④', + '5' => '⑤', + '6' => '⑥', + '7' => '⑦', + '8' => '⑧', + '9' => '⑨', + ':' => '∶', + ';' => '⁏', + '<' => '≤', + '=' => '≂', + '>' => '≥', + '?' => '¿', + '@' => '՞', + 'A' => 'Å', + 'B' => 'Ɓ', + 'C' => 'Ç', + 'D' => 'Ð', + 'E' => 'É', + 'F' => 'Ƒ', + 'G' => 'Ĝ', + 'H' => 'Ĥ', + 'I' => 'Î', + 'J' => 'Ĵ', + 'K' => 'Ķ', + 'L' => 'Ļ', + 'M' => 'Ṁ', + 'N' => 'Ñ', + 'O' => 'Ö', + 'P' => 'Þ', + 'Q' => 'Ǫ', + 'R' => 'Ŕ', + 'S' => 'Š', + 'T' => 'Ţ', + 'U' => 'Û', + 'V' => 'Ṽ', + 'W' => 'Ŵ', + 'X' => 'Ẋ', + 'Y' => 'Ý', + 'Z' => 'Ž', + '[' => '⁅', + '\\' => '∖', + ']' => '⁆', + '^' => '˄', + '_' => '‿', + '`' => '‵', + 'a' => 'å', + 'b' => 'ƀ', + 'c' => 'ç', + 'd' => 'ð', + 'e' => 'é', + 'f' => 'ƒ', + 'g' => 'ĝ', + 'h' => 'ĥ', + 'i' => 'î', + 'j' => 'ĵ', + 'k' => 'ķ', + 'l' => 'ļ', + 'm' => 'ɱ', + 'n' => 'ñ', + 'o' => 'ö', + 'p' => 'þ', + 'q' => 'ǫ', + 'r' => 'ŕ', + 's' => 'š', + 't' => 'ţ', + 'u' => 'û', + 'v' => 'ṽ', + 'w' => 'ŵ', + 'x' => 'ẋ', + 'y' => 'ý', + 'z' => 'ž', + '{' => '(', + '|' => '¦', + '}' => ')', + '~' => '˞', + ]) : $text; + } + + private function expand(string &$trans, string $visibleText): void + { + if (1.0 >= $this->expansionFactor) { + return; + } + + $visibleLength = $this->strlen($visibleText); + $missingLength = (int) (ceil($visibleLength * $this->expansionFactor)) - $visibleLength; + if ($this->brackets) { + $missingLength -= 2; + } + + if (0 >= $missingLength) { + return; + } + + $words = []; + $wordsCount = 0; + foreach (preg_split('/ +/', $visibleText, -1, \PREG_SPLIT_NO_EMPTY) as $word) { + $wordLength = $this->strlen($word); + + if ($wordLength >= $missingLength) { + continue; + } + + if (!isset($words[$wordLength])) { + $words[$wordLength] = 0; + } + + ++$words[$wordLength]; + ++$wordsCount; + } + + if (!$words) { + $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1); + + return; + } + + arsort($words, \SORT_NUMERIC); + + $longestWordLength = max(array_keys($words)); + + while (true) { + $r = mt_rand(1, $wordsCount); + + foreach ($words as $length => $count) { + $r -= $count; + if ($r <= 0) { + break; + } + } + + $trans .= ' '.str_repeat(self::EXPANSION_CHARACTER, $length); + + $missingLength -= $length + 1; + + if (0 === $missingLength) { + return; + } + + while ($longestWordLength >= $missingLength) { + $wordsCount -= $words[$longestWordLength]; + unset($words[$longestWordLength]); + + if (!$words) { + $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1); + + return; + } + + $longestWordLength = max(array_keys($words)); + } + } + } + + private function addBrackets(string &$trans): void + { + if (!$this->brackets) { + return; + } + + $trans = '['.$trans.']'; + } + + private function strlen(string $s): int + { + return false === ($encoding = mb_detect_encoding($s, null, true)) ? \strlen($s) : mb_strlen($s, $encoding); + } +} diff --git a/tests/integration/vendor/symfony/translation/README.md b/tests/integration/vendor/symfony/translation/README.md index 46f3d1f2f..adda9a5b2 100644 --- a/tests/integration/vendor/symfony/translation/README.md +++ b/tests/integration/vendor/symfony/translation/README.md @@ -3,11 +3,46 @@ Translation Component The Translation component provides tools to internationalize your application. +Getting Started +--------------- + +``` +$ composer require symfony/translation +``` + +```php +use Symfony\Component\Translation\Translator; +use Symfony\Component\Translation\Loader\ArrayLoader; + +$translator = new Translator('fr_FR'); +$translator->addLoader('array', new ArrayLoader()); +$translator->addResource('array', [ + 'Hello World!' => 'Bonjour !', +], 'fr_FR'); + +echo $translator->trans('Hello World!'); // outputs « Bonjour ! » +``` + +Sponsor +------- + +The Translation component for Symfony 5.4/6.0 is [backed][1] by: + + * [Crowdin][2], a cloud-based localization management software helping teams to go global and stay agile. + * [Lokalise][3], a continuous localization and translation management platform that integrates into your development workflow so you can ship localized products, faster. + +Help Symfony by [sponsoring][4] its development! + Resources --------- - * [Documentation](https://symfony.com/doc/current/components/translation/index.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/translation.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) + +[1]: https://symfony.com/backers +[2]: https://crowdin.com +[3]: https://lokalise.com +[4]: https://symfony.com/sponsor diff --git a/tests/integration/vendor/symfony/translation/Reader/TranslationReader.php b/tests/integration/vendor/symfony/translation/Reader/TranslationReader.php new file mode 100644 index 000000000..e8e8638a0 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Reader/TranslationReader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Reader; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TranslationReader reads translation messages from translation files. + * + * @author Michel Salib + */ +class TranslationReader implements TranslationReaderInterface +{ + /** + * Loaders used for import. + * + * @var array + */ + private $loaders = []; + + /** + * Adds a loader to the translation extractor. + * + * @param string $format The format of the loader + */ + public function addLoader(string $format, LoaderInterface $loader) + { + $this->loaders[$format] = $loader; + } + + /** + * {@inheritdoc} + */ + public function read(string $directory, MessageCatalogue $catalogue) + { + if (!is_dir($directory)) { + return; + } + + foreach ($this->loaders as $format => $loader) { + // load any existing translation files + $finder = new Finder(); + $extension = $catalogue->getLocale().'.'.$format; + $files = $finder->files()->name('*.'.$extension)->in($directory); + foreach ($files as $file) { + $domain = substr($file->getFilename(), 0, -1 * \strlen($extension) - 1); + $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain)); + } + } + } +} diff --git a/tests/integration/vendor/symfony/translation/Reader/TranslationReaderInterface.php b/tests/integration/vendor/symfony/translation/Reader/TranslationReaderInterface.php new file mode 100644 index 000000000..bc37204f9 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Reader/TranslationReaderInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Reader; + +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TranslationReader reads translation messages from translation files. + * + * @author Tobias Nyholm + */ +interface TranslationReaderInterface +{ + /** + * Reads translation messages from a directory to the catalogue. + */ + public function read(string $directory, MessageCatalogue $catalogue); +} diff --git a/tests/integration/vendor/symfony/translation/Resources/bin/translation-status.php b/tests/integration/vendor/symfony/translation/Resources/bin/translation-status.php new file mode 100644 index 000000000..fac8acbad --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Resources/bin/translation-status.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$usageInstructions = << false, + // NULL = analyze all locales + 'locale_to_analyze' => null, + // append --incomplete to only show incomplete languages + 'include_completed_languages' => true, + // the reference files all the other translations are compared to + 'original_files' => [ + 'src/Symfony/Component/Form/Resources/translations/validators.en.xlf', + 'src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf', + 'src/Symfony/Component/Validator/Resources/translations/validators.en.xlf', + ], +]; + +$argc = $_SERVER['argc']; +$argv = $_SERVER['argv']; + +if ($argc > 4) { + echo str_replace('translation-status.php', $argv[0], $usageInstructions); + exit(1); +} + +foreach (array_slice($argv, 1) as $argumentOrOption) { + if ('--incomplete' === $argumentOrOption) { + $config['include_completed_languages'] = false; + continue; + } + + if (0 === strpos($argumentOrOption, '-')) { + $config['verbose_output'] = true; + } else { + $config['locale_to_analyze'] = $argumentOrOption; + } +} + +foreach ($config['original_files'] as $originalFilePath) { + if (!file_exists($originalFilePath)) { + echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s %s', \PHP_EOL, $originalFilePath); + exit(1); + } +} + +$totalMissingTranslations = 0; +$totalTranslationMismatches = 0; + +foreach ($config['original_files'] as $originalFilePath) { + $translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']); + $translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths); + + $totalMissingTranslations += array_sum(array_map(function ($translation) { + return count($translation['missingKeys']); + }, array_values($translationStatus))); + $totalTranslationMismatches += array_sum(array_map(function ($translation) { + return count($translation['mismatches']); + }, array_values($translationStatus))); + + printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output'], $config['include_completed_languages']); +} + +exit($totalTranslationMismatches > 0 ? 1 : 0); + +function findTranslationFiles($originalFilePath, $localeToAnalyze) +{ + $translations = []; + + $translationsDir = dirname($originalFilePath); + $originalFileName = basename($originalFilePath); + $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName); + + $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern, \GLOB_NOSORT); + sort($translationFiles); + foreach ($translationFiles as $filePath) { + $locale = extractLocaleFromFilePath($filePath); + + if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) { + continue; + } + + $translations[$locale] = $filePath; + } + + return $translations; +} + +function calculateTranslationStatus($originalFilePath, $translationFilePaths) +{ + $translationStatus = []; + $allTranslationKeys = extractTranslationKeys($originalFilePath); + + foreach ($translationFilePaths as $locale => $translationPath) { + $translatedKeys = extractTranslationKeys($translationPath); + $missingKeys = array_diff_key($allTranslationKeys, $translatedKeys); + $mismatches = findTransUnitMismatches($allTranslationKeys, $translatedKeys); + + $translationStatus[$locale] = [ + 'total' => count($allTranslationKeys), + 'translated' => count($translatedKeys), + 'missingKeys' => $missingKeys, + 'mismatches' => $mismatches, + ]; + $translationStatus[$locale]['is_completed'] = isTranslationCompleted($translationStatus[$locale]); + } + + return $translationStatus; +} + +function isTranslationCompleted(array $translationStatus): bool +{ + return $translationStatus['total'] === $translationStatus['translated'] && 0 === count($translationStatus['mismatches']); +} + +function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput, $includeCompletedLanguages) +{ + printTitle($originalFilePath); + printTable($translationStatus, $verboseOutput, $includeCompletedLanguages); + echo \PHP_EOL.\PHP_EOL; +} + +function extractLocaleFromFilePath($filePath) +{ + $parts = explode('.', $filePath); + + return $parts[count($parts) - 2]; +} + +function extractTranslationKeys($filePath) +{ + $translationKeys = []; + $contents = new \SimpleXMLElement(file_get_contents($filePath)); + + foreach ($contents->file->body->{'trans-unit'} as $translationKey) { + $translationId = (string) $translationKey['id']; + $translationKey = (string) $translationKey->source; + + $translationKeys[$translationId] = $translationKey; + } + + return $translationKeys; +} + +/** + * Check whether the trans-unit id and source match with the base translation. + */ +function findTransUnitMismatches(array $baseTranslationKeys, array $translatedKeys): array +{ + $mismatches = []; + + foreach ($baseTranslationKeys as $translationId => $translationKey) { + if (!isset($translatedKeys[$translationId])) { + continue; + } + if ($translatedKeys[$translationId] !== $translationKey) { + $mismatches[$translationId] = [ + 'found' => $translatedKeys[$translationId], + 'expected' => $translationKey, + ]; + } + } + + return $mismatches; +} + +function printTitle($title) +{ + echo $title.\PHP_EOL; + echo str_repeat('=', strlen($title)).\PHP_EOL.\PHP_EOL; +} + +function printTable($translations, $verboseOutput, bool $includeCompletedLanguages) +{ + if (0 === count($translations)) { + echo 'No translations found'; + + return; + } + $longestLocaleNameLength = max(array_map('strlen', array_keys($translations))); + + foreach ($translations as $locale => $translation) { + if (!$includeCompletedLanguages && $translation['is_completed']) { + continue; + } + + if ($translation['translated'] > $translation['total']) { + textColorRed(); + } elseif (count($translation['mismatches']) > 0) { + textColorRed(); + } elseif ($translation['is_completed']) { + textColorGreen(); + } + + echo sprintf( + '| Locale: %-'.$longestLocaleNameLength.'s | Translated: %2d/%2d | Mismatches: %d |', + $locale, + $translation['translated'], + $translation['total'], + count($translation['mismatches']) + ).\PHP_EOL; + + textColorNormal(); + + $shouldBeClosed = false; + if (true === $verboseOutput && count($translation['missingKeys']) > 0) { + echo '| Missing Translations:'.\PHP_EOL; + + foreach ($translation['missingKeys'] as $id => $content) { + echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL; + } + $shouldBeClosed = true; + } + if (true === $verboseOutput && count($translation['mismatches']) > 0) { + echo '| Mismatches between trans-unit id and source:'.\PHP_EOL; + + foreach ($translation['mismatches'] as $id => $content) { + echo sprintf('| (id=%s) Expected: %s', $id, $content['expected']).\PHP_EOL; + echo sprintf('| Found: %s', $content['found']).\PHP_EOL; + } + $shouldBeClosed = true; + } + if ($shouldBeClosed) { + echo str_repeat('-', 80).\PHP_EOL; + } + } +} + +function textColorGreen() +{ + echo "\033[32m"; +} + +function textColorRed() +{ + echo "\033[31m"; +} + +function textColorNormal() +{ + echo "\033[0m"; +} diff --git a/tests/integration/vendor/symfony/translation/Resources/data/parents.json b/tests/integration/vendor/symfony/translation/Resources/data/parents.json new file mode 100644 index 000000000..288f16300 --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Resources/data/parents.json @@ -0,0 +1,138 @@ +{ + "az_Cyrl": "root", + "bs_Cyrl": "root", + "en_150": "en_001", + "en_AG": "en_001", + "en_AI": "en_001", + "en_AT": "en_150", + "en_AU": "en_001", + "en_BB": "en_001", + "en_BE": "en_150", + "en_BM": "en_001", + "en_BS": "en_001", + "en_BW": "en_001", + "en_BZ": "en_001", + "en_CC": "en_001", + "en_CH": "en_150", + "en_CK": "en_001", + "en_CM": "en_001", + "en_CX": "en_001", + "en_CY": "en_001", + "en_DE": "en_150", + "en_DG": "en_001", + "en_DK": "en_150", + "en_DM": "en_001", + "en_ER": "en_001", + "en_FI": "en_150", + "en_FJ": "en_001", + "en_FK": "en_001", + "en_FM": "en_001", + "en_GB": "en_001", + "en_GD": "en_001", + "en_GG": "en_001", + "en_GH": "en_001", + "en_GI": "en_001", + "en_GM": "en_001", + "en_GY": "en_001", + "en_HK": "en_001", + "en_IE": "en_001", + "en_IL": "en_001", + "en_IM": "en_001", + "en_IN": "en_001", + "en_IO": "en_001", + "en_JE": "en_001", + "en_JM": "en_001", + "en_KE": "en_001", + "en_KI": "en_001", + "en_KN": "en_001", + "en_KY": "en_001", + "en_LC": "en_001", + "en_LR": "en_001", + "en_LS": "en_001", + "en_MG": "en_001", + "en_MO": "en_001", + "en_MS": "en_001", + "en_MT": "en_001", + "en_MU": "en_001", + "en_MW": "en_001", + "en_MY": "en_001", + "en_NA": "en_001", + "en_NF": "en_001", + "en_NG": "en_001", + "en_NL": "en_150", + "en_NR": "en_001", + "en_NU": "en_001", + "en_NZ": "en_001", + "en_PG": "en_001", + "en_PK": "en_001", + "en_PN": "en_001", + "en_PW": "en_001", + "en_RW": "en_001", + "en_SB": "en_001", + "en_SC": "en_001", + "en_SD": "en_001", + "en_SE": "en_150", + "en_SG": "en_001", + "en_SH": "en_001", + "en_SI": "en_150", + "en_SL": "en_001", + "en_SS": "en_001", + "en_SX": "en_001", + "en_SZ": "en_001", + "en_TC": "en_001", + "en_TK": "en_001", + "en_TO": "en_001", + "en_TT": "en_001", + "en_TV": "en_001", + "en_TZ": "en_001", + "en_UG": "en_001", + "en_VC": "en_001", + "en_VG": "en_001", + "en_VU": "en_001", + "en_WS": "en_001", + "en_ZA": "en_001", + "en_ZM": "en_001", + "en_ZW": "en_001", + "es_AR": "es_419", + "es_BO": "es_419", + "es_BR": "es_419", + "es_BZ": "es_419", + "es_CL": "es_419", + "es_CO": "es_419", + "es_CR": "es_419", + "es_CU": "es_419", + "es_DO": "es_419", + "es_EC": "es_419", + "es_GT": "es_419", + "es_HN": "es_419", + "es_MX": "es_419", + "es_NI": "es_419", + "es_PA": "es_419", + "es_PE": "es_419", + "es_PR": "es_419", + "es_PY": "es_419", + "es_SV": "es_419", + "es_US": "es_419", + "es_UY": "es_419", + "es_VE": "es_419", + "ff_Adlm": "root", + "nb": "no", + "nn": "no", + "pa_Arab": "root", + "pt_AO": "pt_PT", + "pt_CH": "pt_PT", + "pt_CV": "pt_PT", + "pt_GQ": "pt_PT", + "pt_GW": "pt_PT", + "pt_LU": "pt_PT", + "pt_MO": "pt_PT", + "pt_MZ": "pt_PT", + "pt_ST": "pt_PT", + "pt_TL": "pt_PT", + "sd_Deva": "root", + "sr_Latn": "root", + "uz_Arab": "root", + "uz_Cyrl": "root", + "zh_Hant": "root", + "zh_Hant_MO": "zh_Hant_HK" +} diff --git a/tests/integration/vendor/symfony/translation/Resources/functions.php b/tests/integration/vendor/symfony/translation/Resources/functions.php new file mode 100644 index 000000000..901d2f87e --- /dev/null +++ b/tests/integration/vendor/symfony/translation/Resources/functions.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation; + +if (!\function_exists(t::class)) { + /** + * @author Nate Wiebe + */ + function t(string $message, array $parameters = [], string $domain = null): TranslatableMessage + { + return new TranslatableMessage($message, $parameters, $domain); + } +} diff --git a/tests/integration/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd b/tests/integration/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd similarity index 99% rename from tests/integration/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd rename to tests/integration/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd index 3ce2a8e8a..dface628c 100644 --- a/tests/integration/vendor/symfony/translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd +++ b/tests/integration/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd @@ -3,16 +3,16 @@