diff --git a/.gitattributes b/.gitattributes index 94213456..cb903a56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,12 +1,13 @@ -/docs export-ignore -/test export-ignore -/.gitattributes export-ignore -/.github export-ignore -/.gitignore export-ignore -/*.yml export-ignore -/CONTRIBUTING.md export-ignore -/*.dist export-ignore -/phpbench.json export-ignore -/composer.lock export-ignore -/README.md export-ignore -/Makefile export-ignore +/docs export-ignore +/test export-ignore +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/*.yml export-ignore +/CONTRIBUTING.md export-ignore +/*.dist export-ignore +/phpbench.json export-ignore +/composer.lock export-ignore +/README.md export-ignore +/Makefile export-ignore +/composer-require-checker.json export-ignore diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 609f60bb..a3075cdf 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -16,6 +16,7 @@ jobs: - "locked" php-version: - "7.4" + - "8.0" operating-system: - "ubuntu-latest" @@ -31,14 +32,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index f25fbe95..11a72075 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -31,14 +31,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/.github/workflows/composer-json-lint.yml b/.github/workflows/composer-json-lint.yml new file mode 100644 index 00000000..bbcbcf9d --- /dev/null +++ b/.github/workflows/composer-json-lint.yml @@ -0,0 +1,67 @@ +name: "Lint composer.json" + +on: + pull_request: + push: + +jobs: + coding-standards: + name: "Lint composer.json" + + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + dependencies: + - "highest" + php-version: + - "7.4" + operating-system: + - "ubuntu-latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + ini-values: memory_limit=-1 + tools: composer:v2, composer-normalize, composer-require-checker, composer-unused + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: "Cache dependencies" + uses: "actions/cache@v2" + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" + + - name: "Install lowest dependencies" + if: ${{ matrix.dependencies == 'lowest' }} + run: "composer update --prefer-lowest --no-interaction --no-progress" + + - name: "Install highest dependencies" + if: ${{ matrix.dependencies == 'highest' }} + run: "composer update --no-interaction --no-progress" + + - name: "Install locked dependencies" + if: ${{ matrix.dependencies == 'locked' }} + run: "composer install --no-interaction --no-progress" + + - name: "Validate composer.json" + run: "composer validate --strict" + + - name: "Normalize composer.json" + run: "composer-normalize --dry-run" + + - name: "Check composer.json explicit dependencies" + run: "composer-require-checker check --config-file=$(realpath composer-require-checker.json)" + + - name: "Check composer.json unused dependencies" + run: "composer-unused" diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index 0707bb2b..d683fb7f 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -31,14 +31,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index fe8989c7..908e6da1 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -19,6 +19,7 @@ jobs: - "development" php-version: - "7.4" + - "8.0" operating-system: - "ubuntu-latest" @@ -29,18 +30,21 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: + coverage: "none" php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} @@ -62,7 +66,7 @@ jobs: run: "make phpunit" phpunit-rc: - name: "PHPUnit tests on PHP 8" + name: "PHPUnit tests (nightly)" runs-on: ${{ matrix.operating-system }} @@ -71,7 +75,7 @@ jobs: dependencies: - "locked" php-version: - - "8.0" + - "8.1" operating-system: - "ubuntu-latest" @@ -87,14 +91,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install locked dependencies" if: ${{ matrix.dependencies == 'locked' }} diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml new file mode 100644 index 00000000..3b69e7b3 --- /dev/null +++ b/.github/workflows/release-on-milestone-closed.yml @@ -0,0 +1,72 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: "Automatic Releases" + +on: + milestone: + types: + - "closed" + +jobs: + release: + name: "GIT tag, release & create merge-up PR" + runs-on: ubuntu-latest + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Release" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:release" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create Merge-Up Pull Request" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:create-merge-up-pull-request" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create and/or Switch to new Release Branch" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Bump Changelog Version On Originating Release Branch" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:bump-changelog" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create new milestones" + uses: "laminas/automatic-releases@v1" + with: + command-name: "laminas:automatic-releases:create-milestones" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index db6068ad..7e4c9d52 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -31,14 +31,16 @@ jobs: ini-values: memory_limit=-1 tools: composer:v2, cs2pr + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: "Cache dependencies" uses: "actions/cache@v2" with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" - restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}" + path: ${{ steps.composer-cache.outputs.dir }} + key: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} diff --git a/Makefile b/Makefile index 5f8692ed..734e3d74 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ phpcs: .PHONY: phpstan phpstan: - @vendor/bin/phpstan analyse + @vendor/bin/phpstan analyse --memory-limit=-1 .PHONY: phpbench phpbench: diff --git a/README.md b/README.md index 7c87b1a2..1e30dd8e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Latest Stable Version]](https://packagist.org/packages/lcobucci/jwt) [![Unstable Version]](https://packagist.org/packages/lcobucci/jwt) -[![Build Status]](https://github.com/lcobucci/jwt/actions?query=workflow%3A%22PHPUnit%20Tests%22+branch%3Amaster) +[![Build Status]](https://github.com/lcobucci/jwt/actions?query=workflow%3A%22PHPUnit%20Tests%22+branch%3A4.1.x) [![Code Coverage]](https://codecov.io/gh/lcobucci/jwt) A simple library to work with JSON Web Token and JSON Web Signature based on the [RFC 7519](https://tools.ietf.org/html/rfc7519). @@ -32,5 +32,5 @@ free to check out Auth0's PHP SDK and free plan at [Total Downloads]: https://img.shields.io/packagist/dt/lcobucci/jwt.svg?style=flat-square [Latest Stable Version]: https://img.shields.io/packagist/v/lcobucci/jwt.svg?style=flat-square [Unstable Version]: https://img.shields.io/packagist/vpre/lcobucci/jwt.svg?style=flat-square -[Build Status]: https://img.shields.io/github/workflow/status/lcobucci/jwt/PHPUnit%20tests/master?style=flat-square -[Code Coverage]: https://codecov.io/gh/lcobucci/jwt/branch/master/graph/badge.svg +[Build Status]: https://img.shields.io/github/workflow/status/lcobucci/jwt/PHPUnit%20tests/4.1.x?style=flat-square +[Code Coverage]: https://codecov.io/gh/lcobucci/jwt/branch/4.1.x/graph/badge.svg diff --git a/composer-require-checker.json b/composer-require-checker.json new file mode 100644 index 00000000..3d3e56d1 --- /dev/null +++ b/composer-require-checker.json @@ -0,0 +1,8 @@ +{ + "symbol-whitelist" : [ + "null", "true", "false", + "static", "self", "parent", + "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", + "OpenSSLAsymmetricKey" + ] +} diff --git a/composer.json b/composer.json index b6788da6..a9369762 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,7 @@ { "name": "lcobucci/jwt", - "description": "A simple library to work with JSON Web Token and JSON Web Signature", "type": "library", - "authors": [ - { - "name": "Luís Cobucci", - "email": "lcobucci@gmail.com", - "role": "Developer" - } - ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", "keywords": [ "JWT", "JWS" @@ -16,24 +9,38 @@ "license": [ "BSD-3-Clause" ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], "require": { "php": "^7.4 || ^8.0", + "ext-hash": "*", + "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", + "ext-sodium": "*", "lcobucci/clock": "^2.0" }, "require-dev": { - "infection/infection": "^0.20", + "infection/infection": "^0.21", "lcobucci/coding-standard": "^6.0", - "mikey179/vfsstream": "^1.6", - "phpbench/phpbench": "^0.17", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.0", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^0.12", "phpstan/phpstan-deprecation-rules": "^0.12", "phpstan/phpstan-phpunit": "^0.12", "phpstan/phpstan-strict-rules": "^0.12", "phpunit/php-invoker": "^3.1", - "phpunit/phpunit": "^9.4" + "phpunit/phpunit": "^9.5" + }, + "config": { + "preferred-install": "dist", + "sort-packages": true }, "autoload": { "psr-4": { @@ -49,14 +56,5 @@ ], "Lcobucci\\JWT\\FunctionalTests\\": "test/functional" } - }, - "config": { - "preferred-install": "dist", - "sort-packages": true - }, - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } } } diff --git a/composer.lock b/composer.lock index 188cfb91..a8d189f3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "36f5b286daebc58d9834b6e695dba174", + "content-hash": "c17f93c7167bce48d52cf7cdd2f7f9ae", "packages": [ { "name": "lcobucci/clock", @@ -51,6 +51,10 @@ } ], "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/2.0.x" + }, "funding": [ { "url": "https://github.com/lcobucci", @@ -65,79 +69,18 @@ } ], "packages-dev": [ - { - "name": "beberlei/assert", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/beberlei/assert.git", - "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/5367e3895976b49704ae671f75bc5f0ba1b986ab", - "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-intl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "*", - "phpstan/phpstan": "*", - "phpunit/phpunit": ">=6.0.0", - "yoast/phpunit-polyfills": "^0.1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Assert\\": "lib/Assert" - }, - "files": [ - "lib/Assert/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de", - "role": "Lead Developer" - }, - { - "name": "Richard Quadling", - "email": "rquadling@gmail.com", - "role": "Collaborator" - } - ], - "description": "Thin assertion library for input validation in business models.", - "keywords": [ - "assert", - "assertion", - "validation" - ], - "time": "2020-11-13T20:02:54+00:00" - }, { "name": "composer/xdebug-handler", - "version": "1.4.5", + "version": "1.4.6", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f28d44c286812c714741478d968104c5e604a1d4" + "reference": "f27e06cd9675801df441b3656569b328e04aa37c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4", - "reference": "f28d44c286812c714741478d968104c5e604a1d4", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", + "reference": "f27e06cd9675801df441b3656569b328e04aa37c", "shasum": "" }, "require": { @@ -145,7 +88,8 @@ "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "autoload": { @@ -168,6 +112,11 @@ "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/1.4.6" + }, "funding": [ { "url": "https://packagist.com", @@ -182,26 +131,26 @@ "type": "tidelift" } ], - "time": "2020-11-13T08:04:11+00:00" + "time": "2021-03-25T17:01:18+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.0", + "version": "v0.7.1", "source": { "type": "git", "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7" + "reference": "fe390591e0241955f22eb9ba327d137e501c771c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/fe390591e0241955f22eb9ba327d137e501c771c", + "reference": "fe390591e0241955f22eb9ba327d137e501c771c", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", "php": ">=5.3", - "squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev" + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" }, "require-dev": { "composer/composer": "*", @@ -248,39 +197,40 @@ "stylecheck", "tests" ], - "time": "2020-06-25T14:57:39+00:00" + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2020-12-07T18:04:37+00:00" }, { "name": "doctrine/annotations", - "version": "1.11.1", + "version": "1.13.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad" + "reference": "5b668aef16090008790395c02c893b1ba13f7e08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/ce77a7ba1770462cd705a91a151b6c3746f9c6ad", - "reference": "ce77a7ba1770462cd705a91a151b6c3746f9c6ad", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5b668aef16090008790395c02c893b1ba13f7e08", + "reference": "5b668aef16090008790395c02c893b1ba13f7e08", "shasum": "" }, "require": { "doctrine/lexer": "1.*", "ext-tokenizer": "*", - "php": "^7.1 || ^8.0" + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "1.*", + "doctrine/cache": "^1.11 || ^2.0", "doctrine/coding-standard": "^6.0 || ^8.1", "phpstan/phpstan": "^0.12.20", - "phpunit/phpunit": "^7.5 || ^9.1.5" + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" @@ -319,27 +269,31 @@ "docblock", "parser" ], - "time": "2020-10-26T10:28:16+00:00" + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.13.2" + }, + "time": "2021-08-05T19:00:23+00:00" }, { "name": "doctrine/coding-standard", - "version": "8.2.0", + "version": "8.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/coding-standard.git", - "reference": "529d385bb3790431080493c0fe7adaec39df368a" + "reference": "f595b060799c1a0d76ead16981804eaa0bbcd8d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/529d385bb3790431080493c0fe7adaec39df368a", - "reference": "529d385bb3790431080493c0fe7adaec39df368a", + "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/f595b060799c1a0d76ead16981804eaa0bbcd8d6", + "reference": "f595b060799c1a0d76ead16981804eaa0bbcd8d6", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", "php": "^7.1 || ^8.0", - "slevomat/coding-standard": "^6.3.9", - "squizlabs/php_codesniffer": "^3.5.5" + "slevomat/coding-standard": "^6.4.1", + "squizlabs/php_codesniffer": "^3.5.8" }, "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", @@ -370,7 +324,11 @@ "standard", "style" ], - "time": "2020-10-25T14:56:19+00:00" + "support": { + "issues": "https://github.com/doctrine/coding-standard/issues", + "source": "https://github.com/doctrine/coding-standard/tree/8.2.1" + }, + "time": "2021-04-03T10:54:55+00:00" }, { "name": "doctrine/instantiator", @@ -421,6 +379,10 @@ "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", @@ -497,6 +459,10 @@ "parser", "php" ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.1" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -552,6 +518,10 @@ } ], "description": "Abstract Test Framework Adapter for Infection", + "support": { + "issues": "https://github.com/infection/abstract-testframework-adapter/issues", + "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.3" + }, "time": "2020-08-30T13:50:12+00:00" }, { @@ -604,20 +574,24 @@ } ], "description": "Infection Extension Installer", + "support": { + "issues": "https://github.com/infection/extension-installer/issues", + "source": "https://github.com/infection/extension-installer/tree/0.1.1" + }, "time": "2020-04-25T22:40:05+00:00" }, { "name": "infection/include-interceptor", - "version": "0.2.4", + "version": "0.2.5", "source": { "type": "git", "url": "https://github.com/infection/include-interceptor.git", - "reference": "e3cf9317a7fd554ab60a5587f028b16418cc4264" + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/include-interceptor/zipball/e3cf9317a7fd554ab60a5587f028b16418cc4264", - "reference": "e3cf9317a7fd554ab60a5587f028b16418cc4264", + "url": "https://api.github.com/repos/infection/include-interceptor/zipball/0cc76d95a79d9832d74e74492b0a30139904bdf7", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7", "shasum": "" }, "require-dev": { @@ -646,20 +620,34 @@ } ], "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", - "time": "2020-08-07T22:40:37+00:00" + "support": { + "issues": "https://github.com/infection/include-interceptor/issues", + "source": "https://github.com/infection/include-interceptor/tree/0.2.5" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-09T10:03:57+00:00" }, { "name": "infection/infection", - "version": "0.20.2", + "version": "0.21.5", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "6035c1566af6a5a8d833a276351e5e18ed412305" + "reference": "cee1ab77f755c9faa8e5b75ba720125037942c80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/6035c1566af6a5a8d833a276351e5e18ed412305", - "reference": "6035c1566af6a5a8d833a276351e5e18ed412305", + "url": "https://api.github.com/repos/infection/infection/zipball/cee1ab77f755c9faa8e5b75ba720125037942c80", + "reference": "cee1ab77f755c9faa8e5b75ba720125037942c80", "shasum": "" }, "require": { @@ -671,11 +659,12 @@ "infection/extension-installer": "^0.1.0", "infection/include-interceptor": "^0.2.4", "justinrainbow/json-schema": "^5.2", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.10.3", "ocramius/package-versions": "^1.2 || ^2.0", "ondram/ci-detector": "^3.3.0", "php": "^7.4 || ^8.0", - "sanmai/pipeline": "^3.1 || ^5.0", + "sanmai/later": "^0.1.1", + "sanmai/pipeline": "^5.1", "sebastian/diff": "^3.0.2 || ^4.0", "seld/jsonlint": "^1.7", "symfony/console": "^3.4.29 || ^4.1.19 || ^5.0", @@ -700,10 +689,9 @@ "phpstan/phpstan-strict-rules": "^0.12.5", "phpstan/phpstan-webmozart-assert": "^0.12.2", "phpunit/phpunit": "^9.3.11", - "symfony/phpunit-bridge": "^4.4.14 || ^5.1.6", + "symfony/phpunit-bridge": "^4.4.18 || ^5.1.10", "symfony/yaml": "^5.0", - "thecodingmachine/phpstan-safe-rule": "^1.0", - "vimeo/psalm": "^4.0" + "thecodingmachine/phpstan-safe-rule": "^1.0" }, "bin": [ "bin/infection" @@ -757,20 +745,34 @@ "testing", "unit testing" ], - "time": "2020-11-20T17:15:57+00:00" + "support": { + "issues": "https://github.com/infection/infection/issues", + "source": "https://github.com/infection/infection/tree/0.21.5" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-03-31T11:40:44+00:00" }, { "name": "justinrainbow/json-schema", - "version": "5.2.10", + "version": "5.2.11", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" + "reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", - "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ab6744b7296ded80f8cc4f9509abbff393399aa", + "reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa", "shasum": "" }, "require": { @@ -823,7 +825,11 @@ "json", "schema" ], - "time": "2020-05-27T16:41:55+00:00" + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.11" + }, + "time": "2021-07-22T09:24:00+00:00" }, { "name": "lcobucci/coding-standard", @@ -858,164 +864,24 @@ } ], "description": "Lcobucci's Coding Standard", - "time": "2020-09-05T21:36:16+00:00" - }, - { - "name": "lstrojny/functional-php", - "version": "1.14.1", - "source": { - "type": "git", - "url": "https://github.com/lstrojny/functional-php.git", - "reference": "9e8363e3cb9db924327f51b5804f4dfba03605aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/lstrojny/functional-php/zipball/9e8363e3cb9db924327f51b5804f4dfba03605aa", - "reference": "9e8363e3cb9db924327f51b5804f4dfba03605aa", - "shasum": "" - }, - "require": { - "php": "~7" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.14", - "phpunit/phpunit": "~7", - "squizlabs/php_codesniffer": "~3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Functional\\": "src/Functional" - }, - "files": [ - "src/Functional/Ary.php", - "src/Functional/Average.php", - "src/Functional/ButLast.php", - "src/Functional/Capture.php", - "src/Functional/ConstFunction.php", - "src/Functional/CompareOn.php", - "src/Functional/CompareObjectHashOn.php", - "src/Functional/Compose.php", - "src/Functional/Concat.php", - "src/Functional/Contains.php", - "src/Functional/Converge.php", - "src/Functional/Curry.php", - "src/Functional/CurryN.php", - "src/Functional/Difference.php", - "src/Functional/DropFirst.php", - "src/Functional/DropLast.php", - "src/Functional/Each.php", - "src/Functional/Equal.php", - "src/Functional/ErrorToException.php", - "src/Functional/Every.php", - "src/Functional/False.php", - "src/Functional/Falsy.php", - "src/Functional/Filter.php", - "src/Functional/First.php", - "src/Functional/FirstIndexOf.php", - "src/Functional/FlatMap.php", - "src/Functional/Flatten.php", - "src/Functional/Flip.php", - "src/Functional/GreaterThan.php", - "src/Functional/GreaterThanOrEqual.php", - "src/Functional/Group.php", - "src/Functional/Head.php", - "src/Functional/Id.php", - "src/Functional/IfElse.php", - "src/Functional/Identical.php", - "src/Functional/IndexesOf.php", - "src/Functional/Intersperse.php", - "src/Functional/Invoke.php", - "src/Functional/InvokeFirst.php", - "src/Functional/InvokeIf.php", - "src/Functional/InvokeLast.php", - "src/Functional/Invoker.php", - "src/Functional/Last.php", - "src/Functional/LastIndexOf.php", - "src/Functional/LessThan.php", - "src/Functional/LessThanOrEqual.php", - "src/Functional/LexicographicCompare.php", - "src/Functional/Map.php", - "src/Functional/Matching.php", - "src/Functional/Maximum.php", - "src/Functional/Memoize.php", - "src/Functional/Minimum.php", - "src/Functional/None.php", - "src/Functional/Noop.php", - "src/Functional/Not.php", - "src/Functional/OmitKeys.php", - "src/Functional/PartialAny.php", - "src/Functional/PartialLeft.php", - "src/Functional/PartialMethod.php", - "src/Functional/PartialRight.php", - "src/Functional/Partition.php", - "src/Functional/Pick.php", - "src/Functional/Pluck.php", - "src/Functional/Poll.php", - "src/Functional/Product.php", - "src/Functional/Ratio.php", - "src/Functional/ReduceLeft.php", - "src/Functional/ReduceRight.php", - "src/Functional/Reindex.php", - "src/Functional/Reject.php", - "src/Functional/Repeat.php", - "src/Functional/Retry.php", - "src/Functional/Select.php", - "src/Functional/SelectKeys.php", - "src/Functional/SequenceConstant.php", - "src/Functional/SequenceExponential.php", - "src/Functional/SequenceLinear.php", - "src/Functional/Some.php", - "src/Functional/Sort.php", - "src/Functional/Sum.php", - "src/Functional/SuppressError.php", - "src/Functional/Tap.php", - "src/Functional/Tail.php", - "src/Functional/TailRecursion.php", - "src/Functional/TakeLeft.php", - "src/Functional/TakeRight.php", - "src/Functional/True.php", - "src/Functional/Truthy.php", - "src/Functional/Unique.php", - "src/Functional/ValueToKey.php", - "src/Functional/With.php", - "src/Functional/Zip.php", - "src/Functional/ZipAll.php" - ] + "support": { + "issues": "https://github.com/lcobucci/coding-standard/issues", + "source": "https://github.com/lcobucci/coding-standard/tree/6.0.1" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Lars Strojny", - "email": "lstrojny@php.net", - "homepage": "http://usrportage.de" - }, - { - "name": "Max Beutel", - "email": "nash12@gmail.com" - } - ], - "description": "Functional primitives for PHP", - "keywords": [ - "functional" - ], - "time": "2020-10-12T09:48:50+00:00" + "time": "2020-09-05T21:36:16+00:00" }, { "name": "mikey179/vfsstream", - "version": "v1.6.8", + "version": "v1.6.10", "source": { "type": "git", "url": "https://github.com/bovigo/vfsStream.git", - "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe" + "reference": "250c0825537d501e327df879fb3d4cd751933b85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe", - "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/250c0825537d501e327df879fb3d4cd751933b85", + "reference": "250c0825537d501e327df879fb3d4cd751933b85", "shasum": "" }, "require": { @@ -1048,7 +914,12 @@ ], "description": "Virtual file system to mock the real file system in unit tests.", "homepage": "http://vfs.bovigo.org/", - "time": "2019-10-30T15:31:00+00:00" + "support": { + "issues": "https://github.com/bovigo/vfsStream/issues", + "source": "https://github.com/bovigo/vfsStream/tree/master", + "wiki": "https://github.com/bovigo/vfsStream/wiki" + }, + "time": "2021-09-25T08:05:01+00:00" }, { "name": "myclabs/deep-copy", @@ -1096,6 +967,10 @@ "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", @@ -1106,16 +981,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.10.2", + "version": "v4.13.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "658f1be311a230e0907f5dfe0213742aff0596de" + "reference": "50953a2691a922aa1769461637869a0a2faa3f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/658f1be311a230e0907f5dfe0213742aff0596de", - "reference": "658f1be311a230e0907f5dfe0213742aff0596de", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", + "reference": "50953a2691a922aa1769461637869a0a2faa3f53", "shasum": "" }, "require": { @@ -1154,7 +1029,11 @@ "parser", "php" ], - "time": "2020-09-26T10:30:38+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" + }, + "time": "2021-09-20T12:20:58+00:00" }, { "name": "ocramius/package-versions", @@ -1284,20 +1163,24 @@ "teamcity", "travis" ], + "support": { + "issues": "https://github.com/OndraM/ci-detector/issues", + "source": "https://github.com/OndraM/ci-detector/tree/main" + }, "time": "2020-09-04T11:21:14+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { @@ -1340,20 +1223,24 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2020-06-27T14:33:11+00:00" + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", - "version": "3.0.2", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0", - "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { @@ -1387,32 +1274,39 @@ } ], "description": "Library for handling version information and constraints", - "time": "2020-06-27T14:39:04+00:00" + "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": "phpbench/container", - "version": "1.2.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", - "reference": "2f2b269b3b8cb9a0053cf98f1c3a84866fe7f0e2" + "reference": "4af6c2619296e95b72409fd6244f000276277047" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/container/zipball/2f2b269b3b8cb9a0053cf98f1c3a84866fe7f0e2", - "reference": "2f2b269b3b8cb9a0053cf98f1c3a84866fe7f0e2", + "url": "https://api.github.com/repos/phpbench/container/zipball/4af6c2619296e95b72409fd6244f000276277047", + "reference": "4af6c2619296e95b72409fd6244f000276277047", "shasum": "" }, "require": { - "psr/container": "^1.0" + "psr/container": "^1.0|^2.0", + "symfony/options-resolver": "^4.2 || ^5.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.36" + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1431,28 +1325,34 @@ } ], "description": "Simple, configurable, service container.", - "time": "2020-08-23T23:43:00+00:00" + "support": { + "issues": "https://github.com/phpbench/container/issues", + "source": "https://github.com/phpbench/container/tree/2.2.0" + }, + "time": "2021-07-14T20:56:29+00:00" }, { "name": "phpbench/dom", - "version": "0.2.0", + "version": "0.3.2", "source": { "type": "git", "url": "https://github.com/phpbench/dom.git", - "reference": "b135378dd0004c05ba5446aeddaf0b83339c1c4c" + "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/dom/zipball/b135378dd0004c05ba5446aeddaf0b83339c1c4c", - "reference": "b135378dd0004c05ba5446aeddaf0b83339c1c4c", + "url": "https://api.github.com/repos/phpbench/dom/zipball/b013b717832ddbaadf2a40984b04bc66af9a7110", + "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110", "shasum": "" }, "require": { "ext-dom": "*", - "php": "^5.4|^7.0" + "php": "^7.2||^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "friendsofphp/php-cs-fixer": "^2.18", + "phpstan/phpstan": "^0.12.83", + "phpunit/phpunit": "^8.0||^9.0" }, "type": "library", "extra": { @@ -1476,37 +1376,40 @@ } ], "description": "DOM wrapper to simplify working with the PHP DOM implementation", - "time": "2016-02-27T12:15:56+00:00" + "support": { + "issues": "https://github.com/phpbench/dom/issues", + "source": "https://github.com/phpbench/dom/tree/0.3.2" + }, + "time": "2021-09-24T15:26:07+00:00" }, { "name": "phpbench/phpbench", - "version": "0.17.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "3211debc3afb9da79d796cf7471d52cad97b17f1" + "reference": "b1ea39a7a3cf996065638fd416f3653bcb5a5aa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/3211debc3afb9da79d796cf7471d52cad97b17f1", - "reference": "3211debc3afb9da79d796cf7471d52cad97b17f1", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/b1ea39a7a3cf996065638fd416f3653bcb5a5aa3", + "reference": "b1ea39a7a3cf996065638fd416f3653bcb5a5aa3", "shasum": "" }, "require": { - "beberlei/assert": "^2.4 || ^3.0", - "doctrine/annotations": "^1.2.7", + "doctrine/annotations": "^1.13", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", - "lstrojny/functional-php": "1.0 || ^1.2.3", - "php": "^7.2", - "phpbench/container": "~1.2", - "phpbench/dom": "~0.2.0", + "ext-tokenizer": "*", + "php": "^7.3 || ^8.0", + "phpbench/container": "^2.1", + "phpbench/dom": "~0.3.1", + "psr/log": "^1.1", "seld/jsonlint": "^1.1", "symfony/console": "^4.2 || ^5.0", - "symfony/debug": "^4.2 || ^5.0", "symfony/filesystem": "^4.2 || ^5.0", "symfony/finder": "^4.2 || ^5.0", "symfony/options-resolver": "^4.2 || ^5.0", @@ -1514,15 +1417,16 @@ "webmozart/path-util": "^2.3" }, "require-dev": { - "doctrine/dbal": "^2.4", - "friendsofphp/php-cs-fixer": "^2.13.1", - "padraic/phar-updater": "^1.0", - "phpspec/prophecy": "^1.8", + "dantleech/invoke": "^2.0", + "friendsofphp/php-cs-fixer": "^3.0", + "jangregor/phpstan-prophecy": "^0.8.1", + "phpspec/prophecy": "^1.12", "phpstan/phpstan": "^0.12.7", - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^8.5.8 || ^9.0", + "symfony/error-handler": "^5.2", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { - "ext-curl": "For (web) reports extension", "ext-xdebug": "For Xdebug profiling extension." }, "bin": [ @@ -1531,13 +1435,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.1-dev" } }, "autoload": { + "files": [ + "lib/Report/Func/functions.php" + ], "psr-4": { "PhpBench\\": "lib/", - "PhpBench\\Extensions\\Dbal\\": "extensions/dbal/lib/", "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/", "PhpBench\\Extensions\\Reports\\": "extensions/reports/lib/" } @@ -1553,7 +1459,17 @@ } ], "description": "PHP Benchmarking Framework", - "time": "2020-06-13T11:59:17+00:00" + "support": { + "issues": "https://github.com/phpbench/phpbench/issues", + "source": "https://github.com/phpbench/phpbench/tree/1.1.2" + }, + "funding": [ + { + "url": "https://github.com/dantleech", + "type": "github" + } + ], + "time": "2021-09-25T19:02:44+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -1602,6 +1518,10 @@ "reflection", "static analysis" ], + "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" }, { @@ -1654,20 +1574,24 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "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": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", "shasum": "" }, "require": { @@ -1675,7 +1599,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -1699,37 +1624,41 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-09-17T18:55:26+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + }, + "time": "2021-09-17T15:28:14+00:00" }, { "name": "phpspec/prophecy", - "version": "1.12.1", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8ce87516be71aae9b956f81906aaf0338e0d8a2d", - "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", - "phpunit/phpunit": "^8.0 || ^9.0 <9.3" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -1762,20 +1691,24 @@ "spy", "stub" ], - "time": "2020-09-29T09:10:42+00:00" + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + }, + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpstan/extension-installer", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/extension-installer.git", - "reference": "5c2da3846819f951385cb6a25d3277051481c48a" + "reference": "66c7adc9dfa38b6b5838a9fb728b68a7d8348051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/5c2da3846819f951385cb6a25d3277051481c48a", - "reference": "5c2da3846819f951385cb6a25d3277051481c48a", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/66c7adc9dfa38b6b5838a9fb728b68a7d8348051", + "reference": "66c7adc9dfa38b6b5838a9fb728b68a7d8348051", "shasum": "" }, "require": { @@ -1785,13 +1718,9 @@ }, "require-dev": { "composer/composer": "^1.8", - "consistence/coding-standard": "^3.8", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "phing/phing": "^2.16", + "phing/phing": "^2.16.3", "php-parallel-lint/php-parallel-lint": "^1.2.0", - "phpstan/phpstan-strict-rules": "^0.11", - "slevomat/coding-standard": "^5.0.4" + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12" }, "type": "composer-plugin", "extra": { @@ -1807,7 +1736,11 @@ "MIT" ], "description": "Composer plugin for automatic installation of PHPStan extensions", - "time": "2020-08-30T12:06:42+00:00" + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.1.0" + }, + "time": "2020-12-13T13:06:13+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -1856,20 +1789,24 @@ "MIT" ], "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/master" + }, "time": "2020-08-03T20:32:43+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.57", + "version": "0.12.99", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f9909d1d0c44b4cbaf72babcf80e8f14d6fdd55b" + "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9909d1d0c44b4cbaf72babcf80e8f14d6fdd55b", - "reference": "f9909d1d0c44b4cbaf72babcf80e8f14d6fdd55b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", + "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", "shasum": "" }, "require": { @@ -1898,11 +1835,19 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/0.12.99" + }, "funding": [ { "url": "https://github.com/ondrejmirtes", "type": "github" }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, { "url": "https://www.patreon.com/phpstan", "type": "patreon" @@ -1912,35 +1857,31 @@ "type": "tidelift" } ], - "time": "2020-11-21T12:53:28+00:00" + "time": "2021-09-12T20:09:55+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", - "version": "0.12.5", + "version": "0.12.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", - "reference": "bfabc6a1b4617fbcbff43f03a4c04eae9bafae21" + "reference": "46dbd43c2db973d2876d6653e53f5c2cc3a01fbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/bfabc6a1b4617fbcbff43f03a4c04eae9bafae21", - "reference": "bfabc6a1b4617fbcbff43f03a4c04eae9bafae21", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/46dbd43c2db973d2876d6653e53f5c2cc3a01fbb", + "reference": "46dbd43c2db973d2876d6653e53f5c2cc3a01fbb", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.26" + "phpstan/phpstan": "^0.12.60" }, "require-dev": { - "consistence/coding-standard": "^3.0.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", + "phing/phing": "^2.16.3", + "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0", - "slevomat/coding-standard": "^4.5.2" + "phpunit/phpunit": "^7.5.20" }, "type": "phpstan-extension", "extra": { @@ -1963,39 +1904,37 @@ "MIT" ], "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", - "time": "2020-07-21T14:52:30+00:00" + "support": { + "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/0.12.6" + }, + "time": "2020-12-13T10:20:54+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "0.12.16", + "version": "0.12.22", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "1dd916d181b0539dea5cd37e91546afb8b107e17" + "reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/1dd916d181b0539dea5cd37e91546afb8b107e17", - "reference": "1dd916d181b0539dea5cd37e91546afb8b107e17", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc", + "reference": "7c01ef93bf128b4ac8bdad38c54b2a4fd6b0b3cc", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.33" + "phpstan/phpstan": "^0.12.92" }, "conflict": { "phpunit/phpunit": "<7.0" }, "require-dev": { - "consistence/coding-standard": "^3.5", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", - "phpstan/phpstan-strict-rules": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", - "satooshi/php-coveralls": "^1.0", - "slevomat/coding-standard": "^4.7.2" + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-strict-rules": "^0.12.6", + "phpunit/phpunit": "^9.5" }, "type": "phpstan-extension", "extra": { @@ -2019,35 +1958,34 @@ "MIT" ], "description": "PHPUnit extensions and rules for PHPStan", - "time": "2020-08-05T13:28:50+00:00" + "support": { + "issues": "https://github.com/phpstan/phpstan-phpunit/issues", + "source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.22" + }, + "time": "2021-08-12T10:53:43+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "0.12.5", + "version": "0.12.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "334898a32217e4605e0f9cfa3d3fc3101bda26be" + "reference": "2b72e8e17d2034145f239126e876e5fb659675e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/334898a32217e4605e0f9cfa3d3fc3101bda26be", - "reference": "334898a32217e4605e0f9cfa3d3fc3101bda26be", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/2b72e8e17d2034145f239126e876e5fb659675e2", + "reference": "2b72e8e17d2034145f239126e876e5fb659675e2", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.33" + "phpstan/phpstan": "^0.12.96" }, "require-dev": { - "consistence/coding-standard": "^3.0.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.0.2", - "jakub-onderka/php-parallel-lint": "^1.0", - "phing/phing": "^2.16.0", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0", - "slevomat/coding-standard": "^4.5.2" + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^0.12.16", + "phpunit/phpunit": "^9.5" }, "type": "phpstan-extension", "extra": { @@ -2070,34 +2008,38 @@ "MIT" ], "description": "Extra strict and opinionated rules for PHPStan", - "time": "2020-08-30T15:42:06+00:00" + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/0.12.11" + }, + "time": "2021-08-21T11:36:27+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.3", + "version": "9.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41" + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6b20e2055f7c29b56cb3870b3de7cc463d7add41", - "reference": "6b20e2055f7c29b56cb3870b3de7cc463d7add41", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.12.0", "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", + "sebastian/lines-of-code": "^1.0.3", "sebastian/version": "^3.0.1", "theseer/tokenizer": "^1.2.0" }, @@ -2137,13 +2079,17 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-10-30T10:46:41+00:00" + "time": "2021-09-17T05:39:03+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2193,6 +2139,10 @@ "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", @@ -2252,6 +2202,10 @@ "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", @@ -2307,6 +2261,10 @@ "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", @@ -2362,6 +2320,10 @@ "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", @@ -2372,16 +2334,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.4.3", + "version": "9.5.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9fa359ff5ddaa5eb2be2bedb08a6a5787a5807ab" + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9fa359ff5ddaa5eb2be2bedb08a6a5787a5807ab", - "reference": "9fa359ff5ddaa5eb2be2bedb08a6a5787a5807ab", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", "shasum": "" }, "require": { @@ -2393,11 +2355,11 @@ "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.1", + "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2", + "phpunit/php-code-coverage": "^9.2.7", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -2411,7 +2373,7 @@ "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3", + "sebastian/type": "^2.3.4", "sebastian/version": "^3.0.2" }, "require-dev": { @@ -2428,7 +2390,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.4-dev" + "dev-master": "9.5-dev" } }, "autoload": { @@ -2457,6 +2419,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -2467,20 +2433,20 @@ "type": "github" } ], - "time": "2020-11-10T12:53:30+00:00" + "time": "2021-09-25T07:38:51+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "psr/cache", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", "shasum": "" }, "require": { @@ -2494,7 +2460,7 @@ }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2507,6 +2473,50 @@ "homepage": "http://www.php-fig.org/" } ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], "description": "Common Container Interface (PHP FIG PSR-11)", "homepage": "https://github.com/php-fig/container", "keywords": [ @@ -2516,20 +2526,24 @@ "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/log", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { @@ -2553,7 +2567,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -2563,7 +2577,68 @@ "psr", "psr-3" ], - "time": "2020-03-23T09:12:05+00:00" + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "sanmai/later", + "version": "0.1.2", + "source": { + "type": "git", + "url": "https://github.com/sanmai/later.git", + "reference": "9b659fecef2030193fd02402955bc39629d5606f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/later/zipball/9b659fecef2030193fd02402955bc39629d5606f", + "reference": "9b659fecef2030193fd02402955bc39629d5606f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.13", + "infection/infection": ">=0.10.5", + "phan/phan": ">=2", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": ">=7.4", + "vimeo/psalm": ">=2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Later\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } + ], + "description": "Later: deferred wrapper object", + "support": { + "issues": "https://github.com/sanmai/later/issues", + "source": "https://github.com/sanmai/later/tree/0.1.2" + }, + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2021-01-02T10:26:44+00:00" }, { "name": "sanmai/pipeline", @@ -2618,6 +2693,10 @@ } ], "description": "General-purpose collections pipeline", + "support": { + "issues": "https://github.com/sanmai/pipeline/issues", + "source": "https://github.com/sanmai/pipeline/tree/v5.1.0" + }, "funding": [ { "url": "https://github.com/sanmai", @@ -2670,6 +2749,10 @@ ], "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", @@ -2722,6 +2805,10 @@ ], "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", @@ -2773,6 +2860,10 @@ ], "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", @@ -2843,6 +2934,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2896,6 +2991,10 @@ ], "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", @@ -2958,6 +3057,10 @@ "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", @@ -3017,6 +3120,10 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3090,6 +3197,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -3100,16 +3211,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.2", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", - "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { @@ -3150,26 +3261,30 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-10-26T15:55:19+00:00" + "time": "2021-06-11T13:31:12+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "acf76492a65401babcf5283296fa510782783a7a" + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/acf76492a65401babcf5283296fa510782783a7a", - "reference": "acf76492a65401babcf5283296fa510782783a7a", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", "shasum": "" }, "require": { @@ -3203,13 +3318,17 @@ ], "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-10-26T17:03:56+00:00" + "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", @@ -3256,6 +3375,10 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "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", @@ -3307,6 +3430,10 @@ ], "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": [ { "url": "https://github.com/sebastianbergmann", @@ -3366,6 +3493,10 @@ ], "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", @@ -3417,6 +3548,10 @@ ], "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", @@ -3427,16 +3562,16 @@ }, { "name": "sebastian/type", - "version": "2.3.1", + "version": "2.3.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", - "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", "shasum": "" }, "require": { @@ -3469,13 +3604,17 @@ ], "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.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-10-26T13:18:59+00:00" + "time": "2021-06-15T12:49:02+00:00" }, { "name": "sebastian/version", @@ -3518,6 +3657,10 @@ ], "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": [ { "url": "https://github.com/sebastianbergmann", @@ -3573,6 +3716,10 @@ "parser", "validator" ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.8.3" + }, "funding": [ { "url": "https://github.com/Seldaek", @@ -3630,6 +3777,10 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/6.4.1" + }, "funding": [ { "url": "https://github.com/kukulich", @@ -3644,16 +3795,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.5.8", + "version": "3.6.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" + "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", - "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625", + "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625", "shasum": "" }, "require": { @@ -3691,31 +3842,38 @@ "phpcs", "standards" ], - "time": "2020-10-23T02:01:07+00:00" + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2021-04-09T00:54:41+00:00" }, { "name": "symfony/console", - "version": "v5.1.8", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e" + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", - "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.15", + "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.1|^2", "symfony/string": "^5.1" }, "conflict": { + "psr/log": ">=3", "symfony/dependency-injection": "<4.4", "symfony/dotenv": "<5.1", "symfony/event-dispatcher": "<4.4", @@ -3723,10 +3881,10 @@ "symfony/process": "<4.4" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0|2.0" }, "require-dev": { - "psr/log": "~1.0", + "psr/log": "^1|^2", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/event-dispatcher": "^4.4|^5.0", @@ -3763,74 +3921,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Console Component", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", - "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" - } + "keywords": [ + "cli", + "command line", + "console", + "terminal" ], - "time": "2020-10-24T12:01:57+00:00" - }, - { - "name": "symfony/debug", - "version": "v4.4.16", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "c87adf3fc1cd0bf4758316a3a150d50a8f957ef4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c87adf3fc1cd0bf4758316a3a150d50a8f957ef4", - "reference": "c87adf3fc1cd0bf4758316a3a150d50a8f957ef4", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "psr/log": "~1.0", - "symfony/polyfill-php80": "^1.15" - }, - "conflict": { - "symfony/http-kernel": "<3.4" - }, - "require-dev": { - "symfony/http-kernel": "^3.4|^4.0|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "support": { + "source": "https://github.com/symfony/console/tree/v5.3.7" }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", "funding": [ { "url": "https://symfony.com/sponsor", @@ -3845,20 +3946,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T11:50:19+00:00" + "time": "2021-08-25T20:02:16+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.2.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", - "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", "shasum": "" }, "require": { @@ -3867,7 +3968,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-main": "2.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -3895,6 +3996,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3909,25 +4013,26 @@ "type": "tidelift" } ], - "time": "2020-09-07T11:33:47+00:00" + "time": "2021-03-23T23:28:01+00:00" }, { "name": "symfony/filesystem", - "version": "v5.1.8", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "df08650ea7aee2d925380069c131a66124d79177" + "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/df08650ea7aee2d925380069c131a66124d79177", - "reference": "df08650ea7aee2d925380069c131a66124d79177", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/343f4fe324383ca46792cae728a3b6e2f708fb32", + "reference": "343f4fe324383ca46792cae728a3b6e2f708fb32", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8" + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -3952,8 +4057,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.3.4" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3968,24 +4076,25 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-07-21T12:40:44+00:00" }, { "name": "symfony/finder", - "version": "v5.1.8", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0" + "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", - "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", + "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", "shasum": "" }, "require": { - "php": ">=7.2.5" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -4010,8 +4119,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.3.7" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4026,26 +4138,27 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-08-04T21:20:46+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.1.8", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02" + "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c6a02905e4ffc7a1498e8ee019db2b477cd1cc02", - "reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4b78e55b179003a42523a362cc0e8327f7a69b5e", + "reference": "4b78e55b179003a42523a362cc0e8327f7a69b5e", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-php80": "^1.15" + "symfony/polyfill-php73": "~1.0", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -4070,13 +4183,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony OptionsResolver Component", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ "config", "configuration", "options" ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.3.7" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4091,20 +4207,20 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-08-04T21:20:46+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.20.0", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", "shasum": "" }, "require": { @@ -4116,7 +4232,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4153,6 +4269,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4167,20 +4286,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.20.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" + "reference": "16880ba9c5ebe3642d1995ab866db29270b36535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", - "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", + "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", "shasum": "" }, "require": { @@ -4192,7 +4311,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4231,6 +4350,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4245,20 +4367,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-05-27T12:26:48+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.20.0", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "727d1096295d807c309fb01a851577302394c897" + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", - "reference": "727d1096295d807c309fb01a851577302394c897", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "shasum": "" }, "require": { @@ -4270,7 +4392,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4312,6 +4434,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4326,20 +4451,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.20.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", - "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", "shasum": "" }, "require": { @@ -4351,7 +4476,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4389,6 +4514,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4403,20 +4531,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-05-27T12:26:48+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.20.0", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", - "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", "shasum": "" }, "require": { @@ -4425,7 +4553,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4465,6 +4593,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4479,20 +4610,20 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.20.0", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", - "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", "shasum": "" }, "require": { @@ -4501,7 +4632,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4545,6 +4676,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4559,25 +4693,25 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-07-28T13:41:28+00:00" }, { "name": "symfony/process", - "version": "v5.1.8", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f00872c3f6804150d6a0f73b4151daab96248101" + "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f00872c3f6804150d6a0f73b4151daab96248101", - "reference": "f00872c3f6804150d6a0f73b4151daab96248101", + "url": "https://api.github.com/repos/symfony/process/zipball/38f26c7d6ed535217ea393e05634cb0b244a1967", + "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.15" + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { @@ -4602,8 +4736,11 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.3.7" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4618,25 +4755,25 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-08-04T21:20:46+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.2.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", - "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.0" + "psr/container": "^1.1" }, "suggest": { "symfony/service-implementation": "" @@ -4644,7 +4781,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-main": "2.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -4680,6 +4817,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4694,20 +4834,20 @@ "type": "tidelift" } ], - "time": "2020-09-07T11:33:47+00:00" + "time": "2021-04-01T10:43:52+00:00" }, { "name": "symfony/string", - "version": "v5.1.8", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea" + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/a97573e960303db71be0dd8fda9be3bca5e0feea", - "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea", + "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", "shasum": "" }, "require": { @@ -4750,7 +4890,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony String component", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "homepage": "https://symfony.com", "keywords": [ "grapheme", @@ -4760,6 +4900,9 @@ "utf-8", "utf8" ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.3.7" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4774,7 +4917,7 @@ "type": "tidelift" } ], - "time": "2020-10-24T12:01:57+00:00" + "time": "2021-08-26T08:00:08+00:00" }, { "name": "thecodingmachine/safe", @@ -4909,20 +5052,24 @@ "MIT" ], "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v1.3.3" + }, "time": "2020-10-28T17:51:34+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "75a63c33a8577608444246075ea0af0d052e452a" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", - "reference": "75a63c33a8577608444246075ea0af0d052e452a", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { @@ -4949,40 +5096,49 @@ } ], "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/1.2.1" + }, "funding": [ { "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-07-12T23:59:07+00:00" + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", + "php": "^7.2 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -5004,7 +5160,11 @@ "check", "validate" ], - "time": "2020-07-08T17:02:28+00:00" + "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/path-util", @@ -5050,6 +5210,10 @@ } ], "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" } ], @@ -5060,8 +5224,11 @@ "prefer-lowest": false, "platform": { "php": "^7.4 || ^8.0", + "ext-hash": "*", + "ext-json": "*", "ext-mbstring": "*", - "ext-openssl": "*" + "ext-openssl": "*", + "ext-sodium": "*" }, "platform-dev": [], "plugin-api-version": "2.1.0" diff --git a/docs/configuration.md b/docs/configuration.md index b0c9f05b..912f874f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -79,7 +79,7 @@ use Lcobucci\JWT\Signer; use Lcobucci\JWT\Signer\Key\InMemory; $configuration = Configuration::forAsymmetricSigner( - // You may use RSA or ECDSA and all their variations (256, 384, and 512) + // You may use RSA or ECDSA and all their variations (256, 384, and 512) and EdDSA over Curve25519 new Signer\Rsa\Sha256(), InMemory::file(__DIR__ . '/my-private-key.pem'), InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=') diff --git a/docs/extending-the-library.md b/docs/extending-the-library.md index b19a6a89..f0018484 100644 --- a/docs/extending-the-library.md +++ b/docs/extending-the-library.md @@ -51,20 +51,17 @@ You may customise and even create your own formatters: ```php use Lcobucci\JWT\ClaimsFormatter; use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\RegisteredClaims; +use Serializable; -final class UnixTimestampDates implements ClaimsFormatter +final class ClaimSerializer implements ClaimsFormatter { /** @inheritdoc */ public function formatClaims(array $claims): array { - foreach (RegisteredClaims::DATE_CLAIMS as $claim) { - if (! array_key_exists($claim, $claims)) { - continue; + foreach ($claims as $claim => $claimValue) { + if ($claimValue instanceof Serializable) { + $claims[$claim] = $claimValue->serialize(); } - - assert($claims[$claim] instanceof DateTimeImmutable); - $claims[$claim] = $claims[$claim]->getTimestamp(); } return $claims; @@ -74,7 +71,7 @@ final class UnixTimestampDates implements ClaimsFormatter $config = $container->get(Configuration::class); assert($config instanceof Configuration); -$config->builder(new UnixTimestampDates()); +$config->builder(new ClaimSerializer()); ``` The class `Lcobucci\JWT\Encoding\ChainedFormatter` allows for users to combine multiple formatters. @@ -172,6 +169,7 @@ To create your own implementation of constraint you must implement the `Lcobucci ```php use Lcobucci\JWT\Token; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\ConstraintViolation; @@ -179,7 +177,7 @@ final class SubjectMustBeAValidUser implements Constraint { public function assert(Token $token): void { - if (! $token instanceof Token\Plain) { + if (! $token instanceof UnencryptedToken) { throw new ConstraintViolation('You should pass a plain token'); } diff --git a/docs/installation.md b/docs/installation.md index c756b000..012eb6c3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -16,7 +16,7 @@ composer require lcobucci/jwt In order to be able to use the classes provided by this library you're also required to include [Composer]'s autoloader in your application: ```php -require 'vendor/bin/autoload.php'; +require 'vendor/autoload.php'; ``` !!! Tip diff --git a/docs/parsing-tokens.md b/docs/parsing-tokens.md index 55cafe30..0388d461 100644 --- a/docs/parsing-tokens.md +++ b/docs/parsing-tokens.md @@ -8,7 +8,7 @@ To parse a token you must create a new parser (easier when using the [configurat ```php use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\Plain; +use Lcobucci\JWT\UnencryptedToken; $config = $container->get(Configuration::class); assert($config instanceof Configuration); @@ -19,7 +19,7 @@ $token = $config->parser()->parse( . '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q' ); -assert($token instanceof Plain); +assert($token instanceof UnencryptedToken); $token->headers(); // Retrieves the token headers $token->claims(); // Retrieves the token claims diff --git a/docs/validating-tokens.md b/docs/validating-tokens.md index 69c5b86b..c0d9c29d 100644 --- a/docs/validating-tokens.md +++ b/docs/validating-tokens.md @@ -15,14 +15,14 @@ This method goes through every single constraint in the set, groups all the viol ```php use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\Plain; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; -$token = $container->get(Configuration::class); +$config = $container->get(Configuration::class); assert($config instanceof Configuration); $token = $config->parser()->parse('...'); -assert($token instanceof Plain); +assert($token instanceof UnencryptedToken); $constraints = $config->validationConstraints(); @@ -39,17 +39,17 @@ try { !!! Warning You **MUST** provide at least one constraint, otherwise `\Lcobucci\JWT\Validation\NoConstraintsGiven` exception will be thrown. -The difference here is that we'll always a get a `boolean` result and stop in the very first violation: +The difference here is that we'll always get a `boolean` result and stop in the very first violation: ```php use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Token\Plain; +use Lcobucci\JWT\UnencryptedToken; -$token = $container->get(Configuration::class); +$config = $container->get(Configuration::class); assert($config instanceof Configuration); $token = $config->parser()->parse('...'); -assert($token instanceof Plain); +assert($token instanceof UnencryptedToken); $constraints = $config->validationConstraints(); @@ -67,6 +67,7 @@ This library provides the following constraints: * `Lcobucci\JWT\Validation\Constraint\PermittedFor`: verifies if the claim `aud` contains the expected value * `Lcobucci\JWT\Validation\Constraint\RelatedTo`: verifies if the claim `sub` matches the expected value * `Lcobucci\JWT\Validation\Constraint\SignedWith`: verifies if the token was signed with the expected signer and key -* `Lcobucci\JWT\Validation\Constraint\ValidAt`: verifies the claims `iat`, `nbf`, and `exp` (supports leeway configuration) +* `Lcobucci\JWT\Validation\Constraint\StrictValidAt`: verifies presence and validity of the claims `iat`, `nbf`, and `exp` (supports leeway configuration) +* `Lcobucci\JWT\Validation\Constraint\LooseValidAt`: verifies the claims `iat`, `nbf`, and `exp`, when present (supports leeway configuration) You may also create your [own validation constraints](extending-the-library.md#validation-constraints). diff --git a/infection.json.dist b/infection.json.dist index ef8e6172..318f231c 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -8,9 +8,48 @@ }, "mutators": { "@default": true, - "@function_signature": true + "@function_signature": true, + "CastInt": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::octetLength", + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::readAsn1Integer" + ] + }, + "GreaterThan": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::toAsn1", + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger", + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::retrievePositiveInteger" + ] + }, + "InstanceOf_": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\OpenSSL::freeKey" + ] + }, + "LessThanOrEqualTo": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger" + ] + }, + "LogicalNot": { + "ignoreSourceCodeByRegex": [ + "if \\(!function_exists\\('sodium_\\w+'\\)\\) \\{" + ] + }, + "MBString": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter" + ] + }, + "MethodCallRemoval": { + "ignore": [ + "Lcobucci\\JWT\\Signer\\OpenSSL::createSignature", + "Lcobucci\\JWT\\Signer\\OpenSSL::verifySignature" + ] + } }, - "minMsi": 93.28, - "minCoveredMsi": 93.28, + "minMsi": 100, + "minCoveredMsi": 100, "testFrameworkOptions": "--testsuite=unit" } diff --git a/phpbench.json b/phpbench.json index bb64a6ae..aad19345 100644 --- a/phpbench.json +++ b/phpbench.json @@ -1,8 +1,4 @@ { - "bootstrap": "vendor/autoload.php", - "path": "test/performance", - - "extensions": [ - "PhpBench\\Extensions\\XDebug\\XDebugExtension" - ] + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "test/performance" } diff --git a/src/Encoding/ChainedFormatter.php b/src/Encoding/ChainedFormatter.php index 8eed7e79..1670138b 100644 --- a/src/Encoding/ChainedFormatter.php +++ b/src/Encoding/ChainedFormatter.php @@ -20,6 +20,11 @@ public static function default(): self return new self(new UnifyAudience(), new MicrosecondBasedDateConversion()); } + public static function withUnixTimestampDates(): self + { + return new self(new UnifyAudience(), new UnixTimestampDates()); + } + /** @inheritdoc */ public function formatClaims(array $claims): array { diff --git a/src/Encoding/JoseEncoder.php b/src/Encoding/JoseEncoder.php index 9cb49c42..597d15f9 100644 --- a/src/Encoding/JoseEncoder.php +++ b/src/Encoding/JoseEncoder.php @@ -6,14 +6,10 @@ use JsonException; use Lcobucci\JWT\Decoder; use Lcobucci\JWT\Encoder; +use Lcobucci\JWT\SodiumBase64Polyfill; -use function base64_decode; -use function base64_encode; -use function is_string; use function json_decode; use function json_encode; -use function rtrim; -use function strtr; use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; @@ -48,18 +44,17 @@ public function jsonDecode(string $json) public function base64UrlEncode(string $data): string { - return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); + return SodiumBase64Polyfill::bin2base64( + $data, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); } public function base64UrlDecode(string $data): string { - // Padding isn't added back because it isn't strictly necessary for decoding with PHP - $decodedContent = base64_decode(strtr($data, '-_', '+/'), true); - - if (! is_string($decodedContent)) { - throw CannotDecodeContent::invalidBase64String(); - } - - return $decodedContent; + return SodiumBase64Polyfill::base642bin( + $data, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); } } diff --git a/src/Encoding/UnixTimestampDates.php b/src/Encoding/UnixTimestampDates.php new file mode 100644 index 00000000..7d58389b --- /dev/null +++ b/src/Encoding/UnixTimestampDates.php @@ -0,0 +1,32 @@ +convertDate($claims[$claim]); + } + + return $claims; + } + + private function convertDate(DateTimeImmutable $date): int + { + return $date->getTimestamp(); + } +} diff --git a/src/Signer/Eddsa.php b/src/Signer/Eddsa.php new file mode 100644 index 00000000..03ad88bd --- /dev/null +++ b/src/Signer/Eddsa.php @@ -0,0 +1,36 @@ +contents()); + } catch (SodiumException $sodiumException) { + throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException); + } + } + + public function verify(string $expected, string $payload, Key $key): bool + { + try { + return sodium_crypto_sign_verify_detached($expected, $payload, $key->contents()); + } catch (SodiumException $sodiumException) { + throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException); + } + } +} diff --git a/src/Signer/Key/InMemory.php b/src/Signer/Key/InMemory.php index 2dbe9916..1a4df0f5 100644 --- a/src/Signer/Key/InMemory.php +++ b/src/Signer/Key/InMemory.php @@ -3,13 +3,12 @@ namespace Lcobucci\JWT\Signer\Key; -use Lcobucci\JWT\Encoding\CannotDecodeContent; use Lcobucci\JWT\Signer\Key; +use Lcobucci\JWT\SodiumBase64Polyfill; use SplFileObject; use Throwable; use function assert; -use function base64_decode; use function is_string; final class InMemory implements Key @@ -35,11 +34,10 @@ public static function plainText(string $contents, string $passphrase = ''): sel public static function base64Encoded(string $contents, string $passphrase = ''): self { - $decoded = base64_decode($contents, true); - - if ($decoded === false) { - throw CannotDecodeContent::invalidBase64String(); - } + $decoded = SodiumBase64Polyfill::base642bin( + $contents, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL + ); return new self($decoded, $passphrase); } diff --git a/src/SodiumBase64Polyfill.php b/src/SodiumBase64Polyfill.php new file mode 100644 index 00000000..a00068db --- /dev/null +++ b/src/SodiumBase64Polyfill.php @@ -0,0 +1,88 @@ +clock = $clock; + $this->leeway = $this->guardLeeway($leeway); + } + + private function guardLeeway(?DateInterval $leeway): DateInterval + { + if ($leeway === null) { + return new DateInterval('PT0S'); + } + + if ($leeway->invert === 1) { + throw LeewayCannotBeNegative::create(); + } + + return $leeway; + } + + public function assert(Token $token): void + { + $now = $this->clock->now(); + + $this->assertIssueTime($token, $now->add($this->leeway)); + $this->assertMinimumTime($token, $now->add($this->leeway)); + $this->assertExpiration($token, $now->sub($this->leeway)); + } + + /** @throws ConstraintViolation */ + private function assertExpiration(Token $token, DateTimeInterface $now): void + { + if ($token->isExpired($now)) { + throw new ConstraintViolation('The token is expired'); + } + } + + /** @throws ConstraintViolation */ + private function assertMinimumTime(Token $token, DateTimeInterface $now): void + { + if (! $token->isMinimumTimeBefore($now)) { + throw new ConstraintViolation('The token cannot be used yet'); + } + } + + /** @throws ConstraintViolation */ + private function assertIssueTime(Token $token, DateTimeInterface $now): void + { + if (! $token->hasBeenIssuedBefore($now)) { + throw new ConstraintViolation('The token was issued in the future'); + } + } +} diff --git a/src/Validation/Constraint/SignedWith.php b/src/Validation/Constraint/SignedWith.php index 56acb2ef..1bc1e905 100644 --- a/src/Validation/Constraint/SignedWith.php +++ b/src/Validation/Constraint/SignedWith.php @@ -5,6 +5,7 @@ use Lcobucci\JWT\Signer; use Lcobucci\JWT\Token; +use Lcobucci\JWT\UnencryptedToken; use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\ConstraintViolation; @@ -21,7 +22,7 @@ public function __construct(Signer $signer, Signer\Key $key) public function assert(Token $token): void { - if (! $token instanceof Token\Plain) { + if (! $token instanceof UnencryptedToken) { throw new ConstraintViolation('You should pass a plain token'); } diff --git a/src/Validation/Constraint/StrictValidAt.php b/src/Validation/Constraint/StrictValidAt.php new file mode 100644 index 00000000..bb221092 --- /dev/null +++ b/src/Validation/Constraint/StrictValidAt.php @@ -0,0 +1,86 @@ +clock = $clock; + $this->leeway = $this->guardLeeway($leeway); + } + + private function guardLeeway(?DateInterval $leeway): DateInterval + { + if ($leeway === null) { + return new DateInterval('PT0S'); + } + + if ($leeway->invert === 1) { + throw LeewayCannotBeNegative::create(); + } + + return $leeway; + } + + public function assert(Token $token): void + { + if (! $token instanceof UnencryptedToken) { + throw new ConstraintViolation('You should pass a plain token'); + } + + $now = $this->clock->now(); + + $this->assertIssueTime($token, $now->add($this->leeway)); + $this->assertMinimumTime($token, $now->add($this->leeway)); + $this->assertExpiration($token, $now->sub($this->leeway)); + } + + /** @throws ConstraintViolation */ + private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void + { + if (! $token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) { + throw new ConstraintViolation('"Expiration Time" claim missing'); + } + + if ($token->isExpired($now)) { + throw new ConstraintViolation('The token is expired'); + } + } + + /** @throws ConstraintViolation */ + private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void + { + if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) { + throw new ConstraintViolation('"Not Before" claim missing'); + } + + if (! $token->isMinimumTimeBefore($now)) { + throw new ConstraintViolation('The token cannot be used yet'); + } + } + + /** @throws ConstraintViolation */ + private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void + { + if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) { + throw new ConstraintViolation('"Issued At" claim missing'); + } + + if (! $token->hasBeenIssuedBefore($now)) { + throw new ConstraintViolation('The token was issued in the future'); + } + } +} diff --git a/src/Validation/Constraint/ValidAt.php b/src/Validation/Constraint/ValidAt.php index 61866864..db7f6baf 100644 --- a/src/Validation/Constraint/ValidAt.php +++ b/src/Validation/Constraint/ValidAt.php @@ -4,66 +4,22 @@ namespace Lcobucci\JWT\Validation\Constraint; use DateInterval; -use DateTimeInterface; use Lcobucci\Clock\Clock; use Lcobucci\JWT\Token; use Lcobucci\JWT\Validation\Constraint; -use Lcobucci\JWT\Validation\ConstraintViolation; +/** @deprecated Use \Lcobucci\JWT\Validation\Constraint\LooseValidAt */ final class ValidAt implements Constraint { - private Clock $clock; - private DateInterval $leeway; + private LooseValidAt $constraint; public function __construct(Clock $clock, ?DateInterval $leeway = null) { - $this->clock = $clock; - $this->leeway = $this->guardLeeway($leeway); - } - - private function guardLeeway(?DateInterval $leeway): DateInterval - { - if ($leeway === null) { - return new DateInterval('PT0S'); - } - - if ($leeway->invert === 1) { - throw LeewayCannotBeNegative::create(); - } - - return $leeway; + $this->constraint = new LooseValidAt($clock, $leeway); } public function assert(Token $token): void { - $now = $this->clock->now(); - - $this->assertIssueTime($token, $now->add($this->leeway)); - $this->assertMinimumTime($token, $now->add($this->leeway)); - $this->assertExpiration($token, $now->sub($this->leeway)); - } - - /** @throws ConstraintViolation */ - private function assertExpiration(Token $token, DateTimeInterface $now): void - { - if ($token->isExpired($now)) { - throw new ConstraintViolation('The token is expired'); - } - } - - /** @throws ConstraintViolation */ - private function assertMinimumTime(Token $token, DateTimeInterface $now): void - { - if (! $token->isMinimumTimeBefore($now)) { - throw new ConstraintViolation('The token cannot be used yet'); - } - } - - /** @throws ConstraintViolation */ - private function assertIssueTime(Token $token, DateTimeInterface $now): void - { - if (! $token->hasBeenIssuedBefore($now)) { - throw new ConstraintViolation('The token was issued in the future'); - } + $this->constraint->assert($token); } } diff --git a/test/_keys/Keys.php b/test/_keys/Keys.php index 87737230..93db8224 100644 --- a/test/_keys/Keys.php +++ b/test/_keys/Keys.php @@ -14,6 +14,9 @@ trait Keys /** @var array */ protected static array $ecdsaKeys; + /** @var array */ + protected static array $eddsaKeys; + /** @beforeClass */ public static function createRsaKeys(): void { @@ -39,4 +42,16 @@ public static function createEcdsaKeys(): void 'public2_ec512' => LocalFileReference::file(__DIR__ . '/ecdsa/public2_ec512.key'), ]; } + + /** @beforeClass */ + public static function createEddsaKeys(): void + { + static::$eddsaKeys = [ + 'private' => Key\InMemory::base64Encoded( + 'K3NWT0XqaH+4jgi42gQmHnFE+HTPVhFYi3u4DFJ3OpRHRMt/aGRBoKD/Pt5H/iYgGCla7Q04CdjOUpLSrjZhtg==' + ), + 'public1' => Key\InMemory::base64Encoded('R0TLf2hkQaCg/z7eR/4mIBgpWu0NOAnYzlKS0q42YbY='), + 'public2' => Key\InMemory::base64Encoded('8uLLzCdMrIWcOrAxS/fteYyJhWIGH+wav2fNz8NZhvI='), + ]; + } } diff --git a/test/functional/ES512TokenTest.php b/test/functional/ES512TokenTest.php index 2d921218..2b6a7004 100644 --- a/test/functional/ES512TokenTest.php +++ b/test/functional/ES512TokenTest.php @@ -35,6 +35,7 @@ * @covers \Lcobucci\JWT\Signer\Ecdsa\Sha512 * @covers \Lcobucci\JWT\Signer\InvalidKeyProvided * @covers \Lcobucci\JWT\Signer\OpenSSL + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith diff --git a/test/functional/EcdsaTokenTest.php b/test/functional/EcdsaTokenTest.php index f8794e6c..d3fa0695 100644 --- a/test/functional/EcdsaTokenTest.php +++ b/test/functional/EcdsaTokenTest.php @@ -37,6 +37,7 @@ * @covers \Lcobucci\JWT\Signer\Ecdsa\Sha512 * @covers \Lcobucci\JWT\Signer\InvalidKeyProvided * @covers \Lcobucci\JWT\Signer\OpenSSL + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith * @covers \Lcobucci\JWT\Validation\Validator diff --git a/test/functional/EddsaTokenTest.php b/test/functional/EddsaTokenTest.php new file mode 100644 index 00000000..54b77af1 --- /dev/null +++ b/test/functional/EddsaTokenTest.php @@ -0,0 +1,142 @@ +config = Configuration::forAsymmetricSigner( + new Eddsa(), + static::$eddsaKeys['private'], + static::$eddsaKeys['public1'] + ); + } + + /** @test */ + public function builderShouldRaiseExceptionWhenKeyIsInvalid(): void + { + $builder = $this->config->builder(); + + $this->expectException(InvalidKeyProvided::class); + $this->expectExceptionMessage('SODIUM_CRYPTO_SIGN_SECRETKEYBYTES'); + + $builder->identifiedBy('1') + ->permittedFor('http://client.abc.com') + ->issuedBy('http://api.abc.com') + ->withClaim('user', ['name' => 'testing', 'email' => 'testing@abc.com']) + ->getToken($this->config->signer(), InMemory::plainText('testing')); + } + + /** @test */ + public function builderCanGenerateAToken(): Token + { + $user = ['name' => 'testing', 'email' => 'testing@abc.com']; + $builder = $this->config->builder(); + + $token = $builder->identifiedBy('1') + ->permittedFor('http://client.abc.com') + ->permittedFor('http://client2.abc.com') + ->issuedBy('http://api.abc.com') + ->withClaim('user', $user) + ->withHeader('jki', '1234') + ->getToken($this->config->signer(), $this->config->signingKey()); + + self::assertEquals('1234', $token->headers()->get('jki')); + self::assertEquals('http://api.abc.com', $token->claims()->get(Token\RegisteredClaims::ISSUER)); + self::assertEquals($user, $token->claims()->get('user')); + + self::assertEquals( + ['http://client.abc.com', 'http://client2.abc.com'], + $token->claims()->get(Token\RegisteredClaims::AUDIENCE) + ); + + return $token; + } + + /** + * @test + * @depends builderCanGenerateAToken + */ + public function parserCanReadAToken(Token $generated): void + { + $read = $this->config->parser()->parse($generated->toString()); + assert($read instanceof Token\Plain); + + self::assertEquals($generated, $read); + self::assertEquals('testing', $read->claims()->get('user')['name']); + } + + /** + * @test + * @depends builderCanGenerateAToken + */ + public function signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight(Token $token): void + { + $this->expectException(RequiredConstraintsViolated::class); + $this->expectExceptionMessage('The token violates some mandatory constraints'); + + $this->config->validator()->assert( + $token, + new SignedWith( + $this->config->signer(), + self::$eddsaKeys['public2'] + ) + ); + } + + /** + * @test + * @depends builderCanGenerateAToken + */ + public function signatureValidationShouldSucceedWhenKeyIsRight(Token $token): void + { + $constraint = new SignedWith( + $this->config->signer(), + $this->config->verificationKey() + ); + + self::assertTrue($this->config->validator()->validate($token, $constraint)); + } +} diff --git a/test/functional/HmacTokenTest.php b/test/functional/HmacTokenTest.php index a7a9a34a..9b3f07f6 100644 --- a/test/functional/HmacTokenTest.php +++ b/test/functional/HmacTokenTest.php @@ -34,6 +34,7 @@ * @covers \Lcobucci\JWT\Signer\Hmac * @covers \Lcobucci\JWT\Signer\Hmac\Sha256 * @covers \Lcobucci\JWT\Signer\Hmac\Sha512 + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith diff --git a/test/functional/MaliciousTamperingPreventionTest.php b/test/functional/MaliciousTamperingPreventionTest.php index 6a46ec64..598901ef 100644 --- a/test/functional/MaliciousTamperingPreventionTest.php +++ b/test/functional/MaliciousTamperingPreventionTest.php @@ -60,6 +60,7 @@ public function createConfiguration(): void * @covers \Lcobucci\JWT\Signer\Hmac * @covers \Lcobucci\JWT\Signer\Hmac\Sha256 * @covers \Lcobucci\JWT\Signer\Hmac\Sha512 + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith * @covers \Lcobucci\JWT\Validation\Validator */ diff --git a/test/functional/RsaTokenTest.php b/test/functional/RsaTokenTest.php index 5ecb0844..ec0c709f 100644 --- a/test/functional/RsaTokenTest.php +++ b/test/functional/RsaTokenTest.php @@ -34,6 +34,7 @@ * @covers \Lcobucci\JWT\Signer\Rsa * @covers \Lcobucci\JWT\Signer\Rsa\Sha256 * @covers \Lcobucci\JWT\Signer\Rsa\Sha512 + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Constraint\SignedWith diff --git a/test/functional/UnsignedTokenTest.php b/test/functional/UnsignedTokenTest.php index f661bfd1..23bfa5d7 100644 --- a/test/functional/UnsignedTokenTest.php +++ b/test/functional/UnsignedTokenTest.php @@ -10,8 +10,8 @@ use Lcobucci\JWT\Validation\Constraint; use Lcobucci\JWT\Validation\Constraint\IdentifiedBy; use Lcobucci\JWT\Validation\Constraint\IssuedBy; +use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\Constraint\PermittedFor; -use Lcobucci\JWT\Validation\Constraint\ValidAt; use Lcobucci\JWT\Validation\ConstraintViolation; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; use PHPUnit\Framework\TestCase; @@ -31,12 +31,13 @@ * @covers \Lcobucci\JWT\Token\Signature * @covers \Lcobucci\JWT\Signer\None * @covers \Lcobucci\JWT\Signer\Key\InMemory + * @covers \Lcobucci\JWT\SodiumBase64Polyfill * @covers \Lcobucci\JWT\Validation\RequiredConstraintsViolated * @covers \Lcobucci\JWT\Validation\Validator * @covers \Lcobucci\JWT\Validation\Constraint\IssuedBy * @covers \Lcobucci\JWT\Validation\Constraint\PermittedFor * @covers \Lcobucci\JWT\Validation\Constraint\IdentifiedBy - * @covers \Lcobucci\JWT\Validation\Constraint\ValidAt + * @covers \Lcobucci\JWT\Validation\Constraint\LooseValidAt */ class UnsignedTokenTest extends TestCase { @@ -99,7 +100,7 @@ public function tokenValidationShouldPassWhenEverythingIsFine(Token $generated): new IdentifiedBy('1'), new PermittedFor('http://client.abc.com'), new IssuedBy('http://issuer.abc.com', 'http://api.abc.com'), - new ValidAt($clock), + new LooseValidAt($clock), ]; self::assertTrue($this->config->validator()->validate($generated, ...$constraints)); diff --git a/test/performance/EddsaBench.php b/test/performance/EddsaBench.php new file mode 100644 index 00000000..3d94cdee --- /dev/null +++ b/test/performance/EddsaBench.php @@ -0,0 +1,30 @@ +formatClaims($claims); + + self::assertSame('test', $formatted[RegisteredClaims::AUDIENCE]); + self::assertSame(1487285080, $formatted[RegisteredClaims::EXPIRATION_TIME]); } } diff --git a/test/unit/Encoding/JoseEncoderTest.php b/test/unit/Encoding/JoseEncoderTest.php index 12db5a36..568a6f07 100644 --- a/test/unit/Encoding/JoseEncoderTest.php +++ b/test/unit/Encoding/JoseEncoderTest.php @@ -103,6 +103,8 @@ public function jsonDecodeMustRaiseExceptionWhenAnErrorHasOccurred(): void * @test * * @covers ::base64UrlEncode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::bin2base64() */ public function base64UrlEncodeMustReturnAUrlSafeBase64(): void { @@ -119,6 +121,8 @@ public function base64UrlEncodeMustReturnAUrlSafeBase64(): void * @test * * @covers ::base64UrlEncode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::bin2base64() */ public function base64UrlEncodeMustEncodeBilboMessageProperly(): void { @@ -139,6 +143,8 @@ public function base64UrlEncodeMustEncodeBilboMessageProperly(): void * @test * * @covers ::base64UrlDecode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64UrlDecodeMustRaiseExceptionWhenInvalidBase64CharsAreUsed(): void { @@ -148,13 +154,15 @@ public function base64UrlDecodeMustRaiseExceptionWhenInvalidBase64CharsAreUsed() $this->expectExceptionCode(0); $this->expectExceptionMessage('Error while decoding from Base64Url, invalid base64 characters detected'); - $decoder->base64UrlDecode('áááááá'); + $decoder->base64UrlDecode('ááá'); } /** * @test * * @covers ::base64UrlDecode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64UrlDecodeMustReturnTheRightData(): void { @@ -170,6 +178,8 @@ public function base64UrlDecodeMustReturnTheRightData(): void * @test * * @covers ::base64UrlDecode + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64UrlDecodeMustDecodeBilboMessageProperly(): void { diff --git a/test/unit/Encoding/UnixTimestampDatesTest.php b/test/unit/Encoding/UnixTimestampDatesTest.php new file mode 100644 index 00000000..f5c4fb8a --- /dev/null +++ b/test/unit/Encoding/UnixTimestampDatesTest.php @@ -0,0 +1,68 @@ + $issuedAt, + RegisteredClaims::NOT_BEFORE => $notBefore, + RegisteredClaims::EXPIRATION_TIME => $expiration, + 'testing' => 'test', + ]; + + $formatter = new UnixTimestampDates(); + $formatted = $formatter->formatClaims($claims); + + self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]); + self::assertSame(1487285080, $formatted[RegisteredClaims::NOT_BEFORE]); + self::assertSame(1487285080, $formatted[RegisteredClaims::EXPIRATION_TIME]); + self::assertSame('test', $formatted['testing']); // this should remain untouched + } + + /** + * @test + * + * @covers ::formatClaims + * @covers ::convertDate + */ + public function notAllDateClaimsNeedToBeConfigured(): void + { + $issuedAt = new DateTimeImmutable('@1487285080'); + $expiration = DateTimeImmutable::createFromFormat('U.u', '1487285080.123456'); + + $claims = [ + RegisteredClaims::ISSUED_AT => $issuedAt, + RegisteredClaims::EXPIRATION_TIME => $expiration, + 'testing' => 'test', + ]; + + $formatter = new UnixTimestampDates(); + $formatted = $formatter->formatClaims($claims); + + self::assertSame(1487285080, $formatted[RegisteredClaims::ISSUED_AT]); + self::assertSame(1487285080, $formatted[RegisteredClaims::EXPIRATION_TIME]); + self::assertSame('test', $formatted['testing']); // this should remain untouched + } +} diff --git a/test/unit/Signer/EddsaTest.php b/test/unit/Signer/EddsaTest.php new file mode 100644 index 00000000..b70def84 --- /dev/null +++ b/test/unit/Signer/EddsaTest.php @@ -0,0 +1,163 @@ +getSigner()->algorithmId()); + } + + /** + * @test + * + * @covers ::sign + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function signShouldReturnAValidEddsaSignature(): void + { + $payload = 'testing'; + + $signer = $this->getSigner(); + $signature = $signer->sign($payload, self::$eddsaKeys['private']); + + $publicKey = self::$eddsaKeys['public1']->contents(); + + self::assertTrue(sodium_crypto_sign_verify_detached($signature, $payload, $publicKey)); + } + + /** + * @test + * + * @covers ::sign + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function signShouldRaiseAnExceptionWhenKeyIsInvalid(): void + { + $signer = $this->getSigner(); + + $this->expectException(InvalidKeyProvided::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('SODIUM_CRYPTO_SIGN_SECRETKEYBYTES'); + + $signer->sign('testing', InMemory::plainText('tooshort')); + } + + /** + * @test + * + * @covers ::verify + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function verifyShouldReturnTrueWhenSignatureIsValid(): void + { + $payload = 'testing'; + $signature = sodium_crypto_sign_detached($payload, self::$eddsaKeys['private']->contents()); + + $signer = $this->getSigner(); + + self::assertTrue($signer->verify($signature, $payload, self::$eddsaKeys['public1'])); + } + + /** + * @test + * + * @covers ::verify + * + * @uses \Lcobucci\JWT\Signer\Key\InMemory + */ + public function verifyShouldRaiseAnExceptionWhenKeyIsNotParseable(): void + { + $signer = $this->getSigner(); + + $this->expectException(InvalidKeyProvided::class); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('SODIUM_CRYPTO_SIGN_BYTES'); + + $signer->verify('testing', 'testing', InMemory::plainText('blablabla')); + } + + /** + * @see https://tools.ietf.org/html/rfc8037#appendix-A.4 + * + * @test + * + * @covers ::sign + * + * @uses \Lcobucci\JWT\Encoding\JoseEncoder + * @uses \Lcobucci\JWT\Signer\Key\InMemory + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::bin2base64() + */ + public function signatureOfRfcExample(): void + { + $signer = $this->getSigner(); + $encoder = new JoseEncoder(); + + $key = InMemory::plainText( + $encoder->base64UrlDecode('nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A') + . $encoder->base64UrlDecode('11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo') + ); + $payload = $encoder->base64UrlEncode('{"alg":"EdDSA"}') + . '.' + . $encoder->base64UrlEncode('Example of Ed25519 signing'); + $signature = $signer->sign($payload, $key); + + self::assertSame('eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc', $payload); + self::assertSame( + 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg', + $encoder->base64UrlEncode($signature) + ); + } + + /** + * @see https://tools.ietf.org/html/rfc8037#appendix-A.5 + * + * @test + * + * @covers ::verify + * + * @uses \Lcobucci\JWT\Encoding\JoseEncoder + * @uses \Lcobucci\JWT\Signer\Key\InMemory + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() + */ + public function verificationOfRfcExample(): void + { + $signer = $this->getSigner(); + $encoder = new JoseEncoder(); + + $key = InMemory::plainText($encoder->base64UrlDecode('11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo')); + $payload = 'eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc'; + $signature = $encoder->base64UrlDecode( + 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg' + ); + + self::assertTrue($signer->verify($signature, $payload, $key)); + } + + private function getSigner(): Eddsa + { + return new Eddsa(); + } +} diff --git a/test/unit/Signer/Key/InMemoryTest.php b/test/unit/Signer/Key/InMemoryTest.php index 9039aa17..2a9716e9 100644 --- a/test/unit/Signer/Key/InMemoryTest.php +++ b/test/unit/Signer/Key/InMemoryTest.php @@ -27,6 +27,8 @@ public function configureRootDir(): void * * @covers ::base64Encoded * @covers \Lcobucci\JWT\Encoding\CannotDecodeContent + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function exceptionShouldBeRaisedWhenInvalidBase64CharsAreUsed(): void { @@ -42,6 +44,8 @@ public function exceptionShouldBeRaisedWhenInvalidBase64CharsAreUsed(): void * @covers ::base64Encoded * @covers ::__construct * @covers ::contents + * + * @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin() */ public function base64EncodedShouldDecodeKeyContents(): void { diff --git a/test/unit/Signer/RsaTest.php b/test/unit/Signer/RsaTest.php index 3dd504e4..f9bda038 100644 --- a/test/unit/Signer/RsaTest.php +++ b/test/unit/Signer/RsaTest.php @@ -68,7 +68,7 @@ public function signShouldRaiseAnExceptionWhenKeyIsInvalid(): void $signer = $this->getSigner(); $this->expectException(CannotSignPayload::class); - $this->expectExceptionMessage('There was an error while creating the signature'); + $this->expectExceptionMessage('There was an error while creating the signature: error:'); $signer->sign('testing', InMemory::plainText($key)); } @@ -87,7 +87,7 @@ public function signShouldRaiseAnExceptionWhenKeyIsNotParseable(): void $signer = $this->getSigner(); $this->expectException(InvalidKeyProvided::class); - $this->expectExceptionMessage('It was not possible to parse your key'); + $this->expectExceptionMessage('It was not possible to parse your key, reason: error:'); $signer->sign('testing', InMemory::plainText('blablabla')); } diff --git a/test/unit/SodiumBase64PolyfillTest.php b/test/unit/SodiumBase64PolyfillTest.php new file mode 100644 index 00000000..be091948 --- /dev/null +++ b/test/unit/SodiumBase64PolyfillTest.php @@ -0,0 +1,139 @@ +testString = sodium_base642bin('I+o2tVq8ynY=', SODIUM_BASE64_VARIANT_ORIGINAL, ''); + } + + /** + * @test + * + * @coversNothing + */ + public function constantsMatchExtensionOnes(): void + { + self::assertSame( + SODIUM_BASE64_VARIANT_ORIGINAL, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL + ); + self::assertSame( + SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING + ); + self::assertSame( + SODIUM_BASE64_VARIANT_URLSAFE, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE + ); + self::assertSame( + SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, + SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); + } + + /** + * @test + * @dataProvider provideVariants + * + * @covers ::bin2base64 + * @covers ::bin2base64Fallback + */ + public function bin2base64(int $variant): void + { + $expected = sodium_bin2base64($this->testString, $variant); + + self::assertSame( + $expected, + SodiumBase64Polyfill::bin2base64($this->testString, $variant) + ); + + self::assertSame( + $expected, + SodiumBase64Polyfill::bin2base64Fallback($this->testString, $variant) + ); + } + + /** + * @test + * @dataProvider provideVariants + * + * @covers ::base642bin + * @covers ::base642binFallback + */ + public function base642binFallback(int $variant): void + { + self::assertSame( + $this->testString, + SodiumBase64Polyfill::base642bin( + sodium_bin2base64($this->testString, $variant), + $variant + ) + ); + + self::assertSame( + $this->testString, + SodiumBase64Polyfill::base642binFallback( + sodium_bin2base64($this->testString, $variant), + $variant + ) + ); + } + + /** @return int[][] */ + public function provideVariants(): array + { + return [ + [SODIUM_BASE64_VARIANT_ORIGINAL], + [SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING], + [SODIUM_BASE64_VARIANT_URLSAFE], + [SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING], + ]; + } + + /** + * @test + * + * @covers ::base642bin + * + * @uses \Lcobucci\JWT\Encoding\CannotDecodeContent::invalidBase64String() + */ + public function sodiumBase642BinRaisesExceptionOnInvalidBase64(): void + { + $this->expectException(CannotDecodeContent::class); + + SodiumBase64Polyfill::base642bin('ááá', SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); + } + + /** + * @test + * + * @covers ::base642binFallback + * + * @uses \Lcobucci\JWT\Encoding\CannotDecodeContent::invalidBase64String() + */ + public function fallbackBase642BinRaisesExceptionOnInvalidBase64(): void + { + $this->expectException(CannotDecodeContent::class); + + SodiumBase64Polyfill::base642binFallback('ááá', SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); + } +} diff --git a/test/unit/Token/BuilderTest.php b/test/unit/Token/BuilderTest.php index 31ba8b9f..2fbeedc3 100644 --- a/test/unit/Token/BuilderTest.php +++ b/test/unit/Token/BuilderTest.php @@ -89,7 +89,12 @@ public function getTokenShouldReturnACompletelyConfigureToken(): void $this->encoder->expects(self::exactly(3)) ->method('base64UrlEncode') - ->willReturnOnConsecutiveCalls('1', '2', '3'); + ->willReturnArgument(0); + + $this->signer->expects(self::once()) + ->method('sign') + ->with('1.2') + ->willReturn('3'); $builder = new Builder($this->encoder, new MicrosecondBasedDateConversion()); $token = $builder->identifiedBy('123456') diff --git a/test/unit/Validation/Constraint/LooseValidAtTest.php b/test/unit/Validation/Constraint/LooseValidAtTest.php new file mode 100644 index 00000000..b741a69b --- /dev/null +++ b/test/unit/Validation/Constraint/LooseValidAtTest.php @@ -0,0 +1,40 @@ +buildToken(); + $constraint = $this->buildValidAtConstraint($this->clock); + + $constraint->assert($token); + $this->addToAssertionCount(1); + } +} diff --git a/test/unit/Validation/Constraint/StrictValidAtTest.php b/test/unit/Validation/Constraint/StrictValidAtTest.php new file mode 100644 index 00000000..fb41bd28 --- /dev/null +++ b/test/unit/Validation/Constraint/StrictValidAtTest.php @@ -0,0 +1,117 @@ +expectException(ConstraintViolation::class); + $this->expectExceptionMessage('You should pass a plain token'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->createMock(Token::class)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * @covers ::guardLeeway + * @covers ::assertIssueTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenIatClaimIsMissing(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('"Issued At" claim missing'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken()); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * @covers ::guardLeeway + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenNbfClaimIsMissing(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('"Not Before" claim missing'); + + $now = $this->clock->now(); + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-5 seconds'), + ]; + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * @covers ::guardLeeway + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * @covers ::assertExpiration + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenExpClaimIsMissing(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('"Expiration Time" claim missing'); + + $now = $this->clock->now(); + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-5 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('-5 seconds'), + ]; + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } +} diff --git a/test/unit/Validation/Constraint/ValidAtTest.php b/test/unit/Validation/Constraint/ValidAtTest.php index bddda7e3..321a4840 100644 --- a/test/unit/Validation/Constraint/ValidAtTest.php +++ b/test/unit/Validation/Constraint/ValidAtTest.php @@ -5,7 +5,6 @@ use DateInterval; use DateTimeImmutable; -use Lcobucci\Clock\Clock; use Lcobucci\Clock\FrozenClock; use Lcobucci\JWT\Token\RegisteredClaims; use Lcobucci\JWT\Validation\ConstraintViolation; @@ -13,214 +12,37 @@ /** @coversDefaultClass \Lcobucci\JWT\Validation\Constraint\ValidAt */ final class ValidAtTest extends ConstraintTestCase { - private Clock $clock; - - /** @before */ - public function createDependencies(): void - { - $this->clock = new FrozenClock(new DateTimeImmutable()); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers \Lcobucci\JWT\Validation\Constraint\LeewayCannotBeNegative - */ - public function constructShouldRaiseExceptionOnNegativeLeeway(): void - { - $leeway = new DateInterval('PT30S'); - $leeway->invert = 1; - - $this->expectException(LeewayCannotBeNegative::class); - $this->expectExceptionMessage('Leeway cannot be negative'); - - new ValidAt($this->clock, $leeway); - } - /** * @test * * @covers ::__construct - * @covers ::guardLeeway * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime * * @uses \Lcobucci\JWT\Token\DataSet * @uses \Lcobucci\JWT\Token\Plain * @uses \Lcobucci\JWT\Token\Signature + * @uses \Lcobucci\JWT\Validation\Constraint\LooseValidAt */ - public function assertShouldRaiseExceptionWhenTokenIsExpired(): void + public function assertIsAProxyToLooseValidAt(): void { - $now = $this->clock->now(); + $clock = new FrozenClock(new DateTimeImmutable()); $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('-10 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('-10 seconds'), + RegisteredClaims::ISSUED_AT => $clock->now(), + RegisteredClaims::NOT_BEFORE => $clock->now()->modify('+5 seconds'), + RegisteredClaims::EXPIRATION_TIME => $clock->now()->modify('15 seconds'), ]; - $this->expectException(ConstraintViolation::class); - $this->expectExceptionMessage('The token is expired'); + // @phpstan-ignore-next-line + $constraint = new ValidAt($clock, new DateInterval('PT1S')); - $constraint = new ValidAt($this->clock); + $clock->setTo($clock->now()->modify('+4 seconds')); $constraint->assert($this->buildToken($claims)); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void - { - $now = $this->clock->now(); - - $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ]; - - $this->expectException(ConstraintViolation::class); - $this->expectExceptionMessage('The token cannot be used yet'); - - $constraint = new ValidAt($this->clock); - $constraint->assert($this->buildToken($claims)); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertIssueTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void - { - $now = $this->clock->now(); - - $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('+20 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ]; + $this->addToAssertionCount(1); $this->expectException(ConstraintViolation::class); - $this->expectExceptionMessage('The token was issued in the future'); - - $constraint = new ValidAt($this->clock); - $constraint->assert($this->buildToken($claims)); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldNotRaiseExceptionWhenLeewayIsUsed(): void - { - $now = $this->clock->now(); - $claims = [ - RegisteredClaims::ISSUED_AT => $now->modify('+5 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('+5 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('-5 seconds'), - ]; - - $constraint = new ValidAt($this->clock, new DateInterval('PT6S')); + $clock->setTo($clock->now()->modify('+20 seconds')); $constraint->assert($this->buildToken($claims)); - - $this->addToAssertionCount(1); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment(): void - { - $constraint = new ValidAt($this->clock); - $now = $this->clock->now(); - - $token = $this->buildToken( - [ - RegisteredClaims::ISSUED_AT => $now->modify('-40 seconds'), - RegisteredClaims::NOT_BEFORE => $now->modify('-20 seconds'), - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ] - ); - - $constraint->assert($token); - $this->addToAssertionCount(1); - - $token = $this->buildToken( - [ - RegisteredClaims::ISSUED_AT => $now, - RegisteredClaims::NOT_BEFORE => $now, - RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), - ] - ); - - $constraint->assert($token); - $this->addToAssertionCount(1); - } - - /** - * @test - * - * @covers ::__construct - * @covers ::guardLeeway - * @covers ::assert - * @covers ::assertExpiration - * @covers ::assertIssueTime - * @covers ::assertMinimumTime - * - * @uses \Lcobucci\JWT\Token\DataSet - * @uses \Lcobucci\JWT\Token\Plain - * @uses \Lcobucci\JWT\Token\Signature - */ - public function assertShouldNotRaiseExceptionWhenTokenDoesNotHaveTimeClaims(): void - { - $token = $this->buildToken(); - $constraint = new ValidAt($this->clock); - - $constraint->assert($token); - $this->addToAssertionCount(1); } } diff --git a/test/unit/Validation/Constraint/ValidAtTestCase.php b/test/unit/Validation/Constraint/ValidAtTestCase.php new file mode 100644 index 00000000..81053e36 --- /dev/null +++ b/test/unit/Validation/Constraint/ValidAtTestCase.php @@ -0,0 +1,205 @@ +clock = new FrozenClock(new DateTimeImmutable()); + } + + abstract protected function buildValidAtConstraint(Clock $clock, ?DateInterval $leeway = null): Constraint; + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers \Lcobucci\JWT\Validation\Constraint\LeewayCannotBeNegative + */ + final public function constructShouldRaiseExceptionOnNegativeLeeway(): void + { + $leeway = new DateInterval('PT30S'); + $leeway->invert = 1; + + $this->expectException(LeewayCannotBeNegative::class); + $this->expectExceptionMessage('Leeway cannot be negative'); + + $this->buildValidAtConstraint($this->clock, $leeway); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertExpiration + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldRaiseExceptionWhenTokenIsExpired(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('-10 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('-10 seconds'), + ]; + + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token is expired'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ]; + + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token cannot be used yet'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertIssueTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('+20 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ]; + + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token was issued in the future'); + + $constraint = $this->buildValidAtConstraint($this->clock); + $constraint->assert($this->buildToken($claims)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertExpiration + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldNotRaiseExceptionWhenLeewayIsUsed(): void + { + $now = $this->clock->now(); + + $claims = [ + RegisteredClaims::ISSUED_AT => $now->modify('+5 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('+5 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('-5 seconds'), + ]; + + $constraint = $this->buildValidAtConstraint($this->clock, new DateInterval('PT6S')); + $constraint->assert($this->buildToken($claims)); + + $this->addToAssertionCount(1); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::guardLeeway + * @covers ::assert + * @covers ::assertExpiration + * @covers ::assertIssueTime + * @covers ::assertMinimumTime + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + final public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment(): void + { + $constraint = $this->buildValidAtConstraint($this->clock); + $now = $this->clock->now(); + + $token = $this->buildToken( + [ + RegisteredClaims::ISSUED_AT => $now->modify('-40 seconds'), + RegisteredClaims::NOT_BEFORE => $now->modify('-20 seconds'), + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ] + ); + + $constraint->assert($token); + $this->addToAssertionCount(1); + + $token = $this->buildToken( + [ + RegisteredClaims::ISSUED_AT => $now, + RegisteredClaims::NOT_BEFORE => $now, + RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'), + ] + ); + + $constraint->assert($token); + $this->addToAssertionCount(1); + } +}