diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index fd5ee63..efb07d7 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,7 @@ If you want to contribute to the Behat API Extension please follow the following guidelines. -## Running tests +## Running tests and static analysis Behat API Extension has both [Behat](http://docs.behat.org/) and [PHPUnit](https://phpunit.de/) tests, and when adding new features or fixing bugs you are required to add relevant test cases. @@ -23,6 +23,14 @@ If you want to run the suites separately they can be executed like this: ./vendor/bin/behat --strict ./vendor/bin/phpunit +PHPStan is used for static code analysis, and a composer script has been added to run this tool: + + composer run sa + +You can also run both test suites along with the static analysis with a single composer script: + + composer run ci + ## Documentation The extension uses [Sphinx](http://www.sphinx-doc.org/en/stable/) for documentation, and all end-user documentation resides in the `docs` directory. To generate the current documentation after checking out your fork simply run the `docs` composer script: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..0a866de --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,54 @@ +name: CI +on: push +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + php: ['7.4', '8.0'] + name: Validate, lint and test on PHP ${{ matrix.php }} + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + + - name: Debug PHP installation + run: | + php -v + php -i + php -m + + - name: Validate composer files + run: composer validate --strict + + - name: Lint all PHP files + run: composer run lint + + - name: Get Composer cache directory + id: composer-cache-dir + run: | + echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache-dir.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install + + - name: Start dev server + run: composer run dev --timeout=0 & + + - name: Run tests + run: composer run test + + - name: Run static code analysis + run: composer run sa diff --git a/.gitignore b/.gitignore index c334401..01098f6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ server.log phpunit.xml docs/_build .idea +.phpunit.result.cache +.vscode diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index cfd27ef..0000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: php - -cache: - directories: - - $HOME/.composer/cache - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - -notifications: - irc: - use_notice: true - skip_join: true - channels: ["irc.freenode.org#imbo"] - -before_script: - - composer self-update - - composer install --prefer-dist - -script: - - composer lint - - php -S localhost:8080 -t ./features/bootstrap > server.log 2>&1 & - - composer test - -after_failure: - - echo "Tests failed - httpd log follows" - - echo "================================" - - cat server.log diff --git a/ChangeLog.md b/ChangeLog.md index b58178c..43c70e1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,26 @@ Changelog for Behat API Extension ================================= +v3.0.0 +------ +__2021-05-25__ + +* [#101](https://github.com/imbo/behat-api-extension/issues/101): Require PHP >= 7.4 +* [#92](https://github.com/imbo/behat-api-extension/pull/92): Add password grant OAuth step ([@ABGEO](https://github.com/ABGEO)) +* [#85](https://github.com/imbo/behat-api-extension/pull/85): Add support for manipulating query parameters using steps + +v2.3.1 +------ +__2020-01-29__ + +* Minor docs fix and bumped copyright year + +v2.3.0 +------ +__2020-01-29__ + +* [#84](https://github.com/imbo/behat-api-extension/pull/84): Added support for `any` and multiple variable types with the `@variableType` matcher + v2.2.1 ------ __2019-09-15__ diff --git a/LICENSE b/LICENSE index 1910ce0..9e5dbfa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2016-2019, Christer Edvartsen +Copyright (c) Christer Edvartsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to diff --git a/README.md b/README.md index deef2bf..e3ea9c4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Behat API Extension -[![Current build Status](https://secure.travis-ci.org/imbo/behat-api-extension.png)](http://travis-ci.org/imbo/behat-api-extension) +![Current build Status](https://github.com/imbo/behat-api-extension/workflows/CI/badge.svg) This Behat extension provides an easy way to test JSON-based API's in [Behat 3](http://behat.org). Inspired by [behat/web-api-extension](https://github.com/Behat/WebApiExtension/) and originally written to test the [Imbo API](http://imbo.io). @@ -10,6 +10,4 @@ End-user docs can be found [here](https://behat-api-extension.readthedocs.io/). ## Copyright / License -Copyright (c) 2016-2019, Christer Edvartsen - -Licensed under the MIT License +Licensed under the [MIT license](LICENSE). diff --git a/composer.json b/composer.json index d242f31..916fde1 100644 --- a/composer.json +++ b/composer.json @@ -20,17 +20,22 @@ "irc": "irc://irc.freenode.net/imbo" }, "require": { - "php": ">=5.6", - "behat/behat": "^3.0", - "guzzlehttp/guzzle": "^6.0", - "beberlei/assert": "^2.1", - "firebase/php-jwt": "^4.0 | ^5.0" + "php": "^7.4 || ^8.0", + "ext-json": "*", + "beberlei/assert": "^3.3", + "behat/behat": "^3.8", + "firebase/php-jwt": "^5.2", + "guzzlehttp/guzzle": "^7.3" }, "require-dev": { - "silex/silex": "^2.0", - "symfony/security": "^3.1", - "symfony/process": "^3.1", - "phpunit/phpunit": "^5.5" + "alexeyshockov/guzzle-psalm-plugin": "^0.3.1", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.15.1", + "slim/psr7": "^1.3", + "slim/slim": "^4.7", + "symfony/process": "^5.2", + "tuupola/slim-basic-auth": "^3.3", + "vimeo/psalm": "^4.7" }, "autoload": { "psr-4": { @@ -43,16 +48,27 @@ } }, "scripts": { - "clean": "rm -rf build", "lint": "for file in `git ls-files '*php'`; do php -l $file; done", - "test-phpunit": "vendor/bin/phpunit", - "test-phpunit-coverage": "vendor/bin/phpunit --coverage-html build/coverage", - "test-behat": "vendor/bin/behat --strict", + "phpunit": "vendor/bin/phpunit", + "phpunit:coverage": "vendor/bin/phpunit --coverage-html build/coverage", + "behat": "vendor/bin/behat --strict", + "psalm": "vendor/bin/psalm", "test": [ - "@test-phpunit", - "@test-behat" + "@phpunit", + "@behat" + ], + "sa": [ + "@psalm" + ], + "ci": [ + "@lint", + "@test", + "@sa" ], "dev": "php -S localhost:8080 -t ./features/bootstrap > server.log 2>&1", "docs": "cd docs; make html" + }, + "config": { + "sort-packages": true } } diff --git a/composer.lock b/composer.lock index 09242a3..2f24707 100644 --- a/composer.lock +++ b/composer.lock @@ -1,32 +1,40 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "505c11a48ac335a4f7132b8ec9e82db9", + "content-hash": "f845e6db7cfc71bc3b9a0638eba05191", "packages": [ { "name": "beberlei/assert", - "version": "v2.7.8", + "version": "v3.3.1", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "be6aa40e3f2d2f0766f159409ab9a80c8db6ca23" + "reference": "5e721d7e937ca3ba2cdec1e1adf195f9e5188372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/be6aa40e3f2d2f0766f159409ab9a80c8db6ca23", - "reference": "be6aa40e3f2d2f0766f159409ab9a80c8db6ca23", + "url": "https://api.github.com/repos/beberlei/assert/zipball/5e721d7e937ca3ba2cdec1e1adf195f9e5188372", + "reference": "5e721d7e937ca3ba2cdec1e1adf195f9e5188372", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-mbstring": "*", - "php": ">=5.3" + "ext-simplexml": "*", + "php": "^7.0 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.1.1", - "phpunit/phpunit": "^4|^5" + "friendsofphp/php-cs-fixer": "*", + "phpstan/phpstan": "*", + "phpunit/phpunit": ">=6.0.0", + "yoast/phpunit-polyfills": "^0.1.0" + }, + "suggest": { + "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" }, "type": "library", "autoload": { @@ -59,46 +67,47 @@ "assertion", "validation" ], - "time": "2017-10-20T15:59:40+00:00" + "support": { + "issues": "https://github.com/beberlei/assert/issues", + "source": "https://github.com/beberlei/assert/tree/v3.3.1" + }, + "time": "2021-04-18T20:11:03+00:00" }, { "name": "behat/behat", - "version": "v3.4.1", + "version": "v3.8.1", "source": { "type": "git", "url": "https://github.com/Behat/Behat.git", - "reference": "cb51d4b0b11ea6d3897f3589a871a63a33632692" + "reference": "fbb065457d523d9856d4b50775b4151a7598b510" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/cb51d4b0b11ea6d3897f3589a871a63a33632692", - "reference": "cb51d4b0b11ea6d3897f3589a871a63a33632692", + "url": "https://api.github.com/repos/Behat/Behat/zipball/fbb065457d523d9856d4b50775b4151a7598b510", + "reference": "fbb065457d523d9856d4b50775b4151a7598b510", "shasum": "" }, "require": { - "behat/gherkin": "^4.5.1", + "behat/gherkin": "^4.6.0", "behat/transliterator": "^1.2", - "container-interop/container-interop": "^1.2", "ext-mbstring": "*", - "php": ">=5.3.3", + "php": "^7.2 || ^8.0", "psr/container": "^1.0", - "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" + "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" }, "require-dev": { + "container-interop/container-interop": "^1.2", "herrera-io/box": "~1.6.1", - "phpunit/phpunit": "~4.5", - "symfony/process": "~2.5|~3.0" + "phpunit/phpunit": "^8.5 || ^9.0", + "symfony/process": "^4.4 || ^5.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" + "ext-dom": "Needed to output test results in JUnit format." }, "bin": [ "bin/behat" @@ -106,13 +115,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "3.8.x-dev" } }, "autoload": { - "psr-0": { - "Behat\\Behat": "src/", - "Behat\\Testwork": "src/" + "psr-4": { + "Behat\\Behat\\": "src/Behat/Behat/", + "Behat\\Testwork\\": "src/Behat/Testwork/" } }, "notification-url": "https://packagist.org/downloads/", @@ -126,7 +135,7 @@ "homepage": "http://everzet.com" } ], - "description": "Scenario-oriented BDD framework for PHP 5.3", + "description": "Scenario-oriented BDD framework for PHP", "homepage": "http://behat.org/", "keywords": [ "Agile", @@ -142,29 +151,34 @@ "symfony", "testing" ], - "time": "2017-09-18T11:10:28+00:00" + "support": { + "issues": "https://github.com/Behat/Behat/issues", + "source": "https://github.com/Behat/Behat/tree/v3.8.1" + }, + "time": "2020-11-07T15:55:18+00:00" }, { "name": "behat/gherkin", - "version": "v4.5.1", + "version": "v4.8.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" + "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", - "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/2391482cd003dfdc36b679b27e9f5326bd656acd", + "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd", "shasum": "" }, "require": { - "php": ">=5.3.1" + "php": "~7.2|~8.0" }, "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3", - "symfony/yaml": "~2.3|~3" + "cucumber/cucumber": "dev-gherkin-16.0.0", + "phpunit/phpunit": "~8|~9", + "symfony/phpunit-bridge": "~3|~4|~5", + "symfony/yaml": "~3|~4|~5" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -191,7 +205,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", @@ -201,20 +215,24 @@ "gherkin", "parser" ], - "time": "2017-08-30T11:04:43+00:00" + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.8.0" + }, + "time": "2021-02-04T12:44:21+00:00" }, { "name": "behat/transliterator", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/Behat/Transliterator.git", - "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c" + "reference": "3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", - "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc", + "reference": "3c4ec1d77c3d05caa1f0bf8fb3aae4845005c7fc", "shasum": "" }, "require": { @@ -222,7 +240,8 @@ }, "require-dev": { "chuyskywalker/rolling-curl": "^3.1", - "php-yaoi/php-yaoi": "^1.0" + "php-yaoi/php-yaoi": "^1.0", + "phpunit/phpunit": "^4.8.36|^6.3" }, "type": "library", "extra": { @@ -231,8 +250,8 @@ } }, "autoload": { - "psr-0": { - "Behat\\Transliterator": "src/" + "psr-4": { + "Behat\\Transliterator\\": "src/Behat/Transliterator" } }, "notification-url": "https://packagist.org/downloads/", @@ -245,58 +264,31 @@ "slug", "transliterator" ], - "time": "2017-04-04T11:38:05+00:00" - }, - { - "name": "container-interop/container-interop", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "shasum": "" - }, - "require": { - "psr/container": "^1.0" + "support": { + "issues": "https://github.com/Behat/Transliterator/issues", + "source": "https://github.com/Behat/Transliterator/tree/v1.3.0" }, - "type": "library", - "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.)", - "homepage": "https://github.com/container-interop/container-interop", - "time": "2017-02-14T19:40:03+00:00" + "time": "2020-01-14T16:39:13+00:00" }, { "name": "firebase/php-jwt", - "version": "v5.0.0", + "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e" + "reference": "f42c9110abe98dd6cfe9053c49bc86acc70b2d23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", - "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/f42c9110abe98dd6cfe9053c49bc86acc70b2d23", + "reference": "f42c9110abe98dd6cfe9053c49bc86acc70b2d23", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": " 4.8.35" + "phpunit/phpunit": ">=4.8 <=9" }, "type": "library", "autoload": { @@ -322,48 +314,65 @@ ], "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", "homepage": "https://github.com/firebase/php-jwt", - "time": "2017-06-27T22:17:23+00:00" + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v5.2.1" + }, + "time": "2021-02-12T00:02:00+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "6.3.0", + "version": "7.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + "reference": "7008573787b430c1c1f650e3722d9bba59967628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", + "reference": "7008573787b430c1c1f650e3722d9bba59967628", "shasum": "" }, "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" + "ext-json": "*", + "guzzlehttp/promises": "^1.4", + "guzzlehttp/psr7": "^1.7 || ^2.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", - "psr/log": "^1.0" + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1" }, "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" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "7.3-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -374,6 +383,11 @@ "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "Guzzle is a PHP HTTP client library", @@ -384,30 +398,54 @@ "framework", "http", "http client", + "psr-18", + "psr-7", "rest", "web service" ], - "time": "2017-06-22T18:50:49+00:00" + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://github.com/alexeyshockov", + "type": "github" + }, + { + "url": "https://github.com/gmponos", + "type": "github" + } + ], + "time": "2021-03-23T11:33:13+00:00" }, { "name": "guzzlehttp/promises", - "version": "v1.3.1", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { - "php": ">=5.5.0" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^4.0" + "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", "extra": { @@ -438,36 +476,45 @@ "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.4.1" + }, + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.4.2", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + "reference": "dc960a912984efb74d0a90222870c72c87f10c91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91", "shasum": "" }, "require": { "php": ">=5.4.0", - "psr/http-message": "~1.0" + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -497,37 +544,37 @@ "keywords": [ "http", "message", + "psr-7", "request", "response", "stream", "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.8.2" + }, + "time": "2021-04-26T09:17:50+00:00" }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -540,7 +587,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -552,24 +599,28 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", "extra": { @@ -579,7 +630,7 @@ }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -592,34 +643,35 @@ "homepage": "http://www.php-fig.org/" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Standard interfaces for event handling.", "keywords": [ - "http", - "http-message", + "events", "psr", - "psr-7", - "request", - "response" + "psr-14" ], - "time": "2016-08-06T14:39:51+00:00" + "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" }, { - "name": "psr/log", - "version": "1.0.2", + "name": "psr/http-client", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" }, "type": "library", "extra": { @@ -629,7 +681,7 @@ }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -642,52 +694,46 @@ "homepage": "http://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ - "log", + "http", + "http-client", "psr", - "psr-3" + "psr-18" ], - "time": "2016-10-10T12:19:37+00:00" + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" }, { - "name": "symfony/class-loader", - "version": "v3.3.10", + "name": "psr/http-message", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/class-loader.git", - "reference": "7572c904b209fa9907c69a6a9a68243c265a4d01" + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/7572c904b209fa9907c69a6a9a68243c265a4d01", - "reference": "7572c904b209fa9907c69a6a9a68243c265a4d01", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" - }, - "require-dev": { - "symfony/finder": "~2.8|~3.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\ClassLoader\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -695,60 +741,50 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Symfony ClassLoader Component", - "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" }, { - "name": "symfony/config", - "version": "v3.3.10", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "4ab62407bff9cd97c410a7feaef04c375aaa5cfd" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/4ab62407bff9cd97c410a7feaef04c375aaa5cfd", - "reference": "4ab62407bff9cd97c410a7feaef04c375aaa5cfd", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/filesystem": "~2.8|~3.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.3", - "symfony/finder": "<3.3" + "php": ">=5.6" }, "require-dev": { - "symfony/dependency-injection": "~3.3", - "symfony/finder": "~3.3", - "symfony/yaml": "~3.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } - }, "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -757,63 +793,55 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" } ], - "description": "Symfony Config Component", - "homepage": "https://symfony.com", - "time": "2017-10-04T18:56:58+00:00" + "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/console", - "version": "v3.3.10", + "name": "symfony/config", + "version": "v5.2.4", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c" + "url": "https://github.com/symfony/config.git", + "reference": "212d54675bf203ff8aef7d8cee8eecfb72f4a263" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/116bc56e45a8e5572e51eb43ab58c769a352366c", - "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c", + "url": "https://api.github.com/repos/symfony/config/zipball/212d54675bf203ff8aef7d8cee8eecfb72f4a263", + "reference": "212d54675bf203ff8aef7d8cee8eecfb72f4a263", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/filesystem": "^4.4|^5.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.15" }, "conflict": { - "symfony/dependency-injection": "<3.3" + "symfony/finder": "<4.4" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3", - "symfony/dependency-injection": "~3.3", - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/filesystem": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0" + "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" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/filesystem": "", - "symfony/process": "" + "symfony/yaml": "To use the yaml reference dumper" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" + "Symfony\\Component\\Config\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -833,43 +861,78 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "support": { + "source": "https://github.com/symfony/config/tree/v5.2.4" + }, + "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": "2021-02-23T23:58:19+00:00" }, { - "name": "symfony/debug", - "version": "v3.3.10", + "name": "symfony/console", + "version": "v5.2.6", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd" + "url": "https://github.com/symfony/console.git", + "reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd", - "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd", + "url": "https://api.github.com/repos/symfony/console/zipball/35f039df40a3b335ebf310f244cb242b3a83ac8d", + "reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" + "php": ">=7.2.5", + "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" }, "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + "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" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0" + "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" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Debug\\": "" + "Symfony\\Component\\Console\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -889,40 +952,68 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Debug Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.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" + } + ], + "time": "2021-03-28T09:42:18+00:00" }, { "name": "symfony/dependency-injection", - "version": "v3.3.10", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "8ebad929aee3ca185b05f55d9cc5521670821ad1" + "reference": "1e66194bed2a69fa395d26bf1067e5e34483afac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8ebad929aee3ca185b05f55d9cc5521670821ad1", - "reference": "8ebad929aee3ca185b05f55d9cc5521670821ad1", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1e66194bed2a69fa395d26bf1067e5e34483afac", + "reference": "1e66194bed2a69fa395d26bf1067e5e34483afac", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "psr/container": "^1.0" + "php": ">=7.2.5", + "psr/container": "^1.0", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1.6|^2" }, "conflict": { - "symfony/config": "<3.3.1", - "symfony/finder": "<3.3", - "symfony/yaml": "<3.3" + "symfony/config": "<5.1", + "symfony/finder": "<4.4", + "symfony/proxy-manager-bridge": "<4.4", + "symfony/yaml": "<4.4" }, "provide": { - "psr/container-implementation": "1.0" + "psr/container-implementation": "1.0", + "symfony/service-implementation": "1.0|2.0" }, "require-dev": { - "symfony/config": "~3.3", - "symfony/expression-language": "~2.8|~3.0", - "symfony/yaml": "~3.3" + "symfony/config": "^5.1", + "symfony/expression-language": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0" }, "suggest": { "symfony/config": "", @@ -932,11 +1023,6 @@ "symfony/yaml": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" @@ -959,53 +1045,57 @@ "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", - "time": "2017-10-04T17:15:30+00:00" + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v5.2.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" + } + ], + "time": "2021-03-22T11:10:24+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v3.3.10", + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d7ba037e4b8221956ab1e221c73c9e27e05dd423", - "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" - }, - "conflict": { - "symfony/dependency-injection": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/dependency-injection": "~3.3", - "symfony/expression-language": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1014,44 +1104,80 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.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": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/filesystem", - "version": "v3.3.10", + "name": "symfony/event-dispatcher", + "version": "v5.2.4", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "d08d6ec121a425897951900ab692b612a61d6240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/90bc45abf02ae6b7deb43895c1052cb0038506f1", - "reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", + "reference": "d08d6ec121a425897951900ab692b612a61d6240", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/event-dispatcher-contracts": "^2", + "symfony/polyfill-php80": "^1.15" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "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" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" + "Symfony\\Component\\EventDispatcher\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1071,43 +1197,62 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem 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", - "time": "2017-10-03T13:33:10+00:00" + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" + }, + "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": "2021-02-18T17:12:37+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.6.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", + "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" }, "suggest": { - "ext-mbstring": "For best performance" + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] + "Symfony\\Contracts\\EventDispatcher\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1123,59 +1268,57 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.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": "2017-10-11T12:05:26+00:00" + "time": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/translation", - "version": "v3.3.10", + "name": "symfony/filesystem", + "version": "v5.2.6", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f" + "url": "https://github.com/symfony/filesystem.git", + "reference": "8c86a82f51658188119e62cff0a050a12d09836f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/409bf229cd552bf7e3faa8ab7e3980b07672073f", - "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/8c86a82f51658188119e62cff0a050a12d09836f", + "reference": "8c86a82f51658188119e62cff0a050a12d09836f", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/config": "<2.8", - "symfony/yaml": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/intl": "^2.8.18|^3.2.5", - "symfony/yaml": "~3.3" - }, - "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\Translation\\": "" + "Symfony\\Component\\Filesystem\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -1195,45 +1338,63 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Translation Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.2.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" + } + ], + "time": "2021-03-28T14:30:26+00:00" }, { - "name": "symfony/yaml", - "version": "v3.3.10", + "name": "symfony/polyfill-ctype", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", - "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" - }, - "require-dev": { - "symfony/console": "~2.8|~3.0" + "php": ">=7.1" }, "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "ext-ctype": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Polyfill\\Ctype\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1242,54 +1403,78 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", - "time": "2017-10-05T14:43:42+00:00" - } - ], - "packages-dev": [ + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, + "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": "2021-01-07T16:49:33+00:00" + }, { - "name": "doctrine/instantiator", - "version": "1.0.5", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": ">=7.1" }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1297,91 +1482,163 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", "keywords": [ - "constructor", - "instantiate" + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + }, + "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": "2015-06-14T21:17:01+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.7.0", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.1" }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, "files": [ - "src/DeepCopy/deep_copy.php" + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "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": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, + "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": "2017-10-19T19:58:43+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { - "name": "paragonie/random_compat", - "version": "v2.0.11", + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", "shasum": "" }, "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*" + "php": ">=7.1" }, "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + "ext-mbstring": "For best performance" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, "files": [ - "lib/random.php" + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1390,51 +1647,79 @@ ], "authors": [ { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", "keywords": [ - "csprng", - "pseudorandom", - "random" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, + "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": "2017-09-27T21:40:39+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "name": "symfony/polyfill-php73", + "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1442,8 +1727,1643 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "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.22.1" + }, + "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": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "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.22.1" + }, + "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": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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/v2.4.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": "2021-04-01T10:43:52+00:00" + }, + { + "name": "symfony/string", + "version": "v5.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "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" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "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.2.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" + } + ], + "time": "2021-03-17T17:12:15+00:00" + }, + { + "name": "symfony/translation", + "version": "v5.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "2cc7f45d96db9adfcf89adf4401d9dfed509f4e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/2cc7f45d96db9adfcf89adf4401d9dfed509f4e1", + "reference": "2cc7f45d96db9adfcf89adf4401d9dfed509f4e1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.15", + "symfony/translation-contracts": "^2.3" + }, + "conflict": { + "symfony/config": "<4.4", + "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.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" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "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.2.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" + } + ], + "time": "2021-03-23T19:33:48+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "95c812666f3e91db75385749fe219c5e494c7f95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/95c812666f3e91db75385749fe219c5e494c7f95", + "reference": "95c812666f3e91db75385749fe219c5e494c7f95", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.4.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": "2021-03-23T23:28:01+00:00" + }, + { + "name": "symfony/yaml", + "version": "v5.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "298a08ddda623485208506fcee08817807a251dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/298a08ddda623485208506fcee08817807a251dd", + "reference": "298a08ddda623485208506fcee08817807a251dd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<4.4" + }, + "require-dev": { + "symfony/console": "^4.4|^5.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "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.2.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" + } + ], + "time": "2021-03-06T07:59:01+00:00" + } + ], + "packages-dev": [ + { + "name": "alexeyshockov/guzzle-psalm-plugin", + "version": "v0.3.1", + "source": { + "type": "git", + "url": "https://github.com/alexeyshockov/guzzle-psalm-plugin.git", + "reference": "32d54582cace749b781c4dca97ad0dddc6711187" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/alexeyshockov/guzzle-psalm-plugin/zipball/32d54582cace749b781c4dca97ad0dddc6711187", + "reference": "32d54582cace749b781c4dca97ad0dddc6711187", + "shasum": "" + }, + "require": { + "vimeo/psalm": "^3 || ^4" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "AlexS\\Guzzle\\PsalmPlugin" + } + }, + "autoload": { + "psr-4": { + "AlexS\\Guzzle\\": [ + "." + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexey Shokov", + "email": "alexey@shockov.com" + } + ], + "description": "Guzzle type information for Psalm", + "support": { + "issues": "https://github.com/alexeyshockov/guzzle-psalm-plugin/issues", + "source": "https://github.com/alexeyshockov/guzzle-psalm-plugin/tree/v0.3.1" + }, + "time": "2020-12-06T12:40:27+00:00" + }, + { + "name": "amphp/amp", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/efca2b32a7580087adb8aabbff6be1dc1bb924a9", + "reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9", + "shasum": "" + }, + "require": { + "php": ">=7" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6.0.9 | ^7", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\": "lib" + }, + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "http://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-01-10T17:06:37+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Amp\\ByteStream\\": "lib" + }, + "files": [ + "lib/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.1", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6", + "reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-11-11T10:22:58+00:00" + }, + { + "name": "composer/semver", + "version": "3.2.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", + "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.54", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.2.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-11-13T08:59:24+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "31d57697eb1971712a08031cfaff5a846d10bdf5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/31d57697eb1971712a08031cfaff5a846d10bdf5", + "reference": "31d57697eb1971712a08031cfaff5a846d10bdf5", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/2.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-09T19:40:06+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/06f0b06043c7438959dbdeed8bb3f699a19be22e", + "reference": "06f0b06043c7438959dbdeed8bb3f699a19be22e", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.0" + }, + "time": "2021-01-10T17:48:47+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" + }, + "time": "2021-02-22T14:02:09+00:00" + }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/e0f1e33a71587aca81be5cffbb9746510e1fe04e", + "reference": "e0f1e33a71587aca81be5cffbb9746510e1fe04e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4 || ~7.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/master" + }, + "time": "2020-04-16T18:48:43+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.10.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + }, + "time": "2020-12-20T10:01:03+00:00" + }, + { + "name": "openlss/lib-array2xml", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/nullivex/lib-array2xml.git", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "LSS": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Bryan Tong", + "email": "bryan@nullivex.com", + "homepage": "https://www.nullivex.com" + }, + { + "name": "Tony Butler", + "email": "spudz76@gmail.com", + "homepage": "https://www.nullivex.com" + } + ], + "description": "Array2XML conversion library credit to lalit.org", + "homepage": "https://www.nullivex.com", + "keywords": [ + "array", + "array conversion", + "xml", + "xml conversion" + ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, + "time": "2019-03-29T20:06:56+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, + "time": "2020-06-27T14:33:11+00:00" + }, + { + "name": "phar-io/version", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "bae7c545bef187884426f042434e561ab1ddb182" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, + "time": "2021-02-23T14:00:09+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], "description": "Common reflection classes used by phpdocumentor to reflect the code structure", @@ -1455,38 +3375,45 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.2.2", + "version": "5.2.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157" + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.3.0", - "webmozart/assert": "^1.0" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "mockery/mockery": "~1.3.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1497,44 +3424,49 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-08T06:39:58+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, + "time": "2020-09-03T19:13:55+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773" + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fb3933512008d8162b3cdf9e18dba9309b7c3773", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1547,42 +3479,47 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-06-03T08:32:36+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + }, + "time": "2020-09-17T18:55:26+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.7.2", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -1610,49 +3547,397 @@ "spy", "stub" ], - "time": "2017-09-04T11:05:03+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + }, + "time": "2021-03-17T13:42:18+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f6293e1b30a2354e8428e004689671b83871edde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", + "reference": "f6293e1b30a2354e8428e004689671b83871edde", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.10.2", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-03-28T07:26:59+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:57:25+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "4.0.8", + "name": "phpunit/phpunit", + "version": "9.5.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "c73c6737305e779771147af66c96ca6a7ed8a741" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741", + "reference": "c73c6737305e779771147af66c96ca6a7ed8a741", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.3", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3", + "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { - "ext-xdebug": "^2.5.1" + "ext-soap": "*", + "ext-xdebug": "*" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "9.5-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1662,260 +3947,336 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "coverage", + "phpunit", "testing", "xunit" ], - "time": "2017-04-02T07:44:40+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4" + }, + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-03-23T07:16:29+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "name": "psalm/plugin-phpunit", + "version": "0.15.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "url": "https://github.com/psalm/psalm-plugin-phpunit.git", + "reference": "30ca25ce069bf4943c36e59b7df6954f6af05e64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/30ca25ce069bf4943c36e59b7df6954f6af05e64", + "reference": "30ca25ce069bf4943c36e59b7df6954f6af05e64", "shasum": "" }, "require": { - "php": ">=5.3.3" + "composer/package-versions-deprecated": "^1.10", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "ext-simplexml": "*", + "php": "^7.1 || ^8.0", + "vimeo/psalm": "dev-master || dev-4.x || ^4.0" + }, + "conflict": { + "phpunit/phpunit": "<7.5" + }, + "require-dev": { + "codeception/codeception": "^4.0.3", + "php": "^7.3 || ^8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.3.1", + "weirdan/codeception-psalm-module": "^0.11.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\PhpUnitPlugin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for PHPUnit", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.15.1" + }, + "time": "2021-01-23T00:19:07+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.4.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ - "filesystem", - "iterator" + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" ], - "time": "2016-10-03T07:40:28+00:00" + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "psr/http-server-handler", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/aff2f80e33b7f026ec96bb42f63242dc50ffcae7", + "reference": "aff2f80e33b7f026ec96bb42f63242dc50ffcae7", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.0", + "psr/http-message": "^1.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Common interface for HTTP server-side request handler", "keywords": [ - "template" + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" ], - "time": "2015-06-21T13:50:34+00:00" + "support": { + "issues": "https://github.com/php-fig/http-server-handler/issues", + "source": "https://github.com/php-fig/http-server-handler/tree/master" + }, + "time": "2018-10-30T16:46:14+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.9", + "name": "psr/http-server-middleware", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/2296f45510945530b9dceb8bcedb5cb84d40c5f5", + "reference": "2296f45510945530b9dceb8bcedb5cb84d40c5f5", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "php": ">=7.0", + "psr/http-message": "^1.0", + "psr/http-server-handler": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Common interface for HTTP server-side middleware", "keywords": [ - "timer" + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" ], - "time": "2017-02-26T11:10:40+00:00" + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/master" + }, + "time": "2018-10-30T17:12:04+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "1.4.11", + "name": "psr/log", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "tokenizer" + "log", + "psr", + "psr-3" ], - "time": "2017-02-27T10:12:30+00:00" + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, + "time": "2020-03-23T09:12:05+00:00" }, { - "name": "phpunit/phpunit", - "version": "5.7.23", + "name": "sebastian/cli-parser", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "78532d5269d984660080d8e0f4c99c5c2ea65ffe" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/78532d5269d984660080d8e0f4c99c5c2ea65ffe", - "reference": "78532d5269d984660080d8e0f4c99c5c2ea65ffe", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0.3|~2.0", - "symfony/yaml": "~2.1|~3.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" + "php": ">=7.3" }, "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" + "phpunit/phpunit": "^9.3" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.7.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -1934,48 +4295,44 @@ "role": "lead" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-10-15T06:13:55+00:00" + "time": "2020-09-28T06:08:49+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "3.4.4", + "name": "sebastian/code-unit", + "version": "1.0.8", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-soap": "*" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -1990,92 +4347,105 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-06-30T09:13:00+00:00" + "time": "2020-10-26T13:08:54+00:00" }, { - "name": "pimple/pimple", - "version": "v3.2.2", + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/4d45fb62d96418396ec58ba76e6f065bca16e10a", - "reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/container": "^1.0" + "php": ">=7.3" }, "require-dev": { - "symfony/phpunit-bridge": "^3.2" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { - "psr-0": { - "Pimple": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Pimple, a simple Dependency Injection Container", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-07-23T07:32:15+00:00" + "time": "2020-09-28T05:30:19+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "name": "sebastian/comparator", + "version": "4.0.6", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2087,42 +4457,68 @@ "license": [ "BSD-3-Clause" ], - "authors": [ + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, + "funding": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "time": "2020-10-26T15:49:45+00:00" }, { - "name": "sebastian/comparator", - "version": "1.2.4", + "name": "sebastian/complexity", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "nikic/php-parser": "^4.7", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2135,56 +4531,51 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-01-29T09:50:25+00:00" + "time": "2020-10-26T15:52:27+00:00" }, { "name": "sebastian/diff", - "version": "1.4.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2197,46 +4588,62 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", - "version": "2.0.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -2261,34 +4668,44 @@ "environment", "hhvm" ], - "time": "2016-11-26T07:53:53+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", - "version": "2.0.0", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2301,6 +4718,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -2309,17 +4730,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -2328,27 +4745,40 @@ "export", "exporter" ], - "time": "2016-11-19T08:54:04+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:24:23+00:00" }, { "name": "sebastian/global-state", - "version": "1.1.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", + "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -2356,7 +4786,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2379,33 +4809,101 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:55:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", - "version": "2.0.1", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", - "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2425,32 +4923,42 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18T15:18:39+00:00" + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" }, { - "name": "sebastian/recursion-context", - "version": "2.0.0", + "name": "sebastian/object-reflector", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2463,44 +4971,49 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" - }, + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19T07:33:16+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { - "name": "sebastian/resource-operations", - "version": "1.0.0", + "name": "sebastian/recursion-context", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2516,33 +5029,54 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "sebastian/resource-operations", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2557,223 +5091,173 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" }, { - "name": "silex/silex", - "version": "v2.2.0", + "name": "sebastian/type", + "version": "2.3.1", "source": { "type": "git", - "url": "https://github.com/silexphp/Silex.git", - "reference": "ec7d5b5334465414952d4b2e935e73bd085dbbbb" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Silex/zipball/ec7d5b5334465414952d4b2e935e73bd085dbbbb", - "reference": "ec7d5b5334465414952d4b2e935e73bd085dbbbb", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", + "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", "shasum": "" }, "require": { - "php": ">=5.5.9", - "pimple/pimple": "~3.0", - "symfony/event-dispatcher": "~2.8|^3.0", - "symfony/http-foundation": "~2.8|^3.0", - "symfony/http-kernel": "~2.8|^3.0", - "symfony/routing": "~2.8|^3.0" - }, - "conflict": { - "phpunit/phpunit": "<4.8.35 || >= 5.0, <5.4.3" - }, - "replace": { - "silex/api": "self.version", - "silex/providers": "self.version" + "php": ">=7.3" }, "require-dev": { - "doctrine/dbal": "~2.2", - "monolog/monolog": "^1.4.1", - "swiftmailer/swiftmailer": "~5", - "symfony/asset": "~2.8|^3.0", - "symfony/browser-kit": "~2.8|^3.0", - "symfony/config": "~2.8|^3.0", - "symfony/css-selector": "~2.8|^3.0", - "symfony/debug": "~2.8|^3.0", - "symfony/doctrine-bridge": "~2.8|^3.0", - "symfony/dom-crawler": "~2.8|^3.0", - "symfony/expression-language": "~2.8|^3.0", - "symfony/finder": "~2.8|^3.0", - "symfony/form": "~2.8|^3.0", - "symfony/intl": "~2.8|^3.0", - "symfony/monolog-bridge": "~2.8|^3.0", - "symfony/options-resolver": "~2.8|^3.0", - "symfony/phpunit-bridge": "^3.2", - "symfony/process": "~2.8|^3.0", - "symfony/security": "~2.8|^3.0", - "symfony/serializer": "~2.8|^3.0", - "symfony/translation": "~2.8|^3.0", - "symfony/twig-bridge": "~2.8|^3.0", - "symfony/validator": "~2.8|^3.0", - "symfony/var-dumper": "~2.8|^3.0", - "symfony/web-link": "^3.3", - "twig/twig": "~1.28|~2.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "2.3-dev" } }, "autoload": { - "psr-4": { - "Silex\\": "src/Silex" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "The PHP micro-framework based on the Symfony Components", - "homepage": "http://silex.sensiolabs.org", - "keywords": [ - "microframework" + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-07-23T07:40:14+00:00" + "time": "2020-10-26T13:18:59+00:00" }, { - "name": "symfony/http-foundation", - "version": "v3.3.10", + "name": "sebastian/version", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "22cf9c2b1d9f67cc8e75ae7f4eaa60e4c1eff1f8" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/22cf9c2b1d9f67cc8e75ae7f4eaa60e4c1eff1f8", - "reference": "22cf9c2b1d9f67cc8e75ae7f4eaa60e4c1eff1f8", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1" - }, - "require-dev": { - "symfony/expression-language": "~2.8|~3.0" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Symfony HttpFoundation Component", - "homepage": "https://symfony.com", - "time": "2017-10-05T23:10:23+00:00" + "time": "2020-09-28T06:39:44+00:00" }, { - "name": "symfony/http-kernel", - "version": "v3.3.10", + "name": "slim/psr7", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "654f047a78756964bf91b619554f956517394018" + "url": "https://github.com/slimphp/Slim-Psr7.git", + "reference": "235d2e5a5ee1ad4b97b96870f37f3091b22fffd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/654f047a78756964bf91b619554f956517394018", - "reference": "654f047a78756964bf91b619554f956517394018", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/235d2e5a5ee1ad4b97b96870f37f3091b22fffd7", + "reference": "235d2e5a5ee1ad4b97b96870f37f3091b22fffd7", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0", - "symfony/debug": "~2.8|~3.0", - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/http-foundation": "~3.3" + "fig/http-message-util": "^1.1.4", + "php": "^7.2 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3", + "symfony/polyfill-php80": "^1.18" }, - "conflict": { - "symfony/config": "<2.8", - "symfony/dependency-injection": "<3.3", - "symfony/var-dumper": "<3.3", - "twig/twig": "<1.34|<2.4,>=2" + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" }, "require-dev": { - "psr/cache": "~1.0", - "symfony/browser-kit": "~2.8|~3.0", - "symfony/class-loader": "~2.8|~3.0", - "symfony/config": "~2.8|~3.0", - "symfony/console": "~2.8|~3.0", - "symfony/css-selector": "~2.8|~3.0", - "symfony/dependency-injection": "~3.3", - "symfony/dom-crawler": "~2.8|~3.0", - "symfony/expression-language": "~2.8|~3.0", - "symfony/finder": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0", - "symfony/routing": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0", - "symfony/templating": "~2.8|~3.0", - "symfony/translation": "~2.8|~3.0", - "symfony/var-dumper": "~3.3" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/class-loader": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "", - "symfony/finder": "", - "symfony/var-dumper": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } + "adriansuter/php-autoload-override": "^1.2", + "ext-json": "*", + "http-interop/http-factory-tests": "^0.7.0", + "php-http/psr7-integration-tests": "dev-master", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5 || ^9.3", + "squizlabs/php_codesniffer": "^3.5", + "weirdan/prophecy-shim": "^1.0 || ^2.0.2" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Slim\\Psr7\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2781,48 +5265,91 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" } ], - "description": "Symfony HttpKernel Component", - "homepage": "https://symfony.com", - "time": "2017-10-05T23:40:19+00:00" + "description": "Strict PSR-7 implementation", + "homepage": "https://www.slimframework.com", + "keywords": [ + "http", + "psr-7", + "psr7" + ], + "support": { + "issues": "https://github.com/slimphp/Slim-Psr7/issues", + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.3.0" + }, + "time": "2020-11-28T06:28:46+00:00" }, { - "name": "symfony/inflector", - "version": "v3.3.10", + "name": "slim/slim", + "version": "4.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/inflector.git", - "reference": "0474dc4d867c7efefd44017f7903465a7f368b6b" + "url": "https://github.com/slimphp/Slim.git", + "reference": "0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/inflector/zipball/0474dc4d867c7efefd44017f7903465a7f368b6b", - "reference": "0474dc4d867c7efefd44017f7903465a7f368b6b", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f", + "reference": "0905e0775f8c1cfb3bbcfabeb6588dcfd8b82d3f", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": "^7.2 || ^8.0", + "psr/container": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", + "psr/log": "^1.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } + "require-dev": { + "adriansuter/php-autoload-override": "^1.2", + "ext-simplexml": "*", + "guzzlehttp/psr7": "^1.7", + "http-interop/http-factory-guzzle": "^1.0", + "laminas/laminas-diactoros": "^2.4", + "nyholm/psr7": "^1.3", + "nyholm/psr7-server": "^1.0.1", + "phpspec/prophecy": "^1.12", + "phpstan/phpstan": "^0.12.58", + "phpunit/phpunit": "^8.5.13", + "slim/http": "^1.2", + "slim/psr7": "^1.3", + "squizlabs/php_codesniffer": "^3.5", + "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + }, + "suggest": { + "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", + "ext-xml": "Needed to support XML format in BodyParsingMiddleware", + "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", + "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Inflector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Slim\\": "Slim" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2830,56 +5357,86 @@ ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "https://joshlockhart.com" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Andrew Smith", + "email": "a.smith@silentworks.co.uk", + "homepage": "http://silentworks.co.uk" + }, + { + "name": "Rob Allen", + "email": "rob@akrabat.com", + "homepage": "http://akrabat.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + }, + { + "name": "Gabriel Manricks", + "email": "gmanricks@me.com", + "homepage": "http://gabrielmanricks.com" } ], - "description": "Symfony Inflector Component", - "homepage": "https://symfony.com", + "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", + "homepage": "https://www.slimframework.com", "keywords": [ - "inflection", - "pluralize", - "singularize", - "string", - "symfony", - "words" + "api", + "framework", + "micro", + "router" + ], + "support": { + "docs": "https://www.slimframework.com/docs/v4/", + "forum": "https://discourse.slimframework.com/", + "irc": "irc://irc.freenode.net:6667/slimphp", + "issues": "https://github.com/slimphp/Slim/issues", + "rss": "https://www.slimframework.com/blog/feed.rss", + "slack": "https://slimphp.slack.com/", + "source": "https://github.com/slimphp/Slim", + "wiki": "https://github.com/slimphp/Slim/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/slimphp", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slim/slim", + "type": "tidelift" + } ], - "time": "2017-07-29T21:54:42+00:00" + "time": "2020-12-01T19:41:22+00:00" }, { - "name": "symfony/polyfill-php56", - "version": "v1.6.0", + "name": "symfony/process", + "version": "v5.2.4", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php56.git", - "reference": "265fc96795492430762c29be291a371494ba3a5b" + "url": "https://github.com/symfony/process.git", + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/265fc96795492430762c29be291a371494ba3a5b", - "reference": "265fc96795492430762c29be291a371494ba3a5b", + "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f", + "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f", "shasum": "" }, "require": { - "php": ">=5.3.3", - "symfony/polyfill-util": "~1.0" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.15" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php56\\": "" + "Symfony\\Component\\Process\\": "" }, - "files": [ - "bootstrap.php" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2888,109 +5445,114 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "support": { + "source": "https://github.com/symfony/process/tree/v5.2.4" + }, + "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": "2017-10-11T12:05:26+00:00" + "time": "2021-01-27T10:15:41+00:00" }, { - "name": "symfony/polyfill-php70", - "version": "v1.6.0", + "name": "theseer/tokenizer", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", - "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0", - "php": ">=5.3.3" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6-dev" - } - }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, - "files": [ - "bootstrap.php" - ], "classmap": [ - "Resources/stubs" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2020-07-12T23:59:07+00:00" }, { - "name": "symfony/polyfill-util", - "version": "v1.6.0", + "name": "tuupola/callable-handler", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-util.git", - "reference": "6e719200c8e540e0c0effeb31f96bdb344b94176" + "url": "https://github.com/tuupola/callable-handler.git", + "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/6e719200c8e540e0c0effeb31f96bdb344b94176", - "reference": "6e719200c8e540e0c0effeb31f96bdb344b94176", + "url": "https://api.github.com/repos/tuupola/callable-handler/zipball/0bc7b88630ca753de9aba8f411046856f5ca6f8c", + "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1|^8.0", + "psr/http-server-middleware": "^1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6-dev" - } + "require-dev": { + "overtrue/phplint": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.2", + "tuupola/http-factory": "^0.4.0|^1.0", + "zendframework/zend-diactoros": "^1.6.0|^2.0" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Polyfill\\Util\\": "" + "Tuupola\\Middleware\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2999,54 +5561,66 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "https://appelsiini.net/", + "role": "Developer" } ], - "description": "Symfony utilities for portability of PHP codes", - "homepage": "https://symfony.com", + "description": "Compatibility layer for PSR-7 double pass and PSR-15 middlewares.", + "homepage": "https://github.com/tuupola/callable-handler", "keywords": [ - "compat", - "compatibility", - "polyfill", - "shim" + "middleware", + "psr-15", + "psr-7" + ], + "support": { + "issues": "https://github.com/tuupola/callable-handler/issues", + "source": "https://github.com/tuupola/callable-handler/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/tuupola", + "type": "github" + } ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2020-09-09T08:31:54+00:00" }, { - "name": "symfony/process", - "version": "v3.3.10", + "name": "tuupola/http-factory", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "fdf89e57a723a29baf536e288d6e232c059697b1" + "url": "https://github.com/tuupola/http-factory.git", + "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/fdf89e57a723a29baf536e288d6e232c059697b1", - "reference": "fdf89e57a723a29baf536e288d6e232c059697b1", + "url": "https://api.github.com/repos/tuupola/http-factory/zipball/aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", + "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1|^8.0", + "psr/http-factory": "^1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } + "conflict": { + "nyholm/psr7": "<1.0" }, + "provide": { + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.7.0", + "overtrue/phplint": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Tuupola\\Http\\Factory\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3054,56 +5628,66 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "https://appelsiini.net/", + "role": "Developer" + } + ], + "description": "Lightweight autodiscovering PSR-17 HTTP factories", + "homepage": "https://github.com/tuupola/http-factory", + "keywords": [ + "http", + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/tuupola/http-factory/issues", + "source": "https://github.com/tuupola/http-factory/tree/1.3.0" + }, + "funding": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/tuupola", + "type": "github" } ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "time": "2020-10-01T07:46:32+00:00" }, { - "name": "symfony/property-access", - "version": "v3.3.10", + "name": "tuupola/slim-basic-auth", + "version": "3.3.1", "source": { "type": "git", - "url": "https://github.com/symfony/property-access.git", - "reference": "8d975b77d10ad8c24a7b88af1b38b333d2d4fa4b" + "url": "https://github.com/tuupola/slim-basic-auth.git", + "reference": "18e49c18f5648b05bb6169d166ccb6f797f0fbc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/8d975b77d10ad8c24a7b88af1b38b333d2d4fa4b", - "reference": "8d975b77d10ad8c24a7b88af1b38b333d2d4fa4b", + "url": "https://api.github.com/repos/tuupola/slim-basic-auth/zipball/18e49c18f5648b05bb6169d166ccb6f797f0fbc4", + "reference": "18e49c18f5648b05bb6169d166ccb6f797f0fbc4", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/inflector": "~3.1", - "symfony/polyfill-php70": "~1.0" + "php": "^7.1|^8.0", + "psr/http-message": "^1.0.1", + "psr/http-server-middleware": "^1.0", + "tuupola/callable-handler": "^0.3.0|^0.4.0|^1.0", + "tuupola/http-factory": "^0.4.0|^1.0.2" }, "require-dev": { - "symfony/cache": "~3.1" - }, - "suggest": { - "psr/cache-implementation": "To cache access methods." + "equip/dispatch": "^2.0", + "overtrue/phplint": "^2.0.2", + "phpstan/phpstan": "^0.12.43", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.3.2", + "symfony/process": "^3.3", + "zendframework/zend-diactoros": "^1.3|^2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\PropertyAccess\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Tuupola\\Middleware\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3111,81 +5695,113 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "https://appelsiini.net/" } ], - "description": "Symfony PropertyAccess Component", - "homepage": "https://symfony.com", + "description": "PSR-7 and PSR-15 HTTP Basic Authentication Middleware", + "homepage": "https://appelsiini.net/projects/slim-basic-auth", "keywords": [ - "access", - "array", - "extraction", - "index", - "injection", - "object", - "property", - "property path", - "reflection" + "auth", + "middleware", + "psr-15", + "psr-7" + ], + "support": { + "issues": "https://github.com/tuupola/slim-basic-auth/issues", + "source": "https://github.com/tuupola/slim-basic-auth/tree/3.3.1" + }, + "funding": [ + { + "url": "https://github.com/tuupola", + "type": "github" + } ], - "time": "2017-10-02T06:42:24+00:00" + "time": "2020-10-28T15:22:12+00:00" }, { - "name": "symfony/routing", - "version": "v3.3.10", + "name": "vimeo/psalm", + "version": "4.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "2e26fa63da029dab49bf9377b3b4f60a8fecb009" + "url": "https://github.com/vimeo/psalm.git", + "reference": "cd53e047a58f71f646dd6bf45476076ab07b5d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/2e26fa63da029dab49bf9377b3b4f60a8fecb009", - "reference": "2e26fa63da029dab49bf9377b3b4f60a8fecb009", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/cd53e047a58f71f646dd6bf45476076ab07b5d44", + "reference": "cd53e047a58f71f646dd6bf45476076ab07b5d44", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer/package-versions-deprecated": "^1.8.0", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^1.1 || ^2.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.0.3", + "felixfbecker/language-server-protocol": "^1.5", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.10.1", + "openlss/lib-array2xml": "^1.0", + "php": "^7.1|^8", + "sebastian/diff": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "webmozart/path-util": "^2.3" }, - "conflict": { - "symfony/config": "<2.8", - "symfony/dependency-injection": "<3.3", - "symfony/yaml": "<3.3" + "provide": { + "psalm/psalm": "self.version" }, "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/common": "~2.2", - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/dependency-injection": "~3.3", - "symfony/expression-language": "~2.8|~3.0", - "symfony/http-foundation": "~2.8|~3.0", - "symfony/yaml": "~3.3" + "bamarni/composer-bin-plugin": "^1.2", + "brianium/paratest": "^4.0||^6.0", + "ext-curl": "*", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpdocumentor/reflection-docblock": "^5", + "phpmyadmin/sql-parser": "5.1.0||dev-master", + "phpspec/prophecy": ">=1.9.0", + "phpunit/phpunit": "^9.0", + "psalm/plugin-phpunit": "^0.13", + "slevomat/coding-standard": "^6.3.11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.3", + "weirdan/phpunit-appveyor-reporter": "^1.0.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" }, "suggest": { - "doctrine/annotations": "For using the annotation loader", - "symfony/config": "For using the all-in-one router or any loader", - "symfony/dependency-injection": "For loading routes from a service", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" + "ext-igbinary": "^2.0.5" }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Routing\\": "" + "Psalm\\": "src/Psalm/" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "src/functions.php", + "src/spl_object_id.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3194,83 +5810,56 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Matthew Brown" } ], - "description": "Symfony Routing Component", - "homepage": "https://symfony.com", + "description": "A static analysis tool for finding errors in PHP applications", "keywords": [ - "router", - "routing", - "uri", - "url" + "code", + "inspection", + "php" ], - "time": "2017-10-02T07:25:00+00:00" + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/4.7.1" + }, + "time": "2021-04-25T21:26:25+00:00" }, { - "name": "symfony/security", - "version": "v3.3.10", + "name": "webmozart/assert", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/symfony/security.git", - "reference": "bc02243d4b09d42b636c89d7ff3c41edd5af0100" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security/zipball/bc02243d4b09d42b636c89d7ff3c41edd5af0100", - "reference": "bc02243d4b09d42b636c89d7ff3c41edd5af0100", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/http-foundation": "~2.8|~3.0", - "symfony/http-kernel": "~3.3", - "symfony/polyfill-php56": "~1.0", - "symfony/polyfill-php70": "~1.0", - "symfony/polyfill-util": "~1.0", - "symfony/property-access": "~2.8|~3.0" + "php": "^7.2 || ^8.0", + "symfony/polyfill-ctype": "^1.8" }, - "replace": { - "symfony/security-core": "self.version", - "symfony/security-csrf": "self.version", - "symfony/security-guard": "self.version", - "symfony/security-http": "self.version" + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "psr/log": "~1.0", - "symfony/expression-language": "~2.8|~3.0", - "symfony/finder": "~2.8|~3.0", - "symfony/ldap": "~3.1", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~2.8|~3.0", - "symfony/validator": "^2.8.18|^3.2.5" - }, - "suggest": { - "symfony/expression-language": "For using the expression voter", - "symfony/form": "", - "symfony/ldap": "For using the LDAP user and authentication providers", - "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", - "symfony/validator": "For using the user password constraint" + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "1.10-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Security\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Webmozart\\Assert\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3278,34 +5867,39 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Symfony Security Component", - "homepage": "https://symfony.com", - "time": "2017-10-02T06:42:24+00:00" + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" }, { - "name": "webmozart/assert", - "version": "1.2.0", + "name": "webmozart/path-util", + "version": "2.3.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "url": "https://github.com/webmozart/path-util.git", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=5.3.3", + "webmozart/assert": "~1.0" }, "require-dev": { "phpunit/phpunit": "^4.6", @@ -3314,12 +5908,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "2.3-dev" } }, "autoload": { "psr-4": { - "Webmozart\\Assert\\": "src/" + "Webmozart\\PathUtil\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3332,13 +5926,12 @@ "email": "bschussek@gmail.com" } ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2016-11-23T20:04:58+00:00" + "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, + "time": "2015-12-17T08:42:14+00:00" } ], "aliases": [], @@ -3347,7 +5940,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.6" + "php": "^7.4 || ^8.0", + "ext-json": "*" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "2.0.0" } diff --git a/docs/conf.py b/docs/conf.py index 75bec64..242d617 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -53,9 +53,9 @@ # built documents. # # The short X.Y version. -version = '2.2' +version = '3.0' # The full version, including alpha/beta/rc tags. -release = '2.2.1' +release = '3.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/guide/setup-request.rst b/docs/guide/setup-request.rst index eccdc92..8e412f2 100644 --- a/docs/guide/setup-request.rst +++ b/docs/guide/setup-request.rst @@ -57,6 +57,28 @@ Step ``:username`` ` Given I am authenticating as "``foo``" with password "``bar``" ``foo`` ``bar`` ============================================================== ============= ============= +Given I get an OAuth token using password grant from ``:path`` with ``:username`` and ``:password`` in scope ``:scope`` using client ID ``:clientId`` (and client secret ``:clientSecret``) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Send a request using password grant to the given ``:path`` for an access token that will be added as a ``Authorization`` header for the next request. The endpoint is required to respond with a JSON object that contains the ``access_token`` key, for instance: + +.. code-block:: json + + { + "access_token": "some-token" + } + +Given the above response body, the next request will have the following header set: ``Authorization: Bearer some-token``. + +**Examples:** + +.. code-block:: gherkin + + Given I get an OAuth token using password grant from "/token" with "user" and "password" in scope "scope" using client ID "id" and client secret "secret" + When I request "/path/that/requires/token/in/header" + +The second step in the above example will include the required ``Authorization`` header given the response from ``/token`` as seen in the first step. + .. _given-the-header-request-header-is-value: Given the ``:header`` request header is ``:value`` @@ -158,3 +180,58 @@ This step can be used to prepare the `JWT `_ custom matcher fun """ The above step would register a JWT which can be matched with ``@jwt(my JWT)`` using the :ref:`@jwt() ` custom matcher function. The way the payload is matched is similar to matching a JSON response body, as explained in the :ref:`then-the-response-body-contains-json` section, which means :ref:`custom matcher functions ` can be used, as seen in the example above. + +Given the query parameter ``:name`` is ``:value`` +------------------------------------------------- + +This step can be used to set a single query parameter to a specific value for the upcoming request. + +**Examples:** + +.. code-block:: gherkin + + Given the query parameter "foo" is "bar" + And the query parameter "bar" is "foo" + When I request "/path" + +The above steps would end up with a request to ``/path?foo=bar&bar=foo``. + +.. note:: When this step is used all query parameters specified in the path portion of ``When I request "/path"`` are ignored. + +Given the query parameter ``:name`` is: ```` +------------------------------------------------------- + +This step can be used to set multiple values to a single query parameter for the upcoming request. + +**Examples:** + +.. code-block:: gherkin + + Given the query parameter "foo" is: + | value | + | foo | + | bar | + When I request "/path" + +The above steps would end up with a request to ``/path?foo[0]=foo&foo[1]=bar``. + +.. note:: When this step is used all query parameters specified in the path portion of ``When I request "/path"`` are ignored. + +Given the following query parameters are set: ```` +------------------------------------------------------------- + +This step can be used to set multiple query parameters at once for the upcoming request. + +**Examples:** + +.. code-block:: gherkin + + Given the following query parameters are set: + | name | value | + | foo | bar | + | bar | foo | + When I request "/path" + +The above steps would end up with a request to ``/path?foo=bar&bar=foo``. + +.. note:: When this step is used all query parameters specified in the path portion of ``When I request "/path"`` are ignored. \ No newline at end of file diff --git a/docs/guide/verify-server-response.rst b/docs/guide/verify-server-response.rst index e4e3e47..b002809 100644 --- a/docs/guide/verify-server-response.rst +++ b/docs/guide/verify-server-response.rst @@ -461,6 +461,7 @@ To be able to assert the variable type of specific values, the ``@variableType`` * ``object`` * ``null`` * ``scalar`` +* ``any`` Given the following response: @@ -495,10 +496,12 @@ the type of the values can be asserted like this: } """ -The ``boolean``, ``integer`` and ``double`` functions can also be expressed using ``bool``, ``int`` and ``float`` respectively. There is no difference in the actual validation being executed. +The ``boolean``, ``integer`` and ``double`` types can also be expressed using ``bool``, ``int`` and ``float`` respectively. There is no difference in the actual validation being executed. For the ``@variableType(scalar)`` assertion refer to the `is_scalar function `_ in the PHP manual as to what is considered to be a scalar. +When using ``any`` as a type, the validation will basically allow any types, including ``null``. One can also match against multiple types using ``|`` (for instance ``@variableType(int|double|string)``). When using multiple types the validation will succeed (and stop) as soon as the value being tested matches one of the supplied types. Validation is done in the order specified. + Regular expression matching - ``@regExp`` """"""""""""""""""""""""""""""""""""""""" diff --git a/docs/installation/configuration.rst b/docs/installation/configuration.rst index 12c3b07..48896b8 100644 --- a/docs/installation/configuration.rst +++ b/docs/installation/configuration.rst @@ -6,20 +6,20 @@ After you have installed the extension you need to activate it in your Behat con .. code-block:: yaml default: - suites: - default: - # ... + suites: + default: + # ... - extensions: - Imbo\BehatApiExtension: ~ + extensions: + Imbo\BehatApiExtension: ~ The following configuration options are required for the extension to work as expected: -====================== ====== ===================== ===================================================================================== +====================== ====== ===================== ======================================= Key Type Default value Description -====================== ====== ===================== ===================================================================================== -``apiClient.base_uri`` string http://localhost:8080 Base URI of the application under test. Must be connectable for the tests to execute. -====================== ====== ===================== ===================================================================================== +====================== ====== ===================== ======================================= +``apiClient.base_uri`` string http://localhost:8080 Base URI of the application under test. +====================== ====== ===================== ======================================= It should be noted that everything in the ``apiClient`` configuration array is passed directly to the Guzzle Client instance used internally by the extension. @@ -28,15 +28,15 @@ Example of a configuration file with several configuration entries: .. code-block:: yaml default: - suites: - default: - # ... - - extensions: - Imbo\BehatApiExtension: - apiClient: - base_uri: http://localhost:8080 - timeout: 5.0 - verify: false + suites: + default: + # ... + + extensions: + Imbo\BehatApiExtension: + apiClient: + base_uri: http://localhost:8080 + timeout: 5.0 + verify: false Refer to the `Guzzle documentation `_ for available configuration options for the Guzzle client. diff --git a/docs/installation/installation.rst b/docs/installation/installation.rst index dc4eeca..66333ab 100644 --- a/docs/installation/installation.rst +++ b/docs/installation/installation.rst @@ -1,17 +1,8 @@ Installation ============ -Using composer --------------- +Install the extension using `Composer `_: -Install the extension by adding the following to your ``composer.json`` file: +.. code-block:: console -.. code-block:: json - - { - "require-dev": { - "imbo/behat-api-extension": "^2.1" - } - } - -and then updating your dependencies by issuing ``composer update imbo/behat-api-extension``. + composer require --dev imbo/behat-api-extension diff --git a/docs/installation/requirements.rst b/docs/installation/requirements.rst index 1f6d617..37fff7b 100644 --- a/docs/installation/requirements.rst +++ b/docs/installation/requirements.rst @@ -1,12 +1,4 @@ Requirements ============ -Behat API extension requires the following packages: - -* `PHP `_ ``^5.6`` -* `behat/behat `_ ``^3.0`` -* `guzzlehttp/guzzle `_ ``^6.0`` -* `beberlei/assert `_ ``^2.1`` -* `firebase/php-jwt `_ ``^4.0 | ^5.0`` - -You do not need to add any of these to your own ``composer.json`` file as the extension requires them. +Behat API extension requires PHP 7.4 or above. Refer to ``composer.json`` for more details. diff --git a/docs/installation/upgrade.rst b/docs/installation/upgrade.rst index 7bc5941..065e378 100644 --- a/docs/installation/upgrade.rst +++ b/docs/installation/upgrade.rst @@ -3,6 +3,15 @@ Upgrading This section will cover breaking changes between major versions and other related information to ease upgrading to the latest version. +Migration from v2.x to v3.x +--------------------------- + +.. contents:: Changes + :local: + :depth: 1 + +The usage of Behat API Extension itself has not changed between these versions, but ``>=3.0`` requires ``PHP >= 7.4``. + Migrating from v1.x to v2.x --------------------------- @@ -20,27 +29,27 @@ In ``v1`` the extension only had a single configuration option, which was ``base .. code-block:: yaml default: - suites: - default: - # ... + suites: + default: + # ... - extensions: - Imbo\BehatApiExtension: - base_uri: http://localhost:8080 + extensions: + Imbo\BehatApiExtension: + base_uri: http://localhost:8080 **v2 behat.yml** .. code-block:: yaml default: - suites: - default: - # ... - - extensions: - Imbo\BehatApiExtension: - apiClient: - base_uri: http://localhost:8080 + suites: + default: + # ... + + extensions: + Imbo\BehatApiExtension: + apiClient: + base_uri: http://localhost:8080 Renamed public methods ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/features/README.md b/features/README.md index 53a5ec1..ab16327 100644 --- a/features/README.md +++ b/features/README.md @@ -4,11 +4,11 @@ For these tests to pass an HTTP server must be set up to listen on `http://local In the project root directory, run the following command: - composer start-server + composer dev --timeout=0 and to execute the tests run (also from the project root): - composer test-behat + composer test:behat or diff --git a/features/add-custom-matcher-functions.feature b/features/add-custom-matcher-functions.feature index 6b8328b..3dca7c2 100644 --- a/features/add-custom-matcher-functions.feature +++ b/features/add-custom-matcher-functions.feature @@ -11,8 +11,17 @@ Feature: Custom function addition use Imbo\BehatApiExtension\ArrayContainsComparator; use Assert\Assertion; + class MyMatcher { + public function __invoke($value) { + if (!is_string($value)) { + throw new InvalidArgumentException('Want string yo'); + } + } + } + class FeatureContext extends ApiContext { public function setArrayContainsComparator(ArrayContainsComparator $comparator) { + $comparator->addFunction('myMatcher', new MyMatcher()); $comparator->addFunction('valueIs', function($actual, $expected) { if ($actual !== $expected) { throw new InvalidArgumentException(sprintf( @@ -92,3 +101,138 @@ Feature: Custom function addition """ Function "valueIs" failed with error message: "Expected "expected", got "actual".". """ + + Scenario: Custom myMatcher class passes + Given a file named "behat.yml" with: + """ + default: + formatters: + progress: ~ + extensions: + Imbo\BehatApiExtension: ~ + """ + And a file named "features/test-custom-matcher-class.feature" with: + """ + Feature: Custom matcher function + In order to use a custom matcher function + As a feature runner + I need to be able to expose the function + + Scenario: Call step that invokes custom matcher function + When I request "/" + Then the response body contains JSON: + ''' + { + "string": "@myMatcher()" + } + ''' + """ + When I run "behat features/test-custom-matcher-class.feature" + Then it should pass with: + """ + .. + + 1 scenario (1 passed) + 2 steps (2 passed) + """ + + Scenario: Custom myMatcher class passes when used in list + Given a file named "behat.yml" with: + """ + default: + formatters: + progress: ~ + extensions: + Imbo\BehatApiExtension: ~ + """ + And a file named "features/test-custom-matcher-class-in-list.feature" with: + """ + Feature: Custom matcher function + In order to use a custom matcher function + As a feature runner + I need to be able to expose the function + + Scenario: Call step that invokes custom matcher function + When I request "/list" + Then the response body contains JSON: + ''' + { + "[0]": { + "string": "@myMatcher()" + } + } + ''' + """ + When I run "behat features/test-custom-matcher-class-in-list.feature" + Then it should pass with: + """ + .. + + 1 scenario (1 passed) + 2 steps (2 passed) + """ + + Scenario: Custom myMatcher class fails + Given a file named "behat.yml" with: + """ + default: + formatters: + progress: ~ + extensions: + Imbo\BehatApiExtension: ~ + """ + And a file named "features/test-custom-matcher-class-fails.feature" with: + """ + Feature: Custom matcher function + In order to use a custom matcher function + As a feature runner + I need to be able to expose the function + + Scenario: Call step that invokes custom matcher function + When I request "/" + Then the response body contains JSON: + ''' + { + "integer": "@myMatcher()" + } + ''' + """ + When I run "behat features/test-custom-matcher-class-fails.feature" + Then it should fail with: + """ + Want string yo + """ + + Scenario: Custom myMatcher class fails when used with list + Given a file named "behat.yml" with: + """ + default: + formatters: + progress: ~ + extensions: + Imbo\BehatApiExtension: ~ + """ + And a file named "features/test-custom-matcher-class-fails-in-list.feature" with: + """ + Feature: Custom matcher function + In order to use a custom matcher function + As a feature runner + I need to be able to expose the function + + Scenario: Call step that invokes custom matcher function + When I request "/list" + Then the response body contains JSON: + ''' + { + "[0]": { + "integer": "@myMatcher()" + } + } + ''' + """ + When I run "behat features/test-custom-matcher-class-fails-in-list.feature" + Then it should fail with: + """ + Want string yo + """ + diff --git a/features/auth.feature b/features/auth.feature new file mode 100644 index 0000000..d46930b --- /dev/null +++ b/features/auth.feature @@ -0,0 +1,120 @@ +Feature: Test auth steps + In order to test the extension + As a developer + I want to be able to test all available steps + + Background: + Given a file named "behat.yml" with: + """ + default: + formatters: + progress: ~ + extensions: + Imbo\BehatApiExtension: + apiClient: + base_uri: http://localhost:8080 + suites: + default: + contexts: ['Imbo\BehatApiExtension\Context\ApiContext'] + """ + + Scenario: Successfully authenticate + Given a file named "features/auth-success.feature" with: + """ + Feature: Set up the request + Scenario: Specify auth + Given I am authenticating as "foo" with password "bar" + When I request "/basicAuth" + Then the response body contains JSON: + ''' + { + "user": "foo" + } + ''' + """ + When I run "behat features/auth-success.feature" + Then it should pass with: + """ + ... + + 1 scenario (1 passed) + 3 steps (3 passed) + """ + + Scenario: Unsuccessful authentication + Given a file named "features/auth-no-success.feature" with: + """ + Feature: Set up the request + Scenario: Specify auth + Given I am authenticating as "foo" with password "foobar" + When I request "/basicAuth" + Then the response code is 401 + """ + When I run "behat features/auth-no-success.feature" + Then it should pass with: + """ + ... + + 1 scenario (1 passed) + 3 steps (3 passed) + """ + + Scenario: Successfully OAuth + Given a file named "features/oauth-success.feature" with: + """ + Feature: Set up the request + Scenario: Specify auth + Given I get an OAuth token using password grant from "/oauth/token" with "foo" and "bar" in scope "baz" using client ID "id" and client secret "secret" + When I request "/securedWithOAuth" + Then the response code is 200 + And the response body contains JSON: + ''' + { + "users": { + "foo": "bar" + } + } + ''' + """ + When I run "behat features/oauth-success.feature" + Then it should pass with: + """ + ... + + 1 scenario (1 passed) + 4 steps (4 passed) + """ + + Scenario: Unsuccessfully OAuth + Given a file named "features/oauth-no-success.feature" with: + """ + Feature: Set up the request + Scenario: Specify auth + Given I get an OAuth token using password grant from "/oauth/token" with "invalid" and "invalid" in scope "baz" using client ID "id" + When I request "/securedWithOAuth" + """ + When I run "behat features/oauth-no-success.feature" + Then it should fail with: + """ + Expected request for access token to pass, got status code 401 with the following response: {"error":"invalid_request"} (RuntimeException) + + 1 scenario (1 failed) + 2 steps (1 failed, 1 skipped) + """ + + Scenario: Invalid OAuth token response + Given a file named "features/oauth-missing-token.feature" with: + """ + Feature: Set up the request + Scenario: Specify auth + Given I get an OAuth token using password grant from "/echoHttpMethod" with "foo" and "bar" in scope "baz" using client ID "id" + When I request "/securedWithOAuth" + """ + When I run "behat features/oauth-missing-token.feature" + Then it should fail with: + """ + Missing access_token from response body: {"method":"POST"} (RuntimeException) + + 1 scenario (1 failed) + 2 steps (1 failed, 1 skipped) + """ \ No newline at end of file diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 6060f53..d3577f1 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -1,5 +1,5 @@ */ -class FeatureContext implements SnippetAcceptingContext { +class FeatureContext implements Context { /** * PHP binary used to trigger Behat from the scenarios * - * @var string + * @var ?string */ private $phpBin; /** * Process instance for executing processes * - * @var Process + * @var ?Process */ private $process; /** * The working directory where files can be created * - * @var string + * @var ?string */ private $workingDir; @@ -45,7 +45,7 @@ class FeatureContext implements SnippetAcceptingContext { * @BeforeSuite * @AfterSuite */ - public static function emptyTestDir(SuiteScope $scope) { + public static function emptyTestDir(SuiteScope $scope) : void { $testDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat-api-extension'; if (is_dir($testDir)) { @@ -61,7 +61,7 @@ public static function emptyTestDir(SuiteScope $scope) { * * @BeforeScenario */ - public function prepareScenario(BeforeScenarioScope $scope) { + public function prepareScenario(BeforeScenarioScope $scope) : void { $dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat-api-extension' . DIRECTORY_SEPARATOR . microtime(true); mkdir($dir . '/features/bootstrap', 0777, true); @@ -72,7 +72,6 @@ public function prepareScenario(BeforeScenarioScope $scope) { $this->workingDir = $dir; $this->phpBin = $bin; - $this->process = new Process(null); } /** @@ -84,8 +83,8 @@ public function prepareScenario(BeforeScenarioScope $scope) { * * @Given a file named :filename with: */ - public function createFile($filename, PyStringNode $content, $readable = true) { - $filename = rtrim($this->workingDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . ltrim($filename, DIRECTORY_SEPARATOR); + public function createFile($filename, PyStringNode $content, $readable = true) : void { + $filename = rtrim((string) $this->workingDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . ltrim($filename, DIRECTORY_SEPARATOR); $path = dirname($filename); $content = str_replace("'''", '"""', (string) $content); @@ -108,29 +107,35 @@ public function createFile($filename, PyStringNode $content, $readable = true) { * * @Given a non-readable file named :filename with: */ - public function createNonReadableFile($filename, PyStringNode $content) { + public function createNonReadableFile($filename, PyStringNode $content) : void { $this->createFile($filename, $content, false); } /** * Runs Behat * - * @param string $argumentsString + * @param string $args * @When /^I run "behat(?: ((?:\"|[^"])*))?"$/ */ - public function runBehat($args = '') { + public function runBehat(string $args = '') : void { + if (!defined('BEHAT_BIN_PATH')) { + throw new RuntimeException('Missing BEHAT_BIN_PATH constant'); + } + $args = strtr($args, ['\'' => '"']); - $this->process->setWorkingDirectory($this->workingDir); - $this->process->setCommandLine( + $this->process = Process::fromShellCommandline( sprintf( '%s %s %s %s', - $this->phpBin, - escapeshellarg(BEHAT_BIN_PATH), + (string) $this->phpBin, + escapeshellarg((string) BEHAT_BIN_PATH), $args, '--format-settings="{\"timer\": false}" --no-colors' - ) + ), + $this->workingDir ); + + $this->process->start(); $this->process->wait(); } @@ -143,7 +148,7 @@ public function runBehat($args = '') { * * @Then /^it should (fail|pass) with:$/ */ - public function assertCommandResultWithOutput($result, PyStringNode $output) { + public function assertCommandResultWithOutput($result, PyStringNode $output) : void { $this->assertCommandResult($result); $this->assertCommandOutputMatches($output); } @@ -151,11 +156,11 @@ public function assertCommandResultWithOutput($result, PyStringNode $output) { /** * Assert command output contains a string * - * @param PyStringNode $output + * @param PyStringNode $content * * @Then the output should contain: */ - public function assertCommandOutputMatches(PyStringNode $content) { + public function assertCommandOutputMatches(PyStringNode $content) : void { Assertion::contains( $this->getOutput(), str_replace("'''", '"""', (string) $content), @@ -169,7 +174,7 @@ public function assertCommandOutputMatches(PyStringNode $content) { * @param string $result * @Then /^it should (fail|pass)$/ */ - public function assertCommandResult($result) { + public function assertCommandResult(string $result) : void { $exitCode = $this->getExitCode(); // Escape % as the callback will pass this value to sprintf() if the assertion fails, and @@ -200,8 +205,18 @@ public function assertCommandResult($result) { * * @return int */ - private function getExitCode() { - return $this->process->getExitCode(); + private function getExitCode() : int { + if (null === $this->process) { + throw new RuntimeException('No process is running'); + } + + $code = $this->process->getExitCode(); + + if (null === $code) { + throw new RuntimeException('Process is not finished'); + } + + return $code; } /** @@ -210,9 +225,13 @@ private function getExitCode() { * @return string */ private function getOutput() { + if (null === $this->process) { + throw new RuntimeException('No process is running'); + } + $output = $this->process->getErrorOutput() . $this->process->getOutput(); - return trim(preg_replace('/ +$/m', '', $output)); + return trim((string) preg_replace('/ +$/m', '', $output)); } /** @@ -220,8 +239,11 @@ private function getOutput() { * * @param string $path Path to a file or a directory */ - private static function rmdir($path) { - foreach (glob($path . '/*') as $file) { + private static function rmdir($path) : void { + /** @var string[] */ + $files = glob(sprintf('%s/*', $path)); + + foreach ($files as $file) { if (is_dir($file)) { self::rmdir($file); } else { diff --git a/features/bootstrap/index.php b/features/bootstrap/index.php index 2aded4b..9c9fc81 100644 --- a/features/bootstrap/index.php +++ b/features/bootstrap/index.php @@ -1,44 +1,28 @@ - */ +namespace Imbo\BehatApiExtension; -use Silex\Application; -use Silex\Provider\SecurityServiceProvider; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; +use Slim\Factory\AppFactory; +use Tuupola\Middleware\HttpBasicAuthentication; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use stdClass; require_once __DIR__ . '/../../vendor/autoload.php'; -$app = new Application(); - -$app->register(new SecurityServiceProvider(), [ - 'security.firewalls' => [ - 'basicAuth' => [ - 'pattern' => '^/basicAuth', - 'http' => true, - 'users' => [ - 'foo' => [ - 'ROLE_ADMIN', - '$2y$13$jUzpuB1A0C0A5utO0hW/1eKngeCIxR8LO/Ios5Tay9b8zRUcLtCMO', // bar - ], - 'bar' => [ - 'ROLE_ADMIN', - '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a', // foo - ], - ], - ], - ], -]); +$app = AppFactory::create(); +$app->add(new HttpBasicAuthentication([ + 'path' => '/basicAuth', + 'realm' => 'Protected', + 'users' => [ + 'foo' => 'bar', + ] +])); /** * Front page */ -$app->match('/', function(Request $request) { - return new JsonResponse([ +$app->any('/', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([ 'null' => null, 'string' => 'value', 'integer' => 42, @@ -63,85 +47,153 @@ 'null' => null, 'scalar' => '123', ], - ], 200, [ - 'X-Foo' => 'foo', - ]); + ])); + return $response + ->withHeader('Content-Type', 'application/json') + ->withHeader('X-Foo', 'foo'); }); /** - * Echo the request body + * List with objects */ -$app->match('/echo', function(Request $request) { - $headers = []; +$app->any('/list', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([ + [ + 'integer' => 123, + 'string' => 'value', + ] + ])); + + return $response + ->withHeader('Content-Type', 'application/json'); +}); +/** + * Echo the request body + */ +$app->any('/echo', function(Request $request, Response $response) : Response { // Set the same Content-Type header in the response as found in the request - if ($contentType = $request->headers->get('Content-Type')) { - $headers['Content-Type'] = $contentType; + if ($contentType = $request->getHeaderLine('Content-Type')) { + $response = $response->withHeader('Content-Type', $contentType); } - return $request->query->has('json') ? - new JsonResponse(json_decode($request->getContent(), true), 200, $headers) : - new Response($request->getContent(), 200, $headers); + $requestBody = (string) $request->getBody(); + + if (array_key_exists('json', $request->getQueryParams())) { + $response->getBody()->write((string) json_encode(json_decode($requestBody, true))); + $response = $response->withHeader('Content-Type', 'application/json'); + } else { + $response->getBody()->write($requestBody); + } + + return $response; }); /** * Return information about uploaded files */ -$app->post('/files', function(Request $request) { - return new JsonResponse($_FILES); +$app->post('/files', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode($_FILES)); + return $response->withHeader('Content-Type', 'application/json'); }); /** * Return information about the request */ -$app->match('/requestInfo', function(Request $request) { - return new JsonResponse([ +$app->any('/requestInfo', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([ '_GET' => $_GET, '_POST' => $_POST, '_FILES' => $_FILES, '_SERVER' => $_SERVER, - 'requestBody' => $request->getContent(), - ]); + 'requestBody' => (string) $request->getBody(), + ])); + return $response->withHeader('Content-Type', 'application/json'); }); /** * Return the HTTP method */ -$app->match('/echoHttpMethod', function(Request $request) { - return new JsonResponse([ +$app->any('/echoHttpMethod', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([ 'method' => $request->getMethod(), - ]); + ])); + return $response->withHeader('Content-Type', 'application/json'); }); /** * Return the authenticated user name */ -$app->match('/basicAuth', function(Application $app) { - return new JsonResponse([ - 'user' => $app['security.token_storage']->getToken()->getUser()->getUsername(), - ]); +$app->any('/basicAuth', function(Request $request, Response $response) { + $response->getBody()->write((string) json_encode([ + 'user' => explode(':', $request->getUri()->getUserInfo())[0], + ])); + + return $response->withHeader('Content-Type', 'application/json'); }); /** - * Return a client error + * Return access token given the correct credentials */ -$app->match('/clientError', function(Application $app) { - return new JsonResponse([ - 'error' => 'client error', - ], 400); +$app->any('/oauth/token', function(Request $request, Response $response) { + /** @var array{username: string, password: string} */ + $body = $request->getParsedBody(); + + if ('foo' === $body['username'] && 'bar' === $body['password']) { + $responseBody = [ + 'access_token' => 'some_access_token', + ]; + } else { + $response = $response->withStatus(401); + $responseBody = [ + 'error' => 'invalid_request', + ]; + } + + $response->getBody()->write((string) json_encode($responseBody)); + + return $response->withHeader('Content-Type', 'application/json'); }); /** - * Return a server error + * Return secured resource if Authorization header is valid. */ -$app->match('/serverError', function(Application $app) { - return new JsonResponse([ - 'error' => 'server error', - ], 500); +$app->any('/securedWithOAuth', function(Request $request, Response $response) { + if ('Bearer some_access_token' === $request->getHeaderLine('Authorization')) { + $responseBody = [ + 'users' => [ + 'foo' => 'bar', + ] + ]; + } else { + $response = $response->withStatus(401); + $responseBody = [ + 'error' => 'invalid_request', + ]; + } + + $response->getBody()->write((string) json_encode($responseBody)); + + return $response->withHeader('Content-Type', 'application/json'); }); -$app->match('/issue-13', function(Application $app) { - return new JsonResponse([ +/** + * Return a client error + */ +$app->any('/clientError', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([ + 'error' => 'client error', + ])); + return $response + ->withHeader('Content-Type', 'appliation/json') + ->withStatus(400); +}); + +/** + * @see https://github.com/imbo/behat-api-extension/issues/13 + */ +$app->any('/issue-13', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([ 'customer' => [ 'id' => '12345', 'name' => 'Behat Testing API', @@ -163,38 +215,43 @@ ], ], ], - ]); + ])); + return $response->withHeader('Content-Type', 'application/json'); }); /** * Return a response with a custom reason phrase */ -$app->get('/customReasonPhrase', function(Application $app) { - return (new Response())->setStatusCode( - isset($_GET['code']) ? $_GET['code'] : 200, - isset($_GET['phrase']) ? $_GET['phrase'] : null +$app->get('/customReasonPhrase', function(Request $request, Response $response) : Response { + $params = $request->getQueryParams(); + + return $response->withStatus( + !empty($params['code']) ? (int) $params['code'] : 200, + !empty($params['phrase']) ? (string) $params['phrase'] : '' ); }); /** * Return a response with an empty array */ -$app->get('/emptyArray', function(Application $app) { - return new JsonResponse([]); +$app->get('/emptyArray', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode([])); + return $response->withHeader('Content-Type', 'application/json'); }); /** * Return a response with an empty object */ -$app->get('/emptyObject', function(Application $app) { - return new JsonResponse(new stdClass()); +$app->get('/emptyObject', function(Request $request, Response $response) : Response { + $response->getBody()->write((string) json_encode(new stdClass())); + return $response->withHeader('Content-Type', 'application/json'); }); /** * Return a response with 403 Forbidden */ -$app->get('/403', function(Application $app) { - return (new Response())->setStatusCode(403); +$app->get('/403', function(Request $request, Response $response) : Response { + return $response->withStatus(403); }); // Run the application diff --git a/features/built-in-custom-matcher-functions-failures.feature b/features/built-in-custom-matcher-functions-failures.feature index c89cdb4..f1cce3d 100644 --- a/features/built-in-custom-matcher-functions-failures.feature +++ b/features/built-in-custom-matcher-functions-failures.feature @@ -119,6 +119,31 @@ Feature: Test built in matcher functions failures Function "variableType" failed with error message: "Expected variable type "string", got "array".". """ + Scenario: Assert that @variableType with multiple types can fail + Given a file named "features/variable-type-multiple-types-failure.feature" with: + """ + Feature: Verify failure + Scenario: Use custom matcher function + Given the request body is: + ''' + { + "list": [1, 2, 3] + } + ''' + When I request "/echo?json" using HTTP POST + Then the response body contains JSON: + ''' + { + "list": "@variableType(string|int|double|object|null)" + } + ''' + """ + When I run "behat features/variable-type-multiple-types-failure.feature" + Then it should fail with: + """ + Function "variableType" failed with error message: "Expected variable type "string|integer|double|object|null", got "array".". + """ + Scenario: Assert that @regExp can fail Given a file named "features/reg-exp-failure.feature" with: """ diff --git a/features/built-in-custom-matcher-functions.feature b/features/built-in-custom-matcher-functions.feature index 1ffc221..6a11dee 100644 --- a/features/built-in-custom-matcher-functions.feature +++ b/features/built-in-custom-matcher-functions.feature @@ -93,6 +93,34 @@ Feature: Test built in matcher functions } } ''' + And the response body contains JSON: + ''' + { + "types": { + "string": "@variableType(any)", + "integer": "@variableType(any)", + "double": "@variableType(any)", + "array": "@variableType(any)", + "boolean": "@variableType(any)", + "null": "@variableType(any)", + "scalar": "@variableType(any)" + } + } + ''' + And the response body contains JSON: + ''' + { + "types": { + "string": "@variableType(int|bool|string)", + "integer": "@variableType(double|array|object|integer)", + "double": "@variableType(bool|double)", + "array": "@variableType(int|string|any)", + "boolean": "@variableType(double | int | bool)", + "null": "@variableType(string|int|null)", + "scalar": "@variableType(string|bool|scalar|array)" + } + } + ''' And the response body contains JSON: ''' { @@ -131,8 +159,8 @@ Feature: Test built in matcher functions When I run "behat features/custom-matcher-functions.feature" Then it should pass with: """ - ............. + ............... 1 scenario (1 passed) - 13 steps (13 passed) + 15 steps (15 passed) """ diff --git a/features/context.feature b/features/context.feature index 00022bc..b75c947 100644 --- a/features/context.feature +++ b/features/context.feature @@ -13,9 +13,11 @@ Feature: Client aware context class FeatureContext implements ApiClientAwareContext { private $client; + private $baseUri; - public function setClient(ClientInterface $client) { + public function setClient(ClientInterface $client, string $baseUri) { $this->client = $client; + $this->baseUri = $baseUri; } /** @@ -52,28 +54,3 @@ Feature: Client aware context 1 scenario (1 passed) 1 step (1 passed) """ - - Scenario: Host / port not connectable - Given a file named "behat.yml" with: - """ - default: - extensions: - Imbo\BehatApiExtension: - apiClient: - base_uri: http://localhost:9999 - """ - And a file named "features/client.feature" with: - """ - Feature: API client - In order to call the API - As feature runner - I need to be able to access the client - - Scenario: client is set - Then the client should be set - """ - When I run "behat -f progress features/client.feature" - Then it should fail with: - """ - Can't connect to base_uri: "http://localhost:9999". - """ diff --git a/features/manipulate-query-string.feature b/features/manipulate-query-string.feature new file mode 100644 index 0000000..d59a701 --- /dev/null +++ b/features/manipulate-query-string.feature @@ -0,0 +1,92 @@ +Feature: Manipulate query string + In order to test the extension + As a developer + I want to be able to test all available steps + + Background: + Given a file named "behat.yml" with: + """ + default: + formatters: + progress: ~ + extensions: + Imbo\BehatApiExtension: + apiClient: + base_uri: http://localhost:8080 + + suites: + default: + contexts: ['Imbo\BehatApiExtension\Context\ApiContext'] + """ + + Scenario: Set single parameters + Given a file named "features/query-params.feature" with: + """ + Feature: Set up the request + Scenario: Add query params + Given the query parameter "foo" is "bar" + Given the query parameter "bar" is "foo" + Given the query parameter "bar" is: + | value | + | foo | + | bar | + Given the following query parameters are set: + | name | value | + | foo | bar | + | bar | foo | + """ + When I run "behat features/query-params.feature" + Then it should pass with: + """ + .... + + 1 scenario (1 passed) + 4 steps (4 passed) + """ + + Scenario: Make a request with query parameters set + Given a file named "features/query-params.feature" with: + """ + Feature: Set up the request + Scenario: Add query params + Given the query parameter "p1" is "v1" + Given the query parameter "p2" is "v2" + Given the query parameter "p3" is "v3" + Given the query parameter "p4" is "v4" + Given the query parameter "p4" is "v5" + Given the query parameter "p1" is: + | value | + | v6 | + | v7 | + Given the query parameter "p2" is: + | value | + | v8 | + Given the following query parameters are set: + | name | value | + | p3 | v9 | + | p5 | v10 | + | p6 | v11 | + When I request "/requestInfo?p5=v12" + Then the response body contains JSON: + ''' + { + "_GET": { + "p1": ["v6", "v7"], + "p2": ["v8"], + "p3": "v9", + "p4": "v5", + "p5": "v10", + "p6": "v11" + } + } + ''' + + """ + When I run "behat features/query-params.feature" + Then it should pass with: + """ + .......... + + 1 scenario (1 passed) + 10 steps (10 passed) + """ \ No newline at end of file diff --git a/features/request-body.feature b/features/request-body.feature index b85d855..a016d25 100644 --- a/features/request-body.feature +++ b/features/request-body.feature @@ -59,7 +59,7 @@ Feature: Test steps to set a request body ''' some file ''' - And the "Content-Type" response header is "text/plain; charset=UTF-8" + And the "Content-Type" response header is "text/plain;charset=UTF-8" """ When I run "behat features/givens.feature" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2eb2714..ae88530 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,14 +1,23 @@ - + + + + src + + - - tests + + tests - - - - src - - diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..dbf7548 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ArrayContainsComparator.php b/src/ArrayContainsComparator.php index 4ec8b35..5d4b9a5 100644 --- a/src/ArrayContainsComparator.php +++ b/src/ArrayContainsComparator.php @@ -1,4 +1,4 @@ - */ class ArrayContainsComparator { /** @@ -17,7 +15,7 @@ class ArrayContainsComparator { * Keys are the names of the functions, and the values represent an invokable piece of code, be * it a function name or the name of an invokable class. * - * @var array + * @var array */ protected $functions = []; @@ -25,20 +23,8 @@ class ArrayContainsComparator { * Add a custom matcher function * * If an existing function exists with the same name it will be replaced - * - * @param string $name The name of the function, for instance "length" - * @param callable $callback The piece of callback code - * @throws InvalidArgumentException Throws an exception if the callback is not callable - * @return self */ - public function addFunction($name, $callback) { - if (!is_callable($callback)) { - throw new InvalidArgumentException(sprintf( - 'Callback provided for function "%s" is not callable.', - $name - )); - } - + public function addFunction(string $name, callable $callback) : self { $this->functions[$name] = $callback; return $this; @@ -46,11 +32,8 @@ public function addFunction($name, $callback) { /** * Get a matcher function by name - * - * @param string $name The name of the matcher function - * @return mixed */ - public function getMatcherFunction($name) { + public function getMatcherFunction(string $name) : callable { if (!isset($this->functions[$name])) { throw new InvalidArgumentException(sprintf( 'No matcher function registered for "%s".', @@ -67,13 +50,11 @@ public function getMatcherFunction($name) { * To clarify, the method (and other methods in the class) refers to "lists" and "objects". A * "list" is a numerically indexed array, and an "object" is an associative array. * - * @param array $needle The needle array - * @param array $haystack The haystack array - * @throws ArrayContainsComparatorException Throws an exception on error - * @return boolean + * @param array $needle + * @param array $haystack */ - public function compare(array $needle, array $haystack) { - $needleIsList = $this->arrayIsList($needle); + public function compare(array $needle, array $haystack) : bool { + $needleIsList = $this->arrayIsList($needle); $haystackIsList = $this->arrayIsList($haystack); // If the needle is a numerically indexed array, the haystack needs to be one as well @@ -82,7 +63,7 @@ public function compare(array $needle, array $haystack) { 'The needle is a list, while the haystack is not.', 0, null, $needle, $haystack ); - } else if ($needleIsList && $haystackIsList) { + } else if ($needleIsList) { // Both arrays are numerically indexed arrays return $this->inArray($needle, $haystack); } @@ -93,11 +74,11 @@ public function compare(array $needle, array $haystack) { // Check if the needle key refers to a specific array key in the haystack $match = []; - if (preg_match('/^(?.*?)\[(?[\d]+)\]$/', $key, $match)) { - $realKey = $match['key'] ?: null; - $index = (int) $match['index']; + if (preg_match('/^(?.*?)\[(?[\d]+)\]$/', (string) $key, $match)) { + $realKey = $match['key']; + $index = (int) $match['index']; - if ($realKey && !array_key_exists($realKey, $haystack)) { + if (!empty($realKey) && !array_key_exists($realKey, $haystack)) { // The key does not exist in the haystack throw new ArrayContainsComparatorException( sprintf('Haystack object is missing the "%s" key.', $realKey), 0, null, @@ -107,6 +88,7 @@ public function compare(array $needle, array $haystack) { // If a key has been specified, use that part of the haystack to compare against, // if no key exists, simply use the haystack as-is. + /** @var mixed */ $subHaystack = $realKey ? $haystack[$realKey] : $haystack; if (!is_array($subHaystack) || !$this->arrayIsList($subHaystack)) { @@ -124,6 +106,13 @@ public function compare(array $needle, array $haystack) { } if (is_array($value)) { + if (!is_array($subHaystack[$index])) { + throw new ArrayContainsComparatorException( + sprintf('Expected haystack element on index "%d" to be an array, got "%s".', $index, gettype($subHaystack[$index])), 0, null, + $needle, $haystack + ); + } + // The value is an array, do a recursive check $this->compare($value, $subHaystack[$index]); } else if (!$this->compareValues($value, $subHaystack[$index])) { @@ -145,6 +134,13 @@ public function compare(array $needle, array $haystack) { } if (is_array($value)) { + if (!is_array($haystack[$key])) { + throw new ArrayContainsComparatorException( + sprintf('Expected haystack value with key "%s" to be an array, got "%s".', $key, gettype($haystack[$key])), 0, null, + $needle, $haystack + ); + } + // If the value is an array, recurse $this->compare($value, $haystack[$key]); } else if (!$this->compareValues($value, $haystack[$key])) { @@ -168,10 +164,8 @@ public function compare(array $needle, array $haystack) { * * @param mixed $needleValue * @param mixed $haystackValue - * @throws ArrayContainsComparatorException - * @return boolean */ - protected function compareValues($needleValue, $haystackValue) { + protected function compareValues($needleValue, $haystackValue) : bool { $match = []; // List of available function names @@ -212,12 +206,10 @@ protected function compareValues($needleValue, $haystackValue) { /** * Make sure all values in the $needle array is present in the $haystack array * - * @param array $needle + * @param array $needle * @param array $haystack - * @throws ArrayContainsComparatorException - * @return boolean */ - protected function inArray(array $needle, array $haystack) { + protected function inArray(array $needle, array $haystack) : bool { // Loop over all the values in the needle array, and make sure each and every one is in some // way present in the haystack, in a recursive manner. foreach ($needle as $needleValue) { @@ -237,7 +229,7 @@ protected function inArray(array $needle, array $haystack) { ); } - $result = array_filter($listElementsInHaystack, function ($haystackListElement) use ($needleValue) { + $result = array_filter($listElementsInHaystack, function (array $haystackListElement) use ($needleValue) : bool { try { return $this->inArray($needleValue, $haystackListElement); } catch (ArrayContainsComparatorException $e) { @@ -257,7 +249,7 @@ protected function inArray(array $needle, array $haystack) { } else { // The needle value is an object, so we only want to compare it to objects in // the haystack - $objectElementsInHaystack = array_filter($haystack, function($element) { + $objectElementsInHaystack = array_filter($haystack, function($element) : bool { return is_array($element) && $this->arrayIsObject($element); }); @@ -268,7 +260,7 @@ protected function inArray(array $needle, array $haystack) { ); } - $result = array_filter($objectElementsInHaystack, function ($haystackObjectElement) use ($needleValue) { + $result = array_filter($objectElementsInHaystack, function (array $haystackObjectElement) use ($needleValue) { try { return $this->compare($needleValue, $haystackObjectElement); } catch (ArrayContainsComparatorException $e) { @@ -306,21 +298,19 @@ protected function inArray(array $needle, array $haystack) { /** * See if a PHP array is a JSON array - * - * @param array $array The array to check - * @return boolean True if the array is a numerically indexed array */ - protected function arrayIsList(array $array) { - return json_encode($array)[0] === '['; + protected function arrayIsList(array $array) : bool { + $encoded = json_encode($array); + + return false !== $encoded && $encoded[0] === '['; } /** * See if a PHP array is a JSON object - * - * @param array $array The array to check - * @return boolean True if the array is an associative array */ - protected function arrayIsObject(array $array) { - return json_encode($array)[0] === '{'; + protected function arrayIsObject(array $array) : bool { + $encoded = json_encode($array); + + return false !== $encoded && $encoded[0] === '{'; } } diff --git a/src/ArrayContainsComparator/Matcher/ArrayLength.php b/src/ArrayContainsComparator/Matcher/ArrayLength.php index 5ca0c72..88adc3e 100644 --- a/src/ArrayContainsComparator/Matcher/ArrayLength.php +++ b/src/ArrayContainsComparator/Matcher/ArrayLength.php @@ -1,25 +1,23 @@ - */ class ArrayLength { /** * Match the exact length of an array * * @param array $array An array - * @param int $length The expected exact length of $array + * @param int|string $length The expected exact length of $array * @throws InvalidArgumentException - * @return void */ - public function __invoke($array, $length) { + public function __invoke($array, $length) : bool { // Encode / decode to make sure we have a "list" - $array = json_decode(json_encode($array)); + /** @var mixed */ + $array = json_decode((string) json_encode($array)); if (!is_array($array)) { throw new InvalidArgumentException(sprintf( @@ -38,5 +36,7 @@ public function __invoke($array, $length) { $actualLength )); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/ArrayMaxLength.php b/src/ArrayContainsComparator/Matcher/ArrayMaxLength.php index e48ca00..f211e46 100644 --- a/src/ArrayContainsComparator/Matcher/ArrayMaxLength.php +++ b/src/ArrayContainsComparator/Matcher/ArrayMaxLength.php @@ -1,25 +1,23 @@ - */ class ArrayMaxLength { /** * Match the max length of an array * * @param array $array An array - * @param int $maxLength The expected maximum length of $array + * @param int|string $maxLength The expected maximum length of $array * @throws InvalidArgumentException - * @return void */ - public function __invoke($array, $maxLength) { + public function __invoke($array, $maxLength) : bool { // Encode / decode to make sure we have a "list" - $array = json_decode(json_encode($array)); + /** @var mixed */ + $array = json_decode((string) json_encode($array)); if (!is_array($array)) { throw new InvalidArgumentException(sprintf( @@ -38,5 +36,7 @@ public function __invoke($array, $maxLength) { $actualLength )); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/ArrayMinLength.php b/src/ArrayContainsComparator/Matcher/ArrayMinLength.php index 316fb5b..79f7208 100644 --- a/src/ArrayContainsComparator/Matcher/ArrayMinLength.php +++ b/src/ArrayContainsComparator/Matcher/ArrayMinLength.php @@ -1,25 +1,23 @@ - */ class ArrayMinLength { /** * Match the min length of an array * * @param array $array An array - * @param int $minLength The expected minimum length of $array + * @param int|string $minLength The expected minimum length of $array * @throws InvalidArgumentException - * @return void */ - public function __invoke($array, $minLength) { + public function __invoke($array, $minLength) : bool { // Encode / decode to make sure we have a "list" - $array = json_decode(json_encode($array)); + /** @var mixed */ + $array = json_decode((string) json_encode($array)); if (!is_array($array)) { throw new InvalidArgumentException(sprintf( @@ -38,5 +36,7 @@ public function __invoke($array, $minLength) { $actualLength )); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/GreaterThan.php b/src/ArrayContainsComparator/Matcher/GreaterThan.php index e35f277..85daa01 100644 --- a/src/ArrayContainsComparator/Matcher/GreaterThan.php +++ b/src/ArrayContainsComparator/Matcher/GreaterThan.php @@ -1,34 +1,31 @@ - */ class GreaterThan { /** * Match a numeric value * - * @param numeric $number A variable - * @param numeric $min The minimum value of $number + * @param mixed $number A variable + * @param mixed $min The minimum value of $number * @throws InvalidArgumentException - * @return void */ - public function __invoke($number, $min) { + public function __invoke($number, $min) : bool { if (!is_numeric($number)) { throw new InvalidArgumentException(sprintf( '"%s" is not numeric.', - $number + (string) $number )); } if (!is_numeric($min)) { throw new InvalidArgumentException(sprintf( '"%s" is not numeric.', - $min + (string) $min )); } @@ -39,5 +36,7 @@ public function __invoke($number, $min) { $min )); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/JWT.php b/src/ArrayContainsComparator/Matcher/JWT.php index 25e86e9..8f4ba22 100644 --- a/src/ArrayContainsComparator/Matcher/JWT.php +++ b/src/ArrayContainsComparator/Matcher/JWT.php @@ -1,4 +1,4 @@ - */ class JWT { /** @@ -21,7 +19,7 @@ class JWT { /** * JWT tokens present in the response body * - * @var array + * @var array */ private $jwtTokens = []; @@ -36,24 +34,14 @@ class JWT { 'HS512', ]; - /** - * Class constructor - * - * @param Comparator $comparator - */ public function __construct(Comparator $comparator) { $this->comparator = $comparator; } /** * Add a JWT token that can be matched - * - * @param string $name String identifying the token - * @param array $payload The payload - * @param string $secret The secret used to sign the token - * @return self */ - public function addToken($name, array $payload, $secret) { + public function addToken(string $name, array $payload, string $secret) : self { $this->jwtTokens[$name] = [ 'payload' => $payload, 'secret' => $secret, @@ -65,12 +53,9 @@ public function addToken($name, array $payload, $secret) { /** * Match an array against a JWT * - * @param string $jwt The encoded JWT - * @param string $name The identifier of the decoded data, added using the addToken method * @throws InvalidArgumentException - * @return void */ - public function __invoke($jwt, $name) { + public function __invoke(string $jwt, string $name) : bool { if (!isset($this->jwtTokens[$name])) { throw new InvalidArgumentException(sprintf('No JWT registered for "%s".', $name)); } @@ -81,5 +66,7 @@ public function __invoke($jwt, $name) { if (!$this->comparator->compare($token['payload'], $result)) { throw new InvalidArgumentException('JWT mismatch.'); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/LessThan.php b/src/ArrayContainsComparator/Matcher/LessThan.php index 70a1adc..66d42ab 100644 --- a/src/ArrayContainsComparator/Matcher/LessThan.php +++ b/src/ArrayContainsComparator/Matcher/LessThan.php @@ -1,34 +1,31 @@ - */ class LessThan { /** * Match a numeric value * - * @param numeric $number A variable - * @param numeric $max The max value of $number + * @param mixed $number A variable + * @param mixed $max The max value of $number * @throws InvalidArgumentException - * @return void */ - public function __invoke($number, $max) { + public function __invoke($number, $max) : bool { if (!is_numeric($number)) { throw new InvalidArgumentException(sprintf( '"%s" is not numeric.', - $number + (string) $number )); } if (!is_numeric($max)) { throw new InvalidArgumentException(sprintf( '"%s" is not numeric.', - $max + (string) $max )); } @@ -39,5 +36,7 @@ public function __invoke($number, $max) { $max )); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/RegExp.php b/src/ArrayContainsComparator/Matcher/RegExp.php index 0265e03..bbbcd5f 100644 --- a/src/ArrayContainsComparator/Matcher/RegExp.php +++ b/src/ArrayContainsComparator/Matcher/RegExp.php @@ -1,12 +1,10 @@ - */ class RegExp { /** @@ -15,9 +13,8 @@ class RegExp { * @param string|int|float $subject A string, integer or floating point value * @param string $pattern A valid regular expression pattern * @throws InvalidArgumentException - * @return void */ - public function __invoke($subject, $pattern) { + public function __invoke($subject, string $pattern) : bool { if (!in_array(gettype($subject), ['string', 'integer', 'double'])) { throw new InvalidArgumentException(sprintf( 'Regular expression matching can only be applied to strings, integers or doubles, got "%s".', @@ -26,7 +23,6 @@ public function __invoke($subject, $pattern) { } $subject = (string) $subject; - $pattern = (string) $pattern; if (!preg_match($pattern, $subject)) { throw new InvalidArgumentException(sprintf( @@ -35,5 +31,7 @@ public function __invoke($subject, $pattern) { $pattern )); } + + return true; } } diff --git a/src/ArrayContainsComparator/Matcher/VariableType.php b/src/ArrayContainsComparator/Matcher/VariableType.php index 1deb217..28cb9f8 100644 --- a/src/ArrayContainsComparator/Matcher/VariableType.php +++ b/src/ArrayContainsComparator/Matcher/VariableType.php @@ -1,12 +1,10 @@ - */ class VariableType { /** @@ -23,56 +21,71 @@ class VariableType { 'object', 'null', 'scalar', + 'any', ]; /** * Match a variable type * * @param mixed $variable A variable - * @param string $expectedType The expected type of $variable + * @param string $expectedTypes The expected types of $variable, separated by | * @throws InvalidArgumentException - * @return void */ - public function __invoke($variable, $expectedType) { - $expectedType = $this->normalizeType($expectedType); + public function __invoke($variable, string $expectedTypes) : bool { + $expectedTypes = $this->normalizeTypes($expectedTypes); - if (!in_array($expectedType, $this->validTypes)) { - throw new InvalidArgumentException(sprintf( - 'Unsupported variable type: "%s".', - $expectedType - )); + foreach ($expectedTypes as $expectedType) { + if (!in_array($expectedType, $this->validTypes)) { + throw new InvalidArgumentException(sprintf( + 'Unsupported variable type: "%s".', + $expectedType + )); + } } - if ($expectedType === 'scalar' && is_scalar($variable)) { - return; + if (in_array('any', $expectedTypes)) { + return true; } // Encode / decode the value to easier check for objects - $variable = json_decode(json_encode($variable)); + /** @var mixed */ + $variable = json_decode((string) json_encode($variable)); // Get the actual type of the value $actualType = strtolower(gettype($variable)); - if ($expectedType !== $actualType) { - throw new InvalidArgumentException(sprintf( - 'Expected variable type "%s", got "%s".', - $expectedType, - $actualType - )); + foreach ($expectedTypes as $expectedType) { + if ( + ($expectedType === 'scalar' && is_scalar($variable)) || + $expectedType === $actualType + ) { + return true; + } } + + throw new InvalidArgumentException(sprintf( + 'Expected variable type "%s", got "%s".', + join('|', $expectedTypes), + $actualType + )); } /** * Normalize the type * - * @param string $type The type from the scenario - * @return string Returns a normalized type + * @param string $types The types from the scenario + * @return string[] Returns an array of normalized types */ - protected function normalizeType($type) { - return strtolower(preg_replace( + protected function normalizeTypes(string $types) : array { + $types = array_map(function(string $type) : string { + return trim(strtolower($type)); + }, explode('|', $types)); + + /** @var string[] */ + return preg_replace( ['/^bool$/i', '/^int$/i', '/^float$/i'], ['boolean', 'integer', 'double'], - $type - )); + $types + ); } } diff --git a/src/Context/ApiClientAwareContext.php b/src/Context/ApiClientAwareContext.php index 852c375..e93a7e5 100644 --- a/src/Context/ApiClientAwareContext.php +++ b/src/Context/ApiClientAwareContext.php @@ -1,4 +1,4 @@ - */ interface ApiClientAwareContext extends Context { /** * Set the Guzzle client and create a pristine request instance * * @param ClientInterface $client + * @param string $baseUri * @return self */ - function setClient(ClientInterface $client); + function setClient(ClientInterface $client, string $baseUri); } diff --git a/src/Context/ApiContext.php b/src/Context/ApiContext.php index 56e3afc..baaa92b 100644 --- a/src/Context/ApiContext.php +++ b/src/Context/ApiContext.php @@ -1,36 +1,40 @@ - */ -class ApiContext implements ApiClientAwareContext, ArrayContainsComparatorAwareContext, SnippetAcceptingContext { +class ApiContext implements ApiClientAwareContext, ArrayContainsComparatorAwareContext { /** * Guzzle client * * @var ClientInterface */ - protected $client; + protected ClientInterface $client; + + /** + * Base URI used by the Guzzle client + */ + protected string $baseUri; /** * Request instance @@ -46,16 +50,26 @@ class ApiContext implements ApiClientAwareContext, ArrayContainsComparatorAwareC * * Options to send with the request. * - * @var array + * @var array{ + * auth: string[], + * form_params: array, + * multipart: array, + * query: array + * } */ - protected $requestOptions = []; + protected $requestOptions = [ + 'auth' => [], + 'form_params' => [], + 'multipart' => [], + 'query' => [], + ]; /** * Response instance * * The response object will be set once the request has been made. * - * @var ResponseInterface + * @var ?ResponseInterface */ protected $response; @@ -74,17 +88,37 @@ class ApiContext implements ApiClientAwareContext, ArrayContainsComparatorAwareC protected $forceHttpMethod = false; /** - * {@inheritdoc} + * Request / response history for the Guzzle Client + * + * @var array{request: RequestInterface, response: ResponseInterface}[] + */ + protected $clientHistory = []; + + /** + * Error message used when a required response instance if missing + * + * @var string + */ + protected $missingResponseError = 'The request has not been made yet, so no response object exists.'; + + /** + * Set the client instance + * + * @return self */ - public function setClient(ClientInterface $client) { + public function setClient(ClientInterface $client, string $baseUri) { $this->client = $client; - $this->request = new Request('GET', $client->getConfig('base_uri')); + $this->baseUri = $baseUri; + + $this->request = new Request('GET', $this->baseUri); return $this; } /** - * {@inheritdoc} + * Set the array contains comparator instance + * + * @return self */ public function setArrayContainsComparator(ArrayContainsComparator $comparator) { $this->arrayContainsComparator = $comparator; @@ -108,9 +142,15 @@ public function addMultipartFileToRequest($path, $partName) { throw new InvalidArgumentException(sprintf('File does not exist: "%s"', $path)); } + $contents = fopen($path, 'r'); + + if (false === $contents) { + throw new RuntimeException(sprintf('Unable to open file: %s', $path)); + } + return $this->addMultipartPart([ - 'name' => $partName, - 'contents' => fopen($path, 'r'), + 'name' => $partName, + 'contents' => $contents, 'filename' => basename($path), ]); } @@ -118,14 +158,10 @@ public function addMultipartFileToRequest($path, $partName) { /** * Add an element to the multipart array * - * @param array $part The part to add + * @param array{name: string, contents: resource|string, filename?: string} $part The part to add * @return self */ private function addMultipartPart($part) { - if (!isset($this->requestOptions['multipart'])) { - $this->requestOptions['multipart'] = []; - } - $this->requestOptions['multipart'][] = $part; return $this; @@ -140,10 +176,10 @@ private function addMultipartPart($part) { * @Given the following multipart form parameters are set: */ public function setRequestMultipartFormParams(TableNode $table) { - foreach ($table as $row) { + foreach ($this->getTableNodeHash($table) as $name => $value) { $this->addMultipartPart([ - 'name' => $row['name'], - 'contents' => $row['value'], + 'name' => $name, + 'contents' => $value, ]); } @@ -165,6 +201,67 @@ public function setBasicAuth($username, $password) { return $this; } + /** + * Send OAuth request using password grant and set Authorization header. + * + * @param string $path The path to get the token from + * @param string $username The username to authenticate with + * @param string $password The password to authenticate with + * @param string $scope The scope to authenticate in + * @param string $clientId The client_id to send + * @param string $clientSecret Optional client_secret to send + * @return self + * + * @Given I get an OAuth token using password grant from :path with :username and :password in scope :scope using client ID :clientId + * @Given I get an OAuth token using password grant from :path with :username and :password in scope :scope using client ID :clientId and client secret :clientSecret + */ + public function oauthWithPasswordGrantInScope($path, $username, $password, $scope, $clientId, $clientSecret = null) { + $this->requestOptions['form_params'] = array_filter([ + 'grant_type' => 'password', + 'username' => $username, + 'password' => $password, + 'scope' => $scope, + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + ]); + + $this->addRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + $this->setRequestPath($path); + $this->setRequestMethod('POST'); + $this->sendRequest(); + + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } + + $this->requestOptions['form_params'] = []; + + if (200 !== $this->response->getStatusCode()) { + throw new RuntimeException(sprintf( + 'Expected request for access token to pass, got status code %d with the following response: %s', + $this->response->getStatusCode(), + (string) $this->response->getBody() + )); + } + + /** @var stdClass */ + $body = $this->getResponseBody(); + + if (empty($body->access_token)) { + throw new RuntimeException(sprintf( + 'Missing access_token from response body: %s', + json_encode($body)) + ); + } + + $this->addRequestHeader( + 'Authorization', + sprintf('Bearer %s', (string) $body->access_token) + ); + + return $this; + } + /** * Set a HTTP request header * @@ -208,20 +305,18 @@ public function addRequestHeader($header, $value) { * @Given the following form parameters are set: */ public function setRequestFormParams(TableNode $table) { - if (!isset($this->requestOptions['form_params'])) { - $this->requestOptions['form_params'] = []; - } + /** @var array */ + $rows = $table->getColumnsHash(); - foreach ($table as $row) { - $name = $row['name']; + foreach ($rows as $row) { + $name = $row['name']; $value = $row['value']; if (isset($this->requestOptions['form_params'][$name]) && !is_array($this->requestOptions['form_params'][$name])) { - $this->requestOptions['form_params'][$name] = [$this->requestOptions['form_params'][$name]]; - } - - if (isset($this->requestOptions['form_params'][$name])) { - $this->requestOptions['form_params'][$name][] = $value; + $this->requestOptions['form_params'][$name] = [ + $this->requestOptions['form_params'][$name], + $value + ]; } else { $this->requestOptions['form_params'][$name] = $value; } @@ -247,7 +342,11 @@ public function setRequestBody($string) { ); } - $this->request = $this->request->withBody(Psr7\stream_for($string)); + if ($string instanceof PyStringNode) { + $string = (string) $string; + } + + $this->request = $this->request->withBody(Utils::streamFor($string)); return $this; } @@ -261,7 +360,7 @@ public function setRequestBody($string) { * mime type of the file. * * @param string $path Path to a file - * @throws InvalidArgumentException + * @throws InvalidArgumentException|RuntimeException * @return self * * @Given the request body contains :path @@ -275,10 +374,13 @@ public function setRequestBodyToFileResource($path) { throw new InvalidArgumentException(sprintf('File is not readable: "%s"', $path)); } + /** @var resource */ + $fp = fopen($path, 'r'); + // Set the Content-Type request header and the request body return $this - ->setRequestHeader('Content-Type', mime_content_type($path)) - ->setRequestBody(fopen($path, 'r')); + ->setRequestHeader('Content-Type', (string) mime_content_type($path)) + ->setRequestBody($fp); } /** @@ -307,6 +409,43 @@ public function addJwtToken($name, $secret, PyStringNode $payload) { return $this; } + /** + * Add a query parameter to the upcoming request + * + * @param string $name The name of the parameter + * @param string|TableNode $value The value to add + * @return self + * + * @Given the query parameter :name is :value + * @Given the query parameter :name is: + */ + public function setQueryStringParameter($name, $value) { + if ($value instanceof TableNode) { + /** @var string[] */ + $value = array_column($value->getHash(), 'value'); + } + + $this->requestOptions['query'][$name] = $value; + + return $this; + } + + /** + * Set multiple query parameters for the upcoming request + * + * @param TableNode $params The values to set + * @return self + * + * @Given the following query parameters are set: + */ + public function setQueryStringParameters(TableNode $params) { + foreach ($this->getTableNodeHash($params) as $name => $value) { + $this->requestOptions['query'][$name] = $value; + } + + return $this; + } + /** * Request a path * @@ -332,47 +471,53 @@ public function requestPath($path, $method = null) { /** * Assert the HTTP response code * - * @param int $code The HTTP response code + * @param int|string $code The HTTP response code * @throws AssertionFailedException - * @return void * * @Then the response code is :code */ - public function assertResponseCodeIs($code) { - $this->requireResponse(); + public function assertResponseCodeIs($code) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::same( $actual = $this->response->getStatusCode(), - $expected = $this->validateResponseCode($code), + $expected = $this->validateResponseCode((int) $code), sprintf('Expected response code %d, got %d.', $expected, $actual) ); } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** * Assert the HTTP response code is not a specific code * - * @param int $code The HTTP response code + * @param int|string $code The HTTP response code * @throws AssertionFailedException - * @return void * * @Then the response code is not :code */ - public function assertResponseCodeIsNot($code) { - $this->requireResponse(); + public function assertResponseCodeIsNot($code) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::notSame( $actual = $this->response->getStatusCode(), - $this->validateResponseCode($code), + $this->validateResponseCode((int) $code), sprintf('Did not expect response code %d.', $actual) ); } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -380,12 +525,13 @@ public function assertResponseCodeIsNot($code) { * * @param string $phrase Expected HTTP response reason phrase * @throws AssertionFailedException - * @return void * * @Then the response reason phrase is :phrase */ - public function assertResponseReasonPhraseIs($phrase) { - $this->requireResponse(); + public function assertResponseReasonPhraseIs($phrase) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::same($phrase, $actual = $this->response->getReasonPhrase(), sprintf( @@ -396,6 +542,8 @@ public function assertResponseReasonPhraseIs($phrase) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -403,12 +551,13 @@ public function assertResponseReasonPhraseIs($phrase) { * * @param string $phrase Reason phrase that the HTTP response should not equal * @throws AssertionFailedException - * @return void * * @Then the response reason phrase is not :phrase */ - public function assertResponseReasonPhraseIsNot($phrase) { - $this->requireResponse(); + public function assertResponseReasonPhraseIsNot($phrase) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::notSame($phrase, $this->response->getReasonPhrase(), sprintf( @@ -418,6 +567,8 @@ public function assertResponseReasonPhraseIsNot($phrase) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -425,12 +576,13 @@ public function assertResponseReasonPhraseIsNot($phrase) { * * @param string $pattern Regular expression pattern * @throws AssertionFailedException - * @return void * * @Then the response reason phrase matches :expression */ - public function assertResponseReasonPhraseMatches($pattern) { - $this->requireResponse(); + public function assertResponseReasonPhraseMatches($pattern) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::regex( @@ -444,6 +596,8 @@ public function assertResponseReasonPhraseMatches($pattern) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -451,12 +605,13 @@ public function assertResponseReasonPhraseMatches($pattern) { * * @param string $line Expected HTTP response status line * @throws AssertionFailedException - * @return void * * @Then the response status line is :line */ - public function assertResponseStatusLineIs($line) { - $this->requireResponse(); + public function assertResponseStatusLineIs($line) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { $actualStatusLine = sprintf( @@ -473,6 +628,8 @@ public function assertResponseStatusLineIs($line) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -480,12 +637,13 @@ public function assertResponseStatusLineIs($line) { * * @param string $line Value that the HTTP response status line must not equal * @throws AssertionFailedException - * @return void * * @Then the response status line is not :line */ - public function assertResponseStatusLineIsNot($line) { - $this->requireResponse(); + public function assertResponseStatusLineIsNot($line) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { $actualStatusLine = sprintf( @@ -501,6 +659,8 @@ public function assertResponseStatusLineIsNot($line) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -508,12 +668,13 @@ public function assertResponseStatusLineIsNot($line) { * * @param string $pattern Regular expression pattern * @throws AssertionFailedException - * @return void * * @Then the response status line matches :expression */ - public function assertResponseStatusLineMatches($pattern) { - $this->requireResponse(); + public function assertResponseStatusLineMatches($pattern) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { $actualStatusLine = sprintf( @@ -533,6 +694,8 @@ public function assertResponseStatusLineMatches($pattern) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -548,16 +711,19 @@ public function assertResponseStatusLineMatches($pattern) { * * @param string $group Name of the group that the response code should be in * @throws AssertionFailedException - * @return void * * @Then the response is :group */ - public function assertResponseIs($group) { - $this->requireResponse(); + public function assertResponseIs($group) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } + $range = $this->getResponseCodeGroupRange($group); + $code = $this->response->getStatusCode(); try { - Assertion::range($code = $this->response->getStatusCode(), $range['min'], $range['max']); + Assertion::range($code, $range['min'], $range['max']); } catch (AssertionFailure $e) { throw new AssertionFailedException(sprintf( 'Expected response group "%s", got "%s" (response code: %d).', @@ -566,6 +732,8 @@ public function assertResponseIs($group) { $code )); } + + return true; } /** @@ -581,16 +749,19 @@ public function assertResponseIs($group) { * * @param string $group Name of the group that the response code is not in * @throws AssertionFailedException - * @return void * * @Then the response is not :group */ - public function assertResponseIsNot($group) { + public function assertResponseIsNot($group) : bool { try { $this->assertResponseIs($group); } catch (AssertionFailedException $e) { // As expected, return - return; + return true; + } + + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); } throw new AssertionFailedException(sprintf( @@ -605,12 +776,13 @@ public function assertResponseIsNot($group) { * * @param string $header Then name of the header * @throws AssertionFailedException - * @return void * * @Then the :header response header exists */ - public function assertResponseHeaderExists($header) { - $this->requireResponse(); + public function assertResponseHeaderExists($header) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::true( @@ -620,6 +792,8 @@ public function assertResponseHeaderExists($header) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -627,12 +801,13 @@ public function assertResponseHeaderExists($header) { * * @param string $header Then name of the header * @throws AssertionFailedException - * @return void * * @Then the :header response header does not exist */ - public function assertResponseHeaderDoesNotExist($header) { - $this->requireResponse(); + public function assertResponseHeaderDoesNotExist($header) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::false( @@ -642,6 +817,8 @@ public function assertResponseHeaderDoesNotExist($header) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -650,12 +827,13 @@ public function assertResponseHeaderDoesNotExist($header) { * @param string $header The name of the header * @param string $value The value to compare with * @throws AssertionFailedException - * @return void * * @Then the :header response header is :value */ - public function assertResponseHeaderIs($header, $value) { - $this->requireResponse(); + public function assertResponseHeaderIs($header, $value) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::same( @@ -671,6 +849,8 @@ public function assertResponseHeaderIs($header, $value) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -679,12 +859,13 @@ public function assertResponseHeaderIs($header, $value) { * @param string $header The name of the header * @param string $value The value to compare with * @throws AssertionFailedException - * @return void * * @Then the :header response header is not :value */ - public function assertResponseHeaderIsNot($header, $value) { - $this->requireResponse(); + public function assertResponseHeaderIsNot($header, $value) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::notSame( @@ -699,6 +880,8 @@ public function assertResponseHeaderIsNot($header, $value) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -707,12 +890,13 @@ public function assertResponseHeaderIsNot($header, $value) { * @param string $header The name of the header * @param string $pattern The regular expression pattern * @throws AssertionFailedException - * @return void * * @Then the :header response header matches :pattern */ - public function assertResponseHeaderMatches($header, $pattern) { - $this->requireResponse(); + public function assertResponseHeaderMatches($header, $pattern) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } try { Assertion::regex( @@ -728,22 +912,23 @@ public function assertResponseHeaderMatches($header, $pattern) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** * Assert that the response body contains an empty JSON object * * @throws AssertionFailedException - * @return void * * @Then the response body is an empty JSON object */ - public function assertResponseBodyIsAnEmptyJsonObject() { + public function assertResponseBodyIsAnEmptyJsonObject() : bool { $this->requireResponse(); $body = $this->getResponseBody(); try { - Assertion::isInstanceOf($body, 'stdClass', 'Expected response body to be a JSON object.'); + Assertion::isInstanceOf($body, stdClass::class, 'Expected response body to be a JSON object.'); Assertion::same('{}', $encoded = json_encode($body, JSON_PRETTY_PRINT), sprintf( 'Expected response body to be an empty JSON object, got "%s".', $encoded @@ -751,17 +936,18 @@ public function assertResponseBodyIsAnEmptyJsonObject() { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** * Assert that the response body contains an empty JSON array * * @throws AssertionFailedException - * @return void * * @Then the response body is an empty JSON array */ - public function assertResponseBodyIsAnEmptyJsonArray() { + public function assertResponseBodyIsAnEmptyJsonArray() : bool { $this->requireResponse(); try { @@ -773,18 +959,19 @@ public function assertResponseBodyIsAnEmptyJsonArray() { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** * Assert that the response body contains an array with a specific length * - * @param int $length The length of the array + * @param int|string $length The length of the array * @throws AssertionFailedException - * @return void * * @Then the response body is a JSON array of length :length */ - public function assertResponseBodyJsonArrayLength($length) { + public function assertResponseBodyJsonArrayLength($length) : bool { $this->requireResponse(); $length = (int) $length; @@ -803,18 +990,19 @@ public function assertResponseBodyJsonArrayLength($length) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** * Assert that the response body contains an array with a length of at least a given length * - * @param int $length The length to use in the assertion + * @param int|string $length The length to use in the assertion * @throws AssertionFailedException - * @return void * * @Then the response body is a JSON array with a length of at least :length */ - public function assertResponseBodyJsonArrayMinLength($length) { + public function assertResponseBodyJsonArrayMinLength($length) : bool { $this->requireResponse(); $length = (int) $length; @@ -827,7 +1015,7 @@ public function assertResponseBodyJsonArrayMinLength($length) { sprintf( 'Expected response body to be a JSON array with at least %d entr%s, got %d: "%s".', $length, - (int) $length === 1 ? 'y' : 'ies', + $length === 1 ? 'y' : 'ies', $bodyLength, json_encode($body, JSON_PRETTY_PRINT) ) @@ -835,18 +1023,19 @@ public function assertResponseBodyJsonArrayMinLength($length) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** * Assert that the response body contains an array with a length of at most a given length * - * @param int $length The length to use in the assertion + * @param int|string $length The length to use in the assertion * @throws AssertionFailedException - * @return void * * @Then the response body is a JSON array with a length of at most :length */ - public function assertResponseBodyJsonArrayMaxLength($length) { + public function assertResponseBodyJsonArrayMaxLength($length) : bool { $this->requireResponse(); $length = (int) $length; @@ -859,7 +1048,7 @@ public function assertResponseBodyJsonArrayMaxLength($length) { sprintf( 'Expected response body to be a JSON array with at most %d entr%s, got %d: "%s".', $length, - (int) $length === 1 ? 'y' : 'ies', + $length === 1 ? 'y' : 'ies', $bodyLength, json_encode($body, JSON_PRETTY_PRINT) ) @@ -867,6 +1056,8 @@ public function assertResponseBodyJsonArrayMaxLength($length) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } @@ -875,12 +1066,14 @@ public function assertResponseBodyJsonArrayMaxLength($length) { * * @param PyStringNode $content The content to match the response body against * @throws AssertionFailedException - * @return void * * @Then the response body is: */ - public function assertResponseBodyIs(PyStringNode $content) { - $this->requireResponse(); + public function assertResponseBodyIs(PyStringNode $content) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } + $content = (string) $content; try { @@ -892,6 +1085,8 @@ public function assertResponseBodyIs(PyStringNode $content) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -899,12 +1094,14 @@ public function assertResponseBodyIs(PyStringNode $content) { * * @param PyStringNode $content The content that the response body should not match * @throws AssertionFailedException - * @return void * * @Then the response body is not: */ - public function assertResponseBodyIsNot(PyStringNode $content) { - $this->requireResponse(); + public function assertResponseBodyIsNot(PyStringNode $content) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } + $content = (string) $content; try { @@ -915,6 +1112,8 @@ public function assertResponseBodyIsNot(PyStringNode $content) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -922,12 +1121,14 @@ public function assertResponseBodyIsNot(PyStringNode $content) { * * @param PyStringNode $pattern The regular expression pattern to use for the match * @throws AssertionFailedException - * @return void * * @Then the response body matches: */ - public function assertResponseBodyMatches(PyStringNode $pattern) { - $this->requireResponse(); + public function assertResponseBodyMatches(PyStringNode $pattern) : bool { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } + $pattern = (string) $pattern; try { @@ -939,6 +1140,8 @@ public function assertResponseBodyMatches(PyStringNode $pattern) { } catch (AssertionFailure $e) { throw new AssertionFailedException($e->getMessage()); } + + return true; } /** @@ -946,18 +1149,18 @@ public function assertResponseBodyMatches(PyStringNode $pattern) { * * @param PyStringNode $contains * @throws AssertionFailedException - * @return void * * @Then the response body contains JSON: */ - public function assertResponseBodyContainsJson(PyStringNode $contains) { + public function assertResponseBodyContainsJson(PyStringNode $contains) : bool { $this->requireResponse(); // Decode the parameter to the step as an array and make sure it's valid JSON $contains = $this->jsonDecode((string) $contains); // Get the decoded response body and make sure it's decoded to an array - $body = json_decode(json_encode($this->getResponseBody()), true); + /** @var array */ + $body = json_decode((string) json_encode($this->getResponseBody()), true); try { // Compare the arrays, on error this will throw an exception @@ -967,6 +1170,8 @@ public function assertResponseBodyContainsJson(PyStringNode $contains) { 'Comparator did not return in a correct manner. Marking assertion as failed.' ); } + + return true; } /** @@ -1004,14 +1209,13 @@ protected function sendRequest() { } } - // Remove form_params from the options, otherwise Guzzle will throw an exception - unset($this->requestOptions['form_params']); + $this->requestOptions['form_params'] = []; } try { $this->response = $this->client->send( $this->request, - $this->requestOptions + array_filter($this->requestOptions) ); } catch (RequestException $e) { $this->response = $e->getResponse(); @@ -1029,7 +1233,7 @@ protected function sendRequest() { * * @throws RuntimeException */ - protected function requireResponse() { + protected function requireResponse() : void { if (!$this->response) { throw new RuntimeException('The request has not been made yet, so no response object exists.'); } @@ -1040,10 +1244,10 @@ protected function requireResponse() { * * @param string $group The name of the group * @throws InvalidArgumentException - * @return array An array with two keys, min and max, which represents the min and max values - * for $group + * @return array{min: int, max: int} An array with two keys, min and max, which represents the + * min and max values for $group */ - protected function getResponseCodeGroupRange($group) { + protected function getResponseCodeGroupRange(string $group) : array { switch ($group) { case 'informational': $min = 100; @@ -1081,9 +1285,7 @@ protected function getResponseCodeGroupRange($group) { * @param int $code The respose code * @return string */ - protected function getResponseGroup($code) { - $code = (int) $code; - + protected function getResponseGroup(int $code) : string { if ($code >= 500) { return 'server error'; } else if ($code >= 400) { @@ -1104,9 +1306,7 @@ protected function getResponseGroup($code) { * @throws InvalidArgumentException * @return int */ - protected function validateResponseCode($code) { - $code = (int) $code; - + protected function validateResponseCode(int $code) : int { try { Assertion::range($code, 100, 599, sprintf('Response code must be between 100 and 599, got %d.', $code)); } catch (AssertionFailure $e) { @@ -1122,9 +1322,9 @@ protected function validateResponseCode($code) { * @param string $path The path to request * @return self */ - protected function setRequestPath($path) { - // Resolve the path with the base_uri set in the client - $uri = Psr7\Uri::resolve($this->client->getConfig('base_uri'), Psr7\uri_for($path)); + protected function setRequestPath(string $path) { + $base = Utils::uriFor($this->baseUri); + $uri = UriResolver::resolve($base, Utils::uriFor($path)); $this->request = $this->request->withUri($uri); return $this; @@ -1134,12 +1334,12 @@ protected function setRequestPath($path) { * Update the HTTP method of the request * * @param string $method The HTTP method - * @param boolean $force Force the HTTP method. If set to false the method set CAN be + * @param bool $force Force the HTTP method. If set to false the method set CAN be * overridden (this occurs for instance when adding form parameters to the * request, and not specifying HTTP POST for the request) * @return self */ - protected function setRequestMethod($method, $force = true) { + protected function setRequestMethod(string $method, bool $force = true) { $this->request = $this->request->withMethod($method); $this->forceHttpMethod = $force; @@ -1150,9 +1350,14 @@ protected function setRequestMethod($method, $force = true) { * Get the JSON-encoded array or stdClass from the response body * * @throws InvalidArgumentException - * @return array|stdClass + * @return array|stdClass */ protected function getResponseBody() { + if (!$this->response) { + throw new RuntimeException($this->missingResponseError); + } + + /** @var mixed */ $body = json_decode((string) $this->response->getBody()); if (json_last_error() !== JSON_ERROR_NONE) { @@ -1161,6 +1366,7 @@ protected function getResponseBody() { throw new InvalidArgumentException('The response body does not contain a valid JSON array / object.'); } + /** @var array|stdClass */ return $body; } @@ -1170,8 +1376,10 @@ protected function getResponseBody() { * @throws InvalidArgumentException * @return array */ - protected function getResponseBodyArray() { - if (!is_array($body = $this->getResponseBody())) { + protected function getResponseBodyArray() : array { + $body = $this->getResponseBody(); + + if (!is_array($body)) { throw new InvalidArgumentException('The response body does not contain a valid JSON array.'); } @@ -1184,9 +1392,10 @@ protected function getResponseBodyArray() { * @param string $value The value to decode * @param string $errorMessage Optional error message * @throws InvalidArgumentException - * @return array + * @return array */ - protected function jsonDecode($value, $errorMessage = null) { + protected function jsonDecode(string $value, string $errorMessage = null) : array { + /** @var array */ $decoded = json_decode($value, true); if (json_last_error() !== JSON_ERROR_NONE) { @@ -1197,4 +1406,17 @@ protected function jsonDecode($value, $errorMessage = null) { return $decoded; } + + /** + * Get an associative array from the TableNode + * + * This method will effectively remove duplicates from TableNode + * + * @param TableNode $table + * @return array + */ + protected function getTableNodeHash(TableNode $table) : array { + /** @var array */ + return array_slice($table->getRowsHash(), 1); + } } diff --git a/src/Context/ArrayContainsComparatorAwareContext.php b/src/Context/ArrayContainsComparatorAwareContext.php index 64cb3e2..2f4ce8b 100644 --- a/src/Context/ArrayContainsComparatorAwareContext.php +++ b/src/Context/ArrayContainsComparatorAwareContext.php @@ -1,4 +1,4 @@ - */ interface ArrayContainsComparatorAwareContext extends Context { /** * Set the instance of the array contains comparator * - * @param ArrayContainsComparator $comparator * @return self */ function setArrayContainsComparator(ArrayContainsComparator $comparator); diff --git a/src/Context/Initializer/ApiClientAwareInitializer.php b/src/Context/Initializer/ApiClientAwareInitializer.php index 00e2cbf..60a507d 100644 --- a/src/Context/Initializer/ApiClientAwareInitializer.php +++ b/src/Context/Initializer/ApiClientAwareInitializer.php @@ -1,29 +1,27 @@ - */ class ApiClientAwareInitializer implements ContextInitializer { /** - * @var array Guzzle client configuration array + * @var array{base_uri?: string} Guzzle client configuration array + * @see http://docs.guzzlephp.org/ Check out the Guzzle docs for a complete overview of available configuration parameters */ - private $guzzleConfig = []; + private array $guzzleConfig = []; /** * Class constructor * - * @param array $guzzleConfig Guzzle client configuration array + * @param array{base_uri?: string} $guzzleConfig Guzzle client configuration array */ public function __construct(array $guzzleConfig) { $this->guzzleConfig = $guzzleConfig; @@ -33,48 +31,13 @@ public function __construct(array $guzzleConfig) { * Initialize the context * * Inject the Guzzle client if the context implements the ApiClientAwareContext interface - * - * @param Context $context */ - public function initializeContext(Context $context) { + public function initializeContext(Context $context) : void { if ($context instanceof ApiClientAwareContext) { - // Fetch base URI from the Guzzle client configuration, if it exists - $baseUri = !empty($this->guzzleConfig['base_uri']) ? $this->guzzleConfig['base_uri'] : null; - - if ($baseUri && !$this->validateConnection($baseUri)) { - throw new RuntimeException(sprintf('Can\'t connect to base_uri: "%s".', $baseUri)); - } - - $context->setClient(new Client($this->guzzleConfig)); + $context->setClient( + new Client($this->guzzleConfig), + $this->guzzleConfig['base_uri'] ?? '', + ); } } - - /** - * Validate a connection to the base URI - * - * @param string $baseUri - * @return boolean - */ - private function validateConnection($baseUri) { - $parts = parse_url($baseUri); - $host = $parts['host']; - $port = isset($parts['port']) ? $parts['port'] : ($parts['scheme'] === 'https' ? 443 : 80); - - set_error_handler(function () { - return true; - }); - - $resource = fsockopen($host, $port); - restore_error_handler(); - - if ($resource === false) { - // Can't connect - return false; - } - - // Connection successful, close connection - fclose($resource); - - return true; - } } diff --git a/src/Context/Initializer/ArrayContainsComparatorAwareInitializer.php b/src/Context/Initializer/ArrayContainsComparatorAwareInitializer.php index a520c47..173d4ea 100644 --- a/src/Context/Initializer/ArrayContainsComparatorAwareInitializer.php +++ b/src/Context/Initializer/ArrayContainsComparatorAwareInitializer.php @@ -1,4 +1,4 @@ - */ class ArrayContainsComparatorAwareInitializer implements ContextInitializer { /** @@ -18,11 +16,6 @@ class ArrayContainsComparatorAwareInitializer implements ContextInitializer { */ private $comparator; - /** - * Class constructor - * - * @param Comparator $comparator - */ public function __construct(Comparator $comparator) { $comparator ->addFunction('arrayLength', new Matcher\ArrayLength()) @@ -37,12 +30,7 @@ public function __construct(Comparator $comparator) { $this->comparator = $comparator; } - /** - * Initialize the context - * - * @param Context $context - */ - public function initializeContext(Context $context) { + public function initializeContext(Context $context) : void { if ($context instanceof ArrayContainsComparatorAwareContext) { $context->setArrayContainsComparator($this->comparator); } diff --git a/src/Exception/ArrayContainsComparatorException.php b/src/Exception/ArrayContainsComparatorException.php index 0c6bdb0..3390753 100644 --- a/src/Exception/ArrayContainsComparatorException.php +++ b/src/Exception/ArrayContainsComparatorException.php @@ -1,27 +1,22 @@ - */ class ArrayContainsComparatorException extends AssertionFailedException { /** * Class constructor * - * @param string $message Exception message - * @param int $code Exception code - * @param Exception $previous Previous exception in the stack - * @param mixed $needle The needle in the comparison - * @param mixed $haystack The haystack in the comparison + * @param string $message + * @param int $code + * @param Exception $previous + * @param mixed $needle + * @param mixed $haystack */ - public function __construct($message, $code = 0, Exception $previous = null, $needle = null, $haystack = null) { - // Reusable line of ='s - $line = str_repeat('=', 80); - + public function __construct(string $message, int $code = 0, Exception $previous = null, $needle = null, $haystack = null) { // Format the error message $message .= PHP_EOL . PHP_EOL . sprintf(<< */ class AssertionFailedException extends Exception {} diff --git a/src/ServiceContainer/BehatApiExtension.php b/src/ServiceContainer/BehatApiExtension.php index 6f82c71..4d9ece1 100644 --- a/src/ServiceContainer/BehatApiExtension.php +++ b/src/ServiceContainer/BehatApiExtension.php @@ -1,4 +1,4 @@ - */ class BehatApiExtension implements ExtensionInterface { /** @@ -50,25 +48,16 @@ class BehatApiExtension implements ExtensionInterface { */ const CONFIG_KEY = 'api_extension'; - /** - * {@inheritdoc} - */ - public function getConfigKey() { + public function getConfigKey() : string { return self::CONFIG_KEY; } /** - * {@inheritdoc} * @codeCoverageIgnore */ - public function initialize(ExtensionManager $extensionManager) { - // Not used - } + public function initialize(ExtensionManager $extensionManager) : void {} - /** - * {@inheritdoc} - */ - public function configure(ArrayNodeDefinition $builder) { + public function configure(ArrayNodeDefinition $builder) : void { $builder ->children() ->arrayNode('apiClient') @@ -78,17 +67,15 @@ public function configure(ArrayNodeDefinition $builder) { ->scalarNode('base_uri') ->isRequired() ->cannotBeEmpty() - ->defaultValue('http://localhost:8080') - ->end() - ->end(); + ->defaultValue('http://localhost:8080'); } /** - * {@inheritdoc} * @codeCoverageIgnore + * @param array $config Guzzle client configuration array + * @see http://docs.guzzlephp.org/ Check out the Guzzle docs for a complete overview of available configuration parameters */ - public function load(ContainerBuilder $container, array $config) { - // Client initializer definition + public function load(ContainerBuilder $container, array $config) : void { $clientInitializerDefinition = new Definition( ApiClientAwareInitializer::class, [ @@ -96,11 +83,7 @@ public function load(ContainerBuilder $container, array $config) { ] ); $clientInitializerDefinition->addTag(ContextExtension::INITIALIZER_TAG); - - // Definition for the array contains comparator $comparatorDefinition = new Definition(ArrayContainsComparator::class); - - // Comparator initializer definition $comparatorInitializerDefinition = new Definition( ArrayContainsComparatorAwareInitializer::class, [ @@ -109,17 +92,13 @@ public function load(ContainerBuilder $container, array $config) { ); $comparatorInitializerDefinition->addTag(ContextExtension::INITIALIZER_TAG); - // Add all definitions to the container $container->setDefinition(self::APICLIENT_INITIALIZER_SERVICE_ID, $clientInitializerDefinition); $container->setDefinition(self::COMPARATOR_SERVICE_ID, $comparatorDefinition); $container->setDefinition(self::COMPARATOR_INITIALIZER_SERVICE_ID, $comparatorInitializerDefinition); } /** - * {@inheritdoc} * @codeCoverageIgnore */ - public function process(ContainerBuilder $container) { - - } + public function process(ContainerBuilder $container) : void {} } diff --git a/tests/ArrayContainsComparator/Matcher/ArrayLengthTest.php b/tests/ArrayContainsComparator/Matcher/ArrayLengthTest.php index a93cb7d..2e507f0 100644 --- a/tests/ArrayContainsComparator/Matcher/ArrayLengthTest.php +++ b/tests/ArrayContainsComparator/Matcher/ArrayLengthTest.php @@ -1,31 +1,24 @@ -matcher = new ArrayLength(); } /** - * Data provider - * - * @return array[] + * @return array{list: int[], length: int}[] */ - public function getArraysAndLengths() { + public function getArraysAndLengths() : array { return [ [ 'list' => [], @@ -43,11 +36,9 @@ public function getArraysAndLengths() { } /** - * Data provider - * - * @return array[] + * @return array{value: int|string|array, message: string}[] */ - public function getInvalidValues() { + public function getInvalidValues() : array { return [ [ 'value' => 123, @@ -65,11 +56,9 @@ public function getInvalidValues() { } /** - * Data provider - * - * @return array[] + * @return array{array: int[], maxLength: int, message: string}[] */ - public function getValuesThatFail() { + public function getValuesThatFail() : array { return [ [ 'array' => [1, 2], @@ -87,28 +76,24 @@ public function getValuesThatFail() { /** * @dataProvider getArraysAndLengths * @covers ::__invoke - * - * @param array $array - * @param int $length + * @param int[] $array */ - public function testCanMatchLengthOfArrays(array $array, $length) { + public function testCanMatchLengthOfArrays(array $array, int $length) : void { $matcher = $this->matcher; - $this->assertNull( + $this->assertTrue( $matcher($array, $length), - 'Matcher is supposed to return null.' + 'Matcher is supposed to return true.' ); } /** * @dataProvider getInvalidValues * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param mixed $value - * @param string $message + * @param int|string|array $value */ - public function testThrowsExceptionWhenMatchingLengthAgainstAnythingOtherThanAnArray($value, $message) { + public function testThrowsExceptionWhenMatchingLengthAgainstAnythingOtherThanAnArray($value, string $message) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($message); $matcher = $this->matcher; $matcher($value, 123); @@ -117,13 +102,10 @@ public function testThrowsExceptionWhenMatchingLengthAgainstAnythingOtherThanAnA /** * @dataProvider getValuesThatFail * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param array $array - * @param int $length - * @param string $message + * @param int[] $array */ - public function testThrowsExceptionWhenLengthIsNotCorrect(array $array, $length, $message) { + public function testThrowsExceptionWhenLengthIsNotCorrect(array $array, int $length, string $message) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($message); $matcher = $this->matcher; $matcher($array, $length); diff --git a/tests/ArrayContainsComparator/Matcher/ArrayMaxLengthTest.php b/tests/ArrayContainsComparator/Matcher/ArrayMaxLengthTest.php index b4f6096..73ae043 100644 --- a/tests/ArrayContainsComparator/Matcher/ArrayMaxLengthTest.php +++ b/tests/ArrayContainsComparator/Matcher/ArrayMaxLengthTest.php @@ -1,31 +1,24 @@ -matcher = new ArrayMaxLength(); } /** - * Data provider - * - * @return array[] + * @return array{list: int[], length: int}[] */ - public function getArraysAndLengths() { + public function getArraysAndLengths() : array { return [ [ 'list' => [], @@ -43,11 +36,9 @@ public function getArraysAndLengths() { } /** - * Data provider - * - * @return array[] + * @return array{value: int|string|array, message: string}[] */ - public function getInvalidValues() { + public function getInvalidValues() : array { return [ [ 'value' => 123, @@ -65,11 +56,9 @@ public function getInvalidValues() { } /** - * Data provider - * - * @return array[] + * @return array{array: int[], maxLength: int, message: string}[] */ - public function getValuesThatFail() { + public function getValuesThatFail() : array { return [ [ 'array' => [1, 2], @@ -87,27 +76,23 @@ public function getValuesThatFail() { /** * @dataProvider getArraysAndLengths * @covers ::__invoke - * - * @param array $array - * @param int $length + * @param int[] $array */ - public function testCanMatchMaxLengthOfArrays(array $array, $length) { + public function testCanMatchMaxLengthOfArrays(array $array, int $length) : void { $matcher = $this->matcher; - $this->assertNull( + $this->assertTrue( $matcher($array, $length), - 'Matcher is supposed to return null.' + 'Matcher is supposed to return true.' ); } /** * @dataProvider getInvalidValues * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param mixed $value - * @param string $message + * @param int|string|array $value */ - public function testThrowsExceptionWhenMatchingAgainstAnythingOtherThanAnArray($value, $message) { + public function testThrowsExceptionWhenMatchingAgainstAnythingOtherThanAnArray($value, string $message) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($message); $matcher = $this->matcher; $matcher($value, 123); @@ -116,13 +101,10 @@ public function testThrowsExceptionWhenMatchingAgainstAnythingOtherThanAnArray($ /** * @dataProvider getValuesThatFail * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param array $array - * @param int $maxLength - * @param string $message + * @param int[] $array */ - public function testThrowsExceptionWhenLengthIsTooShort(array $array, $maxLength, $message) { + public function testThrowsExceptionWhenLengthIsTooShort(array $array, int $maxLength, string $message) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($message); $matcher = $this->matcher; $matcher($array, $maxLength); diff --git a/tests/ArrayContainsComparator/Matcher/ArrayMinLengthTest.php b/tests/ArrayContainsComparator/Matcher/ArrayMinLengthTest.php index e61718b..9b46561 100644 --- a/tests/ArrayContainsComparator/Matcher/ArrayMinLengthTest.php +++ b/tests/ArrayContainsComparator/Matcher/ArrayMinLengthTest.php @@ -1,31 +1,24 @@ -matcher = new ArrayMinLength(); } /** - * Data provider - * - * @return array[] + * @return array{list: int[], min: int}[] */ - public function getArraysAndMinLengths() { + public function getArraysAndMinLengths() : array { return [ [ 'list' => [], @@ -43,11 +36,9 @@ public function getArraysAndMinLengths() { } /** - * Data provider - * - * @return array[] + * @return array{value: int|string|array, message: string}[] */ - public function getInvalidValues() { + public function getInvalidValues() : array { return [ [ 'value' => 123, @@ -65,11 +56,9 @@ public function getInvalidValues() { } /** - * Data provider - * - * @return array[] + * @return array{array: int[], minLength: int, message: string}[] */ - public function getValuesThatFail() { + public function getValuesThatFail() : array { return [ [ 'array' => [], @@ -87,27 +76,23 @@ public function getValuesThatFail() { /** * @dataProvider getArraysAndMinLengths * @covers ::__invoke - * - * @param array $array - * @param int $min + * @param int[] $array */ - public function testCanMatchMinLengthOfArrays(array $array, $min) { + public function testCanMatchMinLengthOfArrays(array $array, int $min) : void { $matcher = $this->matcher; - $this->assertNull( + $this->assertTrue( $matcher($array, $min), - 'Matcher is supposed to return null.' + 'Matcher is supposed to return true.' ); } /** * @dataProvider getInvalidValues * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param mixed $value - * @param string $message + * @param int|string|array $value */ - public function testThrowsExceptionWhenMatchingAgainstAnythingOtherThanAnArray($value, $message) { + public function testThrowsExceptionWhenMatchingAgainstAnythingOtherThanAnArray($value, string $message) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($message); $matcher = $this->matcher; $matcher($value, 123); @@ -116,13 +101,10 @@ public function testThrowsExceptionWhenMatchingAgainstAnythingOtherThanAnArray($ /** * @dataProvider getValuesThatFail * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param array $array - * @param int $minLength - * @param string $message + * @param int[] $array */ - public function testThrowsExceptionWhenLengthIsTooLong(array $array, $minLength, $message) { + public function testThrowsExceptionWhenLengthIsTooLong(array $array, int $minLength, string $message) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($message); $matcher = $this->matcher; $matcher($array, $minLength); diff --git a/tests/ArrayContainsComparator/Matcher/GreaterThanTest.php b/tests/ArrayContainsComparator/Matcher/GreaterThanTest.php index e7f1dc2..d99735e 100644 --- a/tests/ArrayContainsComparator/Matcher/GreaterThanTest.php +++ b/tests/ArrayContainsComparator/Matcher/GreaterThanTest.php @@ -1,31 +1,24 @@ -matcher = new GreaterThan(); } /** - * Data provider - * - * @return array[] + * @return array{number: int|float|string, min: int|float|string}[] */ - public function getValuesForMatching() { + public function getValuesForMatching() : array { return [ 'integer' => [ 'number' => 2, @@ -43,11 +36,9 @@ public function getValuesForMatching() { } /** - * Data provider - * - * @return array[] + * @return array{number: int|string|float, min: int|string|float, errorMessage: string}[] */ - public function getFailingValues() { + public function getFailingValues() : array { return [ [ 'number' => 123, @@ -75,50 +66,47 @@ public function getFailingValues() { /** * @dataProvider getValuesForMatching * @covers ::__invoke - * - * @param numeric $number - * @param numeric $min + * @param int|float|string $number + * @param int|float|string $min */ - public function testCanCompareValuesOfType($number, $min) { + public function testCanCompareValuesOfType($number, $min) : void { $matcher = $this->matcher; - $this->assertNull( + $this->assertTrue( $matcher($number, $min), - 'Matcher is supposed to return null.' + 'Matcher is supposed to return true.' ); } /** * @covers ::__invoke - * @expectedException InvalidArgumentException - * @expectedExceptionMessage "foo" is not numeric. */ - public function testThrowsExceptionIfNumberIsNotNumeric() { + public function testThrowsExceptionIfNumberIsNotNumeric() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"foo" is not numeric.'); $matcher('foo', 123); } /** * @covers ::__invoke - * @expectedException InvalidArgumentException - * @expectedExceptionMessage "foo" is not numeric. */ - public function testThrowsExceptionIfMinimumNumberIsNotNumeric() { + public function testThrowsExceptionIfMinimumNumberIsNotNumeric() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"foo" is not numeric.'); $matcher(123, 'foo'); } /** * @dataProvider getFailingValues * @covers ::__invoke - * @expectedException InvalidArgumentException - * - * @param numeric $number - * @param numeric $min - * @param string $errorMessage + * @param int|string|float $number + * @param int|string|float $min */ - public function testThrowsExceptionWhenComparisonFails($number, $min, $errorMessage) { - $this->expectExceptionMessage($errorMessage); + public function testThrowsExceptionWhenComparisonFails($number, $min, string $errorMessage) : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage($errorMessage); $matcher($number, $min); } } diff --git a/tests/ArrayContainsComparator/Matcher/JWTTest.php b/tests/ArrayContainsComparator/Matcher/JWTTest.php index d52cc81..6e756ce 100644 --- a/tests/ArrayContainsComparator/Matcher/JWTTest.php +++ b/tests/ArrayContainsComparator/Matcher/JWTTest.php @@ -1,33 +1,26 @@ -matcher = new JWT(new ArrayContainsComparator()); } /** - * Data provider - * - * @return array[] + * @return array{jwt: string, name: string, payload: array, secret: string}[] */ - public function getJwt() { + public function getJwt() : array { return [ [ 'jwt' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ', @@ -51,23 +44,23 @@ public function getJwt() { } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage No JWT registered for "some name". * @covers ::__invoke */ - public function testThrowsExceptionWhenMatchingAgainstJwtThatDoesNotExist() { + public function testThrowsExceptionWhenMatchingAgainstJwtThatDoesNotExist() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No JWT registered for "some name".'); $matcher('some jwt', 'some name'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException - * @expectedExceptionMessage Haystack object is missing the "some" key. * @covers ::addToken * @covers ::__invoke */ - public function testThrowsExceptionWhenJwtDoesNotMatch() { - $matcher = $this->matcher->addToken('some name', ['some' => 'data'], 'secret', 'HS256'); + public function testThrowsExceptionWhenJwtDoesNotMatch() : void { + $matcher = $this->matcher->addToken('some name', ['some' => 'data'], 'secret'); + $this->expectException(ArrayContainsComparatorException::class); + $this->expectExceptionMessage('Haystack object is missing the "some" key.'); $matcher( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ', 'some name' @@ -77,27 +70,21 @@ public function testThrowsExceptionWhenJwtDoesNotMatch() { /** * @covers ::__invoke * @dataProvider getJwt - * - * @param string $jwt - * @param string $name - * @param array $payload - * @param string $secret + * @param array $payload */ - public function testCanMatchJwt($jwt, $name, array $payload, $secret) { + public function testCanMatchJwt(string $jwt, string $name, array $payload, string $secret) : void { $matcher = $this->matcher->addToken($name, $payload, $secret); - $matcher( + $this->assertTrue($matcher( $jwt, $name - ); + )); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage JWT mismatch. * @covers ::__construct * @covers ::__invoke */ - public function testThrowsExceptionWhenComparatorDoesNotReturnSuccess() { + public function testThrowsExceptionWhenComparatorDoesNotReturnSuccess() : void { $comparator = $this->createConfiguredMock(ArrayContainsComparator::class, [ 'compare' => false, ]); @@ -110,7 +97,8 @@ public function testThrowsExceptionWhenComparatorDoesNotReturnSuccess() { ], 'secret' ); - + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('JWT mismatch.'); $matcher( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ', 'token' diff --git a/tests/ArrayContainsComparator/Matcher/LessThanTest.php b/tests/ArrayContainsComparator/Matcher/LessThanTest.php index 353016c..bdd41b1 100644 --- a/tests/ArrayContainsComparator/Matcher/LessThanTest.php +++ b/tests/ArrayContainsComparator/Matcher/LessThanTest.php @@ -1,31 +1,24 @@ -matcher = new LessThan(); } /** - * Data provider - * - * @return array[] + * @return array{number: int|string, max: int|string|float}[] */ - public function getValuesForMatching() { + public function getValuesForMatching() : array { return [ 'integer' => [ 'number' => 1, @@ -43,11 +36,9 @@ public function getValuesForMatching() { } /** - * Data provider - * - * @return array[] + * @return array{number: int|float|string, max: int|float|string, errorMessage: string}[] */ - public function getFailingValues() { + public function getFailingValues() : array { return [ [ 'number' => 123, @@ -75,48 +66,46 @@ public function getFailingValues() { /** * @dataProvider getValuesForMatching * @covers ::__invoke - * - * @param numeric $number - * @param numeric $max + * @param int|string $number + * @param int|string|float $max */ - public function testCanCompareValuesOfType($number, $max) { + public function testCanCompareValuesOfType($number, $max) : void { $matcher = $this->matcher; - $this->assertNull( + $this->assertTrue( $matcher($number, $max), - 'Matcher is supposed to return null.' + 'Matcher is supposed to return true.' ); } /** * @covers ::__invoke - * @expectedException InvalidArgumentException - * @expectedExceptionMessage "foo" is not numeric. */ - public function testThrowsExceptionIfNumberIsNotNumeric() { + public function testThrowsExceptionIfNumberIsNotNumeric() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"foo" is not numeric.'); $matcher('foo', 123); } /** * @covers ::__invoke - * @expectedException InvalidArgumentException - * @expectedExceptionMessage "foo" is not numeric. */ - public function testThrowsExceptionIfMaximumNumberIsNotNumeric() { + public function testThrowsExceptionIfMaximumNumberIsNotNumeric() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"foo" is not numeric.'); $matcher(123, 'foo'); } /** * @dataProvider getFailingValues * @covers ::__invoke - * @expectedException InvalidArgumentException + * @param int|float|string $number + * @param int|float|string $max * - * @param numeric $number - * @param numeric $max - * @param string $errorMessage */ - public function testThrowsExceptionWhenComparisonFails($number, $max, $errorMessage) { + public function testThrowsExceptionWhenComparisonFails($number, $max, string $errorMessage) : void { + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage($errorMessage); $matcher = $this->matcher; $matcher($number, $max); diff --git a/tests/ArrayContainsComparator/Matcher/RegExpTest.php b/tests/ArrayContainsComparator/Matcher/RegExpTest.php index e449d7a..136646e 100644 --- a/tests/ArrayContainsComparator/Matcher/RegExpTest.php +++ b/tests/ArrayContainsComparator/Matcher/RegExpTest.php @@ -1,31 +1,24 @@ -matcher = new RegExp(); } /** - * Data provider - * - * @return array[] + * @return array */ - public function getSubjectsAndPatterns() { + public function getSubjectsAndPatterns() : array { return [ 'a regular string' => [ 'subject' => 'some string', @@ -43,33 +36,32 @@ public function getSubjectsAndPatterns() { } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Regular expression matching can only be applied to strings, integers or doubles, got "boolean". * @covers ::__invoke */ - public function testThrowsExceptionIfSubjectIsNotASupportedVariableType() { + public function testThrowsExceptionIfSubjectIsNotASupportedVariableType() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Regular expression matching can only be applied to strings, integers or doubles, got "boolean".'); $matcher(true, '/true/'); } /** * @dataProvider getSubjectsAndPatterns * @covers ::__invoke - * @param scalar $subject - * @param string $pattern + * @param float|int|string $subject */ - public function testCanMatchRegularExpressionPatternsAgainst($subject, $pattern) { + public function testCanMatchRegularExpressionPatternsAgainst($subject, string $pattern) : void { $matcher = $this->matcher; - $matcher($subject, $pattern); + $this->assertTrue($matcher($subject, $pattern)); } /** * @covers ::__invoke - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Subject "some string" did not match pattern "/SOME STRING/". */ - public function testThrowsExceptionIfPatternDoesNotMatchSubject() { + public function testThrowsExceptionIfPatternDoesNotMatchSubject() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Subject "some string" did not match pattern "/SOME STRING/".'); $matcher('some string', '/SOME STRING/'); } } diff --git a/tests/ArrayContainsComparator/Matcher/VariableTypeTest.php b/tests/ArrayContainsComparator/Matcher/VariableTypeTest.php index f5559b9..8d85463 100644 --- a/tests/ArrayContainsComparator/Matcher/VariableTypeTest.php +++ b/tests/ArrayContainsComparator/Matcher/VariableTypeTest.php @@ -1,31 +1,24 @@ -matcher = new VariableType(); } /** - * Data provider - * - * @return array[] + * @return array */ - public function getValuesAndTypes() { + public function getValuesAndTypes() : array { return [ 'int' => [ 'value' => 1, @@ -95,15 +88,49 @@ public function getValuesAndTypes() { 'value' => ['foo' => 'bar'], 'type' => 'object', ], + 'boolean (any)' => [ + 'value' => true, + 'type' => 'any', + ], + 'integer (any)' => [ + 'value' => 123, + 'type' => 'any', + ], + 'double (any)' => [ + 'value' => 1.1, + 'type' => 'any', + ], + 'string (any)' => [ + 'value' => 'some string', + 'type' => 'any', + ], + 'array (any)' => [ + 'value' => [1, 2, 3], + 'type' => 'any', + ], + 'object (any)' => [ + 'value' => ['foo' => 'bar'], + 'type' => 'any', + ], + 'int (multiple)' => [ + 'value' => 1, + 'type' => 'string|array|integer', + ], + 'integer (multiple)' => [ + 'value' => 1, + 'type' => 'int|bool|double', + ], + 'string (multiple)' => [ + 'value' => 'some string', + 'type' => 'integer | bool | array | string', // spaces are intentional + ], ]; } /** - * Data provider - * - * @return array[] + * @return array{value: mixed, type: string, message: string}[] */ - public function getInvalidMatches() { + public function getInvalidMatches() : array { return [ [ 'value' => 123, @@ -131,41 +158,36 @@ public function getInvalidMatches() { /** * @dataProvider getValuesAndTypes * @covers ::__invoke - * @covers ::normalizeType - * + * @covers ::normalizeTypes * @param mixed $value - * @param string $type */ - public function testCanMatchValuesOfType($value, $type) { + public function testCanMatchValuesOfType($value, string $type) : void { $matcher = $this->matcher; - $this->assertNull( + $this->assertTrue( $matcher($value, $type), - 'Matcher is supposed to return null.' + 'Matcher is supposed to return true.' ); } /** * @covers ::__invoke - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Unsupported variable type: "resource". */ - public function testThrowsExceptionWhenGivenInvalidType() { + public function testThrowsExceptionWhenGivenInvalidType() : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unsupported variable type: "resource".'); $matcher('foo', 'resource'); } /** * @dataProvider getInvalidMatches * @covers ::__invoke - * @expectedException InvalidArgumentException - * * @param mixed $value - * @param string $type - * @param string $message */ - public function testThrowsExceptionWhenTypeOfValueDoesNotMatchExpectedType($value, $type, $message) { - $this->expectExceptionMessage($message); + public function testThrowsExceptionWhenTypeOfValueDoesNotMatchExpectedType($value, string $type, string $message) : void { $matcher = $this->matcher; + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage($message); $matcher($value, $type); } } diff --git a/tests/ArrayContainsComparatorTest.php b/tests/ArrayContainsComparatorTest.php index c997c08..1998cd7 100644 --- a/tests/ArrayContainsComparatorTest.php +++ b/tests/ArrayContainsComparatorTest.php @@ -1,33 +1,26 @@ -comparator = new ArrayContainsComparator(); } /** - * Data provider - * - * @return array[] + * @return array */ - public function getDataForInArrayCheck() { + public function getDataForInArrayCheck() : array { $scalarHaystack = [1, 2, 1.1, 2.2, 'foo', 'bar', true, false]; $haystackWithArrayElements = [ 1, 2, 3, @@ -170,11 +163,9 @@ public function getDataForInArrayCheck() { } /** - * Data provider - * - * @return array[] + * @return array */ - public function getDataForCompareCheck() { + public function getDataForCompareCheck() : array { return [ 'simple key value objects' => [ 'needle' => [ @@ -216,11 +207,9 @@ public function getDataForCompareCheck() { } /** - * Data provider - * - * @return array[] + * @return array{needle: array, haystack: array}[] */ - public function getDataForSpecificKeyInListChecks() { + public function getDataForSpecificKeyInListChecks() : array { return [ 'simple list in haystack key' => [ 'needle' => [ @@ -287,11 +276,13 @@ public function getDataForSpecificKeyInListChecks() { } /** - * Data provider - * - * @return array[] + * @return array{ + * needle: array, + * haystack: array, + * exceptionMessage: string + * }[] */ - public function getDataForSpecificKeyInListChecksWithInvalidData() { + public function getDataForSpecificKeyInListChecksWithInvalidData() : array { return [ 'a string' => [ 'needle' => [ @@ -351,11 +342,14 @@ public function getDataForSpecificKeyInListChecksWithInvalidData() { } /** - * Data provider - * - * @return array[] + * @return array{ + * function: string, + * callback: callable, + * needle: array{key: string}, + * haystack: array{key: string|int[]} + * }[] */ - public function getCustomFunctionsAndData() { + public function getCustomFunctionsAndData() : array { return [ '@arrayLength' => [ 'function' => 'arrayLength', @@ -409,7 +403,7 @@ public function getCustomFunctionsAndData() { ], '@jwt' => [ 'function' => 'jwt', - 'callback' => (new Matcher\JWT(new ArrayContainsComparator()))->addToken('my jwt', ['some' => 'data'], 'secret', 'HS256'), + 'callback' => (new Matcher\JWT(new ArrayContainsComparator()))->addToken('my jwt', ['some' => 'data'], 'secret'), 'needle' => [ 'key' => '@jwt(my jwt)', ], @@ -419,7 +413,7 @@ public function getCustomFunctionsAndData() { ], '@customFunction' => [ 'function' => 'customFunction', - 'callback' => function($subject, $param) { + 'callback' => function(string $subject, string $param) : bool { return strtoupper($subject) === $param; }, 'needle' => [ @@ -433,11 +427,9 @@ public function getCustomFunctionsAndData() { } /** - * Data provider - * - * @return array[] + * @return array{function: string, callback: callable, needle: array, haystack: array, errorMessage: string}[] */ - public function getCustomFunctionsAndDataThatWillFail() { + public function getCustomFunctionsAndDataThatWillFail() : array { return [ // @arrayLength [ @@ -507,8 +499,7 @@ public function getCustomFunctionsAndDataThatWillFail() { // @customFunction [ 'function' => 'customFunction', - 'callback' => function($subject, $param) { - unset($subject, $params); + 'callback' => function() : void { throw new InvalidArgumentException('Some custom error message'); }, 'needle' => [ @@ -523,10 +514,10 @@ public function getCustomFunctionsAndDataThatWillFail() { } /** - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException * @covers ::compare */ - public function testThrowsExceptionWhenMatchingANumericallyIndexedArrayAgainstAnAssociativeArray() { + public function testThrowsExceptionWhenMatchingANumericallyIndexedArrayAgainstAnAssociativeArray() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' The needle is a list, while the haystack is not. @@ -557,11 +548,8 @@ public function testThrowsExceptionWhenMatchingANumericallyIndexedArrayAgainstAn * @covers ::compareValues * @covers ::arrayIsList * @covers ::arrayIsObject - * - * @param array $needle - * @param array $haystack */ - public function testCanRecursivelyDoInArrayChecksWith(array $needle, array $haystack) { + public function testCanRecursivelyDoInArrayChecksWith(array $needle, array $haystack) : void { $this->assertTrue( $this->comparator->compare($needle, $haystack), 'Comparator did not return in a correct manner, should return true' @@ -571,9 +559,9 @@ public function testCanRecursivelyDoInArrayChecksWith(array $needle, array $hays /** * @covers ::compare * @covers ::inArray - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenNeedleValueIsAListAndHaystackDoesNotContainAnyLists() { + public function testThrowsExceptionWhenNeedleValueIsAListAndHaystackDoesNotContainAnyLists() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Haystack does not contain any list elements, needle can't be found. @@ -598,9 +586,9 @@ public function testThrowsExceptionWhenNeedleValueIsAListAndHaystackDoesNotConta /** * @covers ::compare * @covers ::inArray - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenNeedleValueIsAnObjectAndHaystackDoesNotContainAnyObjects() { + public function testThrowsExceptionWhenNeedleValueIsAnObjectAndHaystackDoesNotContainAnyObjects() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Haystack does not contain any object elements, needle can't be found. @@ -633,9 +621,9 @@ public function testThrowsExceptionWhenNeedleValueIsAnObjectAndHaystackDoesNotCo /** * @covers ::compare * @covers ::inArray - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenHaystackListIsMissingValuesFromNeedleList() { + public function testThrowsExceptionWhenHaystackListIsMissingValuesFromNeedleList() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' The list in needle was not found in the list elements in the haystack. @@ -678,9 +666,9 @@ public function testThrowsExceptionWhenHaystackListIsMissingValuesFromNeedleList /** * @covers ::compare * @covers ::inArray - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenHaystackObjectIsMissingValuesFromNeedleObject() { + public function testThrowsExceptionWhenHaystackObjectIsMissingValuesFromNeedleObject() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' The object in needle was not found in the object elements in the haystack. @@ -746,11 +734,8 @@ public function testThrowsExceptionWhenHaystackObjectIsMissingValuesFromNeedleOb * @covers ::compareValues * @covers ::arrayIsList * @covers ::arrayIsObject - * - * @param array $needle - * @param array $haystack */ - public function testCanRecursivelyCompareAssociativeArraysWith(array $needle, array $haystack) { + public function testCanRecursivelyCompareAssociativeArraysWith(array $needle, array $haystack) : void { $this->assertTrue( $this->comparator->compare($needle, $haystack), 'Comparator did not return in a correct manner, should return true' @@ -759,9 +744,9 @@ public function testCanRecursivelyCompareAssociativeArraysWith(array $needle, ar /** * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenComparingObjectsAndKeyIsMissingFromHaystack() { + public function testThrowsExceptionWhenComparingObjectsAndKeyIsMissingFromHaystack() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Haystack object is missing the "bar" key. @@ -792,9 +777,9 @@ public function testThrowsExceptionWhenComparingObjectsAndKeyIsMissingFromHaysta /** * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenRegularStringKeyValueDoesNotMatch() { + public function testThrowsExceptionWhenRegularStringKeyValueDoesNotMatch() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Value mismatch for key "foo" in haystack object. @@ -824,7 +809,7 @@ public function testThrowsExceptionWhenRegularStringKeyValueDoesNotMatch() { * @covers ::compare * @covers ::compareValues */ - public function testCanRecursivelyMatchKeysInObjects() { + public function testCanRecursivelyMatchKeysInObjects() : void { $this->assertTrue( $this->comparator->compare([ 'bar' => 'foo', @@ -853,9 +838,9 @@ public function testCanRecursivelyMatchKeysInObjects() { /** * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenRegularStringKeyValueInDeepObjectDoesNotMatch() { + public function testThrowsExceptionWhenRegularStringKeyValueInDeepObjectDoesNotMatch() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Value mismatch for key "foo" in haystack object. @@ -897,11 +882,8 @@ public function testThrowsExceptionWhenRegularStringKeyValueInDeepObjectDoesNotM * @dataProvider getDataForSpecificKeyInListChecks * @covers ::compare * @covers ::compareValues - * - * @param array $needle - * @param array $haystack */ - public function testCanCompareSpecificIndexesInAListWith(array $needle, array $haystack) { + public function testCanCompareSpecificIndexesInAListWith(array $needle, array $haystack) : void { $this->assertTrue( $this->comparator->compare($needle, $haystack), 'Comparator did not return in a correct manner, should return true' @@ -910,9 +892,9 @@ public function testCanCompareSpecificIndexesInAListWith(array $needle, array $h /** * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenTargetingAListIndexWithAKeyThatDoesNotExist() { + public function testThrowsExceptionWhenTargetingAListIndexWithAKeyThatDoesNotExist() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Haystack object is missing the "foo" key. @@ -941,22 +923,20 @@ public function testThrowsExceptionWhenTargetingAListIndexWithAKeyThatDoesNotExi /** * @dataProvider getDataForSpecificKeyInListChecksWithInvalidData * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException - * - * @param array $needle - * @param array $haystack - * @param string $exceptionMessage + * @param array $needle + * @param array $haystack */ - public function testThrowsExceptionWhenTargetingAListIndexWithAKeyThatContains(array $needle, array $haystack, $exceptionMessage) { + public function testThrowsExceptionWhenTargetingAListIndexWithAKeyThatContains(array $needle, array $haystack, string $exceptionMessage) : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage($exceptionMessage); $this->comparator->compare($needle, $haystack); } /** * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionWhenTargetingAListIndexThatDoesNotExist() { + public function testThrowsExceptionWhenTargetingAListIndexThatDoesNotExist() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' The index "2" does not exist in the haystack list. @@ -990,9 +970,9 @@ public function testThrowsExceptionWhenTargetingAListIndexThatDoesNotExist() { /** * @covers ::compare - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testThrowsExceptionOnValueMismatchWhenTargetingSpecificIndexInList() { + public function testThrowsExceptionOnValueMismatchWhenTargetingSpecificIndexInList() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Value mismatch for index "0" in haystack list. @@ -1019,13 +999,10 @@ public function testThrowsExceptionOnValueMismatchWhenTargetingSpecificIndexInLi * @covers ::addFunction * @covers ::compare * @covers ::compareValues - * - * @param string $function - * @param callable $callback - * @param array $needle - * @param array $haystack + * @param array $needle + * @param array $haystack */ - public function testCanUseCustomFunctionMatcher($function, $callback, array $needle, array $haystack) { + public function testCanUseCustomFunctionMatcher(string $function, callable $callback, array $needle, array $haystack) : void { $this->assertTrue( $this->comparator ->addFunction($function, $callback) @@ -1037,9 +1014,9 @@ public function testCanUseCustomFunctionMatcher($function, $callback, array $nee /** * @covers ::compare * @covers ::compareValues - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException */ - public function testPerformsARegularStringComparisonWhenSpecifiedCustomFunctionMatcherDoesNotExist() { + public function testPerformsARegularStringComparisonWhenSpecifiedCustomFunctionMatcherDoesNotExist() : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage(<<<'EXCEPTION' Value mismatch for key "key" in haystack object. @@ -1066,18 +1043,14 @@ public function testPerformsARegularStringComparisonWhenSpecifiedCustomFunctionM /** * @dataProvider getCustomFunctionsAndDataThatWillFail - * @expectedException Imbo\BehatApiExtension\Exception\ArrayContainsComparatorException * @covers ::addFunction * @covers ::compare * @covers ::compareValues - * - * @param string $function - * @param callable $callback - * @param array $needle - * @param array $haystack - * @param string $errorMessage + * @param array $needle + * @param array $haystack */ - public function testThrowsExceptionWhenCustomFunctionMatcherFails($function, $callback, array $needle, array $haystack, $errorMessage) { + public function testThrowsExceptionWhenCustomFunctionMatcherFails(string $function, callable $callback, array $needle, array $haystack, string $errorMessage) : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage($errorMessage); $this->assertTrue( $this->comparator @@ -1087,20 +1060,11 @@ public function testThrowsExceptionWhenCustomFunctionMatcherFails($function, $ca ); } - /** - * @covers ::addFunction - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Callback provided for function "myFunction" is not callable. - */ - public function testThrowsExceptionWhenAddingAFunctionWithAnInvalidCallback() { - $this->comparator->addFunction('myFunction', 'myFunction'); - } - /** * @covers ::compare * @covers ::inArray */ - public function testSupportsInArrayCheckWhenListsAreInADeepStructure() { + public function testSupportsInArrayCheckWhenListsAreInADeepStructure() : void { $needle = [ 'foo' => [ 'bar' => 'baz', @@ -1151,13 +1115,13 @@ public function testSupportsInArrayCheckWhenListsAreInADeepStructure() { ], ], ]; - $this->comparator->compare($needle, $haystack); + $this->assertTrue($this->comparator->compare($needle, $haystack)); } /** * @covers ::getMatcherFunction */ - public function testCanReturnRegisteredMatcherFunction() { + public function testCanReturnRegisteredMatcherFunction() : void { $this->comparator->addFunction('function', $function = function() {}); $this->assertSame( $function, @@ -1168,10 +1132,10 @@ public function testCanReturnRegisteredMatcherFunction() { /** * @covers ::getMatcherFunction - * @expectedException InvalidArgumentException - * @expectedExceptionMessage No matcher function registered for "function". */ - public function testThrowsExceptionWhenGettingFunctionThatDoesNotExist() { + public function testThrowsExceptionWhenGettingFunctionThatDoesNotExist() : void { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('No matcher function registered for "function".'); $this->comparator->getMatcherFunction('function'); } } diff --git a/tests/Context/ApiContextTest.php b/tests/Context/ApiContextTest.php index 94545b6..c5f4e9e 100644 --- a/tests/Context/ApiContextTest.php +++ b/tests/Context/ApiContextTest.php @@ -1,9 +1,11 @@ -historyContainer = []; $this->mockHandler = new MockHandler(); @@ -102,16 +83,14 @@ public function setUp() { $this->comparator = $this->createMock(ArrayContainsComparator::class); $this->context = new ApiContext(); - $this->context->setClient($this->client); + $this->context->setClient($this->client, 'http://base'); $this->context->setArrayContainsComparator($this->comparator); } /** - * Data provider: Get HTTP methods - * - * @return array[] + * @return array{method: string}[] */ - public function getHttpMethods() { + public function getHttpMethods() : array { return [ ['method' => 'GET'], ['method' => 'PUT'], @@ -124,11 +103,9 @@ public function getHttpMethods() { } /** - * Data provider: Get files and mime types - * - * @return array[] + * @return array{filePath: string, method: string, expectedMimeType: string}[] */ - public function getFilesAndMimeTypes() { + public function getFilesAndMimeTypes() : array { return [ [ 'filePath' => __FILE__, @@ -145,12 +122,9 @@ public function getFilesAndMimeTypes() { } /** - * Data provider: Get a a response code, along with an array of other response codes that the - * first parameter does not match - * - * @return array[] + * @return array{code: int, others: int[]}[] */ - public function getResponseCodes() { + public function getResponseCodes() : array { return [ [ 'code' => 200, @@ -172,11 +146,9 @@ public function getResponseCodes() { } /** - * Data provider: Get response codes and groups - * - * @return array[] + * @return array{group: string, codes: int[]}[] */ - public function getGroupAndResponseCodes() { + public function getGroupAndResponseCodes() : array { return [ [ 'group' => 'informational', @@ -202,11 +174,9 @@ public function getGroupAndResponseCodes() { } /** - * Data provider: Return invalid HTTP response codes - * - * @return array[] + * @return int[][] */ - public function getInvalidHttpResponseCodes() { + public function getInvalidHttpResponseCodes() : array { return [ [99], [600], @@ -214,12 +184,9 @@ public function getInvalidHttpResponseCodes() { } /** - * Data provider: Get response body arrays, the length to use in the check, and whether or not - * the test should fail - * - * @return array[] + * @return array{body: int[], lengthToUse: int, willFail: bool}[] */ - public function getResponseBodyArrays() { + public function getResponseBodyArrays() : array { return [ [ 'body' => [1, 2, 3], @@ -245,12 +212,9 @@ public function getResponseBodyArrays() { } /** - * Data provider: Get response body arrays that will be used for testing that the length is at - * least a given length - * - * @return array[] + * @return array{body: int[], min: int}[] */ - public function getResponseBodyArraysForAtLeast() { + public function getResponseBodyArraysForAtLeast() : array { return [ [ 'body' => [1, 2, 3], @@ -272,12 +236,9 @@ public function getResponseBodyArraysForAtLeast() { } /** - * Data provider: Get response body arrays that will be used for testing that the length is at - * most a given length - * - * @return array[] + * @return array{body: int[], max: int}[] */ - public function getResponseBodyArraysForAtMost() { + public function getResponseBodyArraysForAtMost() : array { return [ [ 'body' => [1, 2, 3], @@ -299,11 +260,9 @@ public function getResponseBodyArraysForAtMost() { } /** - * Data provider - * - * @return [] + * @return array{code: int, phrase: string}[] */ - public function getResponseCodesAndReasonPhrases() { + public function getResponseCodesAndReasonPhrases() : array { return [ 200 => [ 'code' => 200, @@ -325,11 +284,9 @@ public function getResponseCodesAndReasonPhrases() { } /** - * Data provider - * - * @return array[] + * @return array{baseUri: string, path: string, fullUri: string}[] */ - public function getUris() { + public function getUris() : array { return [ // The first six sets are from http://docs.guzzlephp.org/en/latest/quickstart.html (2016-12-12) [ @@ -389,11 +346,9 @@ public function getUris() { } /** - * Data provider - * - * @return array[] + * @return array{responseCode: int, actualGroup: string, expectedGroup: string}[] */ - public function getDataForResponseGroupFailures() { + public function getDataForResponseGroupFailures() : array { return [ [ 'responseCode' => 100, @@ -424,11 +379,9 @@ public function getDataForResponseGroupFailures() { } /** - * Data provider - * - * @return array[] + * @return array */ - public function getRequestBodyValues() { + public function getRequestBodyValues() : array { return [ [ 'data' => 'some string', @@ -443,9 +396,8 @@ public function getRequestBodyValues() { /** * @covers ::setRequestHeader - * @group setup */ - public function testCanSetRequestHeaders() { + public function testCanSetRequestHeaders() : void { $this->mockHandler->append(new Response(200)); $this->assertSame($this->context, $this->context @@ -454,7 +406,7 @@ public function testCanSetRequestHeaders() { ->setRequestHeader('bar', 'bar') ); $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame('foo', $request->getHeaderLine('foo')); @@ -463,9 +415,8 @@ public function testCanSetRequestHeaders() { /** * @covers ::addRequestHeader - * @group setup */ - public function testCanAddRequestHeaders() { + public function testCanAddRequestHeaders() : void { $this->mockHandler->append(new Response(200)); $this->assertSame($this->context, $this->context @@ -474,7 +425,7 @@ public function testCanAddRequestHeaders() { ->addRequestHeader('bar', 'bar') ); $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame('foo', $request->getHeaderLine('foo')); @@ -483,9 +434,8 @@ public function testCanAddRequestHeaders() { /** * @covers ::setBasicAuth - * @group setup */ - public function testSupportBasicHttpAuthentication() { + public function testSupportBasicHttpAuthentication() : void { $this->mockHandler->append(new Response(200)); $username = 'user'; @@ -494,18 +444,75 @@ public function testSupportBasicHttpAuthentication() { $this->assertSame($this->context, $this->context->setBasicAuth($username, $password)); $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame('Basic dXNlcjpwYXNz', $request->getHeaderLine('authorization')); } + /** + * @covers ::oauthWithPasswordGrantInScope + */ + public function testSupportOAuthWithPasswordGrant() : void { + $this->mockHandler->append(new Response(200, [], '{"access_token": "some_access_token"}')); + $this->mockHandler->append(new Response(200)); + + $path = '/some/path'; + $username = 'user'; + $password = 'pass'; + $scope = 'scope'; + $clientId = 'client id'; + $clientSecret = 'client secret'; + + $this->assertSame( + $this->context, + $this->context->oauthWithPasswordGrantInScope($path, $username, $password, $scope, $clientId, $clientSecret + )); + $this->assertCount(1, $this->historyContainer); + + parse_str($this->historyContainer[0]['request']->getBody()->getContents(), $requestBody); + + $this->assertSame('password', $requestBody['grant_type'], 'Incorrect grant type'); + $this->assertSame($username, $requestBody['username'], 'Incorrect username'); + $this->assertSame($password, $requestBody['password'], 'Incorrect password'); + $this->assertSame($scope, $requestBody['scope'], 'Incorrect scope'); + $this->assertSame($clientId, $requestBody['client_id'], 'Incorrect client ID'); + $this->assertSame($clientSecret, $requestBody['client_secret'], 'Incorrect client secret'); + + // Create new request with Authorization header + $this->context->requestPath('/some/path', 'POST'); + $this->assertCount(2, $this->historyContainer); + $request = $this->historyContainer[1]['request']; + $this->assertSame('Bearer some_access_token', $request->getHeaderLine('authorization')); + } + + /** + * @covers ::oauthWithPasswordGrantInScope + */ + public function testThrowsExceptionWhenOauthAccessTokenRequestFails() : void { + $this->mockHandler->append(new Response(401, [], '{"error": "some_error"}')); + $this->expectExceptionObject(new RuntimeException( + 'Expected request for access token to pass, got status code 401 with the following response: {"error": "some_error"}' + )); + $this->context->oauthWithPasswordGrantInScope('/path', 'username', 'password', 'scope', 'client_id', 'client_secret'); + } + + /** + * @covers ::oauthWithPasswordGrantInScope + */ + public function testThrowsExceptionWhenOauthAccessTokenIsMissingFromResponse() : void { + $this->mockHandler->append(new Response(200, [], '{"foo": "bar"}')); + $this->expectExceptionObject(new RuntimeException( + 'Missing access_token from response body: {"foo":"bar"}' + )); + $this->context->oauthWithPasswordGrantInScope('/path', 'username', 'password', 'scope', 'client_id', 'client_secret'); + } + /** * @covers ::addMultipartFileToRequest * @covers ::addMultipartPart - * @group setup */ - public function testCanAddMultipleMultipartFilesToTheRequest() { + public function testCanAddMultipleMultipartFilesToTheRequest() : void { $this->mockHandler->append(new Response(200)); $files = [ 'file1' => __FILE__, @@ -518,17 +525,20 @@ public function testCanAddMultipleMultipartFilesToTheRequest() { $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; - $boundary = $request->getBody()->getBoundary(); + + /** @var MultipartStream */ + $requestBody = $request->getBody(); + $boundary = $requestBody->getBoundary(); $this->assertSame(sprintf('multipart/form-data; boundary=%s', $boundary), $request->getHeaderLine('Content-Type')); - $contents = $request->getBody()->getContents(); + $contents = $requestBody->getContents(); foreach ($files as $path) { - $this->assertContains( - file_get_contents($path), + $this->assertStringContainsString( + (string) file_get_contents($path), $contents ); } @@ -537,9 +547,8 @@ public function testCanAddMultipleMultipartFilesToTheRequest() { /** * @covers ::setRequestMultipartFormParams * @covers ::addMultipartPart - * @group setup */ - public function testCanAddMultipartFormDataParametersToTheRequest() { + public function testCanAddMultipartFormDataParametersToTheRequest() : void { $this->mockHandler->append(new Response(200)); $this->assertSame($this->context, $this->context->setRequestMultipartFormParams(new TableNode([ ['name', 'value'], @@ -550,10 +559,13 @@ public function testCanAddMultipartFormDataParametersToTheRequest() { $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; - $boundary = $request->getBody()->getBoundary(); + + /** @var MultipartStream */ + $requestBody = $request->getBody(); + $boundary = $requestBody->getBoundary(); $this->assertSame(sprintf('multipart/form-data; boundary=%s', $boundary), $request->getHeaderLine('Content-Type')); } @@ -561,9 +573,8 @@ public function testCanAddMultipartFormDataParametersToTheRequest() { /** * @covers ::setRequestFormParams * @covers ::sendRequest - * @group setup */ - public function testCanSetFormParametersInTheRequest() { + public function testCanSetFormParametersInTheRequest() : void { $this->mockHandler->append(new Response(200)); $this->assertSame($this->context, $this->context->setRequestFormParams(new TableNode([ ['name', 'value'], @@ -573,7 +584,7 @@ public function testCanSetFormParametersInTheRequest() { ]))); $this->context->requestPath('/some/path'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; @@ -584,11 +595,9 @@ public function testCanSetFormParametersInTheRequest() { } /** - * Data provider - * - * @return array[] + * @return array{httpMethod: string}[] */ - public function getHttpMethodsForFormParametersTest() { + public function getHttpMethodsForFormParametersTest() : array { return [ ['httpMethod' => 'PUT'], ['httpMethod' => 'POST'], @@ -601,11 +610,8 @@ public function getHttpMethodsForFormParametersTest() { * @dataProvider getHttpMethodsForFormParametersTest * @covers ::setRequestFormParams * @covers ::sendRequest - * @group setup - * - * @param string $httpMethod The HTTP method */ - public function testCanSetFormParametersInTheRequestWithCustomMethod($httpMethod) { + public function testCanSetFormParametersInTheRequestWithCustomMethod(string $httpMethod) : void { $this->mockHandler->append(new Response(200)); $this->assertSame($this->context, $this->context->setRequestFormParams(new TableNode([ ['name', 'value'], @@ -615,7 +621,7 @@ public function testCanSetFormParametersInTheRequestWithCustomMethod($httpMethod ]))); $this->context->requestPath('/some/path', $httpMethod); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; @@ -627,9 +633,8 @@ public function testCanSetFormParametersInTheRequestWithCustomMethod($httpMethod /** * @covers ::sendRequest - * @group setup */ - public function testCanSetFormParametersAndAttachAFileInTheSameMultipartRequest() { + public function testCanSetFormParametersAndAttachAFileInTheSameMultipartRequest() : void { $this->mockHandler->append(new Response(200)); $this->context->setRequestFormParams(new TableNode([ ['name', 'value'], @@ -640,18 +645,22 @@ public function testCanSetFormParametersAndAttachAFileInTheSameMultipartRequest( $this->context->addMultipartFileToRequest(__FILE__, 'file'); $this->context->requestPath('/some/path'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; - $boundary = $request->getBody()->getBoundary(); + + /** @var MultipartStream */ + $requestBody = $request->getBody(); + + $boundary = $requestBody->getBoundary(); $this->assertSame('POST', $request->getMethod()); $this->assertSame(sprintf('multipart/form-data; boundary=%s', $boundary), $request->getHeaderLine('Content-Type')); - $contents = $request->getBody()->getContents(); + $contents = $requestBody->getContents(); - $this->assertContains('Content-Disposition: form-data; name="file"; filename="ApiContextTest.php"', $contents); - $this->assertContains(file_get_contents(__FILE__), $contents); + $this->assertStringContainsString('Content-Disposition: form-data; name="file"; filename="ApiContextTest.php"', $contents); + $this->assertStringContainsString((string) file_get_contents(__FILE__), $contents); $foo = <<assertContains($foo, $contents); - $this->assertContains($bar0, $contents); - $this->assertContains($bar1, $contents); + $this->assertStringContainsString($foo, $contents); + $this->assertStringContainsString($bar0, $contents); + $this->assertStringContainsString($bar1, $contents); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage File does not exist: "/foo/bar" * @covers ::addMultipartFileToRequest - * @group setup */ - public function testThrowsExceptionWhenAddingNonExistingFileAsMultipartPartToTheRequest() { + public function testThrowsExceptionWhenAddingNonExistingFileAsMultipartPartToTheRequest() : void { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('File does not exist: "/foo/bar"'); $this->context->addMultipartFileToRequest('/foo/bar', 'foo'); } /** * @covers ::setRequestBodyToFileResource * @covers ::setRequestBody - * @group setup */ - public function testCanSetRequestBodyToAFile() { + public function testCanSetRequestBodyToAFile() : void { $this->mockHandler->append(new Response()); $this->assertSame($this->context, $this->context->setRequestBodyToFileResource(__FILE__)); $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame(file_get_contents(__FILE__), (string) $request->getBody()); $this->assertSame('text/x-php', $request->getHeaderLine('Content-Type')); @@ -706,17 +713,14 @@ public function testCanSetRequestBodyToAFile() { /** * @dataProvider getRequestBodyValues * @covers ::setRequestBody - * @group setup - * * @param string|PyStringNode $data - * @param string $expected */ - public function testCanSetRequestBodyToAString($data, $expected) { + public function testCanSetRequestBodyToAString($data, string $expected) : void { $this->mockHandler->append(new Response()); $this->context->setRequestBody($data); $this->context->requestPath('/some/path', 'POST'); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame($expected, (string) $request->getBody()); } @@ -725,24 +729,19 @@ public function testCanSetRequestBodyToAString($data, $expected) { * @dataProvider getUris * @covers ::setClient * @covers ::setRequestPath - * @group setup - * - * @param string $baseUri - * @param string $path - * @param string $fullUri */ - public function testResolvesFilePathsCorrectlyWhenAttachingFilesToTheRequestBody($baseUri, $path, $fullUri) { + public function testResolvesFilePathsCorrectlyWhenAttachingFilesToTheRequestBody(string $baseUri, string $path, string $fullUri) : void { // Set a new client with the given base_uri (and not the one used in setUp()) $this->assertSame($this->context, $this->context->setClient(new Client([ 'handler' => $this->handlerStack, 'base_uri' => $baseUri, - ]))); + ]), $baseUri)); $this->mockHandler->append(new Response()); $this->assertSame($this->context, $this->context->setRequestBodyToFileResource(__FILE__)); $this->context->requestPath($path); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame($fullUri, (string) $request->getUri()); } @@ -750,9 +749,8 @@ public function testResolvesFilePathsCorrectlyWhenAttachingFilesToTheRequestBody /** * @covers ::addJwtToken * @covers ::jsonDecode - * @group setup */ - public function testCanAddJwtTokensToTheJwtMatcher() { + public function testCanAddJwtTokensToTheJwtMatcher() : void { $name = 'some name'; $payload = ['some' => 'data']; $secret = 'secret'; @@ -778,16 +776,16 @@ public function testCanAddJwtTokensToTheJwtMatcher() { /** * @covers ::addJwtToken - * @expectedException RuntimeException - * @expectedExceptionMessage Matcher registered for the @jwt() matcher function must be an instance of Imbo\BehatApiExtension\ArrayContainsComparator\Matcher\Jwt - * @group setup */ - public function testThrowsExceptionWhenTryingToAddJwtTokenWhenThereIsNoMatcherFunctionRegistered() { + public function testThrowsExceptionWhenTryingToAddJwtTokenWhenThereIsNoMatcherFunctionRegistered() : void { $this->comparator ->expects($this->once()) ->method('getMatcherFunction') ->with('jwt') - ->willReturn(null); + ->willReturn('json_encode'); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Matcher registered for the @jwt() matcher function must be an instance of Imbo\BehatApiExtension\ArrayContainsComparator\Matcher\JWT'); $this->context->addJwtToken('name', 'secret', new PyStringNode(['{"some":"data"}'], 1)); } @@ -798,15 +796,12 @@ public function testThrowsExceptionWhenTryingToAddJwtTokenWhenThereIsNoMatcherFu * @covers ::setRequestPath * @covers ::setRequestMethod * @covers ::sendRequest - * @group request - * - * @param string $method */ - public function testCanMakeRequestsUsingDifferentHttpMethods($method) { + public function testCanMakeRequestsUsingDifferentHttpMethods(string $method) : void { $this->mockHandler->append(new Response(200)); $this->assertSame($this->context, $this->context->requestPath('/some/path', $method)); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $this->assertSame($method, $this->historyContainer[0]['request']->getMethod()); } @@ -816,48 +811,81 @@ public function testCanMakeRequestsUsingDifferentHttpMethods($method) { * @covers ::setRequestPath * @covers ::setRequestBody * @covers ::sendRequest - * @group request */ - public function testCanMakeRequestsWithQueryStringInThePath() { + public function testCanMakeRequestsWithQueryStringInThePath() : void { $this->mockHandler->append(new Response(200)); $this->assertSame( $this->context, $this->context->requestPath('/some/path?foo=bar&bar=foo&a[]=1&a[]=2') ); - $this->assertSame(1, count($this->historyContainer)); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertSame('foo=bar&bar=foo&a%5B%5D=1&a%5B%5D=2', $request->getUri()->getQuery()); } + /** + * @covers ::setQueryStringParameter + * @covers ::setQueryStringParameters + */ + public function testCanSetQueryStringParameters() : void { + $this->mockHandler->append(new Response(200)); + $this->assertSame( + $this->context, + $this->context + ->setQueryStringParameter('p1', 'v1') + ->setQueryStringParameter('p2', 'v2') + ->setQueryStringParameter('p3', 'v3') + ->setQueryStringParameter('p4', 'v4') + ->setQueryStringParameter('p4', 'v5') + ->setQueryStringParameter('p1', new TableNode([ + ['value'], + ['v6'], + ['v7'], + ])) + ->setQueryStringParameter('p2', new TableNode([ + ['value'], + ['v8'], + ])) + ->setQueryStringParameters(new TableNode([ + ['name', 'value'], + ['p3', 'v9'], + ['p5', 'v10'], + ])) + ); + $this->assertSame( + $this->context, + $this->context->requestPath('/some/path?wut=wat') + ); + + $this->assertCount(1, $this->historyContainer); + + $request = $this->historyContainer[0]['request']; + + $this->assertSame('p1%5B0%5D=v6&p1%5B1%5D=v7&p2%5B0%5D=v8&p3=v9&p4=v5&p5=v10', $request->getUri()->getQuery()); + } + /** * @dataProvider getResponseCodes * @covers ::assertResponseCodeIs * @covers ::validateResponseCode - * @group assertions - * - * @param int $code */ - public function testCanAssertWhatTheResponseCodeIs($code) { + public function testCanAssertWhatTheResponseCodeIs(int $code) : void { $this->mockHandler->append(new Response($code)); $this->context->requestPath('/some/path'); - $this->context->assertResponseCodeIs($code); + $this->assertTrue($this->context->assertResponseCodeIs($code)); } /** * @covers ::assertResponseCodeIsNot * @covers ::validateResponseCode - * @group assertions - * - * @param int $code - * @param int[] $otherCodes */ - public function testCanAssertWhatTheResponseCodeIsNot() { + public function testCanAssertWhatTheResponseCodeIsNot() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); - $this->context->assertResponseCodeIsNot(201); + $this->assertTrue($this->context->assertResponseCodeIsNot(201)); } /** @@ -865,16 +893,13 @@ public function testCanAssertWhatTheResponseCodeIsNot() { * @covers ::assertResponseIs * @covers ::requireResponse * @covers ::getResponseCodeGroupRange - * @group assertions - * - * @param string $group * @param int[] $codes */ - public function testCanAssertWhichGroupTheResponseIsIn($group, array $codes) { + public function testCanAssertWhichGroupTheResponseIsIn(string $group, array $codes) : void { foreach ($codes as $code) { $this->mockHandler->append(new Response($code, [], 'response body')); $this->context->requestPath('/some/path'); - $this->context->assertResponseIs($group); + $this->assertTrue($this->context->assertResponseIs($group)); } } @@ -882,12 +907,9 @@ public function testCanAssertWhichGroupTheResponseIsIn($group, array $codes) { * @dataProvider getGroupAndResponseCodes * @covers ::assertResponseIsNot * @covers ::assertResponseIs - * @group assertions - * - * @param string $group * @param int[] $codes */ - public function testCanAssertWhichGroupTheResponseIsNotIn($group, array $codes) { + public function testCanAssertWhichGroupTheResponseIsNotIn(string $group, array $codes) : void { $groups = [ 'informational', 'success', @@ -900,9 +922,9 @@ public function testCanAssertWhichGroupTheResponseIsNotIn($group, array $codes) $this->mockHandler->append(new Response($code)); $this->context->requestPath('/some/path'); - foreach (array_filter($groups, function($g) use ($group) { return $g !== $group; }) as $g) { + foreach (array_filter($groups, function(string $g) use ($group) : bool { return $g !== $group; }) as $g) { // Assert that the response is not in any of the other groups - $this->context->assertResponseIsNot($g); + $this->assertTrue($this->context->assertResponseIsNot($g)); } } } @@ -910,228 +932,196 @@ public function testCanAssertWhichGroupTheResponseIsNotIn($group, array $codes) /** * @dataProvider getResponseCodesAndReasonPhrases * @covers ::assertResponseReasonPhraseIs - * @group assertions - * - * @param int $code The HTTP response code - * @param string $phrase The HTTP response reason phrase */ - public function testCanAssertWhatTheResponseReasonPhraseIs($code, $phrase) { - $this->mockHandler->append(new Response($code, [], null, 1.1, $phrase)); + public function testCanAssertWhatTheResponseReasonPhraseIs(int $code, string $phrase) : void { + $this->mockHandler->append(new Response($code, [], null, '1.1', $phrase)); $this->context->requestPath('/some/path'); - $this->context->assertResponseReasonPhraseIs($phrase); + $this->assertTrue($this->context->assertResponseReasonPhraseIs($phrase)); } /** * @covers ::assertResponseReasonPhraseIsNot - * @group assertions */ - public function testCanAssertWhatTheResponseReasonPhraseIsNot() { + public function testCanAssertWhatTheResponseReasonPhraseIsNot() : void { $this->mockHandler->append(new Response()); $this->context->requestPath('/some/path'); - $this->context->assertResponseReasonPhraseIsNot('Not Modified'); + $this->assertTrue($this->context->assertResponseReasonPhraseIsNot('Not Modified')); } /** * @covers ::assertResponseReasonPhraseMatches - * @group assertions */ - public function testCanAssertThatTheResponseReasonPhraseMatchesAnExpression() { + public function testCanAssertThatTheResponseReasonPhraseMatchesAnExpression() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); - $this->context->assertResponseReasonPhraseMatches('/OK/'); + $this->assertTrue($this->context->assertResponseReasonPhraseMatches('/OK/')); } /** * @dataProvider getResponseCodesAndReasonPhrases * @covers ::assertResponseStatusLineIs - * @group assertions - * - * @param int $code The HTTP response code - * @param string $phrase The HTTP response reason phrase */ - public function testCanAssertWhatTheResponseStatusLineIs($code, $phrase) { - $this->mockHandler->append(new Response($code, [], null, 1.1, $phrase)); + public function testCanAssertWhatTheResponseStatusLineIs(int $code, string $phrase) : void { + $this->mockHandler->append(new Response($code, [], null, '1.1', $phrase)); $this->context->requestPath('/some/path'); - $this->context->assertResponseStatusLineIs(sprintf('%d %s', $code, $phrase)); + $this->assertTrue($this->context->assertResponseStatusLineIs(sprintf('%d %s', $code, $phrase))); } /** * @covers ::assertResponseStatusLineIsNot - * @group assertions */ - public function testCanAssertWhatTheResponseStatusLineIsNot() { + public function testCanAssertWhatTheResponseStatusLineIsNot() : void { $this->mockHandler->append(new Response()); $this->context->requestPath('/some/path'); - $this->context->assertResponseStatusLineIsNot('304 Not Modified'); + $this->assertTrue($this->context->assertResponseStatusLineIsNot('304 Not Modified')); } /** * @covers ::assertResponseStatusLineMatches - * @group assertions */ - public function testCanAssertThatTheResponseStatusLineMatchesAnExpression() { + public function testCanAssertThatTheResponseStatusLineMatchesAnExpression() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); - $this->context->assertResponseStatusLineMatches('/200 OK/'); + $this->assertTrue($this->context->assertResponseStatusLineMatches('/200 OK/')); } /** * @covers ::assertResponseHeaderExists - * @group assertions */ - public function testCanAssertThatAResponseHeaderExists() { + public function testCanAssertThatAResponseHeaderExists() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => 'application/json'])); $this->context->requestPath('/some/path'); - $this->context->assertResponseHeaderExists('Content-Type'); + $this->assertTrue($this->context->assertResponseHeaderExists('Content-Type')); } /** * @covers ::assertResponseHeaderDoesNotExist - * @group assertions */ - public function testCanAssertThatAResponseHeaderDoesNotExist() { + public function testCanAssertThatAResponseHeaderDoesNotExist() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); - $this->context->assertResponseHeaderDoesNotExist('Content-Type'); + $this->assertTrue($this->context->assertResponseHeaderDoesNotExist('Content-Type')); } /** * @covers ::assertResponseHeaderIs - * @group assertions */ - public function testCanAssertWhatAResponseHeaderIs() { + public function testCanAssertWhatAResponseHeaderIs() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => 'application/json'])); $this->context->requestPath('/some/path'); - $this->context->assertResponseHeaderIs('Content-Type', 'application/json'); + $this->assertTrue($this->context->assertResponseHeaderIs('Content-Type', 'application/json')); } /** * @covers ::assertResponseHeaderIsNot - * @group assertions */ - public function testCanAssertWhatAResponseHeaderIsNot() { + public function testCanAssertWhatAResponseHeaderIsNot() : void { $this->mockHandler->append(new Response(200, ['Content-Length' => '123'])); $this->context->requestPath('/some/path'); - $this->context->assertResponseHeaderIsNot('Content-Type', '456'); + $this->assertTrue($this->context->assertResponseHeaderIsNot('Content-Type', '456')); } /** * @covers ::assertResponseHeaderMatches - * @group assertions */ - public function testCanAssertThatAResponseHeaderMatchesAnExpression() { + public function testCanAssertThatAResponseHeaderMatchesAnExpression() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => 'application/json'])); $this->context->requestPath('/some/path'); - $this->context->assertResponseHeaderMatches('Content-Type', '#^application/(json|xml)$#'); + $this->assertTrue($this->context->assertResponseHeaderMatches('Content-Type', '#^application/(json|xml)$#')); } /** * @covers ::assertResponseBodyIs - * @group assertions */ - public function testCanAssertWhatTheResponseBodyIs() { + public function testCanAssertWhatTheResponseBodyIs() : void { $this->mockHandler->append(new Response(200, [], 'response body')); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyIs(new PyStringNode(['response body'], 1)); + $this->assertTrue($this->context->assertResponseBodyIs(new PyStringNode(['response body'], 1))); } /** * @covers ::assertResponseBodyIsNot - * @group assertions */ - public function testCanAssertWhatTheResponseBodyIsNot() { + public function testCanAssertWhatTheResponseBodyIsNot() : void { $this->mockHandler->append(new Response(200, [], 'response body')); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyIsNot(new PyStringNode(['some other response body'], 1)); + $this->assertTrue($this->context->assertResponseBodyIsNot(new PyStringNode(['some other response body'], 1))); } /** * @covers ::assertResponseBodyMatches - * @group assertions */ - public function testCanAssertThatTheResponseBodyMatchesAnExpression() { + public function testCanAssertThatTheResponseBodyMatchesAnExpression() : void { $this->mockHandler->append(new Response(200, [], '{"foo":"bar"}')); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyMatches(new PyStringNode(['/^{"FOO": ?"BAR"}$/i'], 1)); + $this->assertTrue($this->context->assertResponseBodyMatches(new PyStringNode(['/^{"FOO": ?"BAR"}$/i'], 1))); } /** * @covers ::assertResponseBodyIsAnEmptyJsonArray * @covers ::getResponseBodyArray * @covers ::getResponseBody - * @group assertions */ - public function testCanAssertThatTheResponseIsAnEmptyArray() { - $this->mockHandler->append(new Response(200, [], json_encode([]))); + public function testCanAssertThatTheResponseIsAnEmptyArray() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode([]))); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyIsAnEmptyJsonArray(); + $this->assertTrue($this->context->assertResponseBodyIsAnEmptyJsonArray()); } /** * @covers ::assertResponseBodyIsAnEmptyJsonObject - * @group assertions */ - public function testCanAssertThatTheResponseIsAnEmptyObject() { - $this->mockHandler->append(new Response(200, [], json_encode(new stdClass()))); + public function testCanAssertThatTheResponseIsAnEmptyObject() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode(new stdClass()))); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyIsAnEmptyJsonObject(); + $this->assertTrue($this->context->assertResponseBodyIsAnEmptyJsonObject()); } /** * @dataProvider getResponseBodyArrays * @covers ::assertResponseBodyJsonArrayLength * @covers ::getResponseBodyArray - * @group assertions - * - * @param array $body - * @param int $lenthToUse - * @param boolean $willFail - */ - public function testCanAssertThatTheArrayInTheResponseBodyHasACertainLength(array $body, $lengthToUse, $willFail) { - $this->mockHandler->append(new Response(200, [], json_encode($body))); + * @param int[] $body + */ + public function testCanAssertThatTheArrayInTheResponseBodyHasACertainLength(array $body, int $lengthToUse, bool $willFail) : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode($body))); $this->context->requestPath('/some/path'); if ($willFail) { - $this->expectException('Imbo\BehatApiExtension\Exception\AssertionFailedException'); + $this->expectException(AssertionFailedException::class); $this->expectExceptionMessage(sprintf( 'Expected response body to be a JSON array with %d entr%s, got %d: "[', $lengthToUse, - (int) $lengthToUse === 1 ? 'y' : 'ies', + $lengthToUse === 1 ? 'y' : 'ies', count($body) )); } - $this->context->assertResponseBodyJsonArrayLength($lengthToUse); + $this->assertTrue($this->context->assertResponseBodyJsonArrayLength($lengthToUse)); } /** * @dataProvider getResponseBodyArraysForAtLeast * @covers ::assertResponseBodyJsonArrayMinLength * @covers ::getResponseBody - * @group assertions - * - * @param array $body - * @param int $min + * @param int[] $body */ - public function testCanAssertTheMinLengthOfAnArrayInTheResponseBody(array $body, $min) { - $this->mockHandler->append(new Response(200, [], json_encode($body))); + public function testCanAssertTheMinLengthOfAnArrayInTheResponseBody(array $body, int $min) : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode($body))); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyJsonArrayMinLength($min); + $this->assertTrue($this->context->assertResponseBodyJsonArrayMinLength($min)); } /** * @dataProvider getResponseBodyArraysForAtMost * @covers ::assertResponseBodyJsonArrayMaxLength * @covers ::getResponseBody - * @group assertions - * - * @param array $body - * @param int $max + * @param int[] $body */ - public function testCanAssertTheMaxLengthOfAnArrayInTheResponseBody(array $body, $max) { - $this->mockHandler->append(new Response(200, [], json_encode($body))); + public function testCanAssertTheMaxLengthOfAnArrayInTheResponseBody(array $body, int $max) : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode($body))); $this->context->requestPath('/some/path'); - $this->context->assertResponseBodyJsonArrayMaxLength($max); + $this->assertTrue($this->context->assertResponseBodyJsonArrayMaxLength($max)); } /** @@ -1139,75 +1129,69 @@ public function testCanAssertTheMaxLengthOfAnArrayInTheResponseBody(array $body, * @covers ::assertResponseBodyContainsJson * @covers ::getResponseBody * @covers ::jsonDecode - * @group assertions */ - public function testCanAssertThatTheResponseBodyContainsJson() { + public function testCanAssertThatTheResponseBodyContainsJson() : void { $this->mockHandler->append(new Response(200, [], '{"foo":"bar","bar":"foo"}')); $this->context->requestPath('/some/path'); $this->comparator ->expects($this->once()) ->method('compare') ->with(['bar' => 'foo', 'foo' => 'bar'], ['foo' => 'bar', 'bar' => 'foo']) - ->will($this->returnValue(true)); + ->willReturn(true); $this->context->assertResponseBodyContainsJson(new PyStringNode(['{"bar":"foo","foo":"bar"}'], 1)); } /** * @see https://github.com/imbo/behat-api-extension/issues/7 - * @expectedException InvalidArgumentException - * @expectedExceptionMessage It's not allowed to set a request body when using multipart/form-data or form parameters. * @covers ::setRequestBody - * @group setup */ - public function testThrowsExceptionWhenTryingToCombineARequestBodyWithMultipartOrFormData() { + public function testThrowsExceptionWhenTryingToCombineARequestBodyWithMultipartOrFormData() : void { $this->mockHandler->append(new Response(200)); $this->context->addMultipartFileToRequest(__FILE__, 'file'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('It\'s not allowed to set a request body when using multipart/form-data or form parameters.'); $this->context->setRequestBody('some body'); } /** * @covers ::setRequestBodyToFileResource - * @expectedException InvalidArgumentException - * @expectedExceptionMessage File does not exist: "/foo/bar" - * @group setup */ - public function testThrowsExceptionWhenAttachingANonExistingFileToTheRequestBody() { + public function testThrowsExceptionWhenAttachingANonExistingFileToTheRequestBody() : void { $this->mockHandler->append(new Response()); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('File does not exist: "/foo/bar"'); $this->context->setRequestBodyToFileResource('/foo/bar'); } /** * @covers ::setRequestBodyToFileResource - * @expectedException InvalidArgumentException - * @expectedExceptionMessage File is not readable: "/non/readable/file" - * @group setup */ - public function testThrowsExceptionWhenAttachingANonReadableFileToTheRequestBody() { + public function testThrowsExceptionWhenAttachingANonReadableFileToTheRequestBody() : void { $this->mockHandler->append(new Response()); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('File is not readable: "/non/readable/file"'); $this->context->setRequestBodyToFileResource('/non/readable/file'); } /** - * @expectedException GuzzleHttp\Exception\RequestException - * @expectedExceptionMessage error * @covers ::sendRequest - * @group request */ - public function testThrowsExceptionWhenTheRequestCanNotBeSent() { + public function testThrowsExceptionWhenTheRequestCanNotBeSent() : void { $this->mockHandler->append(new RequestException('error', new Request('GET', 'path'))); + $this->expectException(RequestException::class); + $this->expectExceptionMessage('error'); $this->context->requestPath('path'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response code 400, got 200 * @covers ::assertResponseCodeIs - * @group assertions */ - public function testAssertingWhatTheResponseCodeIsCanFail() { + public function testAssertingWhatTheResponseCodeIsCanFail() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response code 400, got 200'); $this->context->assertResponseCodeIs(400); } @@ -1215,40 +1199,35 @@ public function testAssertingWhatTheResponseCodeIsCanFail() { * @dataProvider getInvalidHttpResponseCodes * @covers ::assertResponseCodeIs * @covers ::validateResponseCode - * @group assertions - * - * @param int $code */ - public function testThrowsExceptionWhenSpecifyingAnInvalidCodeWhenAssertingWhatTheResponseCodeIs($code) { + public function testThrowsExceptionWhenSpecifyingAnInvalidCodeWhenAssertingWhatTheResponseCodeIs(int $code) : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage(sprintf('Response code must be between 100 and 599, got %d.', $code)); $this->context->assertResponseCodeIs($code); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseCodeIs * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseCodeIsWhenNoResponseExists() { + public function testThrowsExceptionWhenAssertingWhatTheResponseCodeIsWhenNoResponseExists() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseCodeIs(200); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Did not expect response code 200 * @covers ::assertResponseCodeIsNot - * @group assertions */ - public function testAssertingWhatTheResponseCodeIsNotCanFail() { + public function testAssertingWhatTheResponseCodeIsNotCanFail() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Did not expect response code 200'); $this->context->assertResponseCodeIsNot(200); } @@ -1256,28 +1235,24 @@ public function testAssertingWhatTheResponseCodeIsNotCanFail() { * @dataProvider getInvalidHttpResponseCodes * @covers ::assertResponseCodeIsNot * @covers ::validateResponseCode - * @group assertions - * - * @param int $code */ - public function testThrowsExceptionWhenSpecifyingAnInvalidCodeWhenAssertingWhatTheResponseCodeIsNot($code) { + public function testThrowsExceptionWhenSpecifyingAnInvalidCodeWhenAssertingWhatTheResponseCodeIsNot(int $code) : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); - $this->expectException('InvalidArgumentException'); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage(sprintf('Response code must be between 100 and 599, got %d.', $code)); $this->context->assertResponseCodeIsNot($code); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseCodeIsNot * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseCodeIsNotWhenNoResponseExists() { + public function testThrowsExceptionWhenAssertingWhatTheResponseCodeIsNotWhenNoResponseExists() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseCodeIsNot(200); } @@ -1286,17 +1261,12 @@ public function testThrowsExceptionWhenAssertingWhatTheResponseCodeIsNotWhenNoRe * @covers ::assertResponseIs * @covers ::getResponseCodeGroupRange * @covers ::getResponseGroup - * @group assertions - * - * @param int $responseCode - * @param string $actualGroup - * @param string $expectedGroup */ - public function testAssertingThatTheResponseIsInAGroupCanFail($responseCode, $actualGroup, $expectedGroup) { + public function testAssertingThatTheResponseIsInAGroupCanFail(int $responseCode, string $actualGroup, string $expectedGroup) : void { $this->mockHandler->append(new Response($responseCode)); $this->context->requestPath('/some/path'); - $this->expectException('Imbo\BehatApiExtension\Exception\AssertionFailedException'); + $this->expectException(AssertionFailedException::class); $this->expectExceptionMessage(sprintf( 'Expected response group "%s", got "%s" (response code: %d).', $expectedGroup, @@ -1307,627 +1277,577 @@ public function testAssertingThatTheResponseIsInAGroupCanFail($responseCode, $ac } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseIs * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhichGroupTheResponseIsInWhenNoResponseExists() { + public function testThrowsExceptionWhenAssertingWhichGroupTheResponseIsInWhenNoResponseExists() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseIs('success'); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Invalid response code group: foobar * @covers ::assertResponseIs * @covers ::getResponseCodeGroupRange - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseIsInAnInvalidGroup() { + public function testThrowsExceptionWhenAssertingThatTheResponseIsInAnInvalidGroup() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid response code group: foobar'); $this->context->assertResponseIs('foobar'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Did not expect response to be in the "success" group (response code: 200). * @covers ::assertResponseIsNot * @covers ::assertResponseIs - * @group assertions */ - public function testAssertingThatTheResponseIsNotInAGroupCanFail() { + public function testAssertingThatTheResponseIsNotInAGroupCanFail() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Did not expect response to be in the "success" group (response code: 200).'); $this->context->assertResponseIsNot('success'); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage Invalid response code group: foobar * @covers ::assertResponseIsNot * @covers ::getResponseCodeGroupRange - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseIsNotInAnInvalidGroup() { + public function testThrowsExceptionWhenAssertingThatTheResponseIsNotInAnInvalidGroup() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid response code group: foobar'); $this->context->assertResponseIsNot('foobar'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseIsNot * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhichGroupTheResponseIsNotInWhenNoResponseExists() { + public function testThrowsExceptionWhenAssertingWhichGroupTheResponseIsNotInWhenNoResponseExists() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseIsNot('success'); } /** * @covers ::assertResponseReasonPhraseIs - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response reason phrase "ok", got "OK". - * @group assertions */ - public function testAssertingWhatTheResponseReasonPhraseIsCanFail() { + public function testAssertingWhatTheResponseReasonPhraseIsCanFail() : void { $this->mockHandler->append(new Response()); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response reason phrase "ok", got "OK".'); $this->context->assertResponseReasonPhraseIs('ok'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseReasonPhraseIs * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseReasonPhraseIsWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatTheResponseReasonPhraseIsWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseReasonPhraseIs('OK'); } /** * @covers ::assertResponseReasonPhraseIsNot - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Did not expect response reason phrase "OK". - * @group assertions */ - public function testAssertingWhatTheResponseReasonPhraseIsNotCanFail() { + public function testAssertingWhatTheResponseReasonPhraseIsNotCanFail() : void { $this->mockHandler->append(new Response()); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Did not expect response reason phrase "OK".'); $this->context->assertResponseReasonPhraseIsNot('OK'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseReasonPhraseIsNot * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseReasonPhraseIsNotWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatTheResponseReasonPhraseIsNotWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseReasonPhraseIsNot('OK'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected the response reason phrase to match the regular expression "/ok/", got "OK". * @covers ::assertResponseReasonPhraseMatches - * @group assertions */ - public function testAssertingThatTheResponseReasonPhraseMatchesAnExpressionCanFail() { + public function testAssertingThatTheResponseReasonPhraseMatchesAnExpressionCanFail() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected the response reason phrase to match the regular expression "/ok/", got "OK".'); $this->context->assertResponseReasonPhraseMatches('/ok/'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseReasonPhraseMatches * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseReasonPhraseMatchesAnExpressionWhenThereIsNoResponse() { + public function testThrowsExceptionWhenAssertingThatTheResponseReasonPhraseMatchesAnExpressionWhenThereIsNoResponse() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseReasonPhraseMatches('/ok/'); } /** * @covers ::assertResponseStatusLineIs - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response status line "200 Foobar", got "200 OK". - * @group assertions */ - public function testAssertingWhatTheResponseStatusLineIsCanFail() { + public function testAssertingWhatTheResponseStatusLineIsCanFail() : void { $this->mockHandler->append(new Response()); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response status line "200 Foobar", got "200 OK".'); $this->context->assertResponseStatusLineIs('200 Foobar'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseStatusLineIs * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseStatusLineIsWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatTheResponseStatusLineIsWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseStatusLineIs('200 OK'); } /** * @covers ::assertResponseStatusLineIsNot - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Did not expect response status line "200 OK". - * @group assertions */ - public function testAssertingWhatTheResponseStatusLineIsNotCanFail() { + public function testAssertingWhatTheResponseStatusLineIsNotCanFail() : void { $this->mockHandler->append(new Response()); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Did not expect response status line "200 OK".'); $this->context->assertResponseStatusLineIsNot('200 OK'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseStatusLineIsNot * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseStatusLineIsNotWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatTheResponseStatusLineIsNotWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseStatusLineIsNot('200 OK'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected the response status line to match the regular expression "/200 ok/", got "200 OK". * @covers ::assertResponseStatusLineMatches - * @group assertions */ - public function testAssertingThatTheResponseStatusLineMatchesAnExpressionCanFail() { + public function testAssertingThatTheResponseStatusLineMatchesAnExpressionCanFail() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected the response status line to match the regular expression "/200 ok/", got "200 OK".'); $this->context->assertResponseStatusLineMatches('/200 ok/'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseStatusLineMatches * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseStatusLineMatchesAnExpressionWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingThatTheResponseStatusLineMatchesAnExpressionWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseStatusLineMatches('/200 OK/'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage The "Content-Type" response header does not exist * @covers ::assertResponseHeaderExists - * @group assertions */ - public function testAssertingThatAResponseHeaderExistsCanFail() { + public function testAssertingThatAResponseHeaderExistsCanFail() : void { $this->mockHandler->append(new Response(200)); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('The "Content-Type" response header does not exist.'); $this->context->assertResponseHeaderExists('Content-Type'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseHeaderExists * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatAResponseHeaderExistWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingThatAResponseHeaderExistWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseHeaderExists('Connection'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage The "Content-Type" response header should not exist * @covers ::assertResponseHeaderDoesNotExist - * @group assertions */ - public function testAssertingThatAResponseHeaderDoesNotExistCanFail() { + public function testAssertingThatAResponseHeaderDoesNotExistCanFail() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => 'application/json'])); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('The "Content-Type" response header should not exist.'); $this->context->assertResponseHeaderDoesNotExist('Content-Type'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseHeaderDoesNotExist * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatAResponseHeaderDoesNotExistWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingThatAResponseHeaderDoesNotExistWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseHeaderDoesNotExist('Connection'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected the "Content-Type" response header to be "application/xml", got "application/json". * @covers ::assertResponseHeaderIs - * @group assertions */ - public function testAssertingWhatAResponseHeaderIsCanFail() { + public function testAssertingWhatAResponseHeaderIsCanFail() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => 'application/json'])); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected the "Content-Type" response header to be "application/xml", got "application/json".'); $this->context->assertResponseHeaderIs('Content-Type', 'application/xml'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseHeaderIs * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatAResponseHeaderIsWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatAResponseHeaderIsWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseHeaderIs('Connection', 'close'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Did not expect the "content-type" response header to be "123". * @covers ::assertResponseHeaderIsNot - * @group assertions */ - public function testAssertingWhatAResponseHeaderIsNotCanFail() { + public function testAssertingWhatAResponseHeaderIsNotCanFail() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => '123'])); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Did not expect the "content-type" response header to be "123".'); $this->context->assertResponseHeaderIsNot('content-type', '123'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseHeaderIsNot * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatAResponseHeaderIsNotWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatAResponseHeaderIsNotWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseHeaderIsNot('header', 'value'); } + /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected the "Content-Type" response header to match the regular expression "#^application/xml$#", got "application/json". * @covers ::assertResponseHeaderMatches - * @group assertions */ - public function testAssertingThatAResponseHeaderMatchesAnExpressionCanFail() { + public function testAssertingThatAResponseHeaderMatchesAnExpressionCanFail() : void { $this->mockHandler->append(new Response(200, ['Content-Type' => 'application/json'])); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected the "Content-Type" response header to match the regular expression "#^application/xml$#", got "application/json".'); $this->context->assertResponseHeaderMatches('Content-Type', '#^application/xml$#'); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseHeaderMatches * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatAResponseHeaderMatchesAnExpressionWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingThatAResponseHeaderMatchesAnExpressionWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseHeaderMatches('Connection', 'close'); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body "foo", got "response body". * @covers ::assertResponseBodyIs - * @group assertions */ - public function testAssertingWhatTheResponseBodyIsCanFail() { + public function testAssertingWhatTheResponseBodyIsCanFail() : void { $this->mockHandler->append(new Response(200, [], 'response body')); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body "foo", got "response body".'); $this->context->assertResponseBodyIs(new PyStringNode(['foo'], 1)); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyIs * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseBodyIsWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatTheResponseBodyIsWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyIs(new PyStringNode(['some body'], 1)); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Did not expect response body to be "response body". * @covers ::assertResponseBodyIsNot - * @group assertions */ - public function testAssertingWhatTheResponseBodyIsNotCanFail() { + public function testAssertingWhatTheResponseBodyIsNotCanFail() : void { $this->mockHandler->append(new Response(200, [], 'response body')); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Did not expect response body to be "response body".'); $this->context->assertResponseBodyIsNot(new PyStringNode(['response body'], 1)); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyIsNot * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingWhatTheResponseBodyIsNotWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingWhatTheResponseBodyIsNotWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyIsNot(new PyStringNode(['some body'], 1)); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to match regular expression "/^{"FOO": "BAR"}$/", got "{"foo":"bar"}". * @covers ::assertResponseBodyMatches - * @group assertions */ - public function testAssertingThatTheResponseBodyMatchesAnExpressionCanFail() { + public function testAssertingThatTheResponseBodyMatchesAnExpressionCanFail() : void { $this->mockHandler->append(new Response(200, [], '{"foo":"bar"}')); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to match regular expression "/^{"FOO": "BAR"}$/", got "{"foo":"bar"}".'); $this->context->assertResponseBodyMatches(new PyStringNode(['/^{"FOO": "BAR"}$/'], 1)); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyMatches * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseBodyMatchesAnExpressionWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingThatTheResponseBodyMatchesAnExpressionWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyMatches(new PyStringNode(['/foo/'], 1)); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to be an empty JSON array, got "[ * @covers ::assertResponseBodyIsAnEmptyJsonArray * @covers ::getResponseBodyArray * @covers ::getResponseBody - * @group assertions */ - public function testAssertingThatTheResponseIsAnEmptyArrayCanFail() { - $this->mockHandler->append(new Response(200, [], json_encode([1, 2, 3]))); + public function testAssertingThatTheResponseIsAnEmptyArrayCanFail() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode([1, 2, 3]))); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to be an empty JSON array, got "['); $this->context->assertResponseBodyIsAnEmptyJsonArray(); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The response body does not contain a valid JSON array / object. * @covers ::assertResponseBodyIsAnEmptyJsonArray * @covers ::getResponseBodyArray * @covers ::getResponseBody - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseBodyIsAnEmptyArrayWhenTheBodyDoesNotContainAnArray() { - $this->mockHandler->append(new Response(200, [], 123)); + public function testThrowsExceptionWhenAssertingThatTheResponseBodyIsAnEmptyArrayWhenTheBodyDoesNotContainAnArray() : void { + $this->mockHandler->append(new Response(200, [], '123')); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The response body does not contain a valid JSON array / object.'); $this->context->assertResponseBodyIsAnEmptyJsonArray(); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to be an empty JSON object, got "{ * @covers ::assertResponseBodyIsAnEmptyJsonObject - * @group assertions */ - public function testAssertingThatTheResponseIsAnEmptyObjectCanFail() { + public function testAssertingThatTheResponseIsAnEmptyObjectCanFail() : void { $object = new stdClass(); $object->foo = 'bar'; - $this->mockHandler->append(new Response(200, [], json_encode($object))); + $this->mockHandler->append(new Response(200, [], (string) json_encode($object))); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to be an empty JSON object, got "{'); $this->context->assertResponseBodyIsAnEmptyJsonObject(); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to be a JSON object. * @covers ::assertResponseBodyIsAnEmptyJsonObject * @covers ::getResponseBody - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseBodyIsAnEmptyObjectWhenTheBodyDoesNotContainAnObject() { - $this->mockHandler->append(new Response(200, [], json_encode([]))); + public function testThrowsExceptionWhenAssertingThatTheResponseBodyIsAnEmptyObjectWhenTheBodyDoesNotContainAnObject() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode([]))); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to be a JSON object.'); $this->context->assertResponseBodyIsAnEmptyJsonObject(); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to be a JSON array with 2 entries, got 3: "[ * @covers ::assertResponseBodyJsonArrayLength * @covers ::getResponseBodyArray * @covers ::getResponseBody - * @group assertions */ - public function testAssertingThatTheResponseBodyIsAJsonArrayWithACertainLengthCanFail() { - $this->mockHandler->append(new Response(200, [], json_encode([1, 2, 3]))); + public function testAssertingThatTheResponseBodyIsAJsonArrayWithACertainLengthCanFail() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode([1, 2, 3]))); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to be a JSON array with 2 entries, got 3: "['); $this->context->assertResponseBodyJsonArrayLength(2); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyJsonArrayLength * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingTheLengthOfAJsonArrayInTheResponseBodyWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingTheLengthOfAJsonArrayInTheResponseBodyWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyJsonArrayLength(5); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The response body does not contain a valid JSON array. * @covers ::assertResponseBodyJsonArrayLength * @covers ::getResponseBodyArray - * @group assertions */ - public function testThrowsExceptionWhenAssertingTheLengthOfAJsonArrayInTheResponseBodyAndTheBodyDoesNotContainAnArray() { - $this->mockHandler->append(new Response(200, [], json_encode(['foo' => 'bar']))); + public function testThrowsExceptionWhenAssertingTheLengthOfAJsonArrayInTheResponseBodyAndTheBodyDoesNotContainAnArray() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode(['foo' => 'bar']))); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The response body does not contain a valid JSON array.'); $this->context->assertResponseBodyJsonArrayLength(0); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to be a JSON array with at least 4 entries, got 3: "[ * @covers ::assertResponseBodyJsonArrayMinLength * @covers ::getResponseBody - * @group assertions */ - public function testAssertingThatTheResponseBodyContainsAJsonArrayWithAMinimumLengthCanFail() { - $this->mockHandler->append(new Response(200, [], json_encode([1, 2, 3]))); + public function testAssertingThatTheResponseBodyContainsAJsonArrayWithAMinimumLengthCanFail() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode([1, 2, 3]))); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to be a JSON array with at least 4 entries, got 3: "['); $this->context->assertResponseBodyJsonArrayMinLength(4); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyJsonArrayMinLength * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingTheMinimumLengthOfAnArrayInTheResponseBodyWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingTheMinimumLengthOfAnArrayInTheResponseBodyWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyJsonArrayMinLength(5); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The response body does not contain a valid JSON array. * @covers ::assertResponseBodyJsonArrayMinLength * @covers ::getResponseBody - * @group assertions */ - public function testThrowsExceptionWhenAssertingTheMinimumLengthOfAnArrayInTheResponseBodyAndTheBodyDoesNotContainAnArray() { - $this->mockHandler->append(new Response(200, [], json_encode(['foo' => 'bar']))); + public function testThrowsExceptionWhenAssertingTheMinimumLengthOfAnArrayInTheResponseBodyAndTheBodyDoesNotContainAnArray() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode(['foo' => 'bar']))); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The response body does not contain a valid JSON array.'); $this->context->assertResponseBodyJsonArrayMinLength(2); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Expected response body to be a JSON array with at most 2 entries, got 3: "[ * @covers ::assertResponseBodyJsonArrayMaxLength * @covers ::getResponseBody - * @group assertions */ - public function testAssertingThatTheResponseBodyContainsAJsonArrayWithAMaximumLengthCanFail() { - $this->mockHandler->append(new Response(200, [], json_encode([1, 2, 3]))); + public function testAssertingThatTheResponseBodyContainsAJsonArrayWithAMaximumLengthCanFail() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode([1, 2, 3]))); $this->context->requestPath('/some/path'); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Expected response body to be a JSON array with at most 2 entries, got 3: "['); $this->context->assertResponseBodyJsonArrayMaxLength(2); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyJsonArrayMaxLength * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingTheMaximumLengthOfAnArrayInTheResponseBodyWhenNoResponseExist() { + public function testThrowsExceptionWhenAssertingTheMaximumLengthOfAnArrayInTheResponseBodyWhenNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyJsonArrayMaxLength(5); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The response body does not contain a valid JSON array. * @covers ::assertResponseBodyJsonArrayMaxLength * @covers ::getResponseBody - * @group assertions */ - public function testThrowsExceptionWhenAssertingTheMaximumLengthOfAnArrayInTheResponseBodyAndTheBodyDoesNotContainAnArray() { - $this->mockHandler->append(new Response(200, [], json_encode(['foo' => 'bar']))); + public function testThrowsExceptionWhenAssertingTheMaximumLengthOfAnArrayInTheResponseBodyAndTheBodyDoesNotContainAnArray() : void { + $this->mockHandler->append(new Response(200, [], (string) json_encode(['foo' => 'bar']))); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The response body does not contain a valid JSON array.'); $this->context->assertResponseBodyJsonArrayMaxLength(2); } /** - * @expectedException OutOfRangeException - * @expectedExceptionMessage error message * @covers ::assertResponseBodyContainsJson * @covers ::getResponseBody - * @group assertions */ - public function testAssertingThatTheResponseBodyContainsJsonCanFail() { + public function testAssertingThatTheResponseBodyContainsJsonCanFail() : void { $this->mockHandler->append(new Response(200, [], '{"foo":"bar"}')); $this->context->requestPath('/some/path'); $this->comparator ->expects($this->once()) ->method('compare') ->with(['bar' => 'foo'], ['foo' => 'bar']) - ->will($this->throwException(new OutOfRangeException('error message'))); + ->willThrowException(new OutOfRangeException('error message')); + $this->expectException(OutOfRangeException::class); + $this->expectExceptionMessage('error message'); $this->context->assertResponseBodyContainsJson(new PyStringNode(['{"bar":"foo"}'], 1)); } /** - * @expectedException Imbo\BehatApiExtension\Exception\AssertionFailedException - * @expectedExceptionMessage Comparator did not return in a correct manner. Marking assertion as failed. * @covers ::setArrayContainsComparator * @covers ::assertResponseBodyContainsJson * @covers ::getResponseBody - * @group assertions */ - public function testWillThrowExceptionWhenArrayContainsComparatorDoesNotReturnInACorrectMannerWhenCheckingTheResponseBodyForJson() { + public function testWillThrowExceptionWhenArrayContainsComparatorDoesNotReturnInACorrectMannerWhenCheckingTheResponseBodyForJson() : void { $this->mockHandler->append(new Response(200, [], '{"foo":"bar"}')); $this->context->requestPath('/some/path'); $this->comparator ->expects($this->once()) ->method('compare') ->with(['bar' => 'foo'], ['foo' => 'bar']) - ->will($this->returnValue(null)); + ->willReturn(false); + $this->expectException(AssertionFailedException::class); + $this->expectExceptionMessage('Comparator did not return in a correct manner. Marking assertion as failed.'); $this->context->assertResponseBodyContainsJson(new PyStringNode(['{"bar":"foo"}'], 1)); } /** - * @expectedException RuntimeException - * @expectedExceptionMessage The request has not been made yet, so no response object exists. * @covers ::assertResponseBodyContainsJson * @covers ::requireResponse - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseContainsJsonAndNoResponseExist() { + public function testThrowsExceptionWhenAssertingThatTheResponseContainsJsonAndNoResponseExist() : void { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The request has not been made yet, so no response object exists.'); $this->context->assertResponseBodyContainsJson(new PyStringNode(['{"foo":"bar"}'], 1)); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The response body does not contain valid JSON data. * @covers ::assertResponseBodyContainsJson * @covers ::getResponseBody - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheResponseContainsJsonAndTheResponseContainsInvalidData() { + public function testThrowsExceptionWhenAssertingThatTheResponseContainsJsonAndTheResponseContainsInvalidData() : void { $this->mockHandler->append(new Response(200, [], "{'foo':'bar'}")); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The response body does not contain valid JSON data.'); $this->context->assertResponseBodyContainsJson(new PyStringNode(['{"foo":"bar"}'], 1)); } /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The supplied parameter is not a valid JSON object. * @covers ::assertResponseBodyContainsJson * @covers ::jsonDecode - * @group assertions */ - public function testThrowsExceptionWhenAssertingThatTheBodyContainsJsonAndTheParameterFromTheTestIsInvalid() { + public function testThrowsExceptionWhenAssertingThatTheBodyContainsJsonAndTheParameterFromTheTestIsInvalid() : void { $this->mockHandler->append(new Response(200, [], '{"foo":"bar"}')); $this->context->requestPath('/some/path'); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The supplied parameter is not a valid JSON object.'); $this->context->assertResponseBodyContainsJson(new PyStringNode(["{'foo':'bar'}"], 1)); } @@ -1936,13 +1856,13 @@ public function testThrowsExceptionWhenAssertingThatTheBodyContainsJsonAndThePar * @covers ::setRequestMethod * @see https://github.com/imbo/behat-api-extension/issues/51 */ - public function testUsesHttpGetByDefaultWhenRequesting() { + public function testUsesHttpGetByDefaultWhenRequesting() : void { $this->mockHandler->append(new Response(200), new Response(200)); $this->context->requestPath('/some/path', 'POST'); $this->context->requestPath('/some/path'); - $this->assertSame(2, count($this->historyContainer)); + $this->assertCount(2, $this->historyContainer); $this->assertSame( 'POST', diff --git a/tests/Context/Initializer/ApiClientAwareInitializerTest.php b/tests/Context/Initializer/ApiClientAwareInitializerTest.php index 8db447e..164b310 100644 --- a/tests/Context/Initializer/ApiClientAwareInitializerTest.php +++ b/tests/Context/Initializer/ApiClientAwareInitializerTest.php @@ -1,38 +1,32 @@ - 'http://localhost:123']); - $initializer->initializeContext($this->createMock(ApiClientAwareContext::class)); - } - - /** - * @covers ::initializeContext - * @covers ::validateConnection * @covers ::__construct */ - public function testInjectsClientWhenInitializingContext() { + public function testInjectsClientWhenInitializingContext() : void { // Set up a socket for the test case, try all ports between 8000 and 8079. If no ports are // available the test case will be marked as skipped. This is to get past the base URI // validation set_error_handler(function() { return true; }); $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + if (false === $sock) { + $this->fail('Unable to create socket'); + } + + $result = false; + for ($port = 8000; $port < 8079; $port++) { if ($result = socket_bind($sock, 'localhost', $port)) { break; @@ -47,7 +41,9 @@ public function testInjectsClientWhenInitializingContext() { } // Listen for connections - socket_listen($sock); + if (!socket_listen($sock)) { + $this->markTestSkipped('Unable to listen for a connection, skipping test for now.'); + } $context = $this->createMock(ApiClientAwareContext::class); $context diff --git a/tests/Context/Initializer/ArrayContainsComparatorAwareInitializerTest.php b/tests/Context/Initializer/ArrayContainsComparatorAwareInitializerTest.php index 0b44f20..94e00d8 100644 --- a/tests/Context/Initializer/ArrayContainsComparatorAwareInitializerTest.php +++ b/tests/Context/Initializer/ArrayContainsComparatorAwareInitializerTest.php @@ -1,20 +1,19 @@ -createMock(ArrayContainsComparator::class); $comparator ->expects($this->exactly(8)) @@ -29,25 +28,24 @@ public function testInitializerInjectsDefaultMatcherFunctions() { ['lt', $this->isInstanceOf(Matcher\LessThan::class)], ['jwt', $this->isInstanceOf(Matcher\JWT::class)] ) - ->will($this->returnSelf()); + ->willReturnSelf(); - $initializer = new ArrayContainsComparatorAwareInitializer($comparator); + new ArrayContainsComparatorAwareInitializer($comparator); } /** * @covers ::initializeContext */ - public function testInjectsComparatorWhenInitializingContext() { + public function testInjectsComparatorWhenInitializingContext() : void { $comparator = $this->createMock(ArrayContainsComparator::class); $comparator ->expects($this->exactly(8)) ->method('addFunction') - ->will($this->returnSelf()); + ->willReturnSelf(); $context = $this->createMock(ArrayContainsComparatorAwareContext::class); $context->expects($this->once())->method('setArrayContainsComparator')->with($comparator); - $initializer = new ArrayContainsComparatorAwareInitializer($comparator); - $initializer->initializeContext($context); + (new ArrayContainsComparatorAwareInitializer($comparator))->initializeContext($context); } } diff --git a/tests/Exception/ArrayContainsComparatorExceptionTest.php b/tests/Exception/ArrayContainsComparatorExceptionTest.php index 22af020..dc49233 100644 --- a/tests/Exception/ArrayContainsComparatorExceptionTest.php +++ b/tests/Exception/ArrayContainsComparatorExceptionTest.php @@ -1,19 +1,17 @@ -, haystack: array, formattedMessage: string}> */ - public function getExceptionData() { + public function getExceptionData() : array { return [ 'with no needle / haystack' => [ 'message' => $someMessage = 'some message', @@ -35,8 +33,8 @@ public function getExceptionData() { ], 'with needle and haystack' => [ 'message' => $someMessage = 'some message', - 'needle' => $needle = ['needle' => 'value'], - 'haystack' => $haystack = ['haystack' => 'value'], + 'needle' => ['needle' => 'value'], + 'haystack' => ['haystack' => 'value'], 'formattedMessage' => << $needle + * @param array $haystack * @param string $formattedMessage */ - public function testCanProperlyFormatErrorMessages($message, array $needle, array $haystack, $formattedMessage) { + public function testCanProperlyFormatErrorMessages(string $message, array $needle, array $haystack, string $formattedMessage) : void { + $this->expectException(ArrayContainsComparatorException::class); $this->expectExceptionMessage($formattedMessage); throw new ArrayContainsComparatorException($message, 0, null, $needle, $haystack); } diff --git a/tests/ServiceContainer/BehatApiExtensionTest.php b/tests/ServiceContainer/BehatApiExtensionTest.php index 6d0f966..86651a1 100644 --- a/tests/ServiceContainer/BehatApiExtensionTest.php +++ b/tests/ServiceContainer/BehatApiExtensionTest.php @@ -1,24 +1,19 @@ -extension = new BehatApiExtension(); } @@ -26,9 +21,9 @@ public function setUp() { * @covers ::getConfigKey * @covers ::configure */ - public function testCanBuildConfiguration() { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root($this->extension->getConfigKey()); + public function testCanBuildConfiguration() : void { + /** @var ArrayNodeDefinition */ + $rootNode = (new TreeBuilder($this->extension->getConfigKey()))->getRootNode(); // Configure the root node builder $this->extension->configure($rootNode); @@ -46,9 +41,9 @@ public function testCanBuildConfiguration() { /** * @covers ::configure */ - public function testCanOverrideDefaultValuesWhenBuildingConfiguration() { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root($this->extension->getConfigKey()); + public function testCanOverrideDefaultValuesWhenBuildingConfiguration() : void { + /** @var ArrayNodeDefinition */ + $rootNode = (new TreeBuilder($this->extension->getConfigKey()))->getRootNode(); // Configure the root node builder $this->extension->configure($rootNode);