├── .github └── workflows │ ├── ci.yaml │ └── release.yaml ├── .gitignore ├── .php_cs.php ├── .releaserc.json ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── composer.lock ├── examples └── full-flow.php ├── phpunit.xml ├── src ├── Key │ ├── AbstractKey.php │ ├── KeyInterface.php │ └── Rsa.php ├── KeyConverter.php ├── KeyFactory.php ├── KeySet.php ├── KeySetFactory.php └── Util │ ├── Base64UrlConverter.php │ └── Base64UrlConverterInterface.php └── tests ├── Key ├── AbstractKeyTest.php └── RsaTest.php ├── KeyConverterTest.php ├── KeyFactoryTest.php ├── KeySetFactoryTest.php ├── KeySetTest.php └── Util └── Base64UrlConverterTest.php /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | 4 | name: CI 5 | 6 | jobs: 7 | dependency-validation: 8 | name: Dependency Validation 9 | 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 5 12 | 13 | steps: 14 | - name: Setup PHP 15 | uses: shivammathur/setup-php@v2 16 | with: 17 | php-version: '8.3' 18 | 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Cache Composer dependencies 23 | uses: actions/cache@v4 24 | with: 25 | path: /tmp/composer-cache 26 | key: "${{ runner.os }}-${{ hashFiles('**/composer.lock') }}" 27 | 28 | - name: Install Composer 29 | uses: php-actions/composer@v6 30 | 31 | - name: Ensure that composer.json is valid 32 | run: composer validate --no-ansi --strict composer.json 33 | 34 | - name: Ensure that dependencies can be installed 35 | run: composer install --no-ansi --dry-run 36 | 37 | 38 | unit-tests: 39 | name: Unit Tests 40 | runs-on: ubuntu-latest 41 | 42 | needs: 43 | - dependency-validation 44 | 45 | steps: 46 | - name: Setup PHP 47 | uses: shivammathur/setup-php@v2 48 | with: 49 | php-version: '8.3' 50 | 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | 54 | - name: Cache Composer dependencies 55 | uses: actions/cache@v4 56 | with: 57 | path: /tmp/composer-cache 58 | key: "${{ runner.os }}-${{ hashFiles('**/composer.lock') }}" 59 | 60 | - name: Install Composer 61 | uses: php-actions/composer@v6 62 | 63 | - name: Run PHPUnit 64 | run: make test-unit 65 | 66 | - name: Run php-cs-fixer 67 | run: make php-cs-fixer 68 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | name: Release 7 | 8 | jobs: 9 | release: 10 | name: Semantic Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Setup PHP 14 | uses: shivammathur/setup-php@v2 15 | with: 16 | php-version: '8.3' 17 | 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Cache Composer dependencies 22 | uses: actions/cache@v4 23 | with: 24 | path: /tmp/composer-cache 25 | key: "${{ runner.os }}-${{ hashFiles('**/composer.lock') }}" 26 | 27 | - name: Install Composer 28 | uses: php-actions/composer@v6 29 | 30 | - name: Run PHPUnit 31 | run: php -d xdebug.mode=coverage ./vendor/bin/phpunit 32 | 33 | - name: Make Coverage Badge 34 | uses: action-badges/cobertura-coverage-xml-badges@0.3.0 35 | with: 36 | file-name: coverage.svg 37 | badge-branch: gh-pages 38 | coverage-file-name: tests/coverage.latest/coverage.xml 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | 41 | - name: Execute Semantic Release 42 | id: semantic 43 | uses: cycjimmy/semantic-release-action@v4 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | .phpunit.result.cache 4 | composer.phar 5 | vendor 6 | coverage.xml 7 | .php_cs.cache 8 | .phpunit.cache 9 | .php-cs-fixer.cache 10 | -------------------------------------------------------------------------------- /.php_cs.php: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 7 | ->setFinder( 8 | PhpCsFixer\Finder::create() 9 | ->in(__DIR__ . '/src') 10 | ->in(__DIR__ . '/tests') 11 | ) 12 | ; 13 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "+([0-9])?(.{+([0-9]),x}).x", 4 | "master" 5 | ], 6 | "plugins": [ 7 | "@semantic-release/commit-analyzer", 8 | "@semantic-release/release-notes-generator", 9 | "@semantic-release/github" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to php-jwk 2 | 3 | This is a very small library with a very limited scope but if you want to contribute please feel free to do so. 4 | 5 | ## How can I contribute? 6 | 7 | ### Reporting bugs 8 | 9 | You can open [a new issue](https://github.com/Strobotti/php-jwk/issues) once you have checked one covering your issue hasn't been opened yet. 10 | 11 | ### Open a pull-request 12 | 13 | If you want to fix a bug or add a missing feature you can open a new pull-request. 14 | 15 | Please note that you need a PHP 7.3 or later for using this library (running the tests requires at least php 8.2). 16 | 17 | Basic steps: 18 | 19 | 1. Fork this repository 20 | 1. Clone it locally 21 | 1. Install dependencies with Composer 22 | ```bash 23 | $ composer install 24 | ``` 25 | 1. Create a branch on your fork 26 | 1. Commit & push 27 | 1. Open a pull-request from your branch 28 | 1. Profit 29 | 30 | Some guidelines: 31 | 32 | 1. Before committing make sure to format the code accordingly: 33 | ```bash 34 | $ make php-cs-fixer-fix 35 | ``` 36 | 1. Also make sure the tests pass successfully and you have sufficient coverage 37 | ```bash 38 | $ make test-unit 39 | ``` 40 | 41 | ### Git Commit Messages 42 | 43 | * use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) to make the commit messages more readable and to allow for automatic semantic versioning and changelog generation 44 | * Use the present tense ("add feature" not "added feature") 45 | * Use the imperative mood ("move cursor to..." not "moves cursor to...") 46 | * Limit the first line to 72 characters, or less 47 | 48 | ## License 49 | 50 | [MIT](https://github.com/Strobotti/php-jwk/blob/master/LICENSE) 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Juha Jantunen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test-unit: 2 | php -d xdebug.mode=coverage ./vendor/bin/phpunit 3 | 4 | php-cs-fixer: 5 | ./vendor/bin/php-cs-fixer fix --verbose --dry-run --show-progress none --config .php_cs.php 6 | 7 | php-cs-fixer-fix: 8 | ./vendor/bin/php-cs-fixer fix --verbose --show-progress none --config .php_cs.php 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-jwk 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/strobotti/php-jwk/v/stable)](https://packagist.org/packages/strobotti/php-jwk) 4 | ![coverage](https://raw.githubusercontent.com/Strobotti/php-jwk/gh-pages/.badges/master/coverage.svg) 5 | [![License](https://poser.pugx.org/strobotti/php-jwk/license)](https://packagist.org/packages/strobotti/php-jwk) 6 | 7 | A small PHP library to handle JWKs (Json Web Keys) 8 | 9 | This library helps to create json web key sets from PEM and is also able to pull out PEMs from json web key sets. 10 | 11 | Please note that **only RSA keys are supported at the moment!** 12 | 13 | See [JSON Web Key RFC](https://tools.ietf.org/html/rfc7517) for reference. 14 | 15 | ## Installation 16 | 17 | This library requires PHP version 7.3 or higher and can be installed with composer: 18 | 19 | ```bash 20 | $ composer require strobotti/php-jwk 21 | ``` 22 | 23 | ## Example usage 24 | 25 | See full example [here](examples/full-flow.php). 26 | 27 | ### Create a key-object from PEM 28 | 29 | ```php 30 | 'sig', 46 | 'alg' => 'RS256', 47 | 'kid' => 'eXaunmL', 48 | ]; 49 | 50 | $keyFactory = new Strobotti\JWK\KeyFactory(); 51 | $key = $keyFactory->createFromPem($pem, $options); 52 | 53 | echo "$key"; 54 | ``` 55 | 56 | Outputs: 57 | 58 | ```json 59 | { 60 | "kty": "RSA", 61 | "use": "sig", 62 | "alg": "RS256", 63 | "kid": "eXaunmL", 64 | "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw", 65 | "e": "AQAB" 66 | } 67 | ``` 68 | 69 | ### Create a JWK set (jwks) from a key 70 | 71 | ```php 72 | addKey($key); 77 | 78 | echo "$keySet" ; 79 | 80 | ``` 81 | 82 | Outputs: 83 | 84 | ```json 85 | { 86 | "keys": [ 87 | { 88 | "kty": "RSA", 89 | "use": "sig", 90 | "alg": "RS256", 91 | "kid": "eXaunmL", 92 | "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw", 93 | "e": "AQAB" 94 | } 95 | ] 96 | } 97 | ``` 98 | 99 | ### Get a key from keyset by `kid` and convert it to PEM 100 | 101 | ```php 102 | getKeyById('eXaunmL'); 106 | $pem = (new \Strobotti\JWK\KeyConverter())->keyToPem($key); 107 | 108 | echo "$pem"; 109 | 110 | ``` 111 | 112 | Outputs: 113 | 114 | ```text 115 | -----BEGIN PUBLIC KEY----- 116 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf 117 | ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD 118 | Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE 119 | Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU 120 | lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe 121 | 4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh 122 | xwIDAQAB 123 | -----END PUBLIC KEY----- 124 | ``` 125 | 126 | ### Contributing 127 | 128 | See [CONTRIBUTING.md](CONTRIBUTING.md) for more details. 129 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strobotti/php-jwk", 3 | "description": "A small PHP library to handle JWKs (Json Web Keys)", 4 | "type": "library", 5 | "keywords": ["jwk", "jwks"], 6 | "homepage": "https://github.com/Strobotti/php-jwk", 7 | "authors": [ 8 | { 9 | "name": "Juha Jantunen", 10 | "email": "juha@strobotti.com", 11 | "homepage": "https://www.strobotti.com", 12 | "role": "Developer" 13 | } 14 | ], 15 | "license": "MIT", 16 | "require": { 17 | "php": ">=7.3.0", 18 | "ext-json": "*", 19 | "ext-openssl": "*", 20 | "phpseclib/phpseclib": "^3.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Strobotti\\JWK\\": "src/" 25 | } 26 | }, 27 | "autoload-dev": { 28 | "psr-4": { 29 | "Strobotti\\JWK\\Tests\\": "tests/" 30 | } 31 | }, 32 | "require-dev": { 33 | "ext-xml": "*", 34 | "phpunit/phpunit": "^10.0", 35 | "friendsofphp/php-cs-fixer": "3.54.0" 36 | }, 37 | "scripts": { 38 | "test": "./vendor/bin/phpunit" 39 | }, 40 | "scripts-descriptions": { 41 | "test": "Run all tests" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "3dc8b3e205fb2a57b1ca08802ea8c034", 8 | "packages": [ 9 | { 10 | "name": "paragonie/constant_time_encoding", 11 | "version": "v2.6.3", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/paragonie/constant_time_encoding.git", 15 | "reference": "58c3f47f650c94ec05a151692652a868995d2938" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", 20 | "reference": "58c3f47f650c94ec05a151692652a868995d2938", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^7|^8" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^6|^7|^8|^9", 28 | "vimeo/psalm": "^1|^2|^3|^4" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-4": { 33 | "ParagonIE\\ConstantTime\\": "src/" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Paragon Initiative Enterprises", 43 | "email": "security@paragonie.com", 44 | "homepage": "https://paragonie.com", 45 | "role": "Maintainer" 46 | }, 47 | { 48 | "name": "Steve 'Sc00bz' Thomas", 49 | "email": "steve@tobtu.com", 50 | "homepage": "https://www.tobtu.com", 51 | "role": "Original Developer" 52 | } 53 | ], 54 | "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", 55 | "keywords": [ 56 | "base16", 57 | "base32", 58 | "base32_decode", 59 | "base32_encode", 60 | "base64", 61 | "base64_decode", 62 | "base64_encode", 63 | "bin2hex", 64 | "encoding", 65 | "hex", 66 | "hex2bin", 67 | "rfc4648" 68 | ], 69 | "support": { 70 | "email": "info@paragonie.com", 71 | "issues": "https://github.com/paragonie/constant_time_encoding/issues", 72 | "source": "https://github.com/paragonie/constant_time_encoding" 73 | }, 74 | "time": "2022-06-14T06:56:20+00:00" 75 | }, 76 | { 77 | "name": "paragonie/random_compat", 78 | "version": "v9.99.100", 79 | "source": { 80 | "type": "git", 81 | "url": "https://github.com/paragonie/random_compat.git", 82 | "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" 83 | }, 84 | "dist": { 85 | "type": "zip", 86 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", 87 | "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", 88 | "shasum": "" 89 | }, 90 | "require": { 91 | "php": ">= 7" 92 | }, 93 | "require-dev": { 94 | "phpunit/phpunit": "4.*|5.*", 95 | "vimeo/psalm": "^1" 96 | }, 97 | "suggest": { 98 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." 99 | }, 100 | "type": "library", 101 | "notification-url": "https://packagist.org/downloads/", 102 | "license": [ 103 | "MIT" 104 | ], 105 | "authors": [ 106 | { 107 | "name": "Paragon Initiative Enterprises", 108 | "email": "security@paragonie.com", 109 | "homepage": "https://paragonie.com" 110 | } 111 | ], 112 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 113 | "keywords": [ 114 | "csprng", 115 | "polyfill", 116 | "pseudorandom", 117 | "random" 118 | ], 119 | "support": { 120 | "email": "info@paragonie.com", 121 | "issues": "https://github.com/paragonie/random_compat/issues", 122 | "source": "https://github.com/paragonie/random_compat" 123 | }, 124 | "time": "2020-10-15T08:29:30+00:00" 125 | }, 126 | { 127 | "name": "phpseclib/phpseclib", 128 | "version": "3.0.37", 129 | "source": { 130 | "type": "git", 131 | "url": "https://github.com/phpseclib/phpseclib.git", 132 | "reference": "cfa2013d0f68c062055180dd4328cc8b9d1f30b8" 133 | }, 134 | "dist": { 135 | "type": "zip", 136 | "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/cfa2013d0f68c062055180dd4328cc8b9d1f30b8", 137 | "reference": "cfa2013d0f68c062055180dd4328cc8b9d1f30b8", 138 | "shasum": "" 139 | }, 140 | "require": { 141 | "paragonie/constant_time_encoding": "^1|^2", 142 | "paragonie/random_compat": "^1.4|^2.0|^9.99.99", 143 | "php": ">=5.6.1" 144 | }, 145 | "require-dev": { 146 | "phpunit/phpunit": "*" 147 | }, 148 | "suggest": { 149 | "ext-dom": "Install the DOM extension to load XML formatted public keys.", 150 | "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", 151 | "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", 152 | "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", 153 | "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." 154 | }, 155 | "type": "library", 156 | "autoload": { 157 | "files": [ 158 | "phpseclib/bootstrap.php" 159 | ], 160 | "psr-4": { 161 | "phpseclib3\\": "phpseclib/" 162 | } 163 | }, 164 | "notification-url": "https://packagist.org/downloads/", 165 | "license": [ 166 | "MIT" 167 | ], 168 | "authors": [ 169 | { 170 | "name": "Jim Wigginton", 171 | "email": "terrafrost@php.net", 172 | "role": "Lead Developer" 173 | }, 174 | { 175 | "name": "Patrick Monnerat", 176 | "email": "pm@datasphere.ch", 177 | "role": "Developer" 178 | }, 179 | { 180 | "name": "Andreas Fischer", 181 | "email": "bantu@phpbb.com", 182 | "role": "Developer" 183 | }, 184 | { 185 | "name": "Hans-Jürgen Petrich", 186 | "email": "petrich@tronic-media.com", 187 | "role": "Developer" 188 | }, 189 | { 190 | "name": "Graham Campbell", 191 | "email": "graham@alt-three.com", 192 | "role": "Developer" 193 | } 194 | ], 195 | "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", 196 | "homepage": "http://phpseclib.sourceforge.net", 197 | "keywords": [ 198 | "BigInteger", 199 | "aes", 200 | "asn.1", 201 | "asn1", 202 | "blowfish", 203 | "crypto", 204 | "cryptography", 205 | "encryption", 206 | "rsa", 207 | "security", 208 | "sftp", 209 | "signature", 210 | "signing", 211 | "ssh", 212 | "twofish", 213 | "x.509", 214 | "x509" 215 | ], 216 | "support": { 217 | "issues": "https://github.com/phpseclib/phpseclib/issues", 218 | "source": "https://github.com/phpseclib/phpseclib/tree/3.0.37" 219 | }, 220 | "funding": [ 221 | { 222 | "url": "https://github.com/terrafrost", 223 | "type": "github" 224 | }, 225 | { 226 | "url": "https://www.patreon.com/phpseclib", 227 | "type": "patreon" 228 | }, 229 | { 230 | "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", 231 | "type": "tidelift" 232 | } 233 | ], 234 | "time": "2024-03-03T02:14:58+00:00" 235 | } 236 | ], 237 | "packages-dev": [ 238 | { 239 | "name": "composer/pcre", 240 | "version": "3.1.3", 241 | "source": { 242 | "type": "git", 243 | "url": "https://github.com/composer/pcre.git", 244 | "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" 245 | }, 246 | "dist": { 247 | "type": "zip", 248 | "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", 249 | "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", 250 | "shasum": "" 251 | }, 252 | "require": { 253 | "php": "^7.4 || ^8.0" 254 | }, 255 | "require-dev": { 256 | "phpstan/phpstan": "^1.3", 257 | "phpstan/phpstan-strict-rules": "^1.1", 258 | "symfony/phpunit-bridge": "^5" 259 | }, 260 | "type": "library", 261 | "extra": { 262 | "branch-alias": { 263 | "dev-main": "3.x-dev" 264 | } 265 | }, 266 | "autoload": { 267 | "psr-4": { 268 | "Composer\\Pcre\\": "src" 269 | } 270 | }, 271 | "notification-url": "https://packagist.org/downloads/", 272 | "license": [ 273 | "MIT" 274 | ], 275 | "authors": [ 276 | { 277 | "name": "Jordi Boggiano", 278 | "email": "j.boggiano@seld.be", 279 | "homepage": "http://seld.be" 280 | } 281 | ], 282 | "description": "PCRE wrapping library that offers type-safe preg_* replacements.", 283 | "keywords": [ 284 | "PCRE", 285 | "preg", 286 | "regex", 287 | "regular expression" 288 | ], 289 | "support": { 290 | "issues": "https://github.com/composer/pcre/issues", 291 | "source": "https://github.com/composer/pcre/tree/3.1.3" 292 | }, 293 | "funding": [ 294 | { 295 | "url": "https://packagist.com", 296 | "type": "custom" 297 | }, 298 | { 299 | "url": "https://github.com/composer", 300 | "type": "github" 301 | }, 302 | { 303 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 304 | "type": "tidelift" 305 | } 306 | ], 307 | "time": "2024-03-19T10:26:25+00:00" 308 | }, 309 | { 310 | "name": "composer/semver", 311 | "version": "3.4.0", 312 | "source": { 313 | "type": "git", 314 | "url": "https://github.com/composer/semver.git", 315 | "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" 316 | }, 317 | "dist": { 318 | "type": "zip", 319 | "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", 320 | "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", 321 | "shasum": "" 322 | }, 323 | "require": { 324 | "php": "^5.3.2 || ^7.0 || ^8.0" 325 | }, 326 | "require-dev": { 327 | "phpstan/phpstan": "^1.4", 328 | "symfony/phpunit-bridge": "^4.2 || ^5" 329 | }, 330 | "type": "library", 331 | "extra": { 332 | "branch-alias": { 333 | "dev-main": "3.x-dev" 334 | } 335 | }, 336 | "autoload": { 337 | "psr-4": { 338 | "Composer\\Semver\\": "src" 339 | } 340 | }, 341 | "notification-url": "https://packagist.org/downloads/", 342 | "license": [ 343 | "MIT" 344 | ], 345 | "authors": [ 346 | { 347 | "name": "Nils Adermann", 348 | "email": "naderman@naderman.de", 349 | "homepage": "http://www.naderman.de" 350 | }, 351 | { 352 | "name": "Jordi Boggiano", 353 | "email": "j.boggiano@seld.be", 354 | "homepage": "http://seld.be" 355 | }, 356 | { 357 | "name": "Rob Bast", 358 | "email": "rob.bast@gmail.com", 359 | "homepage": "http://robbast.nl" 360 | } 361 | ], 362 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 363 | "keywords": [ 364 | "semantic", 365 | "semver", 366 | "validation", 367 | "versioning" 368 | ], 369 | "support": { 370 | "irc": "ircs://irc.libera.chat:6697/composer", 371 | "issues": "https://github.com/composer/semver/issues", 372 | "source": "https://github.com/composer/semver/tree/3.4.0" 373 | }, 374 | "funding": [ 375 | { 376 | "url": "https://packagist.com", 377 | "type": "custom" 378 | }, 379 | { 380 | "url": "https://github.com/composer", 381 | "type": "github" 382 | }, 383 | { 384 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 385 | "type": "tidelift" 386 | } 387 | ], 388 | "time": "2023-08-31T09:50:34+00:00" 389 | }, 390 | { 391 | "name": "composer/xdebug-handler", 392 | "version": "3.0.4", 393 | "source": { 394 | "type": "git", 395 | "url": "https://github.com/composer/xdebug-handler.git", 396 | "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255" 397 | }, 398 | "dist": { 399 | "type": "zip", 400 | "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255", 401 | "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255", 402 | "shasum": "" 403 | }, 404 | "require": { 405 | "composer/pcre": "^1 || ^2 || ^3", 406 | "php": "^7.2.5 || ^8.0", 407 | "psr/log": "^1 || ^2 || ^3" 408 | }, 409 | "require-dev": { 410 | "phpstan/phpstan": "^1.0", 411 | "phpstan/phpstan-strict-rules": "^1.1", 412 | "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" 413 | }, 414 | "type": "library", 415 | "autoload": { 416 | "psr-4": { 417 | "Composer\\XdebugHandler\\": "src" 418 | } 419 | }, 420 | "notification-url": "https://packagist.org/downloads/", 421 | "license": [ 422 | "MIT" 423 | ], 424 | "authors": [ 425 | { 426 | "name": "John Stevenson", 427 | "email": "john-stevenson@blueyonder.co.uk" 428 | } 429 | ], 430 | "description": "Restarts a process without Xdebug.", 431 | "keywords": [ 432 | "Xdebug", 433 | "performance" 434 | ], 435 | "support": { 436 | "irc": "ircs://irc.libera.chat:6697/composer", 437 | "issues": "https://github.com/composer/xdebug-handler/issues", 438 | "source": "https://github.com/composer/xdebug-handler/tree/3.0.4" 439 | }, 440 | "funding": [ 441 | { 442 | "url": "https://packagist.com", 443 | "type": "custom" 444 | }, 445 | { 446 | "url": "https://github.com/composer", 447 | "type": "github" 448 | }, 449 | { 450 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 451 | "type": "tidelift" 452 | } 453 | ], 454 | "time": "2024-03-26T18:29:49+00:00" 455 | }, 456 | { 457 | "name": "friendsofphp/php-cs-fixer", 458 | "version": "v3.54.0", 459 | "source": { 460 | "type": "git", 461 | "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", 462 | "reference": "2aecbc8640d7906c38777b3dcab6f4ca79004d08" 463 | }, 464 | "dist": { 465 | "type": "zip", 466 | "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2aecbc8640d7906c38777b3dcab6f4ca79004d08", 467 | "reference": "2aecbc8640d7906c38777b3dcab6f4ca79004d08", 468 | "shasum": "" 469 | }, 470 | "require": { 471 | "composer/semver": "^3.4", 472 | "composer/xdebug-handler": "^3.0.3", 473 | "ext-filter": "*", 474 | "ext-json": "*", 475 | "ext-tokenizer": "*", 476 | "php": "^7.4 || ^8.0", 477 | "sebastian/diff": "^4.0 || ^5.0 || ^6.0", 478 | "symfony/console": "^5.4 || ^6.0 || ^7.0", 479 | "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", 480 | "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", 481 | "symfony/finder": "^5.4 || ^6.0 || ^7.0", 482 | "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", 483 | "symfony/polyfill-mbstring": "^1.28", 484 | "symfony/polyfill-php80": "^1.28", 485 | "symfony/polyfill-php81": "^1.28", 486 | "symfony/process": "^5.4 || ^6.0 || ^7.0", 487 | "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" 488 | }, 489 | "require-dev": { 490 | "facile-it/paraunit": "^1.3 || ^2.0", 491 | "infection/infection": "^0.27.11", 492 | "justinrainbow/json-schema": "^5.2", 493 | "keradus/cli-executor": "^2.1", 494 | "mikey179/vfsstream": "^1.6.11", 495 | "php-coveralls/php-coveralls": "^2.7", 496 | "php-cs-fixer/accessible-object": "^1.1", 497 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", 498 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", 499 | "phpunit/phpunit": "^9.6 || ^10.5.5 || ^11.0.2", 500 | "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", 501 | "symfony/yaml": "^5.4 || ^6.0 || ^7.0" 502 | }, 503 | "suggest": { 504 | "ext-dom": "For handling output formats in XML", 505 | "ext-mbstring": "For handling non-UTF8 characters." 506 | }, 507 | "bin": [ 508 | "php-cs-fixer" 509 | ], 510 | "type": "application", 511 | "autoload": { 512 | "psr-4": { 513 | "PhpCsFixer\\": "src/" 514 | } 515 | }, 516 | "notification-url": "https://packagist.org/downloads/", 517 | "license": [ 518 | "MIT" 519 | ], 520 | "authors": [ 521 | { 522 | "name": "Fabien Potencier", 523 | "email": "fabien@symfony.com" 524 | }, 525 | { 526 | "name": "Dariusz Rumiński", 527 | "email": "dariusz.ruminski@gmail.com" 528 | } 529 | ], 530 | "description": "A tool to automatically fix PHP code style", 531 | "keywords": [ 532 | "Static code analysis", 533 | "fixer", 534 | "standards", 535 | "static analysis" 536 | ], 537 | "support": { 538 | "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", 539 | "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.54.0" 540 | }, 541 | "funding": [ 542 | { 543 | "url": "https://github.com/keradus", 544 | "type": "github" 545 | } 546 | ], 547 | "time": "2024-04-17T08:12:13+00:00" 548 | }, 549 | { 550 | "name": "myclabs/deep-copy", 551 | "version": "1.11.1", 552 | "source": { 553 | "type": "git", 554 | "url": "https://github.com/myclabs/DeepCopy.git", 555 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" 556 | }, 557 | "dist": { 558 | "type": "zip", 559 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", 560 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", 561 | "shasum": "" 562 | }, 563 | "require": { 564 | "php": "^7.1 || ^8.0" 565 | }, 566 | "conflict": { 567 | "doctrine/collections": "<1.6.8", 568 | "doctrine/common": "<2.13.3 || >=3,<3.2.2" 569 | }, 570 | "require-dev": { 571 | "doctrine/collections": "^1.6.8", 572 | "doctrine/common": "^2.13.3 || ^3.2.2", 573 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 574 | }, 575 | "type": "library", 576 | "autoload": { 577 | "files": [ 578 | "src/DeepCopy/deep_copy.php" 579 | ], 580 | "psr-4": { 581 | "DeepCopy\\": "src/DeepCopy/" 582 | } 583 | }, 584 | "notification-url": "https://packagist.org/downloads/", 585 | "license": [ 586 | "MIT" 587 | ], 588 | "description": "Create deep copies (clones) of your objects", 589 | "keywords": [ 590 | "clone", 591 | "copy", 592 | "duplicate", 593 | "object", 594 | "object graph" 595 | ], 596 | "support": { 597 | "issues": "https://github.com/myclabs/DeepCopy/issues", 598 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" 599 | }, 600 | "funding": [ 601 | { 602 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 603 | "type": "tidelift" 604 | } 605 | ], 606 | "time": "2023-03-08T13:26:56+00:00" 607 | }, 608 | { 609 | "name": "nikic/php-parser", 610 | "version": "v5.0.2", 611 | "source": { 612 | "type": "git", 613 | "url": "https://github.com/nikic/PHP-Parser.git", 614 | "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" 615 | }, 616 | "dist": { 617 | "type": "zip", 618 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", 619 | "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", 620 | "shasum": "" 621 | }, 622 | "require": { 623 | "ext-ctype": "*", 624 | "ext-json": "*", 625 | "ext-tokenizer": "*", 626 | "php": ">=7.4" 627 | }, 628 | "require-dev": { 629 | "ircmaxell/php-yacc": "^0.0.7", 630 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" 631 | }, 632 | "bin": [ 633 | "bin/php-parse" 634 | ], 635 | "type": "library", 636 | "extra": { 637 | "branch-alias": { 638 | "dev-master": "5.0-dev" 639 | } 640 | }, 641 | "autoload": { 642 | "psr-4": { 643 | "PhpParser\\": "lib/PhpParser" 644 | } 645 | }, 646 | "notification-url": "https://packagist.org/downloads/", 647 | "license": [ 648 | "BSD-3-Clause" 649 | ], 650 | "authors": [ 651 | { 652 | "name": "Nikita Popov" 653 | } 654 | ], 655 | "description": "A PHP parser written in PHP", 656 | "keywords": [ 657 | "parser", 658 | "php" 659 | ], 660 | "support": { 661 | "issues": "https://github.com/nikic/PHP-Parser/issues", 662 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" 663 | }, 664 | "time": "2024-03-05T20:51:40+00:00" 665 | }, 666 | { 667 | "name": "phar-io/manifest", 668 | "version": "2.0.4", 669 | "source": { 670 | "type": "git", 671 | "url": "https://github.com/phar-io/manifest.git", 672 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 673 | }, 674 | "dist": { 675 | "type": "zip", 676 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 677 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 678 | "shasum": "" 679 | }, 680 | "require": { 681 | "ext-dom": "*", 682 | "ext-libxml": "*", 683 | "ext-phar": "*", 684 | "ext-xmlwriter": "*", 685 | "phar-io/version": "^3.0.1", 686 | "php": "^7.2 || ^8.0" 687 | }, 688 | "type": "library", 689 | "extra": { 690 | "branch-alias": { 691 | "dev-master": "2.0.x-dev" 692 | } 693 | }, 694 | "autoload": { 695 | "classmap": [ 696 | "src/" 697 | ] 698 | }, 699 | "notification-url": "https://packagist.org/downloads/", 700 | "license": [ 701 | "BSD-3-Clause" 702 | ], 703 | "authors": [ 704 | { 705 | "name": "Arne Blankerts", 706 | "email": "arne@blankerts.de", 707 | "role": "Developer" 708 | }, 709 | { 710 | "name": "Sebastian Heuer", 711 | "email": "sebastian@phpeople.de", 712 | "role": "Developer" 713 | }, 714 | { 715 | "name": "Sebastian Bergmann", 716 | "email": "sebastian@phpunit.de", 717 | "role": "Developer" 718 | } 719 | ], 720 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 721 | "support": { 722 | "issues": "https://github.com/phar-io/manifest/issues", 723 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 724 | }, 725 | "funding": [ 726 | { 727 | "url": "https://github.com/theseer", 728 | "type": "github" 729 | } 730 | ], 731 | "time": "2024-03-03T12:33:53+00:00" 732 | }, 733 | { 734 | "name": "phar-io/version", 735 | "version": "3.2.1", 736 | "source": { 737 | "type": "git", 738 | "url": "https://github.com/phar-io/version.git", 739 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 740 | }, 741 | "dist": { 742 | "type": "zip", 743 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 744 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 745 | "shasum": "" 746 | }, 747 | "require": { 748 | "php": "^7.2 || ^8.0" 749 | }, 750 | "type": "library", 751 | "autoload": { 752 | "classmap": [ 753 | "src/" 754 | ] 755 | }, 756 | "notification-url": "https://packagist.org/downloads/", 757 | "license": [ 758 | "BSD-3-Clause" 759 | ], 760 | "authors": [ 761 | { 762 | "name": "Arne Blankerts", 763 | "email": "arne@blankerts.de", 764 | "role": "Developer" 765 | }, 766 | { 767 | "name": "Sebastian Heuer", 768 | "email": "sebastian@phpeople.de", 769 | "role": "Developer" 770 | }, 771 | { 772 | "name": "Sebastian Bergmann", 773 | "email": "sebastian@phpunit.de", 774 | "role": "Developer" 775 | } 776 | ], 777 | "description": "Library for handling version information and constraints", 778 | "support": { 779 | "issues": "https://github.com/phar-io/version/issues", 780 | "source": "https://github.com/phar-io/version/tree/3.2.1" 781 | }, 782 | "time": "2022-02-21T01:04:05+00:00" 783 | }, 784 | { 785 | "name": "phpunit/php-code-coverage", 786 | "version": "10.1.14", 787 | "source": { 788 | "type": "git", 789 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 790 | "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" 791 | }, 792 | "dist": { 793 | "type": "zip", 794 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", 795 | "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", 796 | "shasum": "" 797 | }, 798 | "require": { 799 | "ext-dom": "*", 800 | "ext-libxml": "*", 801 | "ext-xmlwriter": "*", 802 | "nikic/php-parser": "^4.18 || ^5.0", 803 | "php": ">=8.1", 804 | "phpunit/php-file-iterator": "^4.0", 805 | "phpunit/php-text-template": "^3.0", 806 | "sebastian/code-unit-reverse-lookup": "^3.0", 807 | "sebastian/complexity": "^3.0", 808 | "sebastian/environment": "^6.0", 809 | "sebastian/lines-of-code": "^2.0", 810 | "sebastian/version": "^4.0", 811 | "theseer/tokenizer": "^1.2.0" 812 | }, 813 | "require-dev": { 814 | "phpunit/phpunit": "^10.1" 815 | }, 816 | "suggest": { 817 | "ext-pcov": "PHP extension that provides line coverage", 818 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 819 | }, 820 | "type": "library", 821 | "extra": { 822 | "branch-alias": { 823 | "dev-main": "10.1-dev" 824 | } 825 | }, 826 | "autoload": { 827 | "classmap": [ 828 | "src/" 829 | ] 830 | }, 831 | "notification-url": "https://packagist.org/downloads/", 832 | "license": [ 833 | "BSD-3-Clause" 834 | ], 835 | "authors": [ 836 | { 837 | "name": "Sebastian Bergmann", 838 | "email": "sebastian@phpunit.de", 839 | "role": "lead" 840 | } 841 | ], 842 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 843 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 844 | "keywords": [ 845 | "coverage", 846 | "testing", 847 | "xunit" 848 | ], 849 | "support": { 850 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 851 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 852 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" 853 | }, 854 | "funding": [ 855 | { 856 | "url": "https://github.com/sebastianbergmann", 857 | "type": "github" 858 | } 859 | ], 860 | "time": "2024-03-12T15:33:41+00:00" 861 | }, 862 | { 863 | "name": "phpunit/php-file-iterator", 864 | "version": "4.1.0", 865 | "source": { 866 | "type": "git", 867 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 868 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" 869 | }, 870 | "dist": { 871 | "type": "zip", 872 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", 873 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", 874 | "shasum": "" 875 | }, 876 | "require": { 877 | "php": ">=8.1" 878 | }, 879 | "require-dev": { 880 | "phpunit/phpunit": "^10.0" 881 | }, 882 | "type": "library", 883 | "extra": { 884 | "branch-alias": { 885 | "dev-main": "4.0-dev" 886 | } 887 | }, 888 | "autoload": { 889 | "classmap": [ 890 | "src/" 891 | ] 892 | }, 893 | "notification-url": "https://packagist.org/downloads/", 894 | "license": [ 895 | "BSD-3-Clause" 896 | ], 897 | "authors": [ 898 | { 899 | "name": "Sebastian Bergmann", 900 | "email": "sebastian@phpunit.de", 901 | "role": "lead" 902 | } 903 | ], 904 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 905 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 906 | "keywords": [ 907 | "filesystem", 908 | "iterator" 909 | ], 910 | "support": { 911 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 912 | "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", 913 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" 914 | }, 915 | "funding": [ 916 | { 917 | "url": "https://github.com/sebastianbergmann", 918 | "type": "github" 919 | } 920 | ], 921 | "time": "2023-08-31T06:24:48+00:00" 922 | }, 923 | { 924 | "name": "phpunit/php-invoker", 925 | "version": "4.0.0", 926 | "source": { 927 | "type": "git", 928 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 929 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" 930 | }, 931 | "dist": { 932 | "type": "zip", 933 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 934 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 935 | "shasum": "" 936 | }, 937 | "require": { 938 | "php": ">=8.1" 939 | }, 940 | "require-dev": { 941 | "ext-pcntl": "*", 942 | "phpunit/phpunit": "^10.0" 943 | }, 944 | "suggest": { 945 | "ext-pcntl": "*" 946 | }, 947 | "type": "library", 948 | "extra": { 949 | "branch-alias": { 950 | "dev-main": "4.0-dev" 951 | } 952 | }, 953 | "autoload": { 954 | "classmap": [ 955 | "src/" 956 | ] 957 | }, 958 | "notification-url": "https://packagist.org/downloads/", 959 | "license": [ 960 | "BSD-3-Clause" 961 | ], 962 | "authors": [ 963 | { 964 | "name": "Sebastian Bergmann", 965 | "email": "sebastian@phpunit.de", 966 | "role": "lead" 967 | } 968 | ], 969 | "description": "Invoke callables with a timeout", 970 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 971 | "keywords": [ 972 | "process" 973 | ], 974 | "support": { 975 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 976 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" 977 | }, 978 | "funding": [ 979 | { 980 | "url": "https://github.com/sebastianbergmann", 981 | "type": "github" 982 | } 983 | ], 984 | "time": "2023-02-03T06:56:09+00:00" 985 | }, 986 | { 987 | "name": "phpunit/php-text-template", 988 | "version": "3.0.1", 989 | "source": { 990 | "type": "git", 991 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 992 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" 993 | }, 994 | "dist": { 995 | "type": "zip", 996 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", 997 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", 998 | "shasum": "" 999 | }, 1000 | "require": { 1001 | "php": ">=8.1" 1002 | }, 1003 | "require-dev": { 1004 | "phpunit/phpunit": "^10.0" 1005 | }, 1006 | "type": "library", 1007 | "extra": { 1008 | "branch-alias": { 1009 | "dev-main": "3.0-dev" 1010 | } 1011 | }, 1012 | "autoload": { 1013 | "classmap": [ 1014 | "src/" 1015 | ] 1016 | }, 1017 | "notification-url": "https://packagist.org/downloads/", 1018 | "license": [ 1019 | "BSD-3-Clause" 1020 | ], 1021 | "authors": [ 1022 | { 1023 | "name": "Sebastian Bergmann", 1024 | "email": "sebastian@phpunit.de", 1025 | "role": "lead" 1026 | } 1027 | ], 1028 | "description": "Simple template engine.", 1029 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 1030 | "keywords": [ 1031 | "template" 1032 | ], 1033 | "support": { 1034 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 1035 | "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", 1036 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" 1037 | }, 1038 | "funding": [ 1039 | { 1040 | "url": "https://github.com/sebastianbergmann", 1041 | "type": "github" 1042 | } 1043 | ], 1044 | "time": "2023-08-31T14:07:24+00:00" 1045 | }, 1046 | { 1047 | "name": "phpunit/php-timer", 1048 | "version": "6.0.0", 1049 | "source": { 1050 | "type": "git", 1051 | "url": "https://github.com/sebastianbergmann/php-timer.git", 1052 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" 1053 | }, 1054 | "dist": { 1055 | "type": "zip", 1056 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", 1057 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", 1058 | "shasum": "" 1059 | }, 1060 | "require": { 1061 | "php": ">=8.1" 1062 | }, 1063 | "require-dev": { 1064 | "phpunit/phpunit": "^10.0" 1065 | }, 1066 | "type": "library", 1067 | "extra": { 1068 | "branch-alias": { 1069 | "dev-main": "6.0-dev" 1070 | } 1071 | }, 1072 | "autoload": { 1073 | "classmap": [ 1074 | "src/" 1075 | ] 1076 | }, 1077 | "notification-url": "https://packagist.org/downloads/", 1078 | "license": [ 1079 | "BSD-3-Clause" 1080 | ], 1081 | "authors": [ 1082 | { 1083 | "name": "Sebastian Bergmann", 1084 | "email": "sebastian@phpunit.de", 1085 | "role": "lead" 1086 | } 1087 | ], 1088 | "description": "Utility class for timing", 1089 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1090 | "keywords": [ 1091 | "timer" 1092 | ], 1093 | "support": { 1094 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 1095 | "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" 1096 | }, 1097 | "funding": [ 1098 | { 1099 | "url": "https://github.com/sebastianbergmann", 1100 | "type": "github" 1101 | } 1102 | ], 1103 | "time": "2023-02-03T06:57:52+00:00" 1104 | }, 1105 | { 1106 | "name": "phpunit/phpunit", 1107 | "version": "10.5.20", 1108 | "source": { 1109 | "type": "git", 1110 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1111 | "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3" 1112 | }, 1113 | "dist": { 1114 | "type": "zip", 1115 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3", 1116 | "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3", 1117 | "shasum": "" 1118 | }, 1119 | "require": { 1120 | "ext-dom": "*", 1121 | "ext-json": "*", 1122 | "ext-libxml": "*", 1123 | "ext-mbstring": "*", 1124 | "ext-xml": "*", 1125 | "ext-xmlwriter": "*", 1126 | "myclabs/deep-copy": "^1.10.1", 1127 | "phar-io/manifest": "^2.0.3", 1128 | "phar-io/version": "^3.0.2", 1129 | "php": ">=8.1", 1130 | "phpunit/php-code-coverage": "^10.1.5", 1131 | "phpunit/php-file-iterator": "^4.0", 1132 | "phpunit/php-invoker": "^4.0", 1133 | "phpunit/php-text-template": "^3.0", 1134 | "phpunit/php-timer": "^6.0", 1135 | "sebastian/cli-parser": "^2.0", 1136 | "sebastian/code-unit": "^2.0", 1137 | "sebastian/comparator": "^5.0", 1138 | "sebastian/diff": "^5.0", 1139 | "sebastian/environment": "^6.0", 1140 | "sebastian/exporter": "^5.1", 1141 | "sebastian/global-state": "^6.0.1", 1142 | "sebastian/object-enumerator": "^5.0", 1143 | "sebastian/recursion-context": "^5.0", 1144 | "sebastian/type": "^4.0", 1145 | "sebastian/version": "^4.0" 1146 | }, 1147 | "suggest": { 1148 | "ext-soap": "To be able to generate mocks based on WSDL files" 1149 | }, 1150 | "bin": [ 1151 | "phpunit" 1152 | ], 1153 | "type": "library", 1154 | "extra": { 1155 | "branch-alias": { 1156 | "dev-main": "10.5-dev" 1157 | } 1158 | }, 1159 | "autoload": { 1160 | "files": [ 1161 | "src/Framework/Assert/Functions.php" 1162 | ], 1163 | "classmap": [ 1164 | "src/" 1165 | ] 1166 | }, 1167 | "notification-url": "https://packagist.org/downloads/", 1168 | "license": [ 1169 | "BSD-3-Clause" 1170 | ], 1171 | "authors": [ 1172 | { 1173 | "name": "Sebastian Bergmann", 1174 | "email": "sebastian@phpunit.de", 1175 | "role": "lead" 1176 | } 1177 | ], 1178 | "description": "The PHP Unit Testing framework.", 1179 | "homepage": "https://phpunit.de/", 1180 | "keywords": [ 1181 | "phpunit", 1182 | "testing", 1183 | "xunit" 1184 | ], 1185 | "support": { 1186 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 1187 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 1188 | "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20" 1189 | }, 1190 | "funding": [ 1191 | { 1192 | "url": "https://phpunit.de/sponsors.html", 1193 | "type": "custom" 1194 | }, 1195 | { 1196 | "url": "https://github.com/sebastianbergmann", 1197 | "type": "github" 1198 | }, 1199 | { 1200 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 1201 | "type": "tidelift" 1202 | } 1203 | ], 1204 | "time": "2024-04-24T06:32:35+00:00" 1205 | }, 1206 | { 1207 | "name": "psr/container", 1208 | "version": "2.0.2", 1209 | "source": { 1210 | "type": "git", 1211 | "url": "https://github.com/php-fig/container.git", 1212 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" 1213 | }, 1214 | "dist": { 1215 | "type": "zip", 1216 | "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", 1217 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", 1218 | "shasum": "" 1219 | }, 1220 | "require": { 1221 | "php": ">=7.4.0" 1222 | }, 1223 | "type": "library", 1224 | "extra": { 1225 | "branch-alias": { 1226 | "dev-master": "2.0.x-dev" 1227 | } 1228 | }, 1229 | "autoload": { 1230 | "psr-4": { 1231 | "Psr\\Container\\": "src/" 1232 | } 1233 | }, 1234 | "notification-url": "https://packagist.org/downloads/", 1235 | "license": [ 1236 | "MIT" 1237 | ], 1238 | "authors": [ 1239 | { 1240 | "name": "PHP-FIG", 1241 | "homepage": "https://www.php-fig.org/" 1242 | } 1243 | ], 1244 | "description": "Common Container Interface (PHP FIG PSR-11)", 1245 | "homepage": "https://github.com/php-fig/container", 1246 | "keywords": [ 1247 | "PSR-11", 1248 | "container", 1249 | "container-interface", 1250 | "container-interop", 1251 | "psr" 1252 | ], 1253 | "support": { 1254 | "issues": "https://github.com/php-fig/container/issues", 1255 | "source": "https://github.com/php-fig/container/tree/2.0.2" 1256 | }, 1257 | "time": "2021-11-05T16:47:00+00:00" 1258 | }, 1259 | { 1260 | "name": "psr/event-dispatcher", 1261 | "version": "1.0.0", 1262 | "source": { 1263 | "type": "git", 1264 | "url": "https://github.com/php-fig/event-dispatcher.git", 1265 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" 1266 | }, 1267 | "dist": { 1268 | "type": "zip", 1269 | "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", 1270 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", 1271 | "shasum": "" 1272 | }, 1273 | "require": { 1274 | "php": ">=7.2.0" 1275 | }, 1276 | "type": "library", 1277 | "extra": { 1278 | "branch-alias": { 1279 | "dev-master": "1.0.x-dev" 1280 | } 1281 | }, 1282 | "autoload": { 1283 | "psr-4": { 1284 | "Psr\\EventDispatcher\\": "src/" 1285 | } 1286 | }, 1287 | "notification-url": "https://packagist.org/downloads/", 1288 | "license": [ 1289 | "MIT" 1290 | ], 1291 | "authors": [ 1292 | { 1293 | "name": "PHP-FIG", 1294 | "homepage": "http://www.php-fig.org/" 1295 | } 1296 | ], 1297 | "description": "Standard interfaces for event handling.", 1298 | "keywords": [ 1299 | "events", 1300 | "psr", 1301 | "psr-14" 1302 | ], 1303 | "support": { 1304 | "issues": "https://github.com/php-fig/event-dispatcher/issues", 1305 | "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" 1306 | }, 1307 | "time": "2019-01-08T18:20:26+00:00" 1308 | }, 1309 | { 1310 | "name": "psr/log", 1311 | "version": "3.0.0", 1312 | "source": { 1313 | "type": "git", 1314 | "url": "https://github.com/php-fig/log.git", 1315 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" 1316 | }, 1317 | "dist": { 1318 | "type": "zip", 1319 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", 1320 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", 1321 | "shasum": "" 1322 | }, 1323 | "require": { 1324 | "php": ">=8.0.0" 1325 | }, 1326 | "type": "library", 1327 | "extra": { 1328 | "branch-alias": { 1329 | "dev-master": "3.x-dev" 1330 | } 1331 | }, 1332 | "autoload": { 1333 | "psr-4": { 1334 | "Psr\\Log\\": "src" 1335 | } 1336 | }, 1337 | "notification-url": "https://packagist.org/downloads/", 1338 | "license": [ 1339 | "MIT" 1340 | ], 1341 | "authors": [ 1342 | { 1343 | "name": "PHP-FIG", 1344 | "homepage": "https://www.php-fig.org/" 1345 | } 1346 | ], 1347 | "description": "Common interface for logging libraries", 1348 | "homepage": "https://github.com/php-fig/log", 1349 | "keywords": [ 1350 | "log", 1351 | "psr", 1352 | "psr-3" 1353 | ], 1354 | "support": { 1355 | "source": "https://github.com/php-fig/log/tree/3.0.0" 1356 | }, 1357 | "time": "2021-07-14T16:46:02+00:00" 1358 | }, 1359 | { 1360 | "name": "sebastian/cli-parser", 1361 | "version": "2.0.1", 1362 | "source": { 1363 | "type": "git", 1364 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 1365 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" 1366 | }, 1367 | "dist": { 1368 | "type": "zip", 1369 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", 1370 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", 1371 | "shasum": "" 1372 | }, 1373 | "require": { 1374 | "php": ">=8.1" 1375 | }, 1376 | "require-dev": { 1377 | "phpunit/phpunit": "^10.0" 1378 | }, 1379 | "type": "library", 1380 | "extra": { 1381 | "branch-alias": { 1382 | "dev-main": "2.0-dev" 1383 | } 1384 | }, 1385 | "autoload": { 1386 | "classmap": [ 1387 | "src/" 1388 | ] 1389 | }, 1390 | "notification-url": "https://packagist.org/downloads/", 1391 | "license": [ 1392 | "BSD-3-Clause" 1393 | ], 1394 | "authors": [ 1395 | { 1396 | "name": "Sebastian Bergmann", 1397 | "email": "sebastian@phpunit.de", 1398 | "role": "lead" 1399 | } 1400 | ], 1401 | "description": "Library for parsing CLI options", 1402 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 1403 | "support": { 1404 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 1405 | "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", 1406 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" 1407 | }, 1408 | "funding": [ 1409 | { 1410 | "url": "https://github.com/sebastianbergmann", 1411 | "type": "github" 1412 | } 1413 | ], 1414 | "time": "2024-03-02T07:12:49+00:00" 1415 | }, 1416 | { 1417 | "name": "sebastian/code-unit", 1418 | "version": "2.0.0", 1419 | "source": { 1420 | "type": "git", 1421 | "url": "https://github.com/sebastianbergmann/code-unit.git", 1422 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" 1423 | }, 1424 | "dist": { 1425 | "type": "zip", 1426 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", 1427 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", 1428 | "shasum": "" 1429 | }, 1430 | "require": { 1431 | "php": ">=8.1" 1432 | }, 1433 | "require-dev": { 1434 | "phpunit/phpunit": "^10.0" 1435 | }, 1436 | "type": "library", 1437 | "extra": { 1438 | "branch-alias": { 1439 | "dev-main": "2.0-dev" 1440 | } 1441 | }, 1442 | "autoload": { 1443 | "classmap": [ 1444 | "src/" 1445 | ] 1446 | }, 1447 | "notification-url": "https://packagist.org/downloads/", 1448 | "license": [ 1449 | "BSD-3-Clause" 1450 | ], 1451 | "authors": [ 1452 | { 1453 | "name": "Sebastian Bergmann", 1454 | "email": "sebastian@phpunit.de", 1455 | "role": "lead" 1456 | } 1457 | ], 1458 | "description": "Collection of value objects that represent the PHP code units", 1459 | "homepage": "https://github.com/sebastianbergmann/code-unit", 1460 | "support": { 1461 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 1462 | "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" 1463 | }, 1464 | "funding": [ 1465 | { 1466 | "url": "https://github.com/sebastianbergmann", 1467 | "type": "github" 1468 | } 1469 | ], 1470 | "time": "2023-02-03T06:58:43+00:00" 1471 | }, 1472 | { 1473 | "name": "sebastian/code-unit-reverse-lookup", 1474 | "version": "3.0.0", 1475 | "source": { 1476 | "type": "git", 1477 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1478 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" 1479 | }, 1480 | "dist": { 1481 | "type": "zip", 1482 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 1483 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 1484 | "shasum": "" 1485 | }, 1486 | "require": { 1487 | "php": ">=8.1" 1488 | }, 1489 | "require-dev": { 1490 | "phpunit/phpunit": "^10.0" 1491 | }, 1492 | "type": "library", 1493 | "extra": { 1494 | "branch-alias": { 1495 | "dev-main": "3.0-dev" 1496 | } 1497 | }, 1498 | "autoload": { 1499 | "classmap": [ 1500 | "src/" 1501 | ] 1502 | }, 1503 | "notification-url": "https://packagist.org/downloads/", 1504 | "license": [ 1505 | "BSD-3-Clause" 1506 | ], 1507 | "authors": [ 1508 | { 1509 | "name": "Sebastian Bergmann", 1510 | "email": "sebastian@phpunit.de" 1511 | } 1512 | ], 1513 | "description": "Looks up which function or method a line of code belongs to", 1514 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1515 | "support": { 1516 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 1517 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" 1518 | }, 1519 | "funding": [ 1520 | { 1521 | "url": "https://github.com/sebastianbergmann", 1522 | "type": "github" 1523 | } 1524 | ], 1525 | "time": "2023-02-03T06:59:15+00:00" 1526 | }, 1527 | { 1528 | "name": "sebastian/comparator", 1529 | "version": "5.0.1", 1530 | "source": { 1531 | "type": "git", 1532 | "url": "https://github.com/sebastianbergmann/comparator.git", 1533 | "reference": "2db5010a484d53ebf536087a70b4a5423c102372" 1534 | }, 1535 | "dist": { 1536 | "type": "zip", 1537 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", 1538 | "reference": "2db5010a484d53ebf536087a70b4a5423c102372", 1539 | "shasum": "" 1540 | }, 1541 | "require": { 1542 | "ext-dom": "*", 1543 | "ext-mbstring": "*", 1544 | "php": ">=8.1", 1545 | "sebastian/diff": "^5.0", 1546 | "sebastian/exporter": "^5.0" 1547 | }, 1548 | "require-dev": { 1549 | "phpunit/phpunit": "^10.3" 1550 | }, 1551 | "type": "library", 1552 | "extra": { 1553 | "branch-alias": { 1554 | "dev-main": "5.0-dev" 1555 | } 1556 | }, 1557 | "autoload": { 1558 | "classmap": [ 1559 | "src/" 1560 | ] 1561 | }, 1562 | "notification-url": "https://packagist.org/downloads/", 1563 | "license": [ 1564 | "BSD-3-Clause" 1565 | ], 1566 | "authors": [ 1567 | { 1568 | "name": "Sebastian Bergmann", 1569 | "email": "sebastian@phpunit.de" 1570 | }, 1571 | { 1572 | "name": "Jeff Welch", 1573 | "email": "whatthejeff@gmail.com" 1574 | }, 1575 | { 1576 | "name": "Volker Dusch", 1577 | "email": "github@wallbash.com" 1578 | }, 1579 | { 1580 | "name": "Bernhard Schussek", 1581 | "email": "bschussek@2bepublished.at" 1582 | } 1583 | ], 1584 | "description": "Provides the functionality to compare PHP values for equality", 1585 | "homepage": "https://github.com/sebastianbergmann/comparator", 1586 | "keywords": [ 1587 | "comparator", 1588 | "compare", 1589 | "equality" 1590 | ], 1591 | "support": { 1592 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 1593 | "security": "https://github.com/sebastianbergmann/comparator/security/policy", 1594 | "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" 1595 | }, 1596 | "funding": [ 1597 | { 1598 | "url": "https://github.com/sebastianbergmann", 1599 | "type": "github" 1600 | } 1601 | ], 1602 | "time": "2023-08-14T13:18:12+00:00" 1603 | }, 1604 | { 1605 | "name": "sebastian/complexity", 1606 | "version": "3.2.0", 1607 | "source": { 1608 | "type": "git", 1609 | "url": "https://github.com/sebastianbergmann/complexity.git", 1610 | "reference": "68ff824baeae169ec9f2137158ee529584553799" 1611 | }, 1612 | "dist": { 1613 | "type": "zip", 1614 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", 1615 | "reference": "68ff824baeae169ec9f2137158ee529584553799", 1616 | "shasum": "" 1617 | }, 1618 | "require": { 1619 | "nikic/php-parser": "^4.18 || ^5.0", 1620 | "php": ">=8.1" 1621 | }, 1622 | "require-dev": { 1623 | "phpunit/phpunit": "^10.0" 1624 | }, 1625 | "type": "library", 1626 | "extra": { 1627 | "branch-alias": { 1628 | "dev-main": "3.2-dev" 1629 | } 1630 | }, 1631 | "autoload": { 1632 | "classmap": [ 1633 | "src/" 1634 | ] 1635 | }, 1636 | "notification-url": "https://packagist.org/downloads/", 1637 | "license": [ 1638 | "BSD-3-Clause" 1639 | ], 1640 | "authors": [ 1641 | { 1642 | "name": "Sebastian Bergmann", 1643 | "email": "sebastian@phpunit.de", 1644 | "role": "lead" 1645 | } 1646 | ], 1647 | "description": "Library for calculating the complexity of PHP code units", 1648 | "homepage": "https://github.com/sebastianbergmann/complexity", 1649 | "support": { 1650 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1651 | "security": "https://github.com/sebastianbergmann/complexity/security/policy", 1652 | "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" 1653 | }, 1654 | "funding": [ 1655 | { 1656 | "url": "https://github.com/sebastianbergmann", 1657 | "type": "github" 1658 | } 1659 | ], 1660 | "time": "2023-12-21T08:37:17+00:00" 1661 | }, 1662 | { 1663 | "name": "sebastian/diff", 1664 | "version": "5.1.1", 1665 | "source": { 1666 | "type": "git", 1667 | "url": "https://github.com/sebastianbergmann/diff.git", 1668 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" 1669 | }, 1670 | "dist": { 1671 | "type": "zip", 1672 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", 1673 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", 1674 | "shasum": "" 1675 | }, 1676 | "require": { 1677 | "php": ">=8.1" 1678 | }, 1679 | "require-dev": { 1680 | "phpunit/phpunit": "^10.0", 1681 | "symfony/process": "^6.4" 1682 | }, 1683 | "type": "library", 1684 | "extra": { 1685 | "branch-alias": { 1686 | "dev-main": "5.1-dev" 1687 | } 1688 | }, 1689 | "autoload": { 1690 | "classmap": [ 1691 | "src/" 1692 | ] 1693 | }, 1694 | "notification-url": "https://packagist.org/downloads/", 1695 | "license": [ 1696 | "BSD-3-Clause" 1697 | ], 1698 | "authors": [ 1699 | { 1700 | "name": "Sebastian Bergmann", 1701 | "email": "sebastian@phpunit.de" 1702 | }, 1703 | { 1704 | "name": "Kore Nordmann", 1705 | "email": "mail@kore-nordmann.de" 1706 | } 1707 | ], 1708 | "description": "Diff implementation", 1709 | "homepage": "https://github.com/sebastianbergmann/diff", 1710 | "keywords": [ 1711 | "diff", 1712 | "udiff", 1713 | "unidiff", 1714 | "unified diff" 1715 | ], 1716 | "support": { 1717 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1718 | "security": "https://github.com/sebastianbergmann/diff/security/policy", 1719 | "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" 1720 | }, 1721 | "funding": [ 1722 | { 1723 | "url": "https://github.com/sebastianbergmann", 1724 | "type": "github" 1725 | } 1726 | ], 1727 | "time": "2024-03-02T07:15:17+00:00" 1728 | }, 1729 | { 1730 | "name": "sebastian/environment", 1731 | "version": "6.1.0", 1732 | "source": { 1733 | "type": "git", 1734 | "url": "https://github.com/sebastianbergmann/environment.git", 1735 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" 1736 | }, 1737 | "dist": { 1738 | "type": "zip", 1739 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", 1740 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", 1741 | "shasum": "" 1742 | }, 1743 | "require": { 1744 | "php": ">=8.1" 1745 | }, 1746 | "require-dev": { 1747 | "phpunit/phpunit": "^10.0" 1748 | }, 1749 | "suggest": { 1750 | "ext-posix": "*" 1751 | }, 1752 | "type": "library", 1753 | "extra": { 1754 | "branch-alias": { 1755 | "dev-main": "6.1-dev" 1756 | } 1757 | }, 1758 | "autoload": { 1759 | "classmap": [ 1760 | "src/" 1761 | ] 1762 | }, 1763 | "notification-url": "https://packagist.org/downloads/", 1764 | "license": [ 1765 | "BSD-3-Clause" 1766 | ], 1767 | "authors": [ 1768 | { 1769 | "name": "Sebastian Bergmann", 1770 | "email": "sebastian@phpunit.de" 1771 | } 1772 | ], 1773 | "description": "Provides functionality to handle HHVM/PHP environments", 1774 | "homepage": "https://github.com/sebastianbergmann/environment", 1775 | "keywords": [ 1776 | "Xdebug", 1777 | "environment", 1778 | "hhvm" 1779 | ], 1780 | "support": { 1781 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1782 | "security": "https://github.com/sebastianbergmann/environment/security/policy", 1783 | "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" 1784 | }, 1785 | "funding": [ 1786 | { 1787 | "url": "https://github.com/sebastianbergmann", 1788 | "type": "github" 1789 | } 1790 | ], 1791 | "time": "2024-03-23T08:47:14+00:00" 1792 | }, 1793 | { 1794 | "name": "sebastian/exporter", 1795 | "version": "5.1.2", 1796 | "source": { 1797 | "type": "git", 1798 | "url": "https://github.com/sebastianbergmann/exporter.git", 1799 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf" 1800 | }, 1801 | "dist": { 1802 | "type": "zip", 1803 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", 1804 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf", 1805 | "shasum": "" 1806 | }, 1807 | "require": { 1808 | "ext-mbstring": "*", 1809 | "php": ">=8.1", 1810 | "sebastian/recursion-context": "^5.0" 1811 | }, 1812 | "require-dev": { 1813 | "phpunit/phpunit": "^10.0" 1814 | }, 1815 | "type": "library", 1816 | "extra": { 1817 | "branch-alias": { 1818 | "dev-main": "5.1-dev" 1819 | } 1820 | }, 1821 | "autoload": { 1822 | "classmap": [ 1823 | "src/" 1824 | ] 1825 | }, 1826 | "notification-url": "https://packagist.org/downloads/", 1827 | "license": [ 1828 | "BSD-3-Clause" 1829 | ], 1830 | "authors": [ 1831 | { 1832 | "name": "Sebastian Bergmann", 1833 | "email": "sebastian@phpunit.de" 1834 | }, 1835 | { 1836 | "name": "Jeff Welch", 1837 | "email": "whatthejeff@gmail.com" 1838 | }, 1839 | { 1840 | "name": "Volker Dusch", 1841 | "email": "github@wallbash.com" 1842 | }, 1843 | { 1844 | "name": "Adam Harvey", 1845 | "email": "aharvey@php.net" 1846 | }, 1847 | { 1848 | "name": "Bernhard Schussek", 1849 | "email": "bschussek@gmail.com" 1850 | } 1851 | ], 1852 | "description": "Provides the functionality to export PHP variables for visualization", 1853 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1854 | "keywords": [ 1855 | "export", 1856 | "exporter" 1857 | ], 1858 | "support": { 1859 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1860 | "security": "https://github.com/sebastianbergmann/exporter/security/policy", 1861 | "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" 1862 | }, 1863 | "funding": [ 1864 | { 1865 | "url": "https://github.com/sebastianbergmann", 1866 | "type": "github" 1867 | } 1868 | ], 1869 | "time": "2024-03-02T07:17:12+00:00" 1870 | }, 1871 | { 1872 | "name": "sebastian/global-state", 1873 | "version": "6.0.2", 1874 | "source": { 1875 | "type": "git", 1876 | "url": "https://github.com/sebastianbergmann/global-state.git", 1877 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" 1878 | }, 1879 | "dist": { 1880 | "type": "zip", 1881 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", 1882 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", 1883 | "shasum": "" 1884 | }, 1885 | "require": { 1886 | "php": ">=8.1", 1887 | "sebastian/object-reflector": "^3.0", 1888 | "sebastian/recursion-context": "^5.0" 1889 | }, 1890 | "require-dev": { 1891 | "ext-dom": "*", 1892 | "phpunit/phpunit": "^10.0" 1893 | }, 1894 | "type": "library", 1895 | "extra": { 1896 | "branch-alias": { 1897 | "dev-main": "6.0-dev" 1898 | } 1899 | }, 1900 | "autoload": { 1901 | "classmap": [ 1902 | "src/" 1903 | ] 1904 | }, 1905 | "notification-url": "https://packagist.org/downloads/", 1906 | "license": [ 1907 | "BSD-3-Clause" 1908 | ], 1909 | "authors": [ 1910 | { 1911 | "name": "Sebastian Bergmann", 1912 | "email": "sebastian@phpunit.de" 1913 | } 1914 | ], 1915 | "description": "Snapshotting of global state", 1916 | "homepage": "https://www.github.com/sebastianbergmann/global-state", 1917 | "keywords": [ 1918 | "global state" 1919 | ], 1920 | "support": { 1921 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1922 | "security": "https://github.com/sebastianbergmann/global-state/security/policy", 1923 | "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" 1924 | }, 1925 | "funding": [ 1926 | { 1927 | "url": "https://github.com/sebastianbergmann", 1928 | "type": "github" 1929 | } 1930 | ], 1931 | "time": "2024-03-02T07:19:19+00:00" 1932 | }, 1933 | { 1934 | "name": "sebastian/lines-of-code", 1935 | "version": "2.0.2", 1936 | "source": { 1937 | "type": "git", 1938 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1939 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" 1940 | }, 1941 | "dist": { 1942 | "type": "zip", 1943 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", 1944 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", 1945 | "shasum": "" 1946 | }, 1947 | "require": { 1948 | "nikic/php-parser": "^4.18 || ^5.0", 1949 | "php": ">=8.1" 1950 | }, 1951 | "require-dev": { 1952 | "phpunit/phpunit": "^10.0" 1953 | }, 1954 | "type": "library", 1955 | "extra": { 1956 | "branch-alias": { 1957 | "dev-main": "2.0-dev" 1958 | } 1959 | }, 1960 | "autoload": { 1961 | "classmap": [ 1962 | "src/" 1963 | ] 1964 | }, 1965 | "notification-url": "https://packagist.org/downloads/", 1966 | "license": [ 1967 | "BSD-3-Clause" 1968 | ], 1969 | "authors": [ 1970 | { 1971 | "name": "Sebastian Bergmann", 1972 | "email": "sebastian@phpunit.de", 1973 | "role": "lead" 1974 | } 1975 | ], 1976 | "description": "Library for counting the lines of code in PHP source code", 1977 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1978 | "support": { 1979 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1980 | "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", 1981 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" 1982 | }, 1983 | "funding": [ 1984 | { 1985 | "url": "https://github.com/sebastianbergmann", 1986 | "type": "github" 1987 | } 1988 | ], 1989 | "time": "2023-12-21T08:38:20+00:00" 1990 | }, 1991 | { 1992 | "name": "sebastian/object-enumerator", 1993 | "version": "5.0.0", 1994 | "source": { 1995 | "type": "git", 1996 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1997 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" 1998 | }, 1999 | "dist": { 2000 | "type": "zip", 2001 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", 2002 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", 2003 | "shasum": "" 2004 | }, 2005 | "require": { 2006 | "php": ">=8.1", 2007 | "sebastian/object-reflector": "^3.0", 2008 | "sebastian/recursion-context": "^5.0" 2009 | }, 2010 | "require-dev": { 2011 | "phpunit/phpunit": "^10.0" 2012 | }, 2013 | "type": "library", 2014 | "extra": { 2015 | "branch-alias": { 2016 | "dev-main": "5.0-dev" 2017 | } 2018 | }, 2019 | "autoload": { 2020 | "classmap": [ 2021 | "src/" 2022 | ] 2023 | }, 2024 | "notification-url": "https://packagist.org/downloads/", 2025 | "license": [ 2026 | "BSD-3-Clause" 2027 | ], 2028 | "authors": [ 2029 | { 2030 | "name": "Sebastian Bergmann", 2031 | "email": "sebastian@phpunit.de" 2032 | } 2033 | ], 2034 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 2035 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 2036 | "support": { 2037 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 2038 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" 2039 | }, 2040 | "funding": [ 2041 | { 2042 | "url": "https://github.com/sebastianbergmann", 2043 | "type": "github" 2044 | } 2045 | ], 2046 | "time": "2023-02-03T07:08:32+00:00" 2047 | }, 2048 | { 2049 | "name": "sebastian/object-reflector", 2050 | "version": "3.0.0", 2051 | "source": { 2052 | "type": "git", 2053 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 2054 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" 2055 | }, 2056 | "dist": { 2057 | "type": "zip", 2058 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", 2059 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", 2060 | "shasum": "" 2061 | }, 2062 | "require": { 2063 | "php": ">=8.1" 2064 | }, 2065 | "require-dev": { 2066 | "phpunit/phpunit": "^10.0" 2067 | }, 2068 | "type": "library", 2069 | "extra": { 2070 | "branch-alias": { 2071 | "dev-main": "3.0-dev" 2072 | } 2073 | }, 2074 | "autoload": { 2075 | "classmap": [ 2076 | "src/" 2077 | ] 2078 | }, 2079 | "notification-url": "https://packagist.org/downloads/", 2080 | "license": [ 2081 | "BSD-3-Clause" 2082 | ], 2083 | "authors": [ 2084 | { 2085 | "name": "Sebastian Bergmann", 2086 | "email": "sebastian@phpunit.de" 2087 | } 2088 | ], 2089 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 2090 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 2091 | "support": { 2092 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 2093 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" 2094 | }, 2095 | "funding": [ 2096 | { 2097 | "url": "https://github.com/sebastianbergmann", 2098 | "type": "github" 2099 | } 2100 | ], 2101 | "time": "2023-02-03T07:06:18+00:00" 2102 | }, 2103 | { 2104 | "name": "sebastian/recursion-context", 2105 | "version": "5.0.0", 2106 | "source": { 2107 | "type": "git", 2108 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 2109 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712" 2110 | }, 2111 | "dist": { 2112 | "type": "zip", 2113 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", 2114 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712", 2115 | "shasum": "" 2116 | }, 2117 | "require": { 2118 | "php": ">=8.1" 2119 | }, 2120 | "require-dev": { 2121 | "phpunit/phpunit": "^10.0" 2122 | }, 2123 | "type": "library", 2124 | "extra": { 2125 | "branch-alias": { 2126 | "dev-main": "5.0-dev" 2127 | } 2128 | }, 2129 | "autoload": { 2130 | "classmap": [ 2131 | "src/" 2132 | ] 2133 | }, 2134 | "notification-url": "https://packagist.org/downloads/", 2135 | "license": [ 2136 | "BSD-3-Clause" 2137 | ], 2138 | "authors": [ 2139 | { 2140 | "name": "Sebastian Bergmann", 2141 | "email": "sebastian@phpunit.de" 2142 | }, 2143 | { 2144 | "name": "Jeff Welch", 2145 | "email": "whatthejeff@gmail.com" 2146 | }, 2147 | { 2148 | "name": "Adam Harvey", 2149 | "email": "aharvey@php.net" 2150 | } 2151 | ], 2152 | "description": "Provides functionality to recursively process PHP variables", 2153 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 2154 | "support": { 2155 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 2156 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" 2157 | }, 2158 | "funding": [ 2159 | { 2160 | "url": "https://github.com/sebastianbergmann", 2161 | "type": "github" 2162 | } 2163 | ], 2164 | "time": "2023-02-03T07:05:40+00:00" 2165 | }, 2166 | { 2167 | "name": "sebastian/type", 2168 | "version": "4.0.0", 2169 | "source": { 2170 | "type": "git", 2171 | "url": "https://github.com/sebastianbergmann/type.git", 2172 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" 2173 | }, 2174 | "dist": { 2175 | "type": "zip", 2176 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", 2177 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", 2178 | "shasum": "" 2179 | }, 2180 | "require": { 2181 | "php": ">=8.1" 2182 | }, 2183 | "require-dev": { 2184 | "phpunit/phpunit": "^10.0" 2185 | }, 2186 | "type": "library", 2187 | "extra": { 2188 | "branch-alias": { 2189 | "dev-main": "4.0-dev" 2190 | } 2191 | }, 2192 | "autoload": { 2193 | "classmap": [ 2194 | "src/" 2195 | ] 2196 | }, 2197 | "notification-url": "https://packagist.org/downloads/", 2198 | "license": [ 2199 | "BSD-3-Clause" 2200 | ], 2201 | "authors": [ 2202 | { 2203 | "name": "Sebastian Bergmann", 2204 | "email": "sebastian@phpunit.de", 2205 | "role": "lead" 2206 | } 2207 | ], 2208 | "description": "Collection of value objects that represent the types of the PHP type system", 2209 | "homepage": "https://github.com/sebastianbergmann/type", 2210 | "support": { 2211 | "issues": "https://github.com/sebastianbergmann/type/issues", 2212 | "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" 2213 | }, 2214 | "funding": [ 2215 | { 2216 | "url": "https://github.com/sebastianbergmann", 2217 | "type": "github" 2218 | } 2219 | ], 2220 | "time": "2023-02-03T07:10:45+00:00" 2221 | }, 2222 | { 2223 | "name": "sebastian/version", 2224 | "version": "4.0.1", 2225 | "source": { 2226 | "type": "git", 2227 | "url": "https://github.com/sebastianbergmann/version.git", 2228 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" 2229 | }, 2230 | "dist": { 2231 | "type": "zip", 2232 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", 2233 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", 2234 | "shasum": "" 2235 | }, 2236 | "require": { 2237 | "php": ">=8.1" 2238 | }, 2239 | "type": "library", 2240 | "extra": { 2241 | "branch-alias": { 2242 | "dev-main": "4.0-dev" 2243 | } 2244 | }, 2245 | "autoload": { 2246 | "classmap": [ 2247 | "src/" 2248 | ] 2249 | }, 2250 | "notification-url": "https://packagist.org/downloads/", 2251 | "license": [ 2252 | "BSD-3-Clause" 2253 | ], 2254 | "authors": [ 2255 | { 2256 | "name": "Sebastian Bergmann", 2257 | "email": "sebastian@phpunit.de", 2258 | "role": "lead" 2259 | } 2260 | ], 2261 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 2262 | "homepage": "https://github.com/sebastianbergmann/version", 2263 | "support": { 2264 | "issues": "https://github.com/sebastianbergmann/version/issues", 2265 | "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" 2266 | }, 2267 | "funding": [ 2268 | { 2269 | "url": "https://github.com/sebastianbergmann", 2270 | "type": "github" 2271 | } 2272 | ], 2273 | "time": "2023-02-07T11:34:05+00:00" 2274 | }, 2275 | { 2276 | "name": "symfony/console", 2277 | "version": "v7.0.7", 2278 | "source": { 2279 | "type": "git", 2280 | "url": "https://github.com/symfony/console.git", 2281 | "reference": "c981e0e9380ce9f146416bde3150c79197ce9986" 2282 | }, 2283 | "dist": { 2284 | "type": "zip", 2285 | "url": "https://api.github.com/repos/symfony/console/zipball/c981e0e9380ce9f146416bde3150c79197ce9986", 2286 | "reference": "c981e0e9380ce9f146416bde3150c79197ce9986", 2287 | "shasum": "" 2288 | }, 2289 | "require": { 2290 | "php": ">=8.2", 2291 | "symfony/polyfill-mbstring": "~1.0", 2292 | "symfony/service-contracts": "^2.5|^3", 2293 | "symfony/string": "^6.4|^7.0" 2294 | }, 2295 | "conflict": { 2296 | "symfony/dependency-injection": "<6.4", 2297 | "symfony/dotenv": "<6.4", 2298 | "symfony/event-dispatcher": "<6.4", 2299 | "symfony/lock": "<6.4", 2300 | "symfony/process": "<6.4" 2301 | }, 2302 | "provide": { 2303 | "psr/log-implementation": "1.0|2.0|3.0" 2304 | }, 2305 | "require-dev": { 2306 | "psr/log": "^1|^2|^3", 2307 | "symfony/config": "^6.4|^7.0", 2308 | "symfony/dependency-injection": "^6.4|^7.0", 2309 | "symfony/event-dispatcher": "^6.4|^7.0", 2310 | "symfony/http-foundation": "^6.4|^7.0", 2311 | "symfony/http-kernel": "^6.4|^7.0", 2312 | "symfony/lock": "^6.4|^7.0", 2313 | "symfony/messenger": "^6.4|^7.0", 2314 | "symfony/process": "^6.4|^7.0", 2315 | "symfony/stopwatch": "^6.4|^7.0", 2316 | "symfony/var-dumper": "^6.4|^7.0" 2317 | }, 2318 | "type": "library", 2319 | "autoload": { 2320 | "psr-4": { 2321 | "Symfony\\Component\\Console\\": "" 2322 | }, 2323 | "exclude-from-classmap": [ 2324 | "/Tests/" 2325 | ] 2326 | }, 2327 | "notification-url": "https://packagist.org/downloads/", 2328 | "license": [ 2329 | "MIT" 2330 | ], 2331 | "authors": [ 2332 | { 2333 | "name": "Fabien Potencier", 2334 | "email": "fabien@symfony.com" 2335 | }, 2336 | { 2337 | "name": "Symfony Community", 2338 | "homepage": "https://symfony.com/contributors" 2339 | } 2340 | ], 2341 | "description": "Eases the creation of beautiful and testable command line interfaces", 2342 | "homepage": "https://symfony.com", 2343 | "keywords": [ 2344 | "cli", 2345 | "command-line", 2346 | "console", 2347 | "terminal" 2348 | ], 2349 | "support": { 2350 | "source": "https://github.com/symfony/console/tree/v7.0.7" 2351 | }, 2352 | "funding": [ 2353 | { 2354 | "url": "https://symfony.com/sponsor", 2355 | "type": "custom" 2356 | }, 2357 | { 2358 | "url": "https://github.com/fabpot", 2359 | "type": "github" 2360 | }, 2361 | { 2362 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2363 | "type": "tidelift" 2364 | } 2365 | ], 2366 | "time": "2024-04-18T09:29:19+00:00" 2367 | }, 2368 | { 2369 | "name": "symfony/deprecation-contracts", 2370 | "version": "v3.4.0", 2371 | "source": { 2372 | "type": "git", 2373 | "url": "https://github.com/symfony/deprecation-contracts.git", 2374 | "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" 2375 | }, 2376 | "dist": { 2377 | "type": "zip", 2378 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", 2379 | "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", 2380 | "shasum": "" 2381 | }, 2382 | "require": { 2383 | "php": ">=8.1" 2384 | }, 2385 | "type": "library", 2386 | "extra": { 2387 | "branch-alias": { 2388 | "dev-main": "3.4-dev" 2389 | }, 2390 | "thanks": { 2391 | "name": "symfony/contracts", 2392 | "url": "https://github.com/symfony/contracts" 2393 | } 2394 | }, 2395 | "autoload": { 2396 | "files": [ 2397 | "function.php" 2398 | ] 2399 | }, 2400 | "notification-url": "https://packagist.org/downloads/", 2401 | "license": [ 2402 | "MIT" 2403 | ], 2404 | "authors": [ 2405 | { 2406 | "name": "Nicolas Grekas", 2407 | "email": "p@tchwork.com" 2408 | }, 2409 | { 2410 | "name": "Symfony Community", 2411 | "homepage": "https://symfony.com/contributors" 2412 | } 2413 | ], 2414 | "description": "A generic function and convention to trigger deprecation notices", 2415 | "homepage": "https://symfony.com", 2416 | "support": { 2417 | "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" 2418 | }, 2419 | "funding": [ 2420 | { 2421 | "url": "https://symfony.com/sponsor", 2422 | "type": "custom" 2423 | }, 2424 | { 2425 | "url": "https://github.com/fabpot", 2426 | "type": "github" 2427 | }, 2428 | { 2429 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2430 | "type": "tidelift" 2431 | } 2432 | ], 2433 | "time": "2023-05-23T14:45:45+00:00" 2434 | }, 2435 | { 2436 | "name": "symfony/event-dispatcher", 2437 | "version": "v7.0.7", 2438 | "source": { 2439 | "type": "git", 2440 | "url": "https://github.com/symfony/event-dispatcher.git", 2441 | "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9" 2442 | }, 2443 | "dist": { 2444 | "type": "zip", 2445 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/db2a7fab994d67d92356bb39c367db115d9d30f9", 2446 | "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9", 2447 | "shasum": "" 2448 | }, 2449 | "require": { 2450 | "php": ">=8.2", 2451 | "symfony/event-dispatcher-contracts": "^2.5|^3" 2452 | }, 2453 | "conflict": { 2454 | "symfony/dependency-injection": "<6.4", 2455 | "symfony/service-contracts": "<2.5" 2456 | }, 2457 | "provide": { 2458 | "psr/event-dispatcher-implementation": "1.0", 2459 | "symfony/event-dispatcher-implementation": "2.0|3.0" 2460 | }, 2461 | "require-dev": { 2462 | "psr/log": "^1|^2|^3", 2463 | "symfony/config": "^6.4|^7.0", 2464 | "symfony/dependency-injection": "^6.4|^7.0", 2465 | "symfony/error-handler": "^6.4|^7.0", 2466 | "symfony/expression-language": "^6.4|^7.0", 2467 | "symfony/http-foundation": "^6.4|^7.0", 2468 | "symfony/service-contracts": "^2.5|^3", 2469 | "symfony/stopwatch": "^6.4|^7.0" 2470 | }, 2471 | "type": "library", 2472 | "autoload": { 2473 | "psr-4": { 2474 | "Symfony\\Component\\EventDispatcher\\": "" 2475 | }, 2476 | "exclude-from-classmap": [ 2477 | "/Tests/" 2478 | ] 2479 | }, 2480 | "notification-url": "https://packagist.org/downloads/", 2481 | "license": [ 2482 | "MIT" 2483 | ], 2484 | "authors": [ 2485 | { 2486 | "name": "Fabien Potencier", 2487 | "email": "fabien@symfony.com" 2488 | }, 2489 | { 2490 | "name": "Symfony Community", 2491 | "homepage": "https://symfony.com/contributors" 2492 | } 2493 | ], 2494 | "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", 2495 | "homepage": "https://symfony.com", 2496 | "support": { 2497 | "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.7" 2498 | }, 2499 | "funding": [ 2500 | { 2501 | "url": "https://symfony.com/sponsor", 2502 | "type": "custom" 2503 | }, 2504 | { 2505 | "url": "https://github.com/fabpot", 2506 | "type": "github" 2507 | }, 2508 | { 2509 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2510 | "type": "tidelift" 2511 | } 2512 | ], 2513 | "time": "2024-04-18T09:29:19+00:00" 2514 | }, 2515 | { 2516 | "name": "symfony/event-dispatcher-contracts", 2517 | "version": "v3.4.2", 2518 | "source": { 2519 | "type": "git", 2520 | "url": "https://github.com/symfony/event-dispatcher-contracts.git", 2521 | "reference": "4e64b49bf370ade88e567de29465762e316e4224" 2522 | }, 2523 | "dist": { 2524 | "type": "zip", 2525 | "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/4e64b49bf370ade88e567de29465762e316e4224", 2526 | "reference": "4e64b49bf370ade88e567de29465762e316e4224", 2527 | "shasum": "" 2528 | }, 2529 | "require": { 2530 | "php": ">=8.1", 2531 | "psr/event-dispatcher": "^1" 2532 | }, 2533 | "type": "library", 2534 | "extra": { 2535 | "branch-alias": { 2536 | "dev-main": "3.4-dev" 2537 | }, 2538 | "thanks": { 2539 | "name": "symfony/contracts", 2540 | "url": "https://github.com/symfony/contracts" 2541 | } 2542 | }, 2543 | "autoload": { 2544 | "psr-4": { 2545 | "Symfony\\Contracts\\EventDispatcher\\": "" 2546 | } 2547 | }, 2548 | "notification-url": "https://packagist.org/downloads/", 2549 | "license": [ 2550 | "MIT" 2551 | ], 2552 | "authors": [ 2553 | { 2554 | "name": "Nicolas Grekas", 2555 | "email": "p@tchwork.com" 2556 | }, 2557 | { 2558 | "name": "Symfony Community", 2559 | "homepage": "https://symfony.com/contributors" 2560 | } 2561 | ], 2562 | "description": "Generic abstractions related to dispatching event", 2563 | "homepage": "https://symfony.com", 2564 | "keywords": [ 2565 | "abstractions", 2566 | "contracts", 2567 | "decoupling", 2568 | "interfaces", 2569 | "interoperability", 2570 | "standards" 2571 | ], 2572 | "support": { 2573 | "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.2" 2574 | }, 2575 | "funding": [ 2576 | { 2577 | "url": "https://symfony.com/sponsor", 2578 | "type": "custom" 2579 | }, 2580 | { 2581 | "url": "https://github.com/fabpot", 2582 | "type": "github" 2583 | }, 2584 | { 2585 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2586 | "type": "tidelift" 2587 | } 2588 | ], 2589 | "time": "2024-01-23T14:51:35+00:00" 2590 | }, 2591 | { 2592 | "name": "symfony/filesystem", 2593 | "version": "v7.0.7", 2594 | "source": { 2595 | "type": "git", 2596 | "url": "https://github.com/symfony/filesystem.git", 2597 | "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5" 2598 | }, 2599 | "dist": { 2600 | "type": "zip", 2601 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/cc168be6fbdcdf3401f50ae863ee3818ed4338f5", 2602 | "reference": "cc168be6fbdcdf3401f50ae863ee3818ed4338f5", 2603 | "shasum": "" 2604 | }, 2605 | "require": { 2606 | "php": ">=8.2", 2607 | "symfony/polyfill-ctype": "~1.8", 2608 | "symfony/polyfill-mbstring": "~1.8", 2609 | "symfony/process": "^6.4|^7.0" 2610 | }, 2611 | "type": "library", 2612 | "autoload": { 2613 | "psr-4": { 2614 | "Symfony\\Component\\Filesystem\\": "" 2615 | }, 2616 | "exclude-from-classmap": [ 2617 | "/Tests/" 2618 | ] 2619 | }, 2620 | "notification-url": "https://packagist.org/downloads/", 2621 | "license": [ 2622 | "MIT" 2623 | ], 2624 | "authors": [ 2625 | { 2626 | "name": "Fabien Potencier", 2627 | "email": "fabien@symfony.com" 2628 | }, 2629 | { 2630 | "name": "Symfony Community", 2631 | "homepage": "https://symfony.com/contributors" 2632 | } 2633 | ], 2634 | "description": "Provides basic utilities for the filesystem", 2635 | "homepage": "https://symfony.com", 2636 | "support": { 2637 | "source": "https://github.com/symfony/filesystem/tree/v7.0.7" 2638 | }, 2639 | "funding": [ 2640 | { 2641 | "url": "https://symfony.com/sponsor", 2642 | "type": "custom" 2643 | }, 2644 | { 2645 | "url": "https://github.com/fabpot", 2646 | "type": "github" 2647 | }, 2648 | { 2649 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2650 | "type": "tidelift" 2651 | } 2652 | ], 2653 | "time": "2024-04-18T09:29:19+00:00" 2654 | }, 2655 | { 2656 | "name": "symfony/finder", 2657 | "version": "v7.0.7", 2658 | "source": { 2659 | "type": "git", 2660 | "url": "https://github.com/symfony/finder.git", 2661 | "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c" 2662 | }, 2663 | "dist": { 2664 | "type": "zip", 2665 | "url": "https://api.github.com/repos/symfony/finder/zipball/4d58f0f4fe95a30d7b538d71197135483560b97c", 2666 | "reference": "4d58f0f4fe95a30d7b538d71197135483560b97c", 2667 | "shasum": "" 2668 | }, 2669 | "require": { 2670 | "php": ">=8.2" 2671 | }, 2672 | "require-dev": { 2673 | "symfony/filesystem": "^6.4|^7.0" 2674 | }, 2675 | "type": "library", 2676 | "autoload": { 2677 | "psr-4": { 2678 | "Symfony\\Component\\Finder\\": "" 2679 | }, 2680 | "exclude-from-classmap": [ 2681 | "/Tests/" 2682 | ] 2683 | }, 2684 | "notification-url": "https://packagist.org/downloads/", 2685 | "license": [ 2686 | "MIT" 2687 | ], 2688 | "authors": [ 2689 | { 2690 | "name": "Fabien Potencier", 2691 | "email": "fabien@symfony.com" 2692 | }, 2693 | { 2694 | "name": "Symfony Community", 2695 | "homepage": "https://symfony.com/contributors" 2696 | } 2697 | ], 2698 | "description": "Finds files and directories via an intuitive fluent interface", 2699 | "homepage": "https://symfony.com", 2700 | "support": { 2701 | "source": "https://github.com/symfony/finder/tree/v7.0.7" 2702 | }, 2703 | "funding": [ 2704 | { 2705 | "url": "https://symfony.com/sponsor", 2706 | "type": "custom" 2707 | }, 2708 | { 2709 | "url": "https://github.com/fabpot", 2710 | "type": "github" 2711 | }, 2712 | { 2713 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2714 | "type": "tidelift" 2715 | } 2716 | ], 2717 | "time": "2024-04-28T11:44:19+00:00" 2718 | }, 2719 | { 2720 | "name": "symfony/options-resolver", 2721 | "version": "v7.0.7", 2722 | "source": { 2723 | "type": "git", 2724 | "url": "https://github.com/symfony/options-resolver.git", 2725 | "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa" 2726 | }, 2727 | "dist": { 2728 | "type": "zip", 2729 | "url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa", 2730 | "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa", 2731 | "shasum": "" 2732 | }, 2733 | "require": { 2734 | "php": ">=8.2", 2735 | "symfony/deprecation-contracts": "^2.5|^3" 2736 | }, 2737 | "type": "library", 2738 | "autoload": { 2739 | "psr-4": { 2740 | "Symfony\\Component\\OptionsResolver\\": "" 2741 | }, 2742 | "exclude-from-classmap": [ 2743 | "/Tests/" 2744 | ] 2745 | }, 2746 | "notification-url": "https://packagist.org/downloads/", 2747 | "license": [ 2748 | "MIT" 2749 | ], 2750 | "authors": [ 2751 | { 2752 | "name": "Fabien Potencier", 2753 | "email": "fabien@symfony.com" 2754 | }, 2755 | { 2756 | "name": "Symfony Community", 2757 | "homepage": "https://symfony.com/contributors" 2758 | } 2759 | ], 2760 | "description": "Provides an improved replacement for the array_replace PHP function", 2761 | "homepage": "https://symfony.com", 2762 | "keywords": [ 2763 | "config", 2764 | "configuration", 2765 | "options" 2766 | ], 2767 | "support": { 2768 | "source": "https://github.com/symfony/options-resolver/tree/v7.0.7" 2769 | }, 2770 | "funding": [ 2771 | { 2772 | "url": "https://symfony.com/sponsor", 2773 | "type": "custom" 2774 | }, 2775 | { 2776 | "url": "https://github.com/fabpot", 2777 | "type": "github" 2778 | }, 2779 | { 2780 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2781 | "type": "tidelift" 2782 | } 2783 | ], 2784 | "time": "2024-04-18T09:29:19+00:00" 2785 | }, 2786 | { 2787 | "name": "symfony/polyfill-ctype", 2788 | "version": "v1.29.0", 2789 | "source": { 2790 | "type": "git", 2791 | "url": "https://github.com/symfony/polyfill-ctype.git", 2792 | "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" 2793 | }, 2794 | "dist": { 2795 | "type": "zip", 2796 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", 2797 | "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", 2798 | "shasum": "" 2799 | }, 2800 | "require": { 2801 | "php": ">=7.1" 2802 | }, 2803 | "provide": { 2804 | "ext-ctype": "*" 2805 | }, 2806 | "suggest": { 2807 | "ext-ctype": "For best performance" 2808 | }, 2809 | "type": "library", 2810 | "extra": { 2811 | "thanks": { 2812 | "name": "symfony/polyfill", 2813 | "url": "https://github.com/symfony/polyfill" 2814 | } 2815 | }, 2816 | "autoload": { 2817 | "files": [ 2818 | "bootstrap.php" 2819 | ], 2820 | "psr-4": { 2821 | "Symfony\\Polyfill\\Ctype\\": "" 2822 | } 2823 | }, 2824 | "notification-url": "https://packagist.org/downloads/", 2825 | "license": [ 2826 | "MIT" 2827 | ], 2828 | "authors": [ 2829 | { 2830 | "name": "Gert de Pagter", 2831 | "email": "BackEndTea@gmail.com" 2832 | }, 2833 | { 2834 | "name": "Symfony Community", 2835 | "homepage": "https://symfony.com/contributors" 2836 | } 2837 | ], 2838 | "description": "Symfony polyfill for ctype functions", 2839 | "homepage": "https://symfony.com", 2840 | "keywords": [ 2841 | "compatibility", 2842 | "ctype", 2843 | "polyfill", 2844 | "portable" 2845 | ], 2846 | "support": { 2847 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" 2848 | }, 2849 | "funding": [ 2850 | { 2851 | "url": "https://symfony.com/sponsor", 2852 | "type": "custom" 2853 | }, 2854 | { 2855 | "url": "https://github.com/fabpot", 2856 | "type": "github" 2857 | }, 2858 | { 2859 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2860 | "type": "tidelift" 2861 | } 2862 | ], 2863 | "time": "2024-01-29T20:11:03+00:00" 2864 | }, 2865 | { 2866 | "name": "symfony/polyfill-intl-grapheme", 2867 | "version": "v1.29.0", 2868 | "source": { 2869 | "type": "git", 2870 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 2871 | "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" 2872 | }, 2873 | "dist": { 2874 | "type": "zip", 2875 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", 2876 | "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", 2877 | "shasum": "" 2878 | }, 2879 | "require": { 2880 | "php": ">=7.1" 2881 | }, 2882 | "suggest": { 2883 | "ext-intl": "For best performance" 2884 | }, 2885 | "type": "library", 2886 | "extra": { 2887 | "thanks": { 2888 | "name": "symfony/polyfill", 2889 | "url": "https://github.com/symfony/polyfill" 2890 | } 2891 | }, 2892 | "autoload": { 2893 | "files": [ 2894 | "bootstrap.php" 2895 | ], 2896 | "psr-4": { 2897 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 2898 | } 2899 | }, 2900 | "notification-url": "https://packagist.org/downloads/", 2901 | "license": [ 2902 | "MIT" 2903 | ], 2904 | "authors": [ 2905 | { 2906 | "name": "Nicolas Grekas", 2907 | "email": "p@tchwork.com" 2908 | }, 2909 | { 2910 | "name": "Symfony Community", 2911 | "homepage": "https://symfony.com/contributors" 2912 | } 2913 | ], 2914 | "description": "Symfony polyfill for intl's grapheme_* functions", 2915 | "homepage": "https://symfony.com", 2916 | "keywords": [ 2917 | "compatibility", 2918 | "grapheme", 2919 | "intl", 2920 | "polyfill", 2921 | "portable", 2922 | "shim" 2923 | ], 2924 | "support": { 2925 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" 2926 | }, 2927 | "funding": [ 2928 | { 2929 | "url": "https://symfony.com/sponsor", 2930 | "type": "custom" 2931 | }, 2932 | { 2933 | "url": "https://github.com/fabpot", 2934 | "type": "github" 2935 | }, 2936 | { 2937 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2938 | "type": "tidelift" 2939 | } 2940 | ], 2941 | "time": "2024-01-29T20:11:03+00:00" 2942 | }, 2943 | { 2944 | "name": "symfony/polyfill-intl-normalizer", 2945 | "version": "v1.29.0", 2946 | "source": { 2947 | "type": "git", 2948 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 2949 | "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" 2950 | }, 2951 | "dist": { 2952 | "type": "zip", 2953 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", 2954 | "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", 2955 | "shasum": "" 2956 | }, 2957 | "require": { 2958 | "php": ">=7.1" 2959 | }, 2960 | "suggest": { 2961 | "ext-intl": "For best performance" 2962 | }, 2963 | "type": "library", 2964 | "extra": { 2965 | "thanks": { 2966 | "name": "symfony/polyfill", 2967 | "url": "https://github.com/symfony/polyfill" 2968 | } 2969 | }, 2970 | "autoload": { 2971 | "files": [ 2972 | "bootstrap.php" 2973 | ], 2974 | "psr-4": { 2975 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 2976 | }, 2977 | "classmap": [ 2978 | "Resources/stubs" 2979 | ] 2980 | }, 2981 | "notification-url": "https://packagist.org/downloads/", 2982 | "license": [ 2983 | "MIT" 2984 | ], 2985 | "authors": [ 2986 | { 2987 | "name": "Nicolas Grekas", 2988 | "email": "p@tchwork.com" 2989 | }, 2990 | { 2991 | "name": "Symfony Community", 2992 | "homepage": "https://symfony.com/contributors" 2993 | } 2994 | ], 2995 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 2996 | "homepage": "https://symfony.com", 2997 | "keywords": [ 2998 | "compatibility", 2999 | "intl", 3000 | "normalizer", 3001 | "polyfill", 3002 | "portable", 3003 | "shim" 3004 | ], 3005 | "support": { 3006 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" 3007 | }, 3008 | "funding": [ 3009 | { 3010 | "url": "https://symfony.com/sponsor", 3011 | "type": "custom" 3012 | }, 3013 | { 3014 | "url": "https://github.com/fabpot", 3015 | "type": "github" 3016 | }, 3017 | { 3018 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3019 | "type": "tidelift" 3020 | } 3021 | ], 3022 | "time": "2024-01-29T20:11:03+00:00" 3023 | }, 3024 | { 3025 | "name": "symfony/polyfill-mbstring", 3026 | "version": "v1.29.0", 3027 | "source": { 3028 | "type": "git", 3029 | "url": "https://github.com/symfony/polyfill-mbstring.git", 3030 | "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" 3031 | }, 3032 | "dist": { 3033 | "type": "zip", 3034 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", 3035 | "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", 3036 | "shasum": "" 3037 | }, 3038 | "require": { 3039 | "php": ">=7.1" 3040 | }, 3041 | "provide": { 3042 | "ext-mbstring": "*" 3043 | }, 3044 | "suggest": { 3045 | "ext-mbstring": "For best performance" 3046 | }, 3047 | "type": "library", 3048 | "extra": { 3049 | "thanks": { 3050 | "name": "symfony/polyfill", 3051 | "url": "https://github.com/symfony/polyfill" 3052 | } 3053 | }, 3054 | "autoload": { 3055 | "files": [ 3056 | "bootstrap.php" 3057 | ], 3058 | "psr-4": { 3059 | "Symfony\\Polyfill\\Mbstring\\": "" 3060 | } 3061 | }, 3062 | "notification-url": "https://packagist.org/downloads/", 3063 | "license": [ 3064 | "MIT" 3065 | ], 3066 | "authors": [ 3067 | { 3068 | "name": "Nicolas Grekas", 3069 | "email": "p@tchwork.com" 3070 | }, 3071 | { 3072 | "name": "Symfony Community", 3073 | "homepage": "https://symfony.com/contributors" 3074 | } 3075 | ], 3076 | "description": "Symfony polyfill for the Mbstring extension", 3077 | "homepage": "https://symfony.com", 3078 | "keywords": [ 3079 | "compatibility", 3080 | "mbstring", 3081 | "polyfill", 3082 | "portable", 3083 | "shim" 3084 | ], 3085 | "support": { 3086 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" 3087 | }, 3088 | "funding": [ 3089 | { 3090 | "url": "https://symfony.com/sponsor", 3091 | "type": "custom" 3092 | }, 3093 | { 3094 | "url": "https://github.com/fabpot", 3095 | "type": "github" 3096 | }, 3097 | { 3098 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3099 | "type": "tidelift" 3100 | } 3101 | ], 3102 | "time": "2024-01-29T20:11:03+00:00" 3103 | }, 3104 | { 3105 | "name": "symfony/polyfill-php80", 3106 | "version": "v1.29.0", 3107 | "source": { 3108 | "type": "git", 3109 | "url": "https://github.com/symfony/polyfill-php80.git", 3110 | "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" 3111 | }, 3112 | "dist": { 3113 | "type": "zip", 3114 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", 3115 | "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", 3116 | "shasum": "" 3117 | }, 3118 | "require": { 3119 | "php": ">=7.1" 3120 | }, 3121 | "type": "library", 3122 | "extra": { 3123 | "thanks": { 3124 | "name": "symfony/polyfill", 3125 | "url": "https://github.com/symfony/polyfill" 3126 | } 3127 | }, 3128 | "autoload": { 3129 | "files": [ 3130 | "bootstrap.php" 3131 | ], 3132 | "psr-4": { 3133 | "Symfony\\Polyfill\\Php80\\": "" 3134 | }, 3135 | "classmap": [ 3136 | "Resources/stubs" 3137 | ] 3138 | }, 3139 | "notification-url": "https://packagist.org/downloads/", 3140 | "license": [ 3141 | "MIT" 3142 | ], 3143 | "authors": [ 3144 | { 3145 | "name": "Ion Bazan", 3146 | "email": "ion.bazan@gmail.com" 3147 | }, 3148 | { 3149 | "name": "Nicolas Grekas", 3150 | "email": "p@tchwork.com" 3151 | }, 3152 | { 3153 | "name": "Symfony Community", 3154 | "homepage": "https://symfony.com/contributors" 3155 | } 3156 | ], 3157 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 3158 | "homepage": "https://symfony.com", 3159 | "keywords": [ 3160 | "compatibility", 3161 | "polyfill", 3162 | "portable", 3163 | "shim" 3164 | ], 3165 | "support": { 3166 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" 3167 | }, 3168 | "funding": [ 3169 | { 3170 | "url": "https://symfony.com/sponsor", 3171 | "type": "custom" 3172 | }, 3173 | { 3174 | "url": "https://github.com/fabpot", 3175 | "type": "github" 3176 | }, 3177 | { 3178 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3179 | "type": "tidelift" 3180 | } 3181 | ], 3182 | "time": "2024-01-29T20:11:03+00:00" 3183 | }, 3184 | { 3185 | "name": "symfony/polyfill-php81", 3186 | "version": "v1.29.0", 3187 | "source": { 3188 | "type": "git", 3189 | "url": "https://github.com/symfony/polyfill-php81.git", 3190 | "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" 3191 | }, 3192 | "dist": { 3193 | "type": "zip", 3194 | "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", 3195 | "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", 3196 | "shasum": "" 3197 | }, 3198 | "require": { 3199 | "php": ">=7.1" 3200 | }, 3201 | "type": "library", 3202 | "extra": { 3203 | "thanks": { 3204 | "name": "symfony/polyfill", 3205 | "url": "https://github.com/symfony/polyfill" 3206 | } 3207 | }, 3208 | "autoload": { 3209 | "files": [ 3210 | "bootstrap.php" 3211 | ], 3212 | "psr-4": { 3213 | "Symfony\\Polyfill\\Php81\\": "" 3214 | }, 3215 | "classmap": [ 3216 | "Resources/stubs" 3217 | ] 3218 | }, 3219 | "notification-url": "https://packagist.org/downloads/", 3220 | "license": [ 3221 | "MIT" 3222 | ], 3223 | "authors": [ 3224 | { 3225 | "name": "Nicolas Grekas", 3226 | "email": "p@tchwork.com" 3227 | }, 3228 | { 3229 | "name": "Symfony Community", 3230 | "homepage": "https://symfony.com/contributors" 3231 | } 3232 | ], 3233 | "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", 3234 | "homepage": "https://symfony.com", 3235 | "keywords": [ 3236 | "compatibility", 3237 | "polyfill", 3238 | "portable", 3239 | "shim" 3240 | ], 3241 | "support": { 3242 | "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" 3243 | }, 3244 | "funding": [ 3245 | { 3246 | "url": "https://symfony.com/sponsor", 3247 | "type": "custom" 3248 | }, 3249 | { 3250 | "url": "https://github.com/fabpot", 3251 | "type": "github" 3252 | }, 3253 | { 3254 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3255 | "type": "tidelift" 3256 | } 3257 | ], 3258 | "time": "2024-01-29T20:11:03+00:00" 3259 | }, 3260 | { 3261 | "name": "symfony/process", 3262 | "version": "v7.0.7", 3263 | "source": { 3264 | "type": "git", 3265 | "url": "https://github.com/symfony/process.git", 3266 | "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0" 3267 | }, 3268 | "dist": { 3269 | "type": "zip", 3270 | "url": "https://api.github.com/repos/symfony/process/zipball/3839e56b94dd1dbd13235d27504e66baf23faba0", 3271 | "reference": "3839e56b94dd1dbd13235d27504e66baf23faba0", 3272 | "shasum": "" 3273 | }, 3274 | "require": { 3275 | "php": ">=8.2" 3276 | }, 3277 | "type": "library", 3278 | "autoload": { 3279 | "psr-4": { 3280 | "Symfony\\Component\\Process\\": "" 3281 | }, 3282 | "exclude-from-classmap": [ 3283 | "/Tests/" 3284 | ] 3285 | }, 3286 | "notification-url": "https://packagist.org/downloads/", 3287 | "license": [ 3288 | "MIT" 3289 | ], 3290 | "authors": [ 3291 | { 3292 | "name": "Fabien Potencier", 3293 | "email": "fabien@symfony.com" 3294 | }, 3295 | { 3296 | "name": "Symfony Community", 3297 | "homepage": "https://symfony.com/contributors" 3298 | } 3299 | ], 3300 | "description": "Executes commands in sub-processes", 3301 | "homepage": "https://symfony.com", 3302 | "support": { 3303 | "source": "https://github.com/symfony/process/tree/v7.0.7" 3304 | }, 3305 | "funding": [ 3306 | { 3307 | "url": "https://symfony.com/sponsor", 3308 | "type": "custom" 3309 | }, 3310 | { 3311 | "url": "https://github.com/fabpot", 3312 | "type": "github" 3313 | }, 3314 | { 3315 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3316 | "type": "tidelift" 3317 | } 3318 | ], 3319 | "time": "2024-04-18T09:29:19+00:00" 3320 | }, 3321 | { 3322 | "name": "symfony/service-contracts", 3323 | "version": "v3.4.2", 3324 | "source": { 3325 | "type": "git", 3326 | "url": "https://github.com/symfony/service-contracts.git", 3327 | "reference": "11bbf19a0fb7b36345861e85c5768844c552906e" 3328 | }, 3329 | "dist": { 3330 | "type": "zip", 3331 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e", 3332 | "reference": "11bbf19a0fb7b36345861e85c5768844c552906e", 3333 | "shasum": "" 3334 | }, 3335 | "require": { 3336 | "php": ">=8.1", 3337 | "psr/container": "^1.1|^2.0" 3338 | }, 3339 | "conflict": { 3340 | "ext-psr": "<1.1|>=2" 3341 | }, 3342 | "type": "library", 3343 | "extra": { 3344 | "branch-alias": { 3345 | "dev-main": "3.4-dev" 3346 | }, 3347 | "thanks": { 3348 | "name": "symfony/contracts", 3349 | "url": "https://github.com/symfony/contracts" 3350 | } 3351 | }, 3352 | "autoload": { 3353 | "psr-4": { 3354 | "Symfony\\Contracts\\Service\\": "" 3355 | }, 3356 | "exclude-from-classmap": [ 3357 | "/Test/" 3358 | ] 3359 | }, 3360 | "notification-url": "https://packagist.org/downloads/", 3361 | "license": [ 3362 | "MIT" 3363 | ], 3364 | "authors": [ 3365 | { 3366 | "name": "Nicolas Grekas", 3367 | "email": "p@tchwork.com" 3368 | }, 3369 | { 3370 | "name": "Symfony Community", 3371 | "homepage": "https://symfony.com/contributors" 3372 | } 3373 | ], 3374 | "description": "Generic abstractions related to writing services", 3375 | "homepage": "https://symfony.com", 3376 | "keywords": [ 3377 | "abstractions", 3378 | "contracts", 3379 | "decoupling", 3380 | "interfaces", 3381 | "interoperability", 3382 | "standards" 3383 | ], 3384 | "support": { 3385 | "source": "https://github.com/symfony/service-contracts/tree/v3.4.2" 3386 | }, 3387 | "funding": [ 3388 | { 3389 | "url": "https://symfony.com/sponsor", 3390 | "type": "custom" 3391 | }, 3392 | { 3393 | "url": "https://github.com/fabpot", 3394 | "type": "github" 3395 | }, 3396 | { 3397 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3398 | "type": "tidelift" 3399 | } 3400 | ], 3401 | "time": "2023-12-19T21:51:00+00:00" 3402 | }, 3403 | { 3404 | "name": "symfony/stopwatch", 3405 | "version": "v7.0.7", 3406 | "source": { 3407 | "type": "git", 3408 | "url": "https://github.com/symfony/stopwatch.git", 3409 | "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84" 3410 | }, 3411 | "dist": { 3412 | "type": "zip", 3413 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/41a7a24aa1dc82adf46a06bc292d1923acfe6b84", 3414 | "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84", 3415 | "shasum": "" 3416 | }, 3417 | "require": { 3418 | "php": ">=8.2", 3419 | "symfony/service-contracts": "^2.5|^3" 3420 | }, 3421 | "type": "library", 3422 | "autoload": { 3423 | "psr-4": { 3424 | "Symfony\\Component\\Stopwatch\\": "" 3425 | }, 3426 | "exclude-from-classmap": [ 3427 | "/Tests/" 3428 | ] 3429 | }, 3430 | "notification-url": "https://packagist.org/downloads/", 3431 | "license": [ 3432 | "MIT" 3433 | ], 3434 | "authors": [ 3435 | { 3436 | "name": "Fabien Potencier", 3437 | "email": "fabien@symfony.com" 3438 | }, 3439 | { 3440 | "name": "Symfony Community", 3441 | "homepage": "https://symfony.com/contributors" 3442 | } 3443 | ], 3444 | "description": "Provides a way to profile code", 3445 | "homepage": "https://symfony.com", 3446 | "support": { 3447 | "source": "https://github.com/symfony/stopwatch/tree/v7.0.7" 3448 | }, 3449 | "funding": [ 3450 | { 3451 | "url": "https://symfony.com/sponsor", 3452 | "type": "custom" 3453 | }, 3454 | { 3455 | "url": "https://github.com/fabpot", 3456 | "type": "github" 3457 | }, 3458 | { 3459 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3460 | "type": "tidelift" 3461 | } 3462 | ], 3463 | "time": "2024-04-18T09:29:19+00:00" 3464 | }, 3465 | { 3466 | "name": "symfony/string", 3467 | "version": "v7.0.7", 3468 | "source": { 3469 | "type": "git", 3470 | "url": "https://github.com/symfony/string.git", 3471 | "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63" 3472 | }, 3473 | "dist": { 3474 | "type": "zip", 3475 | "url": "https://api.github.com/repos/symfony/string/zipball/e405b5424dc2528e02e31ba26b83a79fd4eb8f63", 3476 | "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63", 3477 | "shasum": "" 3478 | }, 3479 | "require": { 3480 | "php": ">=8.2", 3481 | "symfony/polyfill-ctype": "~1.8", 3482 | "symfony/polyfill-intl-grapheme": "~1.0", 3483 | "symfony/polyfill-intl-normalizer": "~1.0", 3484 | "symfony/polyfill-mbstring": "~1.0" 3485 | }, 3486 | "conflict": { 3487 | "symfony/translation-contracts": "<2.5" 3488 | }, 3489 | "require-dev": { 3490 | "symfony/error-handler": "^6.4|^7.0", 3491 | "symfony/http-client": "^6.4|^7.0", 3492 | "symfony/intl": "^6.4|^7.0", 3493 | "symfony/translation-contracts": "^2.5|^3.0", 3494 | "symfony/var-exporter": "^6.4|^7.0" 3495 | }, 3496 | "type": "library", 3497 | "autoload": { 3498 | "files": [ 3499 | "Resources/functions.php" 3500 | ], 3501 | "psr-4": { 3502 | "Symfony\\Component\\String\\": "" 3503 | }, 3504 | "exclude-from-classmap": [ 3505 | "/Tests/" 3506 | ] 3507 | }, 3508 | "notification-url": "https://packagist.org/downloads/", 3509 | "license": [ 3510 | "MIT" 3511 | ], 3512 | "authors": [ 3513 | { 3514 | "name": "Nicolas Grekas", 3515 | "email": "p@tchwork.com" 3516 | }, 3517 | { 3518 | "name": "Symfony Community", 3519 | "homepage": "https://symfony.com/contributors" 3520 | } 3521 | ], 3522 | "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", 3523 | "homepage": "https://symfony.com", 3524 | "keywords": [ 3525 | "grapheme", 3526 | "i18n", 3527 | "string", 3528 | "unicode", 3529 | "utf-8", 3530 | "utf8" 3531 | ], 3532 | "support": { 3533 | "source": "https://github.com/symfony/string/tree/v7.0.7" 3534 | }, 3535 | "funding": [ 3536 | { 3537 | "url": "https://symfony.com/sponsor", 3538 | "type": "custom" 3539 | }, 3540 | { 3541 | "url": "https://github.com/fabpot", 3542 | "type": "github" 3543 | }, 3544 | { 3545 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3546 | "type": "tidelift" 3547 | } 3548 | ], 3549 | "time": "2024-04-18T09:29:19+00:00" 3550 | }, 3551 | { 3552 | "name": "theseer/tokenizer", 3553 | "version": "1.2.3", 3554 | "source": { 3555 | "type": "git", 3556 | "url": "https://github.com/theseer/tokenizer.git", 3557 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 3558 | }, 3559 | "dist": { 3560 | "type": "zip", 3561 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 3562 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 3563 | "shasum": "" 3564 | }, 3565 | "require": { 3566 | "ext-dom": "*", 3567 | "ext-tokenizer": "*", 3568 | "ext-xmlwriter": "*", 3569 | "php": "^7.2 || ^8.0" 3570 | }, 3571 | "type": "library", 3572 | "autoload": { 3573 | "classmap": [ 3574 | "src/" 3575 | ] 3576 | }, 3577 | "notification-url": "https://packagist.org/downloads/", 3578 | "license": [ 3579 | "BSD-3-Clause" 3580 | ], 3581 | "authors": [ 3582 | { 3583 | "name": "Arne Blankerts", 3584 | "email": "arne@blankerts.de", 3585 | "role": "Developer" 3586 | } 3587 | ], 3588 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 3589 | "support": { 3590 | "issues": "https://github.com/theseer/tokenizer/issues", 3591 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 3592 | }, 3593 | "funding": [ 3594 | { 3595 | "url": "https://github.com/theseer", 3596 | "type": "github" 3597 | } 3598 | ], 3599 | "time": "2024-03-03T12:36:25+00:00" 3600 | } 3601 | ], 3602 | "aliases": [], 3603 | "minimum-stability": "stable", 3604 | "stability-flags": [], 3605 | "prefer-stable": false, 3606 | "prefer-lowest": false, 3607 | "platform": { 3608 | "php": ">=7.3.0", 3609 | "ext-json": "*", 3610 | "ext-openssl": "*" 3611 | }, 3612 | "platform-dev": { 3613 | "ext-xml": "*" 3614 | }, 3615 | "plugin-api-version": "2.6.0" 3616 | } 3617 | -------------------------------------------------------------------------------- /examples/full-flow.php: -------------------------------------------------------------------------------- 1 | 'sig', 23 | 'alg' => 'RS256', 24 | 'kid' => 'eXaunmL', 25 | ]; 26 | 27 | $keyFactory = new KeyFactory(); 28 | $key = $keyFactory->createFromPem($pem, $options); 29 | 30 | echo $key . PHP_EOL . PHP_EOL; 31 | 32 | echo "Adding the key to the KeySet:" . PHP_EOL; 33 | 34 | $keySet = new \Strobotti\JWK\KeySet(); 35 | $keySet->addKey($key); 36 | 37 | echo $keySet . PHP_EOL . PHP_EOL; 38 | 39 | echo "Fetching the key by it's ID (`kid`) and convert it back to PEM:" . PHP_EOL; 40 | 41 | $key = $keySet->getKeyById('eXaunmL'); 42 | $pem = (new \Strobotti\JWK\KeyConverter())->keyToPem($key); 43 | 44 | echo $pem . PHP_EOL . PHP_EOL; 45 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Key/AbstractKey.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://opensource.org/licenses/MIT MIT 10 | * 11 | * @see https://github.com/Strobotti/php-jwk 12 | * @since 1.0.0 13 | */ 14 | abstract class AbstractKey implements KeyInterface 15 | { 16 | /** 17 | * The key type. 18 | * 19 | * @var string 20 | */ 21 | private $kty; 22 | 23 | /** 24 | * The key ID. 25 | * 26 | * @var null|string 27 | */ 28 | private $kid; 29 | 30 | /** 31 | * The public key use. 32 | * 33 | * @var null|string 34 | */ 35 | private $use; 36 | 37 | /** 38 | * The algorithm. 39 | * 40 | * @var string 41 | */ 42 | private $alg; 43 | 44 | /** 45 | * @since 1.0.0 46 | * 47 | * @return false|string 48 | */ 49 | public function __toString() 50 | { 51 | return \json_encode($this->jsonSerialize(), JSON_PRETTY_PRINT); 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | * 57 | * @since 1.0.0 Protected setter 58 | * @since 1.2.0 Public setter 59 | */ 60 | public function setKeyType(string $kty): KeyInterface 61 | { 62 | $this->kty = $kty; 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | * 70 | * @since 1.0.0 71 | */ 72 | public function getKeyType(): string 73 | { 74 | return $this->kty; 75 | } 76 | 77 | /** 78 | * {@inheritdoc} 79 | * 80 | * @since 1.2.0 81 | */ 82 | public function setKeyId(?string $kid): KeyInterface 83 | { 84 | $this->kid = $kid; 85 | 86 | return $this; 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | * 92 | * @since 1.0.0 93 | */ 94 | public function getKeyId(): ?string 95 | { 96 | return $this->kid; 97 | } 98 | 99 | /** 100 | * {@inheritdoc} 101 | * 102 | * @since 1.2.0 103 | */ 104 | public function setPublicKeyUse(?string $use): KeyInterface 105 | { 106 | $this->use = $use; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * {@inheritdoc} 113 | * 114 | * @since 1.0.0 115 | */ 116 | public function getPublicKeyUse(): ?string 117 | { 118 | return $this->use; 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | * 124 | * @since 1.2.0 125 | */ 126 | public function setAlgorithm(string $alg): KeyInterface 127 | { 128 | $this->alg = $alg; 129 | 130 | return $this; 131 | } 132 | 133 | /** 134 | * {@inheritdoc} 135 | * 136 | * @since 1.0.0 137 | */ 138 | public function getAlgorithm(): string 139 | { 140 | return $this->alg; 141 | } 142 | 143 | /** 144 | * Returns an array presentation of the key. 145 | * 146 | * @since 1.0.0 147 | * 148 | * @return array An assoc to be passed to json_encode 149 | */ 150 | public function jsonSerialize(): array 151 | { 152 | $assoc = [ 153 | 'kty' => $this->kty, 154 | 'use' => $this->use, 155 | 'alg' => $this->alg, 156 | ]; 157 | 158 | if (null !== $this->kid) { 159 | $assoc['kid'] = $this->kid; 160 | } 161 | 162 | return $assoc; 163 | } 164 | 165 | /** 166 | * @since 1.0.0 167 | */ 168 | public static function createFromJSON(string $json, KeyInterface $prototype = null): KeyInterface 169 | { 170 | $assoc = \json_decode($json, true); 171 | 172 | if ($prototype) { 173 | $instance = clone $prototype; 174 | } else { 175 | $instance = new static(); 176 | } 177 | 178 | foreach ($assoc as $key => $value) { 179 | if (!\property_exists($instance, $key)) { 180 | continue; 181 | } 182 | 183 | try { 184 | $instance->{$key} = $value; 185 | } catch (\Throwable $e) { 186 | // only set what you can 187 | } 188 | } 189 | 190 | return $instance; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/Key/KeyInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://opensource.org/licenses/MIT MIT 10 | * 11 | * @see https://github.com/Strobotti/php-jwk 12 | * @since 1.0.0 13 | * 14 | * @method array jsonSerialize() 15 | */ 16 | interface KeyInterface extends \JsonSerializable 17 | { 18 | /** 19 | * @since 1.0.0 20 | */ 21 | public const KEY_TYPE_RSA = 'RSA'; 22 | 23 | /** 24 | * @since 1.0.0 25 | * 26 | * @todo Model currently not implemented 27 | */ 28 | public const KEY_TYPE_OKP = 'OKP'; 29 | 30 | /** 31 | * @since 1.0.0 32 | * 33 | * @todo Model currently not implemented 34 | */ 35 | public const KEY_TYPE_EC = 'EC'; 36 | 37 | public const PUBLIC_KEY_USE_SIGNATURE = 'sig'; 38 | public const PUBLIC_KEY_USE_ENCRYPTION = 'enc'; 39 | 40 | public const ALGORITHM_RS256 = 'RS256'; 41 | 42 | /** 43 | * Converts this key to a string. 44 | * 45 | * @since 1.0.0 46 | * 47 | * @return bool|string 48 | */ 49 | public function __toString(); 50 | 51 | /** 52 | * Sets the key type, ie. the value for the `kty` field. 53 | * 54 | * See the KEY_TYPE_* constants for reference. 55 | * 56 | * @return KeyInterface 57 | * 58 | * @since 1.2.0 59 | */ 60 | public function setKeyType(string $kty): self; 61 | 62 | /** 63 | * Gets the key type, ie. the value of the `kty` field. 64 | * 65 | * @since 1.0.0 66 | */ 67 | public function getKeyType(): string; 68 | 69 | /** 70 | * Sets the key id, ie. the value of the `kid` field. 71 | * 72 | * @return KeyInterface 73 | * 74 | * @since 1.2.0 75 | */ 76 | public function setKeyId(?string $kid): self; 77 | 78 | /** 79 | * Gets the key id, ie. the value of the `kid` field. 80 | * 81 | * @since 1.0.0 82 | */ 83 | public function getKeyId(): ?string; 84 | 85 | /** 86 | * Sets the public key use, ie. the value of the `use` field. 87 | * 88 | * @return KeyInterface 89 | * 90 | * @since 1.2.0 91 | */ 92 | public function setPublicKeyUse(?string $use): self; 93 | 94 | /** 95 | * Gets the public key use, ie. the value of the `use` field. 96 | * 97 | * @since 1.0.0 98 | */ 99 | public function getPublicKeyUse(): ?string; 100 | 101 | /** 102 | * Sets the cryptographic algorithm used to sign the key, ie. the value of the `alg` field. 103 | * 104 | * @return KeyInterface 105 | * 106 | * @since 1.2.0 107 | */ 108 | public function setAlgorithm(string $alg): self; 109 | 110 | /** 111 | * Gets the cryptographic algorithm used to sign the key, ie. the value of the `alg` field. 112 | * 113 | * @since 1.0.0 114 | */ 115 | public function getAlgorithm(): string; 116 | } 117 | -------------------------------------------------------------------------------- /src/Key/Rsa.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://opensource.org/licenses/MIT MIT 10 | * 11 | * @see https://github.com/Strobotti/php-jwk 12 | * @since 1.0.0 13 | */ 14 | class Rsa extends AbstractKey 15 | { 16 | /** 17 | * The modulus for the RSA public key. 18 | * 19 | * @var string 20 | */ 21 | private $n; 22 | 23 | /** 24 | * The exponent for the RSA public key. 25 | * 26 | * @var string 27 | */ 28 | private $e; 29 | 30 | /** 31 | * Rsa key constructor. 32 | */ 33 | public function __construct() 34 | { 35 | $this->setKeyType(KeyInterface::KEY_TYPE_RSA); 36 | } 37 | 38 | /** 39 | * Sets the exponent for the RSA public key, ie. the `e` field. 40 | * 41 | * @since 1.2.0 42 | */ 43 | public function setExponent(string $e): self 44 | { 45 | $this->e = $e; 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Returns the exponent for the RSA public key. 52 | * 53 | * @since 1.0.0 54 | */ 55 | public function getExponent(): string 56 | { 57 | return $this->e; 58 | } 59 | 60 | /** 61 | * Sets the modulus for the RSA public key, ie. the `n` field. 62 | * 63 | * @since 1.2.0 64 | */ 65 | public function setModulus(string $n): KeyInterface 66 | { 67 | $this->n = $n; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * Returns the modulus for the RSA public key, ie. the `n`field. 74 | * 75 | * @since 1.0.0 76 | */ 77 | public function getModulus(): string 78 | { 79 | return $this->n; 80 | } 81 | 82 | /** 83 | * {@inheritdoc} 84 | * 85 | * @since 1.0.0 86 | */ 87 | public function jsonSerialize(): array 88 | { 89 | $assoc = parent::jsonSerialize(); 90 | $assoc['n'] = $this->n; 91 | $assoc['e'] = $this->e; 92 | 93 | return $assoc; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | * 99 | * @since 1.0.0 100 | * 101 | * @return self 102 | */ 103 | public static function createFromJSON(string $json, KeyInterface $prototype = null): KeyInterface 104 | { 105 | if (!$prototype instanceof self) { 106 | $prototype = new static(); 107 | } 108 | 109 | $instance = parent::createFromJSON($json, $prototype); 110 | 111 | $assoc = \json_decode($json, true); 112 | 113 | foreach ($assoc as $key => $value) { 114 | if (!\property_exists($instance, $key)) { 115 | continue; 116 | } 117 | 118 | $instance->{$key} = $value; 119 | } 120 | 121 | return $instance; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/KeyConverter.php: -------------------------------------------------------------------------------- 1 | 16 | * @license https://opensource.org/licenses/MIT MIT 17 | * 18 | * @see https://github.com/Strobotti/php-jwk 19 | * @since 1.0.0 20 | */ 21 | class KeyConverter 22 | { 23 | /** 24 | * @var Base64UrlConverterInterface 25 | */ 26 | private $base64UrlConverter; 27 | 28 | /** 29 | * KeyConverter constructor. 30 | */ 31 | public function __construct() 32 | { 33 | $this->base64UrlConverter = new Base64UrlConverter(); 34 | } 35 | 36 | /** 37 | * @since 1.0.0 38 | */ 39 | public function keyToPem(KeyInterface $key): string 40 | { 41 | if (!$key instanceof RsaKey) { 42 | throw new \InvalidArgumentException(); 43 | } 44 | 45 | $modulus = $this->base64UrlConverter->decode($key->getModulus(), true); 46 | 47 | $rsa = PublicKeyLoader::load([ 48 | 'e' => new BigInteger(\base64_decode($key->getExponent(), true), 256), 49 | 'n' => new BigInteger($modulus, 256), 50 | ]); 51 | 52 | return $rsa->__toString(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/KeyFactory.php: -------------------------------------------------------------------------------- 1 | 13 | * @license https://opensource.org/licenses/MIT MIT 14 | * 15 | * @see https://github.com/Strobotti/php-jwk 16 | * @since 1.0.0 17 | */ 18 | class KeyFactory 19 | { 20 | /** 21 | * @var Base64UrlConverterInterface 22 | */ 23 | private $base64UrlConverter; 24 | 25 | /** 26 | * KeyFactory constructor. 27 | */ 28 | public function __construct() 29 | { 30 | $this->setBase64UrlConverter(new Base64UrlConverter()); 31 | } 32 | 33 | /** 34 | * @since 1.0.0 35 | * 36 | * @return KeyFactory 37 | */ 38 | public function setBase64UrlConverter(Base64UrlConverterInterface $base64UrlConverter): self 39 | { 40 | $this->base64UrlConverter = $base64UrlConverter; 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * @since 1.0.0 47 | */ 48 | public function createFromPem(string $pem, array $options = []): KeyInterface 49 | { 50 | $keyInfo = \openssl_pkey_get_details(\openssl_pkey_get_public($pem)); 51 | 52 | $jsonData = \array_merge( 53 | $options, 54 | [ 55 | 'kty' => 'RSA', 56 | 'n' => $this->base64UrlConverter->encode($keyInfo['rsa']['n']), 57 | 'e' => $this->base64UrlConverter->encode($keyInfo['rsa']['e']), 58 | ] 59 | ); 60 | 61 | // TODO Only RSA is supported atm 62 | return Key\Rsa::createFromJSON(\json_encode($jsonData)); 63 | } 64 | 65 | /** 66 | * @since 1.0.0 67 | */ 68 | public function createFromJson(string $json): KeyInterface 69 | { 70 | // TODO Only RSA is supported atm 71 | return Key\Rsa::createFromJSON($json); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/KeySet.php: -------------------------------------------------------------------------------- 1 | 11 | * @license https://opensource.org/licenses/MIT MIT 12 | * 13 | * @see https://github.com/Strobotti/php-jwk 14 | * @since 1.0.0 15 | */ 16 | class KeySet implements \JsonSerializable, \Countable, \IteratorAggregate 17 | { 18 | /** 19 | * @var KeyFactory 20 | */ 21 | private $keyFactory; 22 | 23 | /** 24 | * @var KeyInterface[] 25 | */ 26 | private $keys = []; 27 | 28 | /** 29 | * KeySet constructor. 30 | */ 31 | public function __construct() 32 | { 33 | $this->setKeyFactory(new KeyFactory()); 34 | } 35 | 36 | /** 37 | * @since 1.0.0 38 | * 39 | * @return false|string 40 | */ 41 | public function __toString() 42 | { 43 | return \json_encode($this->jsonSerialize(), JSON_PRETTY_PRINT); 44 | } 45 | 46 | /** 47 | * @since 1.0.0 48 | */ 49 | public function setKeyFactory(KeyFactory $keyFactory): self 50 | { 51 | $this->keyFactory = $keyFactory; 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * @since 1.0.0 Only $kid parameter 58 | * @since 1.1.0 Added optional $use parameter 59 | */ 60 | public function containsKey(string $kid, string $use = KeyInterface::PUBLIC_KEY_USE_SIGNATURE): bool 61 | { 62 | return null !== $this->getKeyById($kid, $use); 63 | } 64 | 65 | /** 66 | * @since 1.0.0 67 | * @since 1.1.0 Added optional $use parameter 68 | */ 69 | public function getKeyById(string $kid, string $use = KeyInterface::PUBLIC_KEY_USE_SIGNATURE): ?KeyInterface 70 | { 71 | foreach ($this->getKeys() as $key) { 72 | if ($key->getKeyId() === $kid && $key->getPublicKeyUse() === $use) { 73 | return $key; 74 | } 75 | } 76 | 77 | return null; 78 | } 79 | 80 | /** 81 | * @since 1.0.0 82 | * 83 | * @return KeySet 84 | */ 85 | public function addKey(KeyInterface $key): self 86 | { 87 | if ($key->getKeyId() && $this->containsKey($key->getKeyId(), $key->getPublicKeyUse())) { 88 | throw new \InvalidArgumentException(\sprintf('Key with id `%s` and use `%s` already exists in the set', $key->getKeyId(), $key->getPublicKeyUse())); 89 | } 90 | 91 | $this->keys[] = $key; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * @return KeyInterface[] 98 | */ 99 | public function getKeys(): array 100 | { 101 | return \array_values($this->keys); 102 | } 103 | 104 | /** 105 | * @since 1.0.0 106 | */ 107 | public function jsonSerialize(): array 108 | { 109 | $ret = []; 110 | 111 | foreach ($this->getKeys() as $key) { 112 | $ret[$key->getKeyId()] = $key->jsonSerialize(); 113 | } 114 | 115 | return [ 116 | 'keys' => \array_values($ret), 117 | ]; 118 | } 119 | 120 | /** 121 | * @since 1.3.0 122 | */ 123 | public function count(): int 124 | { 125 | return \count($this->keys); 126 | } 127 | 128 | /** 129 | * @since 1.3.0 130 | */ 131 | public function getIterator(): \ArrayIterator 132 | { 133 | return new \ArrayIterator($this->keys); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/KeySetFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * @license https://opensource.org/licenses/MIT MIT 10 | * 11 | * @see https://github.com/Strobotti/php-jwk 12 | * @since 1.0.0 13 | */ 14 | class KeySetFactory 15 | { 16 | /** 17 | * @var KeyFactory 18 | */ 19 | private $keyFactory; 20 | 21 | /** 22 | * KeySet constructor. 23 | */ 24 | public function __construct() 25 | { 26 | $this->setKeyFactory(new KeyFactory()); 27 | } 28 | 29 | /** 30 | * @since 1.0.0 31 | */ 32 | public function setKeyFactory(KeyFactory $keyFactory): self 33 | { 34 | $this->keyFactory = $keyFactory; 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * @since 1.0.0 41 | */ 42 | public function createFromJSON(string $json): KeySet 43 | { 44 | $assoc = \json_decode($json, true); 45 | 46 | $instance = new KeySet(); 47 | 48 | if (! is_array($assoc) || ! array_key_exists('keys', $assoc)) { 49 | return $instance; 50 | } 51 | 52 | foreach ($assoc['keys'] as $keyData) { 53 | $key = $this->keyFactory->createFromJson(\json_encode($keyData)); 54 | 55 | $instance->addKey($key); 56 | } 57 | 58 | return $instance; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Util/Base64UrlConverter.php: -------------------------------------------------------------------------------- 1 | 13 | * @license https://opensource.org/licenses/MIT MIT 14 | * 15 | * @see https://github.com/Strobotti/php-jwk 16 | * @since 1.0.0 17 | */ 18 | class Base64UrlConverter implements Base64UrlConverterInterface 19 | { 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function decode(string $data, $strict = false): string 24 | { 25 | $b64 = \strtr($data, '-_', '+/'); 26 | 27 | return \base64_decode($b64, $strict); 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function encode(string $data): string 34 | { 35 | return \rtrim(\strtr(\base64_encode($data), '+/', '-_'), '='); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Util/Base64UrlConverterInterface.php: -------------------------------------------------------------------------------- 1 | 13 | * @license https://opensource.org/licenses/MIT MIT 14 | * 15 | * @see https://github.com/Strobotti/php-jwk 16 | * @since 1.0.0 17 | */ 18 | interface Base64UrlConverterInterface 19 | { 20 | /** 21 | * Decodes Base64url formatted data to a string. 22 | * 23 | * @since 1.0.0 24 | * 25 | * @param bool $strict 26 | */ 27 | public function decode(string $data, $strict = false): string; 28 | 29 | /** 30 | * Encodes a string to a base64url formatted data. 31 | * 32 | * @since 1.0.0 33 | */ 34 | public function encode(string $data): string; 35 | } 36 | -------------------------------------------------------------------------------- /tests/Key/AbstractKeyTest.php: -------------------------------------------------------------------------------- 1 | assertSame($json, "{$key}"); 30 | } 31 | 32 | public function testSettersAndGetters(): void 33 | { 34 | $key = new AbstractKeyTest__AbstractKey__Mock(); 35 | $key->setAlgorithm(KeyInterface::ALGORITHM_RS256) 36 | ->setPublicKeyUse(KeyInterface::PUBLIC_KEY_USE_SIGNATURE) 37 | ->setKeyType(KeyInterface::KEY_TYPE_RSA) 38 | ->setKeyId('asdf') 39 | ; 40 | 41 | $this->assertSame(KeyInterface::ALGORITHM_RS256, $key->getAlgorithm()); 42 | $this->assertSame(KeyInterface::PUBLIC_KEY_USE_SIGNATURE, $key->getPublicKeyUse()); 43 | $this->assertSame(KeyInterface::KEY_TYPE_RSA, $key->getKeyType()); 44 | $this->assertSame('asdf', $key->getKeyId()); 45 | 46 | // Test nullable fields 47 | $key->setKeyId(null); 48 | $key->setPublicKeyUse(null); 49 | 50 | $this->assertNull($key->getKeyId()); 51 | $this->assertNull($key->getPublicKeyUse()); 52 | } 53 | } 54 | 55 | final class AbstractKeyTest__AbstractKey__Mock extends AbstractKey 56 | { 57 | } 58 | -------------------------------------------------------------------------------- /tests/Key/RsaTest.php: -------------------------------------------------------------------------------- 1 | assertSame($expected['kty'], $key->getKeyType()); 23 | $this->assertSame($expected['kid'], $key->getKeyId()); 24 | $this->assertSame($expected['use'], $key->getPublicKeyUse()); 25 | $this->assertSame($expected['alg'], $key->getAlgorithm()); 26 | $this->assertSame($expected['n'], $key->getModulus()); 27 | $this->assertSame($expected['e'], $key->getExponent()); 28 | } 29 | 30 | public static function provideCreateFromJSON(): \Generator 31 | { 32 | yield [ 33 | 'expected' => [ 34 | 'kty' => 'RSA', 35 | 'kid' => '86D88Kf', 36 | 'use' => 'sig', 37 | 'alg' => 'RS256', 38 | 'n' => 'iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ', 39 | 'e' => 'AQAB', 40 | ], 41 | 'input' => <<<'EOT' 42 | { 43 | "kty": "RSA", 44 | "kid": "86D88Kf", 45 | "use": "sig", 46 | "alg": "RS256", 47 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 48 | "e": "AQAB", 49 | "unsupported": "ignored" 50 | } 51 | EOT 52 | ]; 53 | } 54 | 55 | public function testToString(): void 56 | { 57 | $json = <<<'EOT' 58 | { 59 | "kty": "RSA", 60 | "use": "sig", 61 | "alg": "RS256", 62 | "kid": "86D88Kf", 63 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 64 | "e": "AQAB" 65 | } 66 | EOT; 67 | 68 | $key = Rsa::createFromJSON($json); 69 | 70 | $this->assertSame($json, "{$key}"); 71 | } 72 | 73 | public function testSettersAndGetters(): void 74 | { 75 | $e = 'AQAB'; 76 | $n = 'iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ'; 77 | 78 | $key = new Rsa(); 79 | $key->setExponent($e) 80 | ->setModulus($n) 81 | ; 82 | 83 | $this->assertSame($e, $key->getExponent()); 84 | $this->assertSame($n, $key->getModulus()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/KeyConverterTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 25 | \str_replace("\r", '', $expected), 26 | \str_replace("\r", '', $converter->keyToPem($key)) 27 | ); 28 | } 29 | 30 | public static function provideKeyToPem(): \Generator 31 | { 32 | yield [ 33 | 'key' => Rsa::createFromJSON('{ 34 | "kty": "RSA", 35 | "kid": "eXaunmL", 36 | "use": "sig", 37 | "alg": "RS256", 38 | "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw", 39 | "e": "AQAB" 40 | }'), 41 | 'expected' => <<<'EOT' 42 | -----BEGIN PUBLIC KEY----- 43 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf 44 | ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD 45 | Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE 46 | Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU 47 | lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe 48 | 4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh 49 | xwIDAQAB 50 | -----END PUBLIC KEY----- 51 | EOT 52 | ]; 53 | } 54 | 55 | public function testUnsupportedKeyTypeRaisesException(): void 56 | { 57 | /** @var KeyInterface|MockObject $key */ 58 | $key = $this->getMockBuilder(KeyInterface::class)->getMock(); 59 | 60 | $converter = new KeyConverter(); 61 | 62 | try { 63 | $converter->keyToPem($key); 64 | 65 | $this->fail('converting an unsupported key to PEM should throw an exception'); 66 | } catch (\InvalidArgumentException $e) { 67 | $this->assertTrue(true); 68 | } catch (\Throwable $e) { 69 | $this->fail(\sprintf('converting an unsupported key to PEM threw an unexpected exception %s', \get_class($e))); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/KeyFactoryTest.php: -------------------------------------------------------------------------------- 1 | createFromPem($pem, $options); 23 | 24 | $this->assertInstanceOf($expectedInstance, $key); 25 | $this->assertSame($json, $key->jsonSerialize()); 26 | } 27 | 28 | public static function provideCreateFromPem(): \Generator 29 | { 30 | yield [ 31 | 'pem' => <<<'EOT' 32 | -----BEGIN PUBLIC KEY----- 33 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf 34 | ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD 35 | Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE 36 | Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU 37 | lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe 38 | 4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh 39 | xwIDAQAB 40 | -----END PUBLIC KEY----- 41 | EOT 42 | , 43 | 'options' => [ 44 | 'use' => 'sig', 45 | 'alg' => 'RS256', 46 | 'kid' => 'eXaunmL', 47 | ], 48 | 'json' => [ 49 | 'kty' => 'RSA', 50 | 'use' => 'sig', 51 | 'alg' => 'RS256', 52 | 'kid' => 'eXaunmL', 53 | 'n' => '4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw', 54 | 'e' => 'AQAB', 55 | ], 56 | 'expectedInstance' => Rsa::class, 57 | ]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/KeySetFactoryTest.php: -------------------------------------------------------------------------------- 1 | createFromJSON($input); 24 | $json = $keys->jsonSerialize(); 25 | 26 | $this->assertSame(\json_decode($input, true), $json); 27 | } 28 | 29 | public function testInvalidJsonReturnsEmptyKeySet(): void 30 | { 31 | $invalidJson = '{}'; 32 | 33 | $factory = new KeySetFactory(); 34 | 35 | $keySet = $factory->createFromJSON($invalidJson); 36 | $this->assertInstanceOf(KeySet::class, $keySet); 37 | $this->assertCount(0, $keySet); 38 | } 39 | 40 | public static function provideCreateFromJSON(): \Generator 41 | { 42 | yield [ 43 | 'input' => <<<'EOT' 44 | { 45 | "keys": [ 46 | { 47 | "kty": "RSA", 48 | "use": "sig", 49 | "alg": "RS256", 50 | "kid": "86D88Kf", 51 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 52 | "e": "AQAB" 53 | }, 54 | { 55 | "kty": "RSA", 56 | "use": "sig", 57 | "alg": "RS256", 58 | "kid": "eXaunmL", 59 | "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw", 60 | "e": "AQAB" 61 | } 62 | ] 63 | } 64 | EOT 65 | ]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/KeySetTest.php: -------------------------------------------------------------------------------- 1 | assertSame($expected, "{$keySet}"); 22 | } 23 | 24 | public static function provideCreateFromJSON(): \Generator 25 | { 26 | $keyJson = <<<'EOT' 27 | { 28 | "kty": "RSA", 29 | "use": "sig", 30 | "alg": "RS256", 31 | "kid": "86D88Kf", 32 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 33 | "e": "AQAB" 34 | } 35 | EOT; 36 | 37 | yield [ 38 | 'expected' => <<<'EOT' 39 | { 40 | "keys": [ 41 | { 42 | "kty": "RSA", 43 | "use": "sig", 44 | "alg": "RS256", 45 | "kid": "86D88Kf", 46 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 47 | "e": "AQAB" 48 | } 49 | ] 50 | } 51 | EOT 52 | , 53 | 'keySet' => (new KeySet()) 54 | ->addKey(Rsa::createFromJSON($keyJson)), 55 | ]; 56 | } 57 | 58 | public function testAddKeyThrowsErrorOnDuplicateKid(): void 59 | { 60 | $this->expectException(\InvalidArgumentException::class); 61 | 62 | $keyJson = <<<'EOT' 63 | { 64 | "kty": "RSA", 65 | "use": "sig", 66 | "alg": "RS256", 67 | "kid": "86D88Kf", 68 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 69 | "e": "AQAB" 70 | } 71 | EOT; 72 | $keySet = new KeySet(); 73 | $keySet->addKey(Rsa::createFromJSON($keyJson)) 74 | ->addKey(Rsa::createFromJSON($keyJson)) 75 | ; 76 | } 77 | 78 | public function testGetKeyById(): void 79 | { 80 | $keyJson = <<<'EOT' 81 | { 82 | "kty": "RSA", 83 | "use": "sig", 84 | "alg": "RS256", 85 | "kid": "86D88Kf", 86 | "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ", 87 | "e": "AQAB" 88 | } 89 | EOT; 90 | 91 | $key = Rsa::createFromJSON($keyJson); 92 | 93 | $keySet = new KeySet(); 94 | $keySet->addKey($key); 95 | 96 | $this->assertSame($key, $keySet->getKeyById('86D88Kf')); 97 | 98 | $this->assertNull($keySet->getKeyById('asdf')); 99 | } 100 | 101 | public function testCountable(): void 102 | { 103 | $keyset = new KeySet(); 104 | $this->assertCount(0, $keyset); 105 | 106 | $keyset->addKey(new Rsa()); 107 | $this->assertCount(1, $keyset); 108 | 109 | $keyset->addKey(new Rsa()); 110 | $this->assertCount(2, $keyset); 111 | } 112 | 113 | public function testIteratorAggregate(): void 114 | { 115 | $keyset = new KeySet(); 116 | 117 | $count = 0; 118 | 119 | foreach ($keyset as $key) { 120 | ++$count; 121 | } 122 | 123 | $this->assertSame(0, $count); 124 | 125 | $keyset->addKey(new Rsa()); 126 | $keyset->addKey(new Rsa()); 127 | $keyset->addKey(new Rsa()); 128 | 129 | foreach ($keyset as $index => $key) { 130 | $this->assertInstanceOf(Rsa::class, $key); 131 | $this->assertSame($index, $count); 132 | 133 | ++$count; 134 | } 135 | 136 | $this->assertSame(3, $count); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/Util/Base64UrlConverterTest.php: -------------------------------------------------------------------------------- 1 | assertSame($expected, $converter->decode($input)); 23 | } 24 | 25 | public static function provideDecode(): \Generator 26 | { 27 | yield [ 28 | 'expected' => '/a+quick+brown+fox/jumped-over/the_lazy_dog/', 29 | 'input' => 'L2ErcXVpY2srYnJvd24rZm94L2p1bXBlZC1vdmVyL3RoZV9sYXp5X2RvZy8', 30 | ]; 31 | } 32 | 33 | /** 34 | * @dataProvider provideEncode 35 | */ 36 | public function testEncode(string $expected, string $input): void 37 | { 38 | $converter = new Base64UrlConverter(); 39 | 40 | static::assertSame($expected, $converter->encode($input)); 41 | } 42 | 43 | public static function provideEncode(): \Generator 44 | { 45 | yield [ 46 | 'expected' => 'L2ErcXVpY2srYnJvd24rZm94L2p1bXBlZC1vdmVyL3RoZV9sYXp5X2RvZy8', 47 | 'input' => '/a+quick+brown+fox/jumped-over/the_lazy_dog/', 48 | ]; 49 | } 50 | } 51 | --------------------------------------------------------------------------------