├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml.dist ├── src ├── OAuth2Middleware.php └── TokenService │ ├── AbstractTokenService.php │ ├── AuthorizesRequests.php │ └── Bearer.php └── tests ├── OAuth2MiddlewareTest.php ├── TestCase.php └── TokenService └── BearerTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | external_code_coverage: 3 | timeout: 600 # Timeout in seconds. 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: php 4 | 5 | matrix: 6 | include: 7 | - php: 7.0 8 | - php: 7.0 9 | env: deps=low 10 | - php: 7.1 11 | env: coverage=yes 12 | 13 | env: 14 | global: 15 | - deps=no 16 | 17 | before_install: 18 | - composer self-update 19 | - composer config --quiet github-oauth.github.com $GITHUB_TOKEN 20 | - if [[ ! $GITHUB_TOKEN ]]; then echo "no github token"; fi 21 | - if [[ ! $GITHUB_TOKEN ]]; then flag=--prefer-source; fi 22 | 23 | install: 24 | - if [ "$deps" = "no" ]; then composer $flag install; fi; 25 | - if [ "$deps" = "low" ]; then composer $flag --prefer-lowest --prefer-stable update; fi; 26 | 27 | script: 28 | - mkdir -p build/logs 29 | - if [ "$coverage" = "yes" ]; then ./vendor/bin/phpunit --coverage-clover build/logs/coverage.xml; else ./vendor/bin/phpunit; fi; 30 | 31 | after_script: 32 | - wget https://scrutinizer-ci.com/ocular.phar 33 | - if [ "$coverage" = "yes" ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/coverage.xml; fi; 34 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or 24 | reject comments, commits, code, wiki edits, issues, and other contributions 25 | that are not aligned to this Code of Conduct, or to ban temporarily or 26 | permanently any contributor for other behaviors that they deem inappropriate, 27 | threatening, offensive, or harmful. 28 | 29 | By adopting this Code of Conduct, project maintainers commit themselves to 30 | fairly and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code of 32 | Conduct may be permanently removed from the project team. 33 | 34 | This code of conduct applies both within project spaces and in public spaces 35 | when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 38 | reported by contacting a project maintainer at gabriel@somoza.me. All 39 | complaints will be reviewed and investigated and will result in a response that 40 | is deemed necessary and appropriate to the circumstances. Maintainers are 41 | obligated to maintain confidentiality with regard to the reporter of an 42 | incident. 43 | 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 46 | version 1.3.0, available at 47 | [http://contributor-covenant.org/version/1/3/0/][version] 48 | 49 | [homepage]: http://contributor-covenant.org 50 | [version]: http://contributor-covenant.org/version/1/3/0/ 51 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 Gabriel Somoza 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Abandoned 2 | ``` 3 | This is to inform all users that this package has been abandoned and will no longer receive any 4 | updates or maintenance. We highly recommend users to migrate to an alternative library package. 5 | We apologize for any inconvenience caused. 6 | ``` 7 | 8 | ## OAuth2 client middleware for league/oauth2-client 9 | 10 | [![Build Status](https://travis-ci.org/gsomoza/oauth2-middleware.svg?branch=master)](https://travis-ci.org/gsomoza/oauth2-middleware) 11 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gsomoza/oauth2-middleware/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/gsomoza/oauth2-middleware/?branch=master) 12 | [![Code Coverage](https://scrutinizer-ci.com/g/gsomoza/oauth2-middleware/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/gsomoza/oauth2-middleware/?branch=master) 13 | [![Latest Stable Version](https://poser.pugx.org/somoza/oauth2-client-middleware/v/stable)](https://packagist.org/packages/somoza/oauth2-client-middleware) 14 | 15 | [![Author](https://img.shields.io/badge/author-%40gabriel__somoza-blue.svg)](https://img.shields.io/badge/author-%40gabriel__somoza-blue.svg) 16 | [![License](https://poser.pugx.org/somoza/oauth2-client-middleware/license)](https://packagist.org/packages/somoza/oauth2-client-middleware) 17 | 18 | PSR7 middleware that uses league/oauth2-client to authenticate requests with an OAuth2 server. 19 | 20 | ## Installation 21 | 22 | ``` 23 | composer require somoza/oauth2-client-middleware 24 | ``` 25 | 26 | ## Usage 27 | 28 | The current implementation indirectly depends on Guzzle 6 because it's a direct dependency of `league/oauth2-client`. 29 | 30 | Using Guzzle: 31 | 32 | ```php 33 | use Somoza\OAuth2Middleware\OAuth2Middleware; 34 | use Somoza\OAuth2Middleware\TokenService\Bearer; 35 | 36 | $stack = new \GuzzleHttp\HandlerStack(); 37 | $stack->setHandler(new CurlHandler()); 38 | $client = new \GuzzleHttp\Client(['handler' => $stack]); 39 | 40 | // instantiate a provider, see league/oauth2-client docs 41 | $provider = new GenericProvider( 42 | [ 43 | 'clientId' => 'your_client_id', 44 | 'clientSecret' => 'your_client_secret', 45 | 'urlAuthorize' => 'your_authorization_url', 46 | 'urlAccessToken' => 'your_access_token_url', 47 | 'urlResourceOwnerDetails' => 'your_resource_owner_url', 48 | ], 49 | [ 'httpClient' => $client ] // or don't pass it and let the oauth2-client create its own Guzzle client 50 | ); 51 | 52 | // attach our oauth2 middleware 53 | $bearerMiddleware = new OAuth2Middleware( 54 | new Bearer($provider), // use the Bearer token type 55 | [ // ignore (do not attempt to authorize) the following URLs 56 | $provider->getBaseAuthorizationUrl(), 57 | $provider->getBaseAccessTokenUrl(), 58 | ] 59 | ); 60 | $stack->push($bearerMiddleware); 61 | 62 | // if you want to debug, it might be useful to attach a PSR7 logger here 63 | ``` 64 | 65 | ## Caching the Access Token 66 | 67 | A callback can be assigned to the middleware in order to save the access token for future use. Make sure you know about 68 | the security implications of storing an access token (do it at your own risk). 69 | 70 | Example: 71 | 72 | ```php 73 | use Somoza\OAuth2Middleware\OAuth2Middleware; 74 | use Somoza\OAuth2Middleware\TokenService\Bearer; 75 | use League\OAuth2\Client\Token\AccessToken; 76 | 77 | // see previous example for initialization 78 | $tokenStore = new EncryptedCache(); // you can use whatever you want here 79 | $token = null; 80 | if ($tokenStore->contains($userId)) { 81 | $tokenData = json_decode($cache->fetch($userId)); 82 | $token = new AccessToken($tokenData); 83 | } 84 | 85 | $bearerMiddleware = new OAuth2Middleware( 86 | new Bearer( 87 | $provider, // defined as in the "Usage" example 88 | $token, 89 | function (AccessToken $newToken, AccessToken $oldToken) 90 | use ($tokenStore, $userId) { 91 | // called whenever a new AccessToken is fetched 92 | $tokenStore->save($userId, $newToken->jsonSerialize()); 93 | } 94 | ), 95 | ); 96 | 97 | $stack->push($bearerMiddleware); 98 | ``` 99 | 100 | ## License 101 | 102 | MIT - see LICENSE.md 103 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "somoza/oauth2-client-middleware", 3 | "description": "OAuth2 PSR7 middleware for league/oauth2-client", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Gabriel Somoza", 9 | "email": "gabriel@somoza.me" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "Somoza\\OAuth2Middleware\\": "src" 15 | } 16 | }, 17 | "autoload-dev": { 18 | "psr-4": { 19 | "SomozaTest\\OAuth2Middleware\\": "tests" 20 | } 21 | }, 22 | "require": { 23 | "php": ">=7.0.0", 24 | "league/oauth2-client": "^1.2.0|^2.2.0", 25 | "beberlei/assert": "^2.7" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^4.0|^5.0", 29 | "mockery/mockery": "^0.9.9" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "2e8b22c5cedf6a8860496bb2b412f700", 8 | "packages": [ 9 | { 10 | "name": "beberlei/assert", 11 | "version": "v2.7.6", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/beberlei/assert.git", 15 | "reference": "8726e183ebbb0169cb6cb4832e22ebd355524563" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/beberlei/assert/zipball/8726e183ebbb0169cb6cb4832e22ebd355524563", 20 | "reference": "8726e183ebbb0169cb6cb4832e22ebd355524563", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-mbstring": "*", 25 | "php": ">=5.3" 26 | }, 27 | "require-dev": { 28 | "friendsofphp/php-cs-fixer": "^2.1.1", 29 | "phpunit/phpunit": "^4|^5" 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "psr-4": { 34 | "Assert\\": "lib/Assert" 35 | }, 36 | "files": [ 37 | "lib/Assert/functions.php" 38 | ] 39 | }, 40 | "notification-url": "https://packagist.org/downloads/", 41 | "license": [ 42 | "BSD-2-Clause" 43 | ], 44 | "authors": [ 45 | { 46 | "name": "Benjamin Eberlei", 47 | "email": "kontakt@beberlei.de", 48 | "role": "Lead Developer" 49 | }, 50 | { 51 | "name": "Richard Quadling", 52 | "email": "rquadling@gmail.com", 53 | "role": "Collaborator" 54 | } 55 | ], 56 | "description": "Thin assertion library for input validation in business models.", 57 | "keywords": [ 58 | "assert", 59 | "assertion", 60 | "validation" 61 | ], 62 | "time": "2017-05-04T02:00:24+00:00" 63 | }, 64 | { 65 | "name": "guzzlehttp/guzzle", 66 | "version": "6.3.0", 67 | "source": { 68 | "type": "git", 69 | "url": "https://github.com/guzzle/guzzle.git", 70 | "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" 71 | }, 72 | "dist": { 73 | "type": "zip", 74 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", 75 | "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", 76 | "shasum": "" 77 | }, 78 | "require": { 79 | "guzzlehttp/promises": "^1.0", 80 | "guzzlehttp/psr7": "^1.4", 81 | "php": ">=5.5" 82 | }, 83 | "require-dev": { 84 | "ext-curl": "*", 85 | "phpunit/phpunit": "^4.0 || ^5.0", 86 | "psr/log": "^1.0" 87 | }, 88 | "suggest": { 89 | "psr/log": "Required for using the Log middleware" 90 | }, 91 | "type": "library", 92 | "extra": { 93 | "branch-alias": { 94 | "dev-master": "6.2-dev" 95 | } 96 | }, 97 | "autoload": { 98 | "files": [ 99 | "src/functions_include.php" 100 | ], 101 | "psr-4": { 102 | "GuzzleHttp\\": "src/" 103 | } 104 | }, 105 | "notification-url": "https://packagist.org/downloads/", 106 | "license": [ 107 | "MIT" 108 | ], 109 | "authors": [ 110 | { 111 | "name": "Michael Dowling", 112 | "email": "mtdowling@gmail.com", 113 | "homepage": "https://github.com/mtdowling" 114 | } 115 | ], 116 | "description": "Guzzle is a PHP HTTP client library", 117 | "homepage": "http://guzzlephp.org/", 118 | "keywords": [ 119 | "client", 120 | "curl", 121 | "framework", 122 | "http", 123 | "http client", 124 | "rest", 125 | "web service" 126 | ], 127 | "time": "2017-06-22T18:50:49+00:00" 128 | }, 129 | { 130 | "name": "guzzlehttp/promises", 131 | "version": "v1.3.1", 132 | "source": { 133 | "type": "git", 134 | "url": "https://github.com/guzzle/promises.git", 135 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 136 | }, 137 | "dist": { 138 | "type": "zip", 139 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 140 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 141 | "shasum": "" 142 | }, 143 | "require": { 144 | "php": ">=5.5.0" 145 | }, 146 | "require-dev": { 147 | "phpunit/phpunit": "^4.0" 148 | }, 149 | "type": "library", 150 | "extra": { 151 | "branch-alias": { 152 | "dev-master": "1.4-dev" 153 | } 154 | }, 155 | "autoload": { 156 | "psr-4": { 157 | "GuzzleHttp\\Promise\\": "src/" 158 | }, 159 | "files": [ 160 | "src/functions_include.php" 161 | ] 162 | }, 163 | "notification-url": "https://packagist.org/downloads/", 164 | "license": [ 165 | "MIT" 166 | ], 167 | "authors": [ 168 | { 169 | "name": "Michael Dowling", 170 | "email": "mtdowling@gmail.com", 171 | "homepage": "https://github.com/mtdowling" 172 | } 173 | ], 174 | "description": "Guzzle promises library", 175 | "keywords": [ 176 | "promise" 177 | ], 178 | "time": "2016-12-20T10:07:11+00:00" 179 | }, 180 | { 181 | "name": "guzzlehttp/psr7", 182 | "version": "1.4.2", 183 | "source": { 184 | "type": "git", 185 | "url": "https://github.com/guzzle/psr7.git", 186 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" 187 | }, 188 | "dist": { 189 | "type": "zip", 190 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", 191 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", 192 | "shasum": "" 193 | }, 194 | "require": { 195 | "php": ">=5.4.0", 196 | "psr/http-message": "~1.0" 197 | }, 198 | "provide": { 199 | "psr/http-message-implementation": "1.0" 200 | }, 201 | "require-dev": { 202 | "phpunit/phpunit": "~4.0" 203 | }, 204 | "type": "library", 205 | "extra": { 206 | "branch-alias": { 207 | "dev-master": "1.4-dev" 208 | } 209 | }, 210 | "autoload": { 211 | "psr-4": { 212 | "GuzzleHttp\\Psr7\\": "src/" 213 | }, 214 | "files": [ 215 | "src/functions_include.php" 216 | ] 217 | }, 218 | "notification-url": "https://packagist.org/downloads/", 219 | "license": [ 220 | "MIT" 221 | ], 222 | "authors": [ 223 | { 224 | "name": "Michael Dowling", 225 | "email": "mtdowling@gmail.com", 226 | "homepage": "https://github.com/mtdowling" 227 | }, 228 | { 229 | "name": "Tobias Schultze", 230 | "homepage": "https://github.com/Tobion" 231 | } 232 | ], 233 | "description": "PSR-7 message implementation that also provides common utility methods", 234 | "keywords": [ 235 | "http", 236 | "message", 237 | "request", 238 | "response", 239 | "stream", 240 | "uri", 241 | "url" 242 | ], 243 | "time": "2017-03-20T17:10:46+00:00" 244 | }, 245 | { 246 | "name": "league/oauth2-client", 247 | "version": "2.2.1", 248 | "source": { 249 | "type": "git", 250 | "url": "https://github.com/thephpleague/oauth2-client.git", 251 | "reference": "313250eab923e673a5c0c8f463f443ee79f4383f" 252 | }, 253 | "dist": { 254 | "type": "zip", 255 | "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/313250eab923e673a5c0c8f463f443ee79f4383f", 256 | "reference": "313250eab923e673a5c0c8f463f443ee79f4383f", 257 | "shasum": "" 258 | }, 259 | "require": { 260 | "guzzlehttp/guzzle": "^6.0", 261 | "paragonie/random_compat": "^1|^2", 262 | "php": ">=5.6.0" 263 | }, 264 | "require-dev": { 265 | "eloquent/liberator": "^2.0", 266 | "eloquent/phony": "^0.14.1", 267 | "jakub-onderka/php-parallel-lint": "~0.9", 268 | "phpunit/phpunit": "^5.0", 269 | "squizlabs/php_codesniffer": "^2.0" 270 | }, 271 | "type": "library", 272 | "extra": { 273 | "branch-alias": { 274 | "dev-2.x": "2.0.x-dev" 275 | } 276 | }, 277 | "autoload": { 278 | "psr-4": { 279 | "League\\OAuth2\\Client\\": "src/" 280 | } 281 | }, 282 | "notification-url": "https://packagist.org/downloads/", 283 | "license": [ 284 | "MIT" 285 | ], 286 | "authors": [ 287 | { 288 | "name": "Alex Bilbie", 289 | "email": "hello@alexbilbie.com", 290 | "homepage": "http://www.alexbilbie.com", 291 | "role": "Developer" 292 | }, 293 | { 294 | "name": "Woody Gilk", 295 | "homepage": "https://github.com/shadowhand", 296 | "role": "Contributor" 297 | } 298 | ], 299 | "description": "OAuth 2.0 Client Library", 300 | "keywords": [ 301 | "Authentication", 302 | "SSO", 303 | "authorization", 304 | "identity", 305 | "idp", 306 | "oauth", 307 | "oauth2", 308 | "single sign on" 309 | ], 310 | "time": "2017-04-25T14:43:14+00:00" 311 | }, 312 | { 313 | "name": "paragonie/random_compat", 314 | "version": "v2.0.10", 315 | "source": { 316 | "type": "git", 317 | "url": "https://github.com/paragonie/random_compat.git", 318 | "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" 319 | }, 320 | "dist": { 321 | "type": "zip", 322 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", 323 | "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", 324 | "shasum": "" 325 | }, 326 | "require": { 327 | "php": ">=5.2.0" 328 | }, 329 | "require-dev": { 330 | "phpunit/phpunit": "4.*|5.*" 331 | }, 332 | "suggest": { 333 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." 334 | }, 335 | "type": "library", 336 | "autoload": { 337 | "files": [ 338 | "lib/random.php" 339 | ] 340 | }, 341 | "notification-url": "https://packagist.org/downloads/", 342 | "license": [ 343 | "MIT" 344 | ], 345 | "authors": [ 346 | { 347 | "name": "Paragon Initiative Enterprises", 348 | "email": "security@paragonie.com", 349 | "homepage": "https://paragonie.com" 350 | } 351 | ], 352 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 353 | "keywords": [ 354 | "csprng", 355 | "pseudorandom", 356 | "random" 357 | ], 358 | "time": "2017-03-13T16:27:32+00:00" 359 | }, 360 | { 361 | "name": "psr/http-message", 362 | "version": "1.0.1", 363 | "source": { 364 | "type": "git", 365 | "url": "https://github.com/php-fig/http-message.git", 366 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 367 | }, 368 | "dist": { 369 | "type": "zip", 370 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 371 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 372 | "shasum": "" 373 | }, 374 | "require": { 375 | "php": ">=5.3.0" 376 | }, 377 | "type": "library", 378 | "extra": { 379 | "branch-alias": { 380 | "dev-master": "1.0.x-dev" 381 | } 382 | }, 383 | "autoload": { 384 | "psr-4": { 385 | "Psr\\Http\\Message\\": "src/" 386 | } 387 | }, 388 | "notification-url": "https://packagist.org/downloads/", 389 | "license": [ 390 | "MIT" 391 | ], 392 | "authors": [ 393 | { 394 | "name": "PHP-FIG", 395 | "homepage": "http://www.php-fig.org/" 396 | } 397 | ], 398 | "description": "Common interface for HTTP messages", 399 | "homepage": "https://github.com/php-fig/http-message", 400 | "keywords": [ 401 | "http", 402 | "http-message", 403 | "psr", 404 | "psr-7", 405 | "request", 406 | "response" 407 | ], 408 | "time": "2016-08-06T14:39:51+00:00" 409 | } 410 | ], 411 | "packages-dev": [ 412 | { 413 | "name": "doctrine/instantiator", 414 | "version": "1.0.5", 415 | "source": { 416 | "type": "git", 417 | "url": "https://github.com/doctrine/instantiator.git", 418 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 419 | }, 420 | "dist": { 421 | "type": "zip", 422 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 423 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 424 | "shasum": "" 425 | }, 426 | "require": { 427 | "php": ">=5.3,<8.0-DEV" 428 | }, 429 | "require-dev": { 430 | "athletic/athletic": "~0.1.8", 431 | "ext-pdo": "*", 432 | "ext-phar": "*", 433 | "phpunit/phpunit": "~4.0", 434 | "squizlabs/php_codesniffer": "~2.0" 435 | }, 436 | "type": "library", 437 | "extra": { 438 | "branch-alias": { 439 | "dev-master": "1.0.x-dev" 440 | } 441 | }, 442 | "autoload": { 443 | "psr-4": { 444 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 445 | } 446 | }, 447 | "notification-url": "https://packagist.org/downloads/", 448 | "license": [ 449 | "MIT" 450 | ], 451 | "authors": [ 452 | { 453 | "name": "Marco Pivetta", 454 | "email": "ocramius@gmail.com", 455 | "homepage": "http://ocramius.github.com/" 456 | } 457 | ], 458 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 459 | "homepage": "https://github.com/doctrine/instantiator", 460 | "keywords": [ 461 | "constructor", 462 | "instantiate" 463 | ], 464 | "time": "2015-06-14T21:17:01+00:00" 465 | }, 466 | { 467 | "name": "hamcrest/hamcrest-php", 468 | "version": "v1.2.2", 469 | "source": { 470 | "type": "git", 471 | "url": "https://github.com/hamcrest/hamcrest-php.git", 472 | "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" 473 | }, 474 | "dist": { 475 | "type": "zip", 476 | "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", 477 | "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", 478 | "shasum": "" 479 | }, 480 | "require": { 481 | "php": ">=5.3.2" 482 | }, 483 | "replace": { 484 | "cordoval/hamcrest-php": "*", 485 | "davedevelopment/hamcrest-php": "*", 486 | "kodova/hamcrest-php": "*" 487 | }, 488 | "require-dev": { 489 | "phpunit/php-file-iterator": "1.3.3", 490 | "satooshi/php-coveralls": "dev-master" 491 | }, 492 | "type": "library", 493 | "autoload": { 494 | "classmap": [ 495 | "hamcrest" 496 | ], 497 | "files": [ 498 | "hamcrest/Hamcrest.php" 499 | ] 500 | }, 501 | "notification-url": "https://packagist.org/downloads/", 502 | "license": [ 503 | "BSD" 504 | ], 505 | "description": "This is the PHP port of Hamcrest Matchers", 506 | "keywords": [ 507 | "test" 508 | ], 509 | "time": "2015-05-11T14:41:42+00:00" 510 | }, 511 | { 512 | "name": "mockery/mockery", 513 | "version": "0.9.9", 514 | "source": { 515 | "type": "git", 516 | "url": "https://github.com/mockery/mockery.git", 517 | "reference": "6fdb61243844dc924071d3404bb23994ea0b6856" 518 | }, 519 | "dist": { 520 | "type": "zip", 521 | "url": "https://api.github.com/repos/mockery/mockery/zipball/6fdb61243844dc924071d3404bb23994ea0b6856", 522 | "reference": "6fdb61243844dc924071d3404bb23994ea0b6856", 523 | "shasum": "" 524 | }, 525 | "require": { 526 | "hamcrest/hamcrest-php": "~1.1", 527 | "lib-pcre": ">=7.0", 528 | "php": ">=5.3.2" 529 | }, 530 | "require-dev": { 531 | "phpunit/phpunit": "~4.0" 532 | }, 533 | "type": "library", 534 | "extra": { 535 | "branch-alias": { 536 | "dev-master": "0.9.x-dev" 537 | } 538 | }, 539 | "autoload": { 540 | "psr-0": { 541 | "Mockery": "library/" 542 | } 543 | }, 544 | "notification-url": "https://packagist.org/downloads/", 545 | "license": [ 546 | "BSD-3-Clause" 547 | ], 548 | "authors": [ 549 | { 550 | "name": "Pádraic Brady", 551 | "email": "padraic.brady@gmail.com", 552 | "homepage": "http://blog.astrumfutura.com" 553 | }, 554 | { 555 | "name": "Dave Marshall", 556 | "email": "dave.marshall@atstsolutions.co.uk", 557 | "homepage": "http://davedevelopment.co.uk" 558 | } 559 | ], 560 | "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", 561 | "homepage": "http://github.com/padraic/mockery", 562 | "keywords": [ 563 | "BDD", 564 | "TDD", 565 | "library", 566 | "mock", 567 | "mock objects", 568 | "mockery", 569 | "stub", 570 | "test", 571 | "test double", 572 | "testing" 573 | ], 574 | "time": "2017-02-28T12:52:32+00:00" 575 | }, 576 | { 577 | "name": "myclabs/deep-copy", 578 | "version": "1.6.1", 579 | "source": { 580 | "type": "git", 581 | "url": "https://github.com/myclabs/DeepCopy.git", 582 | "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" 583 | }, 584 | "dist": { 585 | "type": "zip", 586 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", 587 | "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", 588 | "shasum": "" 589 | }, 590 | "require": { 591 | "php": ">=5.4.0" 592 | }, 593 | "require-dev": { 594 | "doctrine/collections": "1.*", 595 | "phpunit/phpunit": "~4.1" 596 | }, 597 | "type": "library", 598 | "autoload": { 599 | "psr-4": { 600 | "DeepCopy\\": "src/DeepCopy/" 601 | } 602 | }, 603 | "notification-url": "https://packagist.org/downloads/", 604 | "license": [ 605 | "MIT" 606 | ], 607 | "description": "Create deep copies (clones) of your objects", 608 | "homepage": "https://github.com/myclabs/DeepCopy", 609 | "keywords": [ 610 | "clone", 611 | "copy", 612 | "duplicate", 613 | "object", 614 | "object graph" 615 | ], 616 | "time": "2017-04-12T18:52:22+00:00" 617 | }, 618 | { 619 | "name": "phpdocumentor/reflection-common", 620 | "version": "1.0", 621 | "source": { 622 | "type": "git", 623 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 624 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" 625 | }, 626 | "dist": { 627 | "type": "zip", 628 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 629 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 630 | "shasum": "" 631 | }, 632 | "require": { 633 | "php": ">=5.5" 634 | }, 635 | "require-dev": { 636 | "phpunit/phpunit": "^4.6" 637 | }, 638 | "type": "library", 639 | "extra": { 640 | "branch-alias": { 641 | "dev-master": "1.0.x-dev" 642 | } 643 | }, 644 | "autoload": { 645 | "psr-4": { 646 | "phpDocumentor\\Reflection\\": [ 647 | "src" 648 | ] 649 | } 650 | }, 651 | "notification-url": "https://packagist.org/downloads/", 652 | "license": [ 653 | "MIT" 654 | ], 655 | "authors": [ 656 | { 657 | "name": "Jaap van Otterdijk", 658 | "email": "opensource@ijaap.nl" 659 | } 660 | ], 661 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 662 | "homepage": "http://www.phpdoc.org", 663 | "keywords": [ 664 | "FQSEN", 665 | "phpDocumentor", 666 | "phpdoc", 667 | "reflection", 668 | "static analysis" 669 | ], 670 | "time": "2015-12-27T11:43:31+00:00" 671 | }, 672 | { 673 | "name": "phpdocumentor/reflection-docblock", 674 | "version": "3.1.1", 675 | "source": { 676 | "type": "git", 677 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 678 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" 679 | }, 680 | "dist": { 681 | "type": "zip", 682 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", 683 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", 684 | "shasum": "" 685 | }, 686 | "require": { 687 | "php": ">=5.5", 688 | "phpdocumentor/reflection-common": "^1.0@dev", 689 | "phpdocumentor/type-resolver": "^0.2.0", 690 | "webmozart/assert": "^1.0" 691 | }, 692 | "require-dev": { 693 | "mockery/mockery": "^0.9.4", 694 | "phpunit/phpunit": "^4.4" 695 | }, 696 | "type": "library", 697 | "autoload": { 698 | "psr-4": { 699 | "phpDocumentor\\Reflection\\": [ 700 | "src/" 701 | ] 702 | } 703 | }, 704 | "notification-url": "https://packagist.org/downloads/", 705 | "license": [ 706 | "MIT" 707 | ], 708 | "authors": [ 709 | { 710 | "name": "Mike van Riel", 711 | "email": "me@mikevanriel.com" 712 | } 713 | ], 714 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 715 | "time": "2016-09-30T07:12:33+00:00" 716 | }, 717 | { 718 | "name": "phpdocumentor/type-resolver", 719 | "version": "0.2.1", 720 | "source": { 721 | "type": "git", 722 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 723 | "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" 724 | }, 725 | "dist": { 726 | "type": "zip", 727 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", 728 | "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", 729 | "shasum": "" 730 | }, 731 | "require": { 732 | "php": ">=5.5", 733 | "phpdocumentor/reflection-common": "^1.0" 734 | }, 735 | "require-dev": { 736 | "mockery/mockery": "^0.9.4", 737 | "phpunit/phpunit": "^5.2||^4.8.24" 738 | }, 739 | "type": "library", 740 | "extra": { 741 | "branch-alias": { 742 | "dev-master": "1.0.x-dev" 743 | } 744 | }, 745 | "autoload": { 746 | "psr-4": { 747 | "phpDocumentor\\Reflection\\": [ 748 | "src/" 749 | ] 750 | } 751 | }, 752 | "notification-url": "https://packagist.org/downloads/", 753 | "license": [ 754 | "MIT" 755 | ], 756 | "authors": [ 757 | { 758 | "name": "Mike van Riel", 759 | "email": "me@mikevanriel.com" 760 | } 761 | ], 762 | "time": "2016-11-25T06:54:22+00:00" 763 | }, 764 | { 765 | "name": "phpspec/prophecy", 766 | "version": "v1.7.0", 767 | "source": { 768 | "type": "git", 769 | "url": "https://github.com/phpspec/prophecy.git", 770 | "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" 771 | }, 772 | "dist": { 773 | "type": "zip", 774 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", 775 | "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", 776 | "shasum": "" 777 | }, 778 | "require": { 779 | "doctrine/instantiator": "^1.0.2", 780 | "php": "^5.3|^7.0", 781 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 782 | "sebastian/comparator": "^1.1|^2.0", 783 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 784 | }, 785 | "require-dev": { 786 | "phpspec/phpspec": "^2.5|^3.2", 787 | "phpunit/phpunit": "^4.8 || ^5.6.5" 788 | }, 789 | "type": "library", 790 | "extra": { 791 | "branch-alias": { 792 | "dev-master": "1.6.x-dev" 793 | } 794 | }, 795 | "autoload": { 796 | "psr-0": { 797 | "Prophecy\\": "src/" 798 | } 799 | }, 800 | "notification-url": "https://packagist.org/downloads/", 801 | "license": [ 802 | "MIT" 803 | ], 804 | "authors": [ 805 | { 806 | "name": "Konstantin Kudryashov", 807 | "email": "ever.zet@gmail.com", 808 | "homepage": "http://everzet.com" 809 | }, 810 | { 811 | "name": "Marcello Duarte", 812 | "email": "marcello.duarte@gmail.com" 813 | } 814 | ], 815 | "description": "Highly opinionated mocking framework for PHP 5.3+", 816 | "homepage": "https://github.com/phpspec/prophecy", 817 | "keywords": [ 818 | "Double", 819 | "Dummy", 820 | "fake", 821 | "mock", 822 | "spy", 823 | "stub" 824 | ], 825 | "time": "2017-03-02T20:05:34+00:00" 826 | }, 827 | { 828 | "name": "phpunit/php-code-coverage", 829 | "version": "4.0.8", 830 | "source": { 831 | "type": "git", 832 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 833 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" 834 | }, 835 | "dist": { 836 | "type": "zip", 837 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 838 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 839 | "shasum": "" 840 | }, 841 | "require": { 842 | "ext-dom": "*", 843 | "ext-xmlwriter": "*", 844 | "php": "^5.6 || ^7.0", 845 | "phpunit/php-file-iterator": "^1.3", 846 | "phpunit/php-text-template": "^1.2", 847 | "phpunit/php-token-stream": "^1.4.2 || ^2.0", 848 | "sebastian/code-unit-reverse-lookup": "^1.0", 849 | "sebastian/environment": "^1.3.2 || ^2.0", 850 | "sebastian/version": "^1.0 || ^2.0" 851 | }, 852 | "require-dev": { 853 | "ext-xdebug": "^2.1.4", 854 | "phpunit/phpunit": "^5.7" 855 | }, 856 | "suggest": { 857 | "ext-xdebug": "^2.5.1" 858 | }, 859 | "type": "library", 860 | "extra": { 861 | "branch-alias": { 862 | "dev-master": "4.0.x-dev" 863 | } 864 | }, 865 | "autoload": { 866 | "classmap": [ 867 | "src/" 868 | ] 869 | }, 870 | "notification-url": "https://packagist.org/downloads/", 871 | "license": [ 872 | "BSD-3-Clause" 873 | ], 874 | "authors": [ 875 | { 876 | "name": "Sebastian Bergmann", 877 | "email": "sb@sebastian-bergmann.de", 878 | "role": "lead" 879 | } 880 | ], 881 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 882 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 883 | "keywords": [ 884 | "coverage", 885 | "testing", 886 | "xunit" 887 | ], 888 | "time": "2017-04-02T07:44:40+00:00" 889 | }, 890 | { 891 | "name": "phpunit/php-file-iterator", 892 | "version": "1.4.2", 893 | "source": { 894 | "type": "git", 895 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 896 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" 897 | }, 898 | "dist": { 899 | "type": "zip", 900 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 901 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 902 | "shasum": "" 903 | }, 904 | "require": { 905 | "php": ">=5.3.3" 906 | }, 907 | "type": "library", 908 | "extra": { 909 | "branch-alias": { 910 | "dev-master": "1.4.x-dev" 911 | } 912 | }, 913 | "autoload": { 914 | "classmap": [ 915 | "src/" 916 | ] 917 | }, 918 | "notification-url": "https://packagist.org/downloads/", 919 | "license": [ 920 | "BSD-3-Clause" 921 | ], 922 | "authors": [ 923 | { 924 | "name": "Sebastian Bergmann", 925 | "email": "sb@sebastian-bergmann.de", 926 | "role": "lead" 927 | } 928 | ], 929 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 930 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 931 | "keywords": [ 932 | "filesystem", 933 | "iterator" 934 | ], 935 | "time": "2016-10-03T07:40:28+00:00" 936 | }, 937 | { 938 | "name": "phpunit/php-text-template", 939 | "version": "1.2.1", 940 | "source": { 941 | "type": "git", 942 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 943 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 944 | }, 945 | "dist": { 946 | "type": "zip", 947 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 948 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 949 | "shasum": "" 950 | }, 951 | "require": { 952 | "php": ">=5.3.3" 953 | }, 954 | "type": "library", 955 | "autoload": { 956 | "classmap": [ 957 | "src/" 958 | ] 959 | }, 960 | "notification-url": "https://packagist.org/downloads/", 961 | "license": [ 962 | "BSD-3-Clause" 963 | ], 964 | "authors": [ 965 | { 966 | "name": "Sebastian Bergmann", 967 | "email": "sebastian@phpunit.de", 968 | "role": "lead" 969 | } 970 | ], 971 | "description": "Simple template engine.", 972 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 973 | "keywords": [ 974 | "template" 975 | ], 976 | "time": "2015-06-21T13:50:34+00:00" 977 | }, 978 | { 979 | "name": "phpunit/php-timer", 980 | "version": "1.0.9", 981 | "source": { 982 | "type": "git", 983 | "url": "https://github.com/sebastianbergmann/php-timer.git", 984 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 985 | }, 986 | "dist": { 987 | "type": "zip", 988 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 989 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 990 | "shasum": "" 991 | }, 992 | "require": { 993 | "php": "^5.3.3 || ^7.0" 994 | }, 995 | "require-dev": { 996 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 997 | }, 998 | "type": "library", 999 | "extra": { 1000 | "branch-alias": { 1001 | "dev-master": "1.0-dev" 1002 | } 1003 | }, 1004 | "autoload": { 1005 | "classmap": [ 1006 | "src/" 1007 | ] 1008 | }, 1009 | "notification-url": "https://packagist.org/downloads/", 1010 | "license": [ 1011 | "BSD-3-Clause" 1012 | ], 1013 | "authors": [ 1014 | { 1015 | "name": "Sebastian Bergmann", 1016 | "email": "sb@sebastian-bergmann.de", 1017 | "role": "lead" 1018 | } 1019 | ], 1020 | "description": "Utility class for timing", 1021 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1022 | "keywords": [ 1023 | "timer" 1024 | ], 1025 | "time": "2017-02-26T11:10:40+00:00" 1026 | }, 1027 | { 1028 | "name": "phpunit/php-token-stream", 1029 | "version": "1.4.11", 1030 | "source": { 1031 | "type": "git", 1032 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 1033 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" 1034 | }, 1035 | "dist": { 1036 | "type": "zip", 1037 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", 1038 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", 1039 | "shasum": "" 1040 | }, 1041 | "require": { 1042 | "ext-tokenizer": "*", 1043 | "php": ">=5.3.3" 1044 | }, 1045 | "require-dev": { 1046 | "phpunit/phpunit": "~4.2" 1047 | }, 1048 | "type": "library", 1049 | "extra": { 1050 | "branch-alias": { 1051 | "dev-master": "1.4-dev" 1052 | } 1053 | }, 1054 | "autoload": { 1055 | "classmap": [ 1056 | "src/" 1057 | ] 1058 | }, 1059 | "notification-url": "https://packagist.org/downloads/", 1060 | "license": [ 1061 | "BSD-3-Clause" 1062 | ], 1063 | "authors": [ 1064 | { 1065 | "name": "Sebastian Bergmann", 1066 | "email": "sebastian@phpunit.de" 1067 | } 1068 | ], 1069 | "description": "Wrapper around PHP's tokenizer extension.", 1070 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 1071 | "keywords": [ 1072 | "tokenizer" 1073 | ], 1074 | "time": "2017-02-27T10:12:30+00:00" 1075 | }, 1076 | { 1077 | "name": "phpunit/phpunit", 1078 | "version": "5.7.21", 1079 | "source": { 1080 | "type": "git", 1081 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1082 | "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db" 1083 | }, 1084 | "dist": { 1085 | "type": "zip", 1086 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b91adfb64264ddec5a2dee9851f354aa66327db", 1087 | "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db", 1088 | "shasum": "" 1089 | }, 1090 | "require": { 1091 | "ext-dom": "*", 1092 | "ext-json": "*", 1093 | "ext-libxml": "*", 1094 | "ext-mbstring": "*", 1095 | "ext-xml": "*", 1096 | "myclabs/deep-copy": "~1.3", 1097 | "php": "^5.6 || ^7.0", 1098 | "phpspec/prophecy": "^1.6.2", 1099 | "phpunit/php-code-coverage": "^4.0.4", 1100 | "phpunit/php-file-iterator": "~1.4", 1101 | "phpunit/php-text-template": "~1.2", 1102 | "phpunit/php-timer": "^1.0.6", 1103 | "phpunit/phpunit-mock-objects": "^3.2", 1104 | "sebastian/comparator": "^1.2.4", 1105 | "sebastian/diff": "^1.4.3", 1106 | "sebastian/environment": "^1.3.4 || ^2.0", 1107 | "sebastian/exporter": "~2.0", 1108 | "sebastian/global-state": "^1.1", 1109 | "sebastian/object-enumerator": "~2.0", 1110 | "sebastian/resource-operations": "~1.0", 1111 | "sebastian/version": "~1.0.3|~2.0", 1112 | "symfony/yaml": "~2.1|~3.0" 1113 | }, 1114 | "conflict": { 1115 | "phpdocumentor/reflection-docblock": "3.0.2" 1116 | }, 1117 | "require-dev": { 1118 | "ext-pdo": "*" 1119 | }, 1120 | "suggest": { 1121 | "ext-xdebug": "*", 1122 | "phpunit/php-invoker": "~1.1" 1123 | }, 1124 | "bin": [ 1125 | "phpunit" 1126 | ], 1127 | "type": "library", 1128 | "extra": { 1129 | "branch-alias": { 1130 | "dev-master": "5.7.x-dev" 1131 | } 1132 | }, 1133 | "autoload": { 1134 | "classmap": [ 1135 | "src/" 1136 | ] 1137 | }, 1138 | "notification-url": "https://packagist.org/downloads/", 1139 | "license": [ 1140 | "BSD-3-Clause" 1141 | ], 1142 | "authors": [ 1143 | { 1144 | "name": "Sebastian Bergmann", 1145 | "email": "sebastian@phpunit.de", 1146 | "role": "lead" 1147 | } 1148 | ], 1149 | "description": "The PHP Unit Testing framework.", 1150 | "homepage": "https://phpunit.de/", 1151 | "keywords": [ 1152 | "phpunit", 1153 | "testing", 1154 | "xunit" 1155 | ], 1156 | "time": "2017-06-21T08:11:54+00:00" 1157 | }, 1158 | { 1159 | "name": "phpunit/phpunit-mock-objects", 1160 | "version": "3.4.3", 1161 | "source": { 1162 | "type": "git", 1163 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 1164 | "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24" 1165 | }, 1166 | "dist": { 1167 | "type": "zip", 1168 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", 1169 | "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", 1170 | "shasum": "" 1171 | }, 1172 | "require": { 1173 | "doctrine/instantiator": "^1.0.2", 1174 | "php": "^5.6 || ^7.0", 1175 | "phpunit/php-text-template": "^1.2", 1176 | "sebastian/exporter": "^1.2 || ^2.0" 1177 | }, 1178 | "conflict": { 1179 | "phpunit/phpunit": "<5.4.0" 1180 | }, 1181 | "require-dev": { 1182 | "phpunit/phpunit": "^5.4" 1183 | }, 1184 | "suggest": { 1185 | "ext-soap": "*" 1186 | }, 1187 | "type": "library", 1188 | "extra": { 1189 | "branch-alias": { 1190 | "dev-master": "3.2.x-dev" 1191 | } 1192 | }, 1193 | "autoload": { 1194 | "classmap": [ 1195 | "src/" 1196 | ] 1197 | }, 1198 | "notification-url": "https://packagist.org/downloads/", 1199 | "license": [ 1200 | "BSD-3-Clause" 1201 | ], 1202 | "authors": [ 1203 | { 1204 | "name": "Sebastian Bergmann", 1205 | "email": "sb@sebastian-bergmann.de", 1206 | "role": "lead" 1207 | } 1208 | ], 1209 | "description": "Mock Object library for PHPUnit", 1210 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 1211 | "keywords": [ 1212 | "mock", 1213 | "xunit" 1214 | ], 1215 | "time": "2016-12-08T20:27:08+00:00" 1216 | }, 1217 | { 1218 | "name": "sebastian/code-unit-reverse-lookup", 1219 | "version": "1.0.1", 1220 | "source": { 1221 | "type": "git", 1222 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1223 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 1224 | }, 1225 | "dist": { 1226 | "type": "zip", 1227 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1228 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1229 | "shasum": "" 1230 | }, 1231 | "require": { 1232 | "php": "^5.6 || ^7.0" 1233 | }, 1234 | "require-dev": { 1235 | "phpunit/phpunit": "^5.7 || ^6.0" 1236 | }, 1237 | "type": "library", 1238 | "extra": { 1239 | "branch-alias": { 1240 | "dev-master": "1.0.x-dev" 1241 | } 1242 | }, 1243 | "autoload": { 1244 | "classmap": [ 1245 | "src/" 1246 | ] 1247 | }, 1248 | "notification-url": "https://packagist.org/downloads/", 1249 | "license": [ 1250 | "BSD-3-Clause" 1251 | ], 1252 | "authors": [ 1253 | { 1254 | "name": "Sebastian Bergmann", 1255 | "email": "sebastian@phpunit.de" 1256 | } 1257 | ], 1258 | "description": "Looks up which function or method a line of code belongs to", 1259 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1260 | "time": "2017-03-04T06:30:41+00:00" 1261 | }, 1262 | { 1263 | "name": "sebastian/comparator", 1264 | "version": "1.2.4", 1265 | "source": { 1266 | "type": "git", 1267 | "url": "https://github.com/sebastianbergmann/comparator.git", 1268 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" 1269 | }, 1270 | "dist": { 1271 | "type": "zip", 1272 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1273 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1274 | "shasum": "" 1275 | }, 1276 | "require": { 1277 | "php": ">=5.3.3", 1278 | "sebastian/diff": "~1.2", 1279 | "sebastian/exporter": "~1.2 || ~2.0" 1280 | }, 1281 | "require-dev": { 1282 | "phpunit/phpunit": "~4.4" 1283 | }, 1284 | "type": "library", 1285 | "extra": { 1286 | "branch-alias": { 1287 | "dev-master": "1.2.x-dev" 1288 | } 1289 | }, 1290 | "autoload": { 1291 | "classmap": [ 1292 | "src/" 1293 | ] 1294 | }, 1295 | "notification-url": "https://packagist.org/downloads/", 1296 | "license": [ 1297 | "BSD-3-Clause" 1298 | ], 1299 | "authors": [ 1300 | { 1301 | "name": "Jeff Welch", 1302 | "email": "whatthejeff@gmail.com" 1303 | }, 1304 | { 1305 | "name": "Volker Dusch", 1306 | "email": "github@wallbash.com" 1307 | }, 1308 | { 1309 | "name": "Bernhard Schussek", 1310 | "email": "bschussek@2bepublished.at" 1311 | }, 1312 | { 1313 | "name": "Sebastian Bergmann", 1314 | "email": "sebastian@phpunit.de" 1315 | } 1316 | ], 1317 | "description": "Provides the functionality to compare PHP values for equality", 1318 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1319 | "keywords": [ 1320 | "comparator", 1321 | "compare", 1322 | "equality" 1323 | ], 1324 | "time": "2017-01-29T09:50:25+00:00" 1325 | }, 1326 | { 1327 | "name": "sebastian/diff", 1328 | "version": "1.4.3", 1329 | "source": { 1330 | "type": "git", 1331 | "url": "https://github.com/sebastianbergmann/diff.git", 1332 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" 1333 | }, 1334 | "dist": { 1335 | "type": "zip", 1336 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", 1337 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", 1338 | "shasum": "" 1339 | }, 1340 | "require": { 1341 | "php": "^5.3.3 || ^7.0" 1342 | }, 1343 | "require-dev": { 1344 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 1345 | }, 1346 | "type": "library", 1347 | "extra": { 1348 | "branch-alias": { 1349 | "dev-master": "1.4-dev" 1350 | } 1351 | }, 1352 | "autoload": { 1353 | "classmap": [ 1354 | "src/" 1355 | ] 1356 | }, 1357 | "notification-url": "https://packagist.org/downloads/", 1358 | "license": [ 1359 | "BSD-3-Clause" 1360 | ], 1361 | "authors": [ 1362 | { 1363 | "name": "Kore Nordmann", 1364 | "email": "mail@kore-nordmann.de" 1365 | }, 1366 | { 1367 | "name": "Sebastian Bergmann", 1368 | "email": "sebastian@phpunit.de" 1369 | } 1370 | ], 1371 | "description": "Diff implementation", 1372 | "homepage": "https://github.com/sebastianbergmann/diff", 1373 | "keywords": [ 1374 | "diff" 1375 | ], 1376 | "time": "2017-05-22T07:24:03+00:00" 1377 | }, 1378 | { 1379 | "name": "sebastian/environment", 1380 | "version": "2.0.0", 1381 | "source": { 1382 | "type": "git", 1383 | "url": "https://github.com/sebastianbergmann/environment.git", 1384 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" 1385 | }, 1386 | "dist": { 1387 | "type": "zip", 1388 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 1389 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 1390 | "shasum": "" 1391 | }, 1392 | "require": { 1393 | "php": "^5.6 || ^7.0" 1394 | }, 1395 | "require-dev": { 1396 | "phpunit/phpunit": "^5.0" 1397 | }, 1398 | "type": "library", 1399 | "extra": { 1400 | "branch-alias": { 1401 | "dev-master": "2.0.x-dev" 1402 | } 1403 | }, 1404 | "autoload": { 1405 | "classmap": [ 1406 | "src/" 1407 | ] 1408 | }, 1409 | "notification-url": "https://packagist.org/downloads/", 1410 | "license": [ 1411 | "BSD-3-Clause" 1412 | ], 1413 | "authors": [ 1414 | { 1415 | "name": "Sebastian Bergmann", 1416 | "email": "sebastian@phpunit.de" 1417 | } 1418 | ], 1419 | "description": "Provides functionality to handle HHVM/PHP environments", 1420 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1421 | "keywords": [ 1422 | "Xdebug", 1423 | "environment", 1424 | "hhvm" 1425 | ], 1426 | "time": "2016-11-26T07:53:53+00:00" 1427 | }, 1428 | { 1429 | "name": "sebastian/exporter", 1430 | "version": "2.0.0", 1431 | "source": { 1432 | "type": "git", 1433 | "url": "https://github.com/sebastianbergmann/exporter.git", 1434 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" 1435 | }, 1436 | "dist": { 1437 | "type": "zip", 1438 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 1439 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 1440 | "shasum": "" 1441 | }, 1442 | "require": { 1443 | "php": ">=5.3.3", 1444 | "sebastian/recursion-context": "~2.0" 1445 | }, 1446 | "require-dev": { 1447 | "ext-mbstring": "*", 1448 | "phpunit/phpunit": "~4.4" 1449 | }, 1450 | "type": "library", 1451 | "extra": { 1452 | "branch-alias": { 1453 | "dev-master": "2.0.x-dev" 1454 | } 1455 | }, 1456 | "autoload": { 1457 | "classmap": [ 1458 | "src/" 1459 | ] 1460 | }, 1461 | "notification-url": "https://packagist.org/downloads/", 1462 | "license": [ 1463 | "BSD-3-Clause" 1464 | ], 1465 | "authors": [ 1466 | { 1467 | "name": "Jeff Welch", 1468 | "email": "whatthejeff@gmail.com" 1469 | }, 1470 | { 1471 | "name": "Volker Dusch", 1472 | "email": "github@wallbash.com" 1473 | }, 1474 | { 1475 | "name": "Bernhard Schussek", 1476 | "email": "bschussek@2bepublished.at" 1477 | }, 1478 | { 1479 | "name": "Sebastian Bergmann", 1480 | "email": "sebastian@phpunit.de" 1481 | }, 1482 | { 1483 | "name": "Adam Harvey", 1484 | "email": "aharvey@php.net" 1485 | } 1486 | ], 1487 | "description": "Provides the functionality to export PHP variables for visualization", 1488 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1489 | "keywords": [ 1490 | "export", 1491 | "exporter" 1492 | ], 1493 | "time": "2016-11-19T08:54:04+00:00" 1494 | }, 1495 | { 1496 | "name": "sebastian/global-state", 1497 | "version": "1.1.1", 1498 | "source": { 1499 | "type": "git", 1500 | "url": "https://github.com/sebastianbergmann/global-state.git", 1501 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1502 | }, 1503 | "dist": { 1504 | "type": "zip", 1505 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1506 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1507 | "shasum": "" 1508 | }, 1509 | "require": { 1510 | "php": ">=5.3.3" 1511 | }, 1512 | "require-dev": { 1513 | "phpunit/phpunit": "~4.2" 1514 | }, 1515 | "suggest": { 1516 | "ext-uopz": "*" 1517 | }, 1518 | "type": "library", 1519 | "extra": { 1520 | "branch-alias": { 1521 | "dev-master": "1.0-dev" 1522 | } 1523 | }, 1524 | "autoload": { 1525 | "classmap": [ 1526 | "src/" 1527 | ] 1528 | }, 1529 | "notification-url": "https://packagist.org/downloads/", 1530 | "license": [ 1531 | "BSD-3-Clause" 1532 | ], 1533 | "authors": [ 1534 | { 1535 | "name": "Sebastian Bergmann", 1536 | "email": "sebastian@phpunit.de" 1537 | } 1538 | ], 1539 | "description": "Snapshotting of global state", 1540 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1541 | "keywords": [ 1542 | "global state" 1543 | ], 1544 | "time": "2015-10-12T03:26:01+00:00" 1545 | }, 1546 | { 1547 | "name": "sebastian/object-enumerator", 1548 | "version": "2.0.1", 1549 | "source": { 1550 | "type": "git", 1551 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1552 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" 1553 | }, 1554 | "dist": { 1555 | "type": "zip", 1556 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", 1557 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", 1558 | "shasum": "" 1559 | }, 1560 | "require": { 1561 | "php": ">=5.6", 1562 | "sebastian/recursion-context": "~2.0" 1563 | }, 1564 | "require-dev": { 1565 | "phpunit/phpunit": "~5" 1566 | }, 1567 | "type": "library", 1568 | "extra": { 1569 | "branch-alias": { 1570 | "dev-master": "2.0.x-dev" 1571 | } 1572 | }, 1573 | "autoload": { 1574 | "classmap": [ 1575 | "src/" 1576 | ] 1577 | }, 1578 | "notification-url": "https://packagist.org/downloads/", 1579 | "license": [ 1580 | "BSD-3-Clause" 1581 | ], 1582 | "authors": [ 1583 | { 1584 | "name": "Sebastian Bergmann", 1585 | "email": "sebastian@phpunit.de" 1586 | } 1587 | ], 1588 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1589 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1590 | "time": "2017-02-18T15:18:39+00:00" 1591 | }, 1592 | { 1593 | "name": "sebastian/recursion-context", 1594 | "version": "2.0.0", 1595 | "source": { 1596 | "type": "git", 1597 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1598 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" 1599 | }, 1600 | "dist": { 1601 | "type": "zip", 1602 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1603 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1604 | "shasum": "" 1605 | }, 1606 | "require": { 1607 | "php": ">=5.3.3" 1608 | }, 1609 | "require-dev": { 1610 | "phpunit/phpunit": "~4.4" 1611 | }, 1612 | "type": "library", 1613 | "extra": { 1614 | "branch-alias": { 1615 | "dev-master": "2.0.x-dev" 1616 | } 1617 | }, 1618 | "autoload": { 1619 | "classmap": [ 1620 | "src/" 1621 | ] 1622 | }, 1623 | "notification-url": "https://packagist.org/downloads/", 1624 | "license": [ 1625 | "BSD-3-Clause" 1626 | ], 1627 | "authors": [ 1628 | { 1629 | "name": "Jeff Welch", 1630 | "email": "whatthejeff@gmail.com" 1631 | }, 1632 | { 1633 | "name": "Sebastian Bergmann", 1634 | "email": "sebastian@phpunit.de" 1635 | }, 1636 | { 1637 | "name": "Adam Harvey", 1638 | "email": "aharvey@php.net" 1639 | } 1640 | ], 1641 | "description": "Provides functionality to recursively process PHP variables", 1642 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1643 | "time": "2016-11-19T07:33:16+00:00" 1644 | }, 1645 | { 1646 | "name": "sebastian/resource-operations", 1647 | "version": "1.0.0", 1648 | "source": { 1649 | "type": "git", 1650 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1651 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" 1652 | }, 1653 | "dist": { 1654 | "type": "zip", 1655 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1656 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1657 | "shasum": "" 1658 | }, 1659 | "require": { 1660 | "php": ">=5.6.0" 1661 | }, 1662 | "type": "library", 1663 | "extra": { 1664 | "branch-alias": { 1665 | "dev-master": "1.0.x-dev" 1666 | } 1667 | }, 1668 | "autoload": { 1669 | "classmap": [ 1670 | "src/" 1671 | ] 1672 | }, 1673 | "notification-url": "https://packagist.org/downloads/", 1674 | "license": [ 1675 | "BSD-3-Clause" 1676 | ], 1677 | "authors": [ 1678 | { 1679 | "name": "Sebastian Bergmann", 1680 | "email": "sebastian@phpunit.de" 1681 | } 1682 | ], 1683 | "description": "Provides a list of PHP built-in functions that operate on resources", 1684 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1685 | "time": "2015-07-28T20:34:47+00:00" 1686 | }, 1687 | { 1688 | "name": "sebastian/version", 1689 | "version": "2.0.1", 1690 | "source": { 1691 | "type": "git", 1692 | "url": "https://github.com/sebastianbergmann/version.git", 1693 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1694 | }, 1695 | "dist": { 1696 | "type": "zip", 1697 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1698 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1699 | "shasum": "" 1700 | }, 1701 | "require": { 1702 | "php": ">=5.6" 1703 | }, 1704 | "type": "library", 1705 | "extra": { 1706 | "branch-alias": { 1707 | "dev-master": "2.0.x-dev" 1708 | } 1709 | }, 1710 | "autoload": { 1711 | "classmap": [ 1712 | "src/" 1713 | ] 1714 | }, 1715 | "notification-url": "https://packagist.org/downloads/", 1716 | "license": [ 1717 | "BSD-3-Clause" 1718 | ], 1719 | "authors": [ 1720 | { 1721 | "name": "Sebastian Bergmann", 1722 | "email": "sebastian@phpunit.de", 1723 | "role": "lead" 1724 | } 1725 | ], 1726 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1727 | "homepage": "https://github.com/sebastianbergmann/version", 1728 | "time": "2016-10-03T07:35:21+00:00" 1729 | }, 1730 | { 1731 | "name": "symfony/yaml", 1732 | "version": "v3.3.2", 1733 | "source": { 1734 | "type": "git", 1735 | "url": "https://github.com/symfony/yaml.git", 1736 | "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063" 1737 | }, 1738 | "dist": { 1739 | "type": "zip", 1740 | "url": "https://api.github.com/repos/symfony/yaml/zipball/9752a30000a8ca9f4b34b5227d15d0101b96b063", 1741 | "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063", 1742 | "shasum": "" 1743 | }, 1744 | "require": { 1745 | "php": ">=5.5.9" 1746 | }, 1747 | "require-dev": { 1748 | "symfony/console": "~2.8|~3.0" 1749 | }, 1750 | "suggest": { 1751 | "symfony/console": "For validating YAML files using the lint command" 1752 | }, 1753 | "type": "library", 1754 | "extra": { 1755 | "branch-alias": { 1756 | "dev-master": "3.3-dev" 1757 | } 1758 | }, 1759 | "autoload": { 1760 | "psr-4": { 1761 | "Symfony\\Component\\Yaml\\": "" 1762 | }, 1763 | "exclude-from-classmap": [ 1764 | "/Tests/" 1765 | ] 1766 | }, 1767 | "notification-url": "https://packagist.org/downloads/", 1768 | "license": [ 1769 | "MIT" 1770 | ], 1771 | "authors": [ 1772 | { 1773 | "name": "Fabien Potencier", 1774 | "email": "fabien@symfony.com" 1775 | }, 1776 | { 1777 | "name": "Symfony Community", 1778 | "homepage": "https://symfony.com/contributors" 1779 | } 1780 | ], 1781 | "description": "Symfony Yaml Component", 1782 | "homepage": "https://symfony.com", 1783 | "time": "2017-06-02T22:05:06+00:00" 1784 | }, 1785 | { 1786 | "name": "webmozart/assert", 1787 | "version": "1.2.0", 1788 | "source": { 1789 | "type": "git", 1790 | "url": "https://github.com/webmozart/assert.git", 1791 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" 1792 | }, 1793 | "dist": { 1794 | "type": "zip", 1795 | "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", 1796 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", 1797 | "shasum": "" 1798 | }, 1799 | "require": { 1800 | "php": "^5.3.3 || ^7.0" 1801 | }, 1802 | "require-dev": { 1803 | "phpunit/phpunit": "^4.6", 1804 | "sebastian/version": "^1.0.1" 1805 | }, 1806 | "type": "library", 1807 | "extra": { 1808 | "branch-alias": { 1809 | "dev-master": "1.3-dev" 1810 | } 1811 | }, 1812 | "autoload": { 1813 | "psr-4": { 1814 | "Webmozart\\Assert\\": "src/" 1815 | } 1816 | }, 1817 | "notification-url": "https://packagist.org/downloads/", 1818 | "license": [ 1819 | "MIT" 1820 | ], 1821 | "authors": [ 1822 | { 1823 | "name": "Bernhard Schussek", 1824 | "email": "bschussek@gmail.com" 1825 | } 1826 | ], 1827 | "description": "Assertions to validate method input/output with nice error messages.", 1828 | "keywords": [ 1829 | "assert", 1830 | "check", 1831 | "validate" 1832 | ], 1833 | "time": "2016-11-23T20:04:58+00:00" 1834 | } 1835 | ], 1836 | "aliases": [], 1837 | "minimum-stability": "stable", 1838 | "stability-flags": [], 1839 | "prefer-stable": false, 1840 | "prefer-lowest": false, 1841 | "platform": { 1842 | "php": ">=7.0.0" 1843 | }, 1844 | "platform-dev": [] 1845 | } 1846 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests 6 | 7 | 8 | 9 | 10 | 11 | src/ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/OAuth2Middleware.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | final class OAuth2Middleware 13 | { 14 | /** @var AuthorizesRequests */ 15 | private $tokenService; 16 | 17 | /** @var string[] */ 18 | private $ignoredUris; 19 | 20 | /** 21 | * Middleware constructor. 22 | * @param AuthorizesRequests $tokenService 23 | * @param \string[] $ignoredUris 24 | */ 25 | public function __construct(AuthorizesRequests $tokenService, array $ignoredUris = []) 26 | { 27 | Assertion::allString($ignoredUris); 28 | $this->ignoredUris = $ignoredUris; 29 | $this->tokenService = $tokenService; 30 | } 31 | 32 | 33 | /** 34 | * @param callable $handler 35 | * @return \Closure 36 | */ 37 | public function __invoke(callable $handler): \Closure 38 | { 39 | return function (RequestInterface $request, array $options) use ($handler) { 40 | $uri = (string) $request->getUri(); 41 | if (!$this->shouldSkipAuthorizationForUri($uri)) { 42 | $request = $this->tokenService->authorize($request); 43 | } 44 | 45 | return $handler($request, $options); 46 | }; 47 | } 48 | 49 | /** 50 | * Returns whether a URL must NOT be authorized 51 | * 52 | * @param string $uri 53 | * @return bool 54 | */ 55 | private function shouldSkipAuthorizationForUri(string $uri): bool 56 | { 57 | return in_array($uri, $this->ignoredUris); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/TokenService/AbstractTokenService.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class AbstractTokenService implements AuthorizesRequests 13 | { 14 | /** @string Refresh Token grant */ 15 | const GRANT_REFRESH_TOKEN = 'refresh_token'; 16 | 17 | /** @string */ 18 | const GRANT_CLIENT_CREDENTIALS = 'client_credentials'; 19 | 20 | /** @var AbstractProvider */ 21 | private $provider; 22 | 23 | /** @var AccessToken */ 24 | private $accessToken; 25 | 26 | /** @var callable */ 27 | private $refreshTokenCallback; 28 | 29 | /** 30 | * @param AbstractProvider $provider A League OAuth2 Client Provider. 31 | * @param null|AccessToken $accessToken Provide an initial (e.g. cached/persisted) access token. 32 | * @param null|callable $refreshTokenCallback Will be called with a new AccessToken as a parameter if the Access 33 | * Token ever needs to be renewed. 34 | */ 35 | public function __construct( 36 | AbstractProvider $provider, 37 | AccessToken $accessToken = null, 38 | callable $refreshTokenCallback = null 39 | ) { 40 | if (null === $accessToken) { 41 | // an empty token that already expired, will trigger a request for a new token 42 | $accessToken = new AccessToken([ 43 | 'access_token' => '123', 44 | 'expires' => time() - 300 // expired 5 minutes ago 45 | ]); 46 | } 47 | $this->accessToken = $accessToken; 48 | 49 | $this->provider = $provider; 50 | $this->refreshTokenCallback = $refreshTokenCallback; 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | final public function authorize(RequestInterface $request): RequestInterface 57 | { 58 | if (!$this->isAuthorized($request)) { 59 | try { 60 | $hasExpired = $this->getAccessToken()->hasExpired(); 61 | } catch (\RuntimeException $e) { 62 | $hasExpired = false; // token has no "expires" data, so we assume it hasn't expired 63 | } 64 | 65 | if ($hasExpired) { 66 | $this->refreshToken(); 67 | } 68 | 69 | $request = $this->getAuthorizedRequest($request); 70 | } 71 | 72 | return $request; 73 | } 74 | 75 | /** 76 | * @return AccessToken 77 | */ 78 | final protected function getAccessToken(): AccessToken 79 | { 80 | return $this->accessToken; 81 | } 82 | 83 | /** 84 | * Refreshes an existing Access Token. Or requests a new one (using the client_credentials grant) if no token 85 | * is available to the service yet. 86 | * 87 | * @return void 88 | */ 89 | final protected function refreshToken() 90 | { 91 | $oldAccessToken = $this->accessToken; 92 | 93 | if ($this->accessToken->getRefreshToken()) { 94 | $this->accessToken = $this->provider->getAccessToken(self::GRANT_REFRESH_TOKEN, [ 95 | 'refresh_token' => $this->accessToken->getRefreshToken(), 96 | ]); 97 | } else { 98 | // request a completely new access token 99 | $this->accessToken = $this->requestAccessToken(); 100 | } 101 | 102 | if ($this->refreshTokenCallback) { 103 | call_user_func($this->refreshTokenCallback, $this->accessToken, $oldAccessToken); 104 | } 105 | } 106 | 107 | /** 108 | * Request a new Access Token from the provider 109 | * 110 | * @return AccessToken 111 | */ 112 | abstract protected function requestAccessToken(): AccessToken; 113 | 114 | /** 115 | * Returns an authorized copy of the request. Only gets called when necessary (i.e. not if the request is already 116 | * authorized), and always with a valid (fresh) Access Token. However, it SHOULD be idempotent. 117 | * 118 | * @param RequestInterface $request An unauthorized request 119 | * 120 | * @return RequestInterface An authorized copy of the request 121 | */ 122 | abstract protected function getAuthorizedRequest(RequestInterface $request): RequestInterface; 123 | 124 | /** 125 | * @return AbstractProvider 126 | */ 127 | final protected function getProvider(): AbstractProvider 128 | { 129 | return $this->provider; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/TokenService/AuthorizesRequests.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface AuthorizesRequests 11 | { 12 | /** 13 | * Checks whether a request is authorized with this service's Access Token. 14 | * 15 | * @param RequestInterface $request 16 | * @return bool 17 | */ 18 | public function isAuthorized(RequestInterface $request): bool; 19 | 20 | /** 21 | * Authorizes a request using an OAuth2 Access Token. SHOULD be idempotent. 22 | * 23 | * @param RequestInterface $request 24 | * @return RequestInterface 25 | */ 26 | public function authorize(RequestInterface $request): RequestInterface; 27 | } 28 | -------------------------------------------------------------------------------- /src/TokenService/Bearer.php: -------------------------------------------------------------------------------- 1 | 12 | * 13 | * @see https://tools.ietf.org/html/rfc6750 14 | */ 15 | final class Bearer extends AbstractTokenService 16 | { 17 | /** @string Name of the authorization header injected into the request */ 18 | const HEADER_AUTHORIZATION = 'Authorization'; 19 | 20 | /** @string Access Token type */ 21 | const TOKEN_TYPE = 'Bearer'; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public function isAuthorized(RequestInterface $request): bool 27 | { 28 | return $request->hasHeader(self::HEADER_AUTHORIZATION); 29 | } 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | protected function requestAccessToken(): AccessToken 35 | { 36 | return $this->getProvider()->getAccessToken(self::GRANT_CLIENT_CREDENTIALS); 37 | } 38 | 39 | /** 40 | * Returns an authorized copy of the request. Only gets called when necessary (i.e. not if the request is already 41 | * authorized), and always with a valid (fresh) Access Token. However, it SHOULD be idempotent. 42 | * 43 | * @param RequestInterface $request An unauthorized request 44 | * 45 | * @return RequestInterface An authorized copy of the request 46 | */ 47 | protected function getAuthorizedRequest(RequestInterface $request): RequestInterface 48 | { 49 | /** @var RequestInterface $request */ 50 | $request = $request->withHeader( 51 | self::HEADER_AUTHORIZATION, 52 | $this->getAuthorizationString() 53 | ); 54 | 55 | return $request; 56 | } 57 | 58 | /** 59 | * @return string 60 | */ 61 | private function getAuthorizationString(): string 62 | { 63 | return self::TOKEN_TYPE . ' ' . $this->getAccessToken()->getToken(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/OAuth2MiddlewareTest.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class OAuth2MiddlewareTest extends TestCase 16 | { 17 | /** @var AuthorizesRequests|m\Mock */ 18 | private $tokenService; 19 | private $proxyHandler; 20 | 21 | public function setUp() 22 | { 23 | $this->tokenService = m::mock(AuthorizesRequests::class); 24 | $this->proxyHandler = function ($request) { 25 | return $request; 26 | }; 27 | } 28 | 29 | public function testConstructor() 30 | { 31 | new OAuth2Middleware($this->tokenService, []); 32 | } 33 | 34 | public function testConstructorWithInvalidIgnoredUri() 35 | { 36 | $this->setExpectedException(InvalidArgumentException::class); 37 | $invalidIgnoredUris = [123]; // not a string 38 | new OAuth2Middleware($this->tokenService, $invalidIgnoredUris); 39 | } 40 | 41 | public function testShouldBehaveLikeMiddleware() 42 | { 43 | $instance = new OAuth2Middleware($this->tokenService, []); 44 | $this->assertTrue(is_callable($instance)); 45 | 46 | $middleware = $instance($this->proxyHandler); 47 | $this->assertTrue(is_callable($middleware)); 48 | } 49 | 50 | public function testShouldSkipsIgnoredUris() 51 | { 52 | $instance = new OAuth2Middleware($this->tokenService, ['/skip_uri']); 53 | $middleware = $instance($this->proxyHandler); 54 | $request = new Request('GET', '/skip_uri'); 55 | 56 | $this->tokenService->shouldNotHaveReceived('authorize'); 57 | 58 | $middleware($request, []); 59 | } 60 | 61 | public function testShouldAuthorizeRequests() 62 | { 63 | $instance = new OAuth2Middleware($this->tokenService, ['/skip_uri']); 64 | $middleware = $instance($this->proxyHandler); 65 | $request = new Request('GET', '/secured/should_authorize'); 66 | 67 | $this->tokenService 68 | ->shouldReceive('authorize') 69 | ->with($request) 70 | ->andReturn($request->withHeader('Authorization', 'Bearer 123')); 71 | 72 | /** @var RequestInterface $response */ 73 | $response = $middleware($request, []); 74 | 75 | $this->assertTrue($response->hasHeader('Authorization')); 76 | $this->assertEquals('Bearer 123', $response->getHeaderLine('Authorization')); 77 | } 78 | 79 | public function tearDown() 80 | { 81 | parent::tearDown(); 82 | $this->proxyHandler = null; 83 | $this->tokenService = null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class TestCase extends \PHPUnit_Framework_TestCase 11 | { 12 | public function tearDown() 13 | { 14 | parent::tearDown(); 15 | m::close(); 16 | } 17 | 18 | /** 19 | * invoke 20 | * @param $object 21 | * @param $method 22 | * @param array $args 23 | * @return mixed 24 | */ 25 | protected function invoke($object, $method, array $args = []) 26 | { 27 | $m = new \ReflectionMethod($object, $method); 28 | $m->setAccessible(true); 29 | return $m->invokeArgs($object, $args); 30 | } 31 | 32 | /** 33 | * getPropVal 34 | * @param $object 35 | * @param $name 36 | * @return mixed 37 | */ 38 | protected function getPropVal($object, $name) 39 | { 40 | $prop = new \ReflectionProperty($object, $name); 41 | $prop->setAccessible(true); 42 | return $prop->getValue($object); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/TokenService/BearerTest.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class BearerTest extends TestCase 19 | { 20 | /** @var AbstractProvider|m\Mock */ 21 | private $provider; 22 | 23 | /** 24 | * setUp 25 | * @return void 26 | */ 27 | public function setUp() 28 | { 29 | $this->provider = m::mock(AbstractProvider::class); 30 | } 31 | 32 | public function testConstructorWithoutAccessToken() 33 | { 34 | $instance = new Bearer($this->provider); 35 | 36 | $method = new \ReflectionMethod(AbstractTokenService::class, 'getAccessToken'); 37 | $method->setAccessible(true); 38 | 39 | // test that a dummy token was created 40 | $token = $method->invoke($instance); 41 | $this->assertInstanceOf(AccessToken::class, $token); 42 | /** @var AccessToken $token */ 43 | $this->assertTrue($token->hasExpired()); 44 | } 45 | 46 | public function testConstructorWithAccessToken() 47 | { 48 | $token = new AccessToken(['access_token' => '123']); 49 | $instance = new Bearer($this->provider, $token); 50 | $method = new \ReflectionMethod(AbstractTokenService::class, 'getAccessToken'); 51 | $method->setAccessible(true); 52 | 53 | // test that a dummy token was created 54 | $result = $method->invoke($instance); 55 | 56 | $this->assertSame($token, $result); 57 | } 58 | 59 | public function testShouldRequestNewAccessTokenIfNoToken() 60 | { 61 | $accessToken = m::mock(AccessToken::class, ['getToken' => 'abc']); 62 | $this->provider->shouldReceive('getAccessToken') 63 | ->once() 64 | ->with('client_credentials') 65 | ->andReturn($accessToken); 66 | 67 | $instance = new Bearer($this->provider); // with an expired token 68 | 69 | $request = new Request('GET', '/secured/resource'); 70 | $instance->authorize($request); 71 | 72 | $method = new \ReflectionMethod(AbstractTokenService::class, 'getAccessToken'); 73 | $method->setAccessible(true); 74 | 75 | // test that the token was returned 76 | $result = $method->invoke($instance); 77 | 78 | $this->assertSame($accessToken, $result); 79 | } 80 | 81 | /** 82 | * should_skip_requests_with_authorization_header 83 | * @return void 84 | * 85 | * @test 86 | */ 87 | public function testShouldSkipAuthorizedRequests() 88 | { 89 | $instance = new Bearer($this->provider); // with an expired token 90 | $request = new Request('GET', '/secured/resource', ['Authorization' => 'Bearer 123']); 91 | 92 | $result = $instance->authorize($request); 93 | 94 | $this->assertSame($request, $result); 95 | $this->provider->shouldNotHaveReceived('getAccessToken'); 96 | } 97 | 98 | public function testShouldRefreshTokenIfExpired() 99 | { 100 | $pastTime = time() - 500; 101 | $oldToken = new AccessToken(['access_token' => '123', 'expires' => $pastTime, 'refresh_token' => 'xyz',]); 102 | $newToken = new AccessToken(['access_token' => 'abc']); 103 | 104 | $this->provider 105 | ->shouldReceive('getAccessToken') 106 | ->once() 107 | ->with('refresh_token', ['refresh_token' => 'xyz']) 108 | ->andReturn($newToken); 109 | 110 | $instance = new Bearer($this->provider, $oldToken); 111 | $request = new Request('GET', 'http://foo.bar/baz'); 112 | 113 | $result = $instance->authorize($request); 114 | 115 | //$result = $this->invoke($instance, 'authorizeRequest', [$request]); 116 | $this->assertResultAuthorizedWithToken($result, $newToken); 117 | } 118 | 119 | /** 120 | * @param RequestInterface $result 121 | * @param AccessToken $accessToken 122 | * @return void 123 | */ 124 | private function assertResultAuthorizedWithToken(RequestInterface $result, AccessToken $accessToken) 125 | { 126 | $this->assertTrue($result->hasHeader(Bearer::HEADER_AUTHORIZATION)); 127 | $this->assertContains(Bearer::TOKEN_TYPE . ' ' . $accessToken->getToken(), 128 | $result->getHeader(Bearer::HEADER_AUTHORIZATION)); 129 | } 130 | 131 | public function testShouldNotRequestNewAccessTokenIfTokenHasNoExpiration() 132 | { 133 | $validToken = new AccessToken(['access_token' => '123']); 134 | 135 | $this->provider->shouldNotReceive('getAccessToken'); 136 | 137 | $instance = new Bearer($this->provider, $validToken); 138 | $request = new Request('GET', 'http://foo.bar/baz'); 139 | $result = $instance->authorize($request); 140 | 141 | $this->assertResultAuthorizedWithToken($result, $validToken); 142 | } 143 | 144 | public function testShouldNotRefreshTokenIfStillValid() 145 | { 146 | $validToken = new AccessToken(['access_token' => '123', 'expires_in' => 300]); 147 | 148 | $this->provider->shouldNotReceive('getAccessToken'); 149 | 150 | $instance = new Bearer($this->provider, $validToken); 151 | $request = new Request('GET', 'http://foo.bar/baz'); 152 | $result = $instance->authorize($request); 153 | 154 | $this->assertResultAuthorizedWithToken($result, $validToken); 155 | } 156 | 157 | public function testShouldInvokeCallbackIfTokenRenewed() 158 | { 159 | $oldToken = new AccessToken(['access_token' => 'oldie', 'expires' => time() - 300]); 160 | $newToken = new AccessToken(['access_token' => '123']); 161 | $this->provider->shouldReceive('getAccessToken') 162 | ->once() 163 | ->andReturn($newToken); 164 | 165 | // the callback that we're testing 166 | $callbackCalled = false; 167 | $tokenCallback = function (AccessToken $newTokenActual, AccessToken $oldTokenActual = null) 168 | use ($newToken, $oldToken, &$callbackCalled) { 169 | $callbackCalled = true; 170 | $this->assertSame($newTokenActual, $newToken); 171 | $this->assertSame($oldToken, $oldTokenActual); 172 | }; 173 | 174 | $instance = new Bearer($this->provider, $oldToken, $tokenCallback); 175 | $request = new Request('GET', 'http://foo.bar/baz'); 176 | 177 | $result = $instance->authorize($request); 178 | 179 | $this->assertTrue($callbackCalled); 180 | $this->assertResultAuthorizedWithToken($result, $newToken); 181 | } 182 | } 183 | --------------------------------------------------------------------------------