├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _config.yml ├── composer.json ├── composer.lock ├── docs ├── exception-handling │ └── index.md ├── json-api-server │ └── index.md └── request-handler │ └── index.md ├── phpunit.xml ├── src ├── JsonApiServer.php └── RequestHandler │ ├── NoRelationshipFetchTrait.php │ ├── NoRelationshipModificationTrait.php │ ├── NoResourceDeletionTrait.php │ ├── NoResourceFetchTrait.php │ ├── NoResourceModificationTrait.php │ └── RequestHandlerInterface.php └── tests └── JsonApiServerTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /build/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.2 5 | 6 | before_script: 7 | - composer install 8 | 9 | script: php vendor/bin/phpunit --coverage-text 10 | 11 | notifications: 12 | email: 13 | - bogomolov@eosnewmedia.de 14 | - marien@eosnewmedia.de 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | 3 | ### 3.1.0 4 | * changed "set included" to "merge included" 5 | 6 | ### 3.0.0 7 | * removed pagination and moved it to json api common 8 | * removed special requests and moved it to json api common 9 | * removed special requests and moved it to json api common 10 | * removed (mostly) unused concept of request providers 11 | * removed request handler chain 12 | * removed "seperated save traits" and moved "separted save methods" to request handler interface 13 | * simplified json api server class 14 | * removed guzzle prs dependency 15 | * removed psr 7 request/response dependency for json api server request and response (of course still possible) 16 | * removed request and (http)response creation from json api server for more flexibility 17 | * require request handlers to create full responses (more flexibility) 18 | * removed \*Only-Traits and implemented No\*-Traits instead 19 | * removed dependency psr/log 20 | * updated dependency enm/json-api-common to ^3.0 21 | * removed request handler registry and moved registry into json api server 22 | 23 | ### 2.3.0 24 | * added `Enm\JsonApi\Server\RequestHandler\FetchRelationshipTrait` 25 | 26 | ### 2.2.0 27 | * added `Enm\JsonApi\Server\Pagination\PaginationLinkGeneratorInterface` 28 | * added `Enm\JsonApi\Server\Pagination\OffsetPaginationLinkGenerator` 29 | * added `Enm\JsonApi\Server\Pagination\PaginationTrait` 30 | 31 | ### 2.1.0 32 | * changed method signature of `Enm\JsonApi\Server\JsonApiServer::handleException` to public 33 | 34 | ### 2.0.0 35 | * renamed namespace `Enm\JsonApi\Server\Provider` to `Enm\JsonApi\Server\ResourceProvider` 36 | * removed method `findRelationship` from `Enm\JsonApi\Server\ResourceProvider\ResourceProviderInterface` 37 | * changed signature of method `findResource` from `Enm\JsonApi\Server\ResourceProvider\ResourceProviderInterface` 38 | * changed signature of method `findResources` from `Enm\JsonApi\Server\ResourceProvider\ResourceProviderInterface` 39 | * changed signature of method `createResource` from `Enm\JsonApi\Server\ResourceProvider\ResourceProviderInterface` 40 | * changed signature of method `patchResource` from `Enm\JsonApi\Server\ResourceProvider\ResourceProviderInterface` 41 | * changed signature of method `deleteResource` from `Enm\JsonApi\Server\ResourceProvider\ResourceProviderInterface` 42 | * removed class `Enm\JsonApi\Server\Provider\AbstractImmutableResourceProvider` 43 | * removed class `Enm\JsonApi\Server\Provider\AbstractResourceProvider` 44 | * removed class `Enm\JsonApi\Server\Provider\ResourceProviderCollection` 45 | * removed interface `Enm\JsonApi\Server\Provider\ResourceProviderRegistryInterface` 46 | * removed class `Enm\JsonApi\Server\Provider\ResourceProviderRegistry` 47 | * removed interface `Enm\JsonApi\Server\Provider\ResourceProviderRegistryAwareInterface` 48 | * removed class `Enm\JsonApi\Server\Provider\ResourceProviderRegistryAwareTrait` 49 | * added trait `Enm\JsonApi\Server\ResourceProvider\FetchOnlyTrait` 50 | * added interface `Enm\JsonApi\Server\RequestHandler\RequestHandlerInterface` 51 | * added class `Enm\JsonApi\Server\RequestHandler\RequestHandlerRegistry` 52 | * added class `Enm\JsonApi\Server\RequestHandler\RequestHandlerChain` 53 | * added class `Enm\JsonApi\Server\RequestHandler\ResourceProviderRequestHandler` 54 | * added trait `Enm\JsonApi\Server\RequestHandler\FetchOnlyTrait` 55 | * added trait `Enm\JsonApi\Server\RequestHandler\NoRelationshipsTrait` 56 | * added interface `Enm\JsonApi\Server\JsonApiAwareInterface` 57 | * added trait `Enm\JsonApi\Server\JsonApiAwareTrait` 58 | * removed class `Enm\JsonApi\Server\JsonApi` 59 | * removed class `Enm\JsonApi\Server\Event\DocumentEvent` 60 | * removed class `Enm\JsonApi\Server\Event\DocumentResponseEvent` 61 | * removed class `Enm\JsonApi\Server\Event\FetchEvent` 62 | * removed class `Enm\JsonApi\Server\Event\ResourceEvent` 63 | * removed interface `Enm\JsonApi\Server\Model\Request\FetchInterface` 64 | * removed interface `Enm\JsonApi\Server\Model\Request\HttpRequestInterface` 65 | * removed interface `Enm\JsonApi\Server\Model\Request\SaveResourceInterface` 66 | * removed class `Enm\JsonApi\Server\Model\Request\AbstractHttpRequest` 67 | * removed class `Enm\JsonApi\Server\Model\Request\SaveResoureRequest` 68 | * removed class `Enm\JsonApi\Server\Model\Request\FetchRequest` 69 | * removed class `Enm\JsonApi\Server\Model\Request\SortInstruction` 70 | * added interface `Enm\JsonApi\Server\Model\FetchRequestInterface` 71 | * added class `Enm\JsonApi\Server\Model\Request\FetchRequest` 72 | * added interface `Enm\JsonApi\Server\Model\Request\SaveRequestInterface` 73 | * added class `Enm\JsonApi\Server\Model\Request\SaveSingleResourceRequest` 74 | * added interface `Enm\JsonApi\Server\Model\Request\AdvancedJsonApiRequestInterface` 75 | * added trait `Enm\JsonApi\Server\Model\Request\AdvancedJsonApiRequestTrait` 76 | * added class `Enm\JsonApi\Server\Model\Request\AdvancedJsonApiRequest` 77 | * removed dependency `symfony/event-dispatcher` 78 | * removed dependency `symfony/http-foundation` 79 | * added dependency `psr/http-message` 80 | * added dependency `guzzlehttp/psr7` 81 | * added dependency `psr/log` 82 | * changed dependency `enm/json-api-common` to version ^2.0 83 | * added class `Enm\JsonApi\Server\JsonApiServer` 84 | * added trait `Enm\JsonApi\Server\Model\ExceptionTrait` 85 | * added class `Enm\JsonApi\Server\JsonApiServer` 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 eos new media GmbH & Co. KG 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 | JSON API Server 2 | =============== 3 | [![Build Status](https://travis-ci.org/eosnewmedia/JSON-API-Server.svg?branch=master)](https://travis-ci.org/eosnewmedia/JSON-API-Server) 4 | 5 | Abstract server-side php implementation of the [json api specification](http://jsonapi.org/format/). 6 | 7 | ## Installation 8 | 9 | ```sh 10 | composer require enm/json-api-server 11 | ``` 12 | 13 | ## Documentation 14 | First you should read the docs at [`enm/json-api-common`](https://eosnewmedia.github.io/JSON-API-Common/) where all basic structures are defined. 15 | 16 | 1. [Json Api Server](docs/json-api-server/index.md) 17 | 1. [Endpoints](docs/json-api-server/index.md#endpoints) 18 | 1. [Usage](docs/json-api-server/index.md#usage) 19 | 1. [Request Handler](docs/request-handler/index.md) 20 | 1. [Concept](docs/request-handler/index.md#concept) 21 | 1. [Interface](docs/request-handler/index.md#interface) 22 | 1. [Usage](docs/request-handler/index.md#usage) 23 | 1. [Exception handling](docs/exception-handling/index.md) 24 | 25 | See [Change Log](CHANGELOG.md) for changes! 26 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enm/json-api-server", 3 | "description": "Abstract server-side php implementation of the json api specification (jsonapi.org)", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Philipp Marien", 9 | "email": "marien@eosnewmedia.de" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "Enm\\JsonApi\\Server\\": "src" 15 | } 16 | }, 17 | "autoload-dev": { 18 | "psr-4": { 19 | "Enm\\JsonApi\\Server\\Tests\\": "tests" 20 | } 21 | }, 22 | "minimum-stability": "stable", 23 | "require": { 24 | "php": ">=7.2", 25 | "enm/json-api-common": "^3.2" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^7.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /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": "1b83a4a3656f1b10943e37aabbc2c6a1", 8 | "packages": [ 9 | { 10 | "name": "enm/json-api-common", 11 | "version": "3.2.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/eosnewmedia/JSON-API-Common.git", 15 | "reference": "d868eb4d76bf75518f1bb72a70e5abf78fcae476" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/eosnewmedia/JSON-API-Common/zipball/d868eb4d76bf75518f1bb72a70e5abf78fcae476", 20 | "reference": "d868eb4d76bf75518f1bb72a70e5abf78fcae476", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=7.2", 25 | "psr/http-message": "^1.0" 26 | }, 27 | "require-dev": { 28 | "guzzlehttp/psr7": "^1.4", 29 | "phpunit/phpunit": "^7.0" 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "psr-4": { 34 | "Enm\\JsonApi\\": "src" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Philipp Marien", 44 | "email": "marien@eosnewmedia.de" 45 | } 46 | ], 47 | "description": "Basic php implementation (shared structures for client and server) of the json api specification (jsonapi.org)", 48 | "time": "2018-10-29T13:01:14+00:00" 49 | }, 50 | { 51 | "name": "psr/http-message", 52 | "version": "1.0.1", 53 | "source": { 54 | "type": "git", 55 | "url": "https://github.com/php-fig/http-message.git", 56 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 57 | }, 58 | "dist": { 59 | "type": "zip", 60 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 61 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 62 | "shasum": "" 63 | }, 64 | "require": { 65 | "php": ">=5.3.0" 66 | }, 67 | "type": "library", 68 | "extra": { 69 | "branch-alias": { 70 | "dev-master": "1.0.x-dev" 71 | } 72 | }, 73 | "autoload": { 74 | "psr-4": { 75 | "Psr\\Http\\Message\\": "src/" 76 | } 77 | }, 78 | "notification-url": "https://packagist.org/downloads/", 79 | "license": [ 80 | "MIT" 81 | ], 82 | "authors": [ 83 | { 84 | "name": "PHP-FIG", 85 | "homepage": "http://www.php-fig.org/" 86 | } 87 | ], 88 | "description": "Common interface for HTTP messages", 89 | "homepage": "https://github.com/php-fig/http-message", 90 | "keywords": [ 91 | "http", 92 | "http-message", 93 | "psr", 94 | "psr-7", 95 | "request", 96 | "response" 97 | ], 98 | "time": "2016-08-06T14:39:51+00:00" 99 | } 100 | ], 101 | "packages-dev": [ 102 | { 103 | "name": "doctrine/instantiator", 104 | "version": "1.1.0", 105 | "source": { 106 | "type": "git", 107 | "url": "https://github.com/doctrine/instantiator.git", 108 | "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" 109 | }, 110 | "dist": { 111 | "type": "zip", 112 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", 113 | "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", 114 | "shasum": "" 115 | }, 116 | "require": { 117 | "php": "^7.1" 118 | }, 119 | "require-dev": { 120 | "athletic/athletic": "~0.1.8", 121 | "ext-pdo": "*", 122 | "ext-phar": "*", 123 | "phpunit/phpunit": "^6.2.3", 124 | "squizlabs/php_codesniffer": "^3.0.2" 125 | }, 126 | "type": "library", 127 | "extra": { 128 | "branch-alias": { 129 | "dev-master": "1.2.x-dev" 130 | } 131 | }, 132 | "autoload": { 133 | "psr-4": { 134 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 135 | } 136 | }, 137 | "notification-url": "https://packagist.org/downloads/", 138 | "license": [ 139 | "MIT" 140 | ], 141 | "authors": [ 142 | { 143 | "name": "Marco Pivetta", 144 | "email": "ocramius@gmail.com", 145 | "homepage": "http://ocramius.github.com/" 146 | } 147 | ], 148 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 149 | "homepage": "https://github.com/doctrine/instantiator", 150 | "keywords": [ 151 | "constructor", 152 | "instantiate" 153 | ], 154 | "time": "2017-07-22T11:58:36+00:00" 155 | }, 156 | { 157 | "name": "myclabs/deep-copy", 158 | "version": "1.8.1", 159 | "source": { 160 | "type": "git", 161 | "url": "https://github.com/myclabs/DeepCopy.git", 162 | "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" 163 | }, 164 | "dist": { 165 | "type": "zip", 166 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", 167 | "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", 168 | "shasum": "" 169 | }, 170 | "require": { 171 | "php": "^7.1" 172 | }, 173 | "replace": { 174 | "myclabs/deep-copy": "self.version" 175 | }, 176 | "require-dev": { 177 | "doctrine/collections": "^1.0", 178 | "doctrine/common": "^2.6", 179 | "phpunit/phpunit": "^7.1" 180 | }, 181 | "type": "library", 182 | "autoload": { 183 | "psr-4": { 184 | "DeepCopy\\": "src/DeepCopy/" 185 | }, 186 | "files": [ 187 | "src/DeepCopy/deep_copy.php" 188 | ] 189 | }, 190 | "notification-url": "https://packagist.org/downloads/", 191 | "license": [ 192 | "MIT" 193 | ], 194 | "description": "Create deep copies (clones) of your objects", 195 | "keywords": [ 196 | "clone", 197 | "copy", 198 | "duplicate", 199 | "object", 200 | "object graph" 201 | ], 202 | "time": "2018-06-11T23:09:50+00:00" 203 | }, 204 | { 205 | "name": "phar-io/manifest", 206 | "version": "1.0.3", 207 | "source": { 208 | "type": "git", 209 | "url": "https://github.com/phar-io/manifest.git", 210 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" 211 | }, 212 | "dist": { 213 | "type": "zip", 214 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 215 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 216 | "shasum": "" 217 | }, 218 | "require": { 219 | "ext-dom": "*", 220 | "ext-phar": "*", 221 | "phar-io/version": "^2.0", 222 | "php": "^5.6 || ^7.0" 223 | }, 224 | "type": "library", 225 | "extra": { 226 | "branch-alias": { 227 | "dev-master": "1.0.x-dev" 228 | } 229 | }, 230 | "autoload": { 231 | "classmap": [ 232 | "src/" 233 | ] 234 | }, 235 | "notification-url": "https://packagist.org/downloads/", 236 | "license": [ 237 | "BSD-3-Clause" 238 | ], 239 | "authors": [ 240 | { 241 | "name": "Arne Blankerts", 242 | "email": "arne@blankerts.de", 243 | "role": "Developer" 244 | }, 245 | { 246 | "name": "Sebastian Heuer", 247 | "email": "sebastian@phpeople.de", 248 | "role": "Developer" 249 | }, 250 | { 251 | "name": "Sebastian Bergmann", 252 | "email": "sebastian@phpunit.de", 253 | "role": "Developer" 254 | } 255 | ], 256 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 257 | "time": "2018-07-08T19:23:20+00:00" 258 | }, 259 | { 260 | "name": "phar-io/version", 261 | "version": "2.0.1", 262 | "source": { 263 | "type": "git", 264 | "url": "https://github.com/phar-io/version.git", 265 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" 266 | }, 267 | "dist": { 268 | "type": "zip", 269 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", 270 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", 271 | "shasum": "" 272 | }, 273 | "require": { 274 | "php": "^5.6 || ^7.0" 275 | }, 276 | "type": "library", 277 | "autoload": { 278 | "classmap": [ 279 | "src/" 280 | ] 281 | }, 282 | "notification-url": "https://packagist.org/downloads/", 283 | "license": [ 284 | "BSD-3-Clause" 285 | ], 286 | "authors": [ 287 | { 288 | "name": "Arne Blankerts", 289 | "email": "arne@blankerts.de", 290 | "role": "Developer" 291 | }, 292 | { 293 | "name": "Sebastian Heuer", 294 | "email": "sebastian@phpeople.de", 295 | "role": "Developer" 296 | }, 297 | { 298 | "name": "Sebastian Bergmann", 299 | "email": "sebastian@phpunit.de", 300 | "role": "Developer" 301 | } 302 | ], 303 | "description": "Library for handling version information and constraints", 304 | "time": "2018-07-08T19:19:57+00:00" 305 | }, 306 | { 307 | "name": "phpdocumentor/reflection-common", 308 | "version": "1.0.1", 309 | "source": { 310 | "type": "git", 311 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 312 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 313 | }, 314 | "dist": { 315 | "type": "zip", 316 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 317 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 318 | "shasum": "" 319 | }, 320 | "require": { 321 | "php": ">=5.5" 322 | }, 323 | "require-dev": { 324 | "phpunit/phpunit": "^4.6" 325 | }, 326 | "type": "library", 327 | "extra": { 328 | "branch-alias": { 329 | "dev-master": "1.0.x-dev" 330 | } 331 | }, 332 | "autoload": { 333 | "psr-4": { 334 | "phpDocumentor\\Reflection\\": [ 335 | "src" 336 | ] 337 | } 338 | }, 339 | "notification-url": "https://packagist.org/downloads/", 340 | "license": [ 341 | "MIT" 342 | ], 343 | "authors": [ 344 | { 345 | "name": "Jaap van Otterdijk", 346 | "email": "opensource@ijaap.nl" 347 | } 348 | ], 349 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 350 | "homepage": "http://www.phpdoc.org", 351 | "keywords": [ 352 | "FQSEN", 353 | "phpDocumentor", 354 | "phpdoc", 355 | "reflection", 356 | "static analysis" 357 | ], 358 | "time": "2017-09-11T18:02:19+00:00" 359 | }, 360 | { 361 | "name": "phpdocumentor/reflection-docblock", 362 | "version": "4.3.0", 363 | "source": { 364 | "type": "git", 365 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 366 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08" 367 | }, 368 | "dist": { 369 | "type": "zip", 370 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", 371 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08", 372 | "shasum": "" 373 | }, 374 | "require": { 375 | "php": "^7.0", 376 | "phpdocumentor/reflection-common": "^1.0.0", 377 | "phpdocumentor/type-resolver": "^0.4.0", 378 | "webmozart/assert": "^1.0" 379 | }, 380 | "require-dev": { 381 | "doctrine/instantiator": "~1.0.5", 382 | "mockery/mockery": "^1.0", 383 | "phpunit/phpunit": "^6.4" 384 | }, 385 | "type": "library", 386 | "extra": { 387 | "branch-alias": { 388 | "dev-master": "4.x-dev" 389 | } 390 | }, 391 | "autoload": { 392 | "psr-4": { 393 | "phpDocumentor\\Reflection\\": [ 394 | "src/" 395 | ] 396 | } 397 | }, 398 | "notification-url": "https://packagist.org/downloads/", 399 | "license": [ 400 | "MIT" 401 | ], 402 | "authors": [ 403 | { 404 | "name": "Mike van Riel", 405 | "email": "me@mikevanriel.com" 406 | } 407 | ], 408 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 409 | "time": "2017-11-30T07:14:17+00:00" 410 | }, 411 | { 412 | "name": "phpdocumentor/type-resolver", 413 | "version": "0.4.0", 414 | "source": { 415 | "type": "git", 416 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 417 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 418 | }, 419 | "dist": { 420 | "type": "zip", 421 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 422 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 423 | "shasum": "" 424 | }, 425 | "require": { 426 | "php": "^5.5 || ^7.0", 427 | "phpdocumentor/reflection-common": "^1.0" 428 | }, 429 | "require-dev": { 430 | "mockery/mockery": "^0.9.4", 431 | "phpunit/phpunit": "^5.2||^4.8.24" 432 | }, 433 | "type": "library", 434 | "extra": { 435 | "branch-alias": { 436 | "dev-master": "1.0.x-dev" 437 | } 438 | }, 439 | "autoload": { 440 | "psr-4": { 441 | "phpDocumentor\\Reflection\\": [ 442 | "src/" 443 | ] 444 | } 445 | }, 446 | "notification-url": "https://packagist.org/downloads/", 447 | "license": [ 448 | "MIT" 449 | ], 450 | "authors": [ 451 | { 452 | "name": "Mike van Riel", 453 | "email": "me@mikevanriel.com" 454 | } 455 | ], 456 | "time": "2017-07-14T14:27:02+00:00" 457 | }, 458 | { 459 | "name": "phpspec/prophecy", 460 | "version": "1.8.0", 461 | "source": { 462 | "type": "git", 463 | "url": "https://github.com/phpspec/prophecy.git", 464 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" 465 | }, 466 | "dist": { 467 | "type": "zip", 468 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", 469 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", 470 | "shasum": "" 471 | }, 472 | "require": { 473 | "doctrine/instantiator": "^1.0.2", 474 | "php": "^5.3|^7.0", 475 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 476 | "sebastian/comparator": "^1.1|^2.0|^3.0", 477 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 478 | }, 479 | "require-dev": { 480 | "phpspec/phpspec": "^2.5|^3.2", 481 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 482 | }, 483 | "type": "library", 484 | "extra": { 485 | "branch-alias": { 486 | "dev-master": "1.8.x-dev" 487 | } 488 | }, 489 | "autoload": { 490 | "psr-0": { 491 | "Prophecy\\": "src/" 492 | } 493 | }, 494 | "notification-url": "https://packagist.org/downloads/", 495 | "license": [ 496 | "MIT" 497 | ], 498 | "authors": [ 499 | { 500 | "name": "Konstantin Kudryashov", 501 | "email": "ever.zet@gmail.com", 502 | "homepage": "http://everzet.com" 503 | }, 504 | { 505 | "name": "Marcello Duarte", 506 | "email": "marcello.duarte@gmail.com" 507 | } 508 | ], 509 | "description": "Highly opinionated mocking framework for PHP 5.3+", 510 | "homepage": "https://github.com/phpspec/prophecy", 511 | "keywords": [ 512 | "Double", 513 | "Dummy", 514 | "fake", 515 | "mock", 516 | "spy", 517 | "stub" 518 | ], 519 | "time": "2018-08-05T17:53:17+00:00" 520 | }, 521 | { 522 | "name": "phpunit/php-code-coverage", 523 | "version": "6.1.3", 524 | "source": { 525 | "type": "git", 526 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 527 | "reference": "4d3ae9b21a7d7e440bd0cf65565533117976859f" 528 | }, 529 | "dist": { 530 | "type": "zip", 531 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4d3ae9b21a7d7e440bd0cf65565533117976859f", 532 | "reference": "4d3ae9b21a7d7e440bd0cf65565533117976859f", 533 | "shasum": "" 534 | }, 535 | "require": { 536 | "ext-dom": "*", 537 | "ext-xmlwriter": "*", 538 | "php": "^7.1", 539 | "phpunit/php-file-iterator": "^2.0", 540 | "phpunit/php-text-template": "^1.2.1", 541 | "phpunit/php-token-stream": "^3.0", 542 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 543 | "sebastian/environment": "^3.1 || ^4.0", 544 | "sebastian/version": "^2.0.1", 545 | "theseer/tokenizer": "^1.1" 546 | }, 547 | "require-dev": { 548 | "phpunit/phpunit": "^7.0" 549 | }, 550 | "suggest": { 551 | "ext-xdebug": "^2.6.0" 552 | }, 553 | "type": "library", 554 | "extra": { 555 | "branch-alias": { 556 | "dev-master": "6.1-dev" 557 | } 558 | }, 559 | "autoload": { 560 | "classmap": [ 561 | "src/" 562 | ] 563 | }, 564 | "notification-url": "https://packagist.org/downloads/", 565 | "license": [ 566 | "BSD-3-Clause" 567 | ], 568 | "authors": [ 569 | { 570 | "name": "Sebastian Bergmann", 571 | "email": "sebastian@phpunit.de", 572 | "role": "lead" 573 | } 574 | ], 575 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 576 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 577 | "keywords": [ 578 | "coverage", 579 | "testing", 580 | "xunit" 581 | ], 582 | "time": "2018-10-23T05:59:32+00:00" 583 | }, 584 | { 585 | "name": "phpunit/php-file-iterator", 586 | "version": "2.0.2", 587 | "source": { 588 | "type": "git", 589 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 590 | "reference": "050bedf145a257b1ff02746c31894800e5122946" 591 | }, 592 | "dist": { 593 | "type": "zip", 594 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", 595 | "reference": "050bedf145a257b1ff02746c31894800e5122946", 596 | "shasum": "" 597 | }, 598 | "require": { 599 | "php": "^7.1" 600 | }, 601 | "require-dev": { 602 | "phpunit/phpunit": "^7.1" 603 | }, 604 | "type": "library", 605 | "extra": { 606 | "branch-alias": { 607 | "dev-master": "2.0.x-dev" 608 | } 609 | }, 610 | "autoload": { 611 | "classmap": [ 612 | "src/" 613 | ] 614 | }, 615 | "notification-url": "https://packagist.org/downloads/", 616 | "license": [ 617 | "BSD-3-Clause" 618 | ], 619 | "authors": [ 620 | { 621 | "name": "Sebastian Bergmann", 622 | "email": "sebastian@phpunit.de", 623 | "role": "lead" 624 | } 625 | ], 626 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 627 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 628 | "keywords": [ 629 | "filesystem", 630 | "iterator" 631 | ], 632 | "time": "2018-09-13T20:33:42+00:00" 633 | }, 634 | { 635 | "name": "phpunit/php-text-template", 636 | "version": "1.2.1", 637 | "source": { 638 | "type": "git", 639 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 640 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 641 | }, 642 | "dist": { 643 | "type": "zip", 644 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 645 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 646 | "shasum": "" 647 | }, 648 | "require": { 649 | "php": ">=5.3.3" 650 | }, 651 | "type": "library", 652 | "autoload": { 653 | "classmap": [ 654 | "src/" 655 | ] 656 | }, 657 | "notification-url": "https://packagist.org/downloads/", 658 | "license": [ 659 | "BSD-3-Clause" 660 | ], 661 | "authors": [ 662 | { 663 | "name": "Sebastian Bergmann", 664 | "email": "sebastian@phpunit.de", 665 | "role": "lead" 666 | } 667 | ], 668 | "description": "Simple template engine.", 669 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 670 | "keywords": [ 671 | "template" 672 | ], 673 | "time": "2015-06-21T13:50:34+00:00" 674 | }, 675 | { 676 | "name": "phpunit/php-timer", 677 | "version": "2.0.0", 678 | "source": { 679 | "type": "git", 680 | "url": "https://github.com/sebastianbergmann/php-timer.git", 681 | "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" 682 | }, 683 | "dist": { 684 | "type": "zip", 685 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", 686 | "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", 687 | "shasum": "" 688 | }, 689 | "require": { 690 | "php": "^7.1" 691 | }, 692 | "require-dev": { 693 | "phpunit/phpunit": "^7.0" 694 | }, 695 | "type": "library", 696 | "extra": { 697 | "branch-alias": { 698 | "dev-master": "2.0-dev" 699 | } 700 | }, 701 | "autoload": { 702 | "classmap": [ 703 | "src/" 704 | ] 705 | }, 706 | "notification-url": "https://packagist.org/downloads/", 707 | "license": [ 708 | "BSD-3-Clause" 709 | ], 710 | "authors": [ 711 | { 712 | "name": "Sebastian Bergmann", 713 | "email": "sebastian@phpunit.de", 714 | "role": "lead" 715 | } 716 | ], 717 | "description": "Utility class for timing", 718 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 719 | "keywords": [ 720 | "timer" 721 | ], 722 | "time": "2018-02-01T13:07:23+00:00" 723 | }, 724 | { 725 | "name": "phpunit/php-token-stream", 726 | "version": "3.0.0", 727 | "source": { 728 | "type": "git", 729 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 730 | "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace" 731 | }, 732 | "dist": { 733 | "type": "zip", 734 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/21ad88bbba7c3d93530d93994e0a33cd45f02ace", 735 | "reference": "21ad88bbba7c3d93530d93994e0a33cd45f02ace", 736 | "shasum": "" 737 | }, 738 | "require": { 739 | "ext-tokenizer": "*", 740 | "php": "^7.1" 741 | }, 742 | "require-dev": { 743 | "phpunit/phpunit": "^7.0" 744 | }, 745 | "type": "library", 746 | "extra": { 747 | "branch-alias": { 748 | "dev-master": "3.0-dev" 749 | } 750 | }, 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": "Sebastian Bergmann", 763 | "email": "sebastian@phpunit.de" 764 | } 765 | ], 766 | "description": "Wrapper around PHP's tokenizer extension.", 767 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 768 | "keywords": [ 769 | "tokenizer" 770 | ], 771 | "time": "2018-02-01T13:16:43+00:00" 772 | }, 773 | { 774 | "name": "phpunit/phpunit", 775 | "version": "7.4.3", 776 | "source": { 777 | "type": "git", 778 | "url": "https://github.com/sebastianbergmann/phpunit.git", 779 | "reference": "c151651fb6ed264038d486ea262e243af72e5e64" 780 | }, 781 | "dist": { 782 | "type": "zip", 783 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c151651fb6ed264038d486ea262e243af72e5e64", 784 | "reference": "c151651fb6ed264038d486ea262e243af72e5e64", 785 | "shasum": "" 786 | }, 787 | "require": { 788 | "doctrine/instantiator": "^1.1", 789 | "ext-dom": "*", 790 | "ext-json": "*", 791 | "ext-libxml": "*", 792 | "ext-mbstring": "*", 793 | "ext-xml": "*", 794 | "myclabs/deep-copy": "^1.7", 795 | "phar-io/manifest": "^1.0.2", 796 | "phar-io/version": "^2.0", 797 | "php": "^7.1", 798 | "phpspec/prophecy": "^1.7", 799 | "phpunit/php-code-coverage": "^6.0.7", 800 | "phpunit/php-file-iterator": "^2.0.1", 801 | "phpunit/php-text-template": "^1.2.1", 802 | "phpunit/php-timer": "^2.0", 803 | "sebastian/comparator": "^3.0", 804 | "sebastian/diff": "^3.0", 805 | "sebastian/environment": "^3.1 || ^4.0", 806 | "sebastian/exporter": "^3.1", 807 | "sebastian/global-state": "^2.0", 808 | "sebastian/object-enumerator": "^3.0.3", 809 | "sebastian/resource-operations": "^2.0", 810 | "sebastian/version": "^2.0.1" 811 | }, 812 | "conflict": { 813 | "phpunit/phpunit-mock-objects": "*" 814 | }, 815 | "require-dev": { 816 | "ext-pdo": "*" 817 | }, 818 | "suggest": { 819 | "ext-soap": "*", 820 | "ext-xdebug": "*", 821 | "phpunit/php-invoker": "^2.0" 822 | }, 823 | "bin": [ 824 | "phpunit" 825 | ], 826 | "type": "library", 827 | "extra": { 828 | "branch-alias": { 829 | "dev-master": "7.4-dev" 830 | } 831 | }, 832 | "autoload": { 833 | "classmap": [ 834 | "src/" 835 | ] 836 | }, 837 | "notification-url": "https://packagist.org/downloads/", 838 | "license": [ 839 | "BSD-3-Clause" 840 | ], 841 | "authors": [ 842 | { 843 | "name": "Sebastian Bergmann", 844 | "email": "sebastian@phpunit.de", 845 | "role": "lead" 846 | } 847 | ], 848 | "description": "The PHP Unit Testing framework.", 849 | "homepage": "https://phpunit.de/", 850 | "keywords": [ 851 | "phpunit", 852 | "testing", 853 | "xunit" 854 | ], 855 | "time": "2018-10-23T05:57:41+00:00" 856 | }, 857 | { 858 | "name": "sebastian/code-unit-reverse-lookup", 859 | "version": "1.0.1", 860 | "source": { 861 | "type": "git", 862 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 863 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 864 | }, 865 | "dist": { 866 | "type": "zip", 867 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 868 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 869 | "shasum": "" 870 | }, 871 | "require": { 872 | "php": "^5.6 || ^7.0" 873 | }, 874 | "require-dev": { 875 | "phpunit/phpunit": "^5.7 || ^6.0" 876 | }, 877 | "type": "library", 878 | "extra": { 879 | "branch-alias": { 880 | "dev-master": "1.0.x-dev" 881 | } 882 | }, 883 | "autoload": { 884 | "classmap": [ 885 | "src/" 886 | ] 887 | }, 888 | "notification-url": "https://packagist.org/downloads/", 889 | "license": [ 890 | "BSD-3-Clause" 891 | ], 892 | "authors": [ 893 | { 894 | "name": "Sebastian Bergmann", 895 | "email": "sebastian@phpunit.de" 896 | } 897 | ], 898 | "description": "Looks up which function or method a line of code belongs to", 899 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 900 | "time": "2017-03-04T06:30:41+00:00" 901 | }, 902 | { 903 | "name": "sebastian/comparator", 904 | "version": "3.0.2", 905 | "source": { 906 | "type": "git", 907 | "url": "https://github.com/sebastianbergmann/comparator.git", 908 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" 909 | }, 910 | "dist": { 911 | "type": "zip", 912 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 913 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 914 | "shasum": "" 915 | }, 916 | "require": { 917 | "php": "^7.1", 918 | "sebastian/diff": "^3.0", 919 | "sebastian/exporter": "^3.1" 920 | }, 921 | "require-dev": { 922 | "phpunit/phpunit": "^7.1" 923 | }, 924 | "type": "library", 925 | "extra": { 926 | "branch-alias": { 927 | "dev-master": "3.0-dev" 928 | } 929 | }, 930 | "autoload": { 931 | "classmap": [ 932 | "src/" 933 | ] 934 | }, 935 | "notification-url": "https://packagist.org/downloads/", 936 | "license": [ 937 | "BSD-3-Clause" 938 | ], 939 | "authors": [ 940 | { 941 | "name": "Jeff Welch", 942 | "email": "whatthejeff@gmail.com" 943 | }, 944 | { 945 | "name": "Volker Dusch", 946 | "email": "github@wallbash.com" 947 | }, 948 | { 949 | "name": "Bernhard Schussek", 950 | "email": "bschussek@2bepublished.at" 951 | }, 952 | { 953 | "name": "Sebastian Bergmann", 954 | "email": "sebastian@phpunit.de" 955 | } 956 | ], 957 | "description": "Provides the functionality to compare PHP values for equality", 958 | "homepage": "https://github.com/sebastianbergmann/comparator", 959 | "keywords": [ 960 | "comparator", 961 | "compare", 962 | "equality" 963 | ], 964 | "time": "2018-07-12T15:12:46+00:00" 965 | }, 966 | { 967 | "name": "sebastian/diff", 968 | "version": "3.0.1", 969 | "source": { 970 | "type": "git", 971 | "url": "https://github.com/sebastianbergmann/diff.git", 972 | "reference": "366541b989927187c4ca70490a35615d3fef2dce" 973 | }, 974 | "dist": { 975 | "type": "zip", 976 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", 977 | "reference": "366541b989927187c4ca70490a35615d3fef2dce", 978 | "shasum": "" 979 | }, 980 | "require": { 981 | "php": "^7.1" 982 | }, 983 | "require-dev": { 984 | "phpunit/phpunit": "^7.0", 985 | "symfony/process": "^2 || ^3.3 || ^4" 986 | }, 987 | "type": "library", 988 | "extra": { 989 | "branch-alias": { 990 | "dev-master": "3.0-dev" 991 | } 992 | }, 993 | "autoload": { 994 | "classmap": [ 995 | "src/" 996 | ] 997 | }, 998 | "notification-url": "https://packagist.org/downloads/", 999 | "license": [ 1000 | "BSD-3-Clause" 1001 | ], 1002 | "authors": [ 1003 | { 1004 | "name": "Kore Nordmann", 1005 | "email": "mail@kore-nordmann.de" 1006 | }, 1007 | { 1008 | "name": "Sebastian Bergmann", 1009 | "email": "sebastian@phpunit.de" 1010 | } 1011 | ], 1012 | "description": "Diff implementation", 1013 | "homepage": "https://github.com/sebastianbergmann/diff", 1014 | "keywords": [ 1015 | "diff", 1016 | "udiff", 1017 | "unidiff", 1018 | "unified diff" 1019 | ], 1020 | "time": "2018-06-10T07:54:39+00:00" 1021 | }, 1022 | { 1023 | "name": "sebastian/environment", 1024 | "version": "3.1.0", 1025 | "source": { 1026 | "type": "git", 1027 | "url": "https://github.com/sebastianbergmann/environment.git", 1028 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" 1029 | }, 1030 | "dist": { 1031 | "type": "zip", 1032 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", 1033 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", 1034 | "shasum": "" 1035 | }, 1036 | "require": { 1037 | "php": "^7.0" 1038 | }, 1039 | "require-dev": { 1040 | "phpunit/phpunit": "^6.1" 1041 | }, 1042 | "type": "library", 1043 | "extra": { 1044 | "branch-alias": { 1045 | "dev-master": "3.1.x-dev" 1046 | } 1047 | }, 1048 | "autoload": { 1049 | "classmap": [ 1050 | "src/" 1051 | ] 1052 | }, 1053 | "notification-url": "https://packagist.org/downloads/", 1054 | "license": [ 1055 | "BSD-3-Clause" 1056 | ], 1057 | "authors": [ 1058 | { 1059 | "name": "Sebastian Bergmann", 1060 | "email": "sebastian@phpunit.de" 1061 | } 1062 | ], 1063 | "description": "Provides functionality to handle HHVM/PHP environments", 1064 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1065 | "keywords": [ 1066 | "Xdebug", 1067 | "environment", 1068 | "hhvm" 1069 | ], 1070 | "time": "2017-07-01T08:51:00+00:00" 1071 | }, 1072 | { 1073 | "name": "sebastian/exporter", 1074 | "version": "3.1.0", 1075 | "source": { 1076 | "type": "git", 1077 | "url": "https://github.com/sebastianbergmann/exporter.git", 1078 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" 1079 | }, 1080 | "dist": { 1081 | "type": "zip", 1082 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", 1083 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", 1084 | "shasum": "" 1085 | }, 1086 | "require": { 1087 | "php": "^7.0", 1088 | "sebastian/recursion-context": "^3.0" 1089 | }, 1090 | "require-dev": { 1091 | "ext-mbstring": "*", 1092 | "phpunit/phpunit": "^6.0" 1093 | }, 1094 | "type": "library", 1095 | "extra": { 1096 | "branch-alias": { 1097 | "dev-master": "3.1.x-dev" 1098 | } 1099 | }, 1100 | "autoload": { 1101 | "classmap": [ 1102 | "src/" 1103 | ] 1104 | }, 1105 | "notification-url": "https://packagist.org/downloads/", 1106 | "license": [ 1107 | "BSD-3-Clause" 1108 | ], 1109 | "authors": [ 1110 | { 1111 | "name": "Jeff Welch", 1112 | "email": "whatthejeff@gmail.com" 1113 | }, 1114 | { 1115 | "name": "Volker Dusch", 1116 | "email": "github@wallbash.com" 1117 | }, 1118 | { 1119 | "name": "Bernhard Schussek", 1120 | "email": "bschussek@2bepublished.at" 1121 | }, 1122 | { 1123 | "name": "Sebastian Bergmann", 1124 | "email": "sebastian@phpunit.de" 1125 | }, 1126 | { 1127 | "name": "Adam Harvey", 1128 | "email": "aharvey@php.net" 1129 | } 1130 | ], 1131 | "description": "Provides the functionality to export PHP variables for visualization", 1132 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1133 | "keywords": [ 1134 | "export", 1135 | "exporter" 1136 | ], 1137 | "time": "2017-04-03T13:19:02+00:00" 1138 | }, 1139 | { 1140 | "name": "sebastian/global-state", 1141 | "version": "2.0.0", 1142 | "source": { 1143 | "type": "git", 1144 | "url": "https://github.com/sebastianbergmann/global-state.git", 1145 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" 1146 | }, 1147 | "dist": { 1148 | "type": "zip", 1149 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 1150 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 1151 | "shasum": "" 1152 | }, 1153 | "require": { 1154 | "php": "^7.0" 1155 | }, 1156 | "require-dev": { 1157 | "phpunit/phpunit": "^6.0" 1158 | }, 1159 | "suggest": { 1160 | "ext-uopz": "*" 1161 | }, 1162 | "type": "library", 1163 | "extra": { 1164 | "branch-alias": { 1165 | "dev-master": "2.0-dev" 1166 | } 1167 | }, 1168 | "autoload": { 1169 | "classmap": [ 1170 | "src/" 1171 | ] 1172 | }, 1173 | "notification-url": "https://packagist.org/downloads/", 1174 | "license": [ 1175 | "BSD-3-Clause" 1176 | ], 1177 | "authors": [ 1178 | { 1179 | "name": "Sebastian Bergmann", 1180 | "email": "sebastian@phpunit.de" 1181 | } 1182 | ], 1183 | "description": "Snapshotting of global state", 1184 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1185 | "keywords": [ 1186 | "global state" 1187 | ], 1188 | "time": "2017-04-27T15:39:26+00:00" 1189 | }, 1190 | { 1191 | "name": "sebastian/object-enumerator", 1192 | "version": "3.0.3", 1193 | "source": { 1194 | "type": "git", 1195 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1196 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" 1197 | }, 1198 | "dist": { 1199 | "type": "zip", 1200 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1201 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1202 | "shasum": "" 1203 | }, 1204 | "require": { 1205 | "php": "^7.0", 1206 | "sebastian/object-reflector": "^1.1.1", 1207 | "sebastian/recursion-context": "^3.0" 1208 | }, 1209 | "require-dev": { 1210 | "phpunit/phpunit": "^6.0" 1211 | }, 1212 | "type": "library", 1213 | "extra": { 1214 | "branch-alias": { 1215 | "dev-master": "3.0.x-dev" 1216 | } 1217 | }, 1218 | "autoload": { 1219 | "classmap": [ 1220 | "src/" 1221 | ] 1222 | }, 1223 | "notification-url": "https://packagist.org/downloads/", 1224 | "license": [ 1225 | "BSD-3-Clause" 1226 | ], 1227 | "authors": [ 1228 | { 1229 | "name": "Sebastian Bergmann", 1230 | "email": "sebastian@phpunit.de" 1231 | } 1232 | ], 1233 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1234 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1235 | "time": "2017-08-03T12:35:26+00:00" 1236 | }, 1237 | { 1238 | "name": "sebastian/object-reflector", 1239 | "version": "1.1.1", 1240 | "source": { 1241 | "type": "git", 1242 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1243 | "reference": "773f97c67f28de00d397be301821b06708fca0be" 1244 | }, 1245 | "dist": { 1246 | "type": "zip", 1247 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", 1248 | "reference": "773f97c67f28de00d397be301821b06708fca0be", 1249 | "shasum": "" 1250 | }, 1251 | "require": { 1252 | "php": "^7.0" 1253 | }, 1254 | "require-dev": { 1255 | "phpunit/phpunit": "^6.0" 1256 | }, 1257 | "type": "library", 1258 | "extra": { 1259 | "branch-alias": { 1260 | "dev-master": "1.1-dev" 1261 | } 1262 | }, 1263 | "autoload": { 1264 | "classmap": [ 1265 | "src/" 1266 | ] 1267 | }, 1268 | "notification-url": "https://packagist.org/downloads/", 1269 | "license": [ 1270 | "BSD-3-Clause" 1271 | ], 1272 | "authors": [ 1273 | { 1274 | "name": "Sebastian Bergmann", 1275 | "email": "sebastian@phpunit.de" 1276 | } 1277 | ], 1278 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1279 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1280 | "time": "2017-03-29T09:07:27+00:00" 1281 | }, 1282 | { 1283 | "name": "sebastian/recursion-context", 1284 | "version": "3.0.0", 1285 | "source": { 1286 | "type": "git", 1287 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1288 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" 1289 | }, 1290 | "dist": { 1291 | "type": "zip", 1292 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1293 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1294 | "shasum": "" 1295 | }, 1296 | "require": { 1297 | "php": "^7.0" 1298 | }, 1299 | "require-dev": { 1300 | "phpunit/phpunit": "^6.0" 1301 | }, 1302 | "type": "library", 1303 | "extra": { 1304 | "branch-alias": { 1305 | "dev-master": "3.0.x-dev" 1306 | } 1307 | }, 1308 | "autoload": { 1309 | "classmap": [ 1310 | "src/" 1311 | ] 1312 | }, 1313 | "notification-url": "https://packagist.org/downloads/", 1314 | "license": [ 1315 | "BSD-3-Clause" 1316 | ], 1317 | "authors": [ 1318 | { 1319 | "name": "Jeff Welch", 1320 | "email": "whatthejeff@gmail.com" 1321 | }, 1322 | { 1323 | "name": "Sebastian Bergmann", 1324 | "email": "sebastian@phpunit.de" 1325 | }, 1326 | { 1327 | "name": "Adam Harvey", 1328 | "email": "aharvey@php.net" 1329 | } 1330 | ], 1331 | "description": "Provides functionality to recursively process PHP variables", 1332 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1333 | "time": "2017-03-03T06:23:57+00:00" 1334 | }, 1335 | { 1336 | "name": "sebastian/resource-operations", 1337 | "version": "2.0.1", 1338 | "source": { 1339 | "type": "git", 1340 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1341 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" 1342 | }, 1343 | "dist": { 1344 | "type": "zip", 1345 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1346 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1347 | "shasum": "" 1348 | }, 1349 | "require": { 1350 | "php": "^7.1" 1351 | }, 1352 | "type": "library", 1353 | "extra": { 1354 | "branch-alias": { 1355 | "dev-master": "2.0-dev" 1356 | } 1357 | }, 1358 | "autoload": { 1359 | "classmap": [ 1360 | "src/" 1361 | ] 1362 | }, 1363 | "notification-url": "https://packagist.org/downloads/", 1364 | "license": [ 1365 | "BSD-3-Clause" 1366 | ], 1367 | "authors": [ 1368 | { 1369 | "name": "Sebastian Bergmann", 1370 | "email": "sebastian@phpunit.de" 1371 | } 1372 | ], 1373 | "description": "Provides a list of PHP built-in functions that operate on resources", 1374 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1375 | "time": "2018-10-04T04:07:39+00:00" 1376 | }, 1377 | { 1378 | "name": "sebastian/version", 1379 | "version": "2.0.1", 1380 | "source": { 1381 | "type": "git", 1382 | "url": "https://github.com/sebastianbergmann/version.git", 1383 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1384 | }, 1385 | "dist": { 1386 | "type": "zip", 1387 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1388 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1389 | "shasum": "" 1390 | }, 1391 | "require": { 1392 | "php": ">=5.6" 1393 | }, 1394 | "type": "library", 1395 | "extra": { 1396 | "branch-alias": { 1397 | "dev-master": "2.0.x-dev" 1398 | } 1399 | }, 1400 | "autoload": { 1401 | "classmap": [ 1402 | "src/" 1403 | ] 1404 | }, 1405 | "notification-url": "https://packagist.org/downloads/", 1406 | "license": [ 1407 | "BSD-3-Clause" 1408 | ], 1409 | "authors": [ 1410 | { 1411 | "name": "Sebastian Bergmann", 1412 | "email": "sebastian@phpunit.de", 1413 | "role": "lead" 1414 | } 1415 | ], 1416 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1417 | "homepage": "https://github.com/sebastianbergmann/version", 1418 | "time": "2016-10-03T07:35:21+00:00" 1419 | }, 1420 | { 1421 | "name": "theseer/tokenizer", 1422 | "version": "1.1.0", 1423 | "source": { 1424 | "type": "git", 1425 | "url": "https://github.com/theseer/tokenizer.git", 1426 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" 1427 | }, 1428 | "dist": { 1429 | "type": "zip", 1430 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", 1431 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", 1432 | "shasum": "" 1433 | }, 1434 | "require": { 1435 | "ext-dom": "*", 1436 | "ext-tokenizer": "*", 1437 | "ext-xmlwriter": "*", 1438 | "php": "^7.0" 1439 | }, 1440 | "type": "library", 1441 | "autoload": { 1442 | "classmap": [ 1443 | "src/" 1444 | ] 1445 | }, 1446 | "notification-url": "https://packagist.org/downloads/", 1447 | "license": [ 1448 | "BSD-3-Clause" 1449 | ], 1450 | "authors": [ 1451 | { 1452 | "name": "Arne Blankerts", 1453 | "email": "arne@blankerts.de", 1454 | "role": "Developer" 1455 | } 1456 | ], 1457 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1458 | "time": "2017-04-07T12:08:54+00:00" 1459 | }, 1460 | { 1461 | "name": "webmozart/assert", 1462 | "version": "1.3.0", 1463 | "source": { 1464 | "type": "git", 1465 | "url": "https://github.com/webmozart/assert.git", 1466 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a" 1467 | }, 1468 | "dist": { 1469 | "type": "zip", 1470 | "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", 1471 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a", 1472 | "shasum": "" 1473 | }, 1474 | "require": { 1475 | "php": "^5.3.3 || ^7.0" 1476 | }, 1477 | "require-dev": { 1478 | "phpunit/phpunit": "^4.6", 1479 | "sebastian/version": "^1.0.1" 1480 | }, 1481 | "type": "library", 1482 | "extra": { 1483 | "branch-alias": { 1484 | "dev-master": "1.3-dev" 1485 | } 1486 | }, 1487 | "autoload": { 1488 | "psr-4": { 1489 | "Webmozart\\Assert\\": "src/" 1490 | } 1491 | }, 1492 | "notification-url": "https://packagist.org/downloads/", 1493 | "license": [ 1494 | "MIT" 1495 | ], 1496 | "authors": [ 1497 | { 1498 | "name": "Bernhard Schussek", 1499 | "email": "bschussek@gmail.com" 1500 | } 1501 | ], 1502 | "description": "Assertions to validate method input/output with nice error messages.", 1503 | "keywords": [ 1504 | "assert", 1505 | "check", 1506 | "validate" 1507 | ], 1508 | "time": "2018-01-29T19:49:41+00:00" 1509 | } 1510 | ], 1511 | "aliases": [], 1512 | "minimum-stability": "stable", 1513 | "stability-flags": [], 1514 | "prefer-stable": false, 1515 | "prefer-lowest": false, 1516 | "platform": { 1517 | "php": ">=7.2" 1518 | }, 1519 | "platform-dev": [] 1520 | } 1521 | -------------------------------------------------------------------------------- /docs/exception-handling/index.md: -------------------------------------------------------------------------------- 1 | # Exception Handling 2 | 3 | You should catch all exceptions and transform them via `JsonApiServer::handleException` into a json api response, which can be handled like a normal document response. 4 | 5 | ***** 6 | 7 | [prev: Requests](../requests/index.md) | [back to README](../../README.md) 8 | -------------------------------------------------------------------------------- /docs/json-api-server/index.md: -------------------------------------------------------------------------------- 1 | # JSON API Server 2 | 3 | `Enm\JsonApi\Server\JsonApiServer`: 4 | 5 | | Method | Return Type | Description | 6 | |-------------------------------------------------------------|------------------------|----------------------------------------------------------------| 7 | | addHandler(string $type, RequestHandlerInterface $handler) | void | Adds a request handler | 8 | | createRequestBody(?string $requestBody) | DocumentInterface|null | Creates a document from the given string | 9 | | handleRequest(RequestInterface $request) | ResponseInterface | Handles a request to generate a response | 10 | | createResponseBody(ResponseInterface $response) | string | Creates the (http) response body for a given json api response | 11 | | handleException(\Throwable $throwable, bool $debug = false) | ResponseInterface | Creates a response for an exception | 12 | 13 | ## Table Of Contents 14 | 15 | 1. [Endpoints](#endpoints) 16 | 1. [Usage](#usage) 17 | 18 | ## Endpoints 19 | 20 | | HTTP-Method | URL-Path (without prefix) | Server Action | 21 | |-------------|----------------------------------------------|------------------------------------------------------------------------------------------------------------------| 22 | | GET | /{type} | The server creates a fetch request and calls method "findResources" of the request handler. | 23 | | GET | /{type}/{id} | The server creates a fetch request and calls method "findResource" of the request handler. | 24 | | GET | /{type}/{id}/relationships/{relationshipName} | The server creates a fetch request and calls method "findRelationship" of the request handler. | 25 | | GET | /{type}/{id}/{relationshipName} | The server creates a fetch request and calls method "findRelationship" of the request handler. | 26 | | POST | /{type} | The server creates a save request and calls method "saveResource" of the request handler. | 27 | | PATCH | /{type}/{id} | The server creates a save request and calls method "saveResource" of the request handler. | 28 | | DELETE | /{type}/{id} | The server creates a simple JSON API request and calls method "deleteResource" of the request handler. | 29 | | POST | /{type}/{id}/relationships/{relationshipName} | The server creates a relationship modification request and calls method "modifyResource" of the request handler. | 30 | | PATCH | /{type}/{id}/relationships/{relationshipName} | The server creates a relationship modification request and calls method "modifyResource" of the request handler. | 31 | | DELETE | /{type}/{id}/relationships/{relationshipName} | The server creates a relationship modification request and calls method "modifyResource" of the request handler. | 32 | 33 | ## Usage 34 | 35 | Here is an example how to use the JSON API server: 36 | 37 | ```php 38 | // create the server 39 | $jsonApi = new JsonApiServer(new Deserializer(), new Serializer()); 40 | 41 | // add your request handlers to the registry of the json api server 42 | $jsonApi->addHandler('customResources', new YourCustomRequestHandler()); 43 | 44 | // create the json api request 45 | $request = new Request( 46 | 'GET', 47 | new \GuzzleHttp\Psr7\Uri('/api/customResources'), 48 | $jsonApi->createRequestBody(file_get_contents('php://input')), 49 | '/api' 50 | ); 51 | 52 | // get a json api response json api request 53 | try{ 54 | $response = $jsonApi->handleHttpRequest($request); 55 | } catch(\Exception $e){ 56 | $response = $jsonApi->handleException($e); 57 | } 58 | 59 | // send the response back to requesting HTTP client... 60 | header('HTTP/1.1 '.$response->status()); 61 | 62 | foreach ($response->headers()->all() as $header => $value){ 63 | header($header.': '.$value); 64 | } 65 | 66 | echo $jsonApi->createResponseBody($response); 67 | 68 | ``` 69 | 70 | ***** 71 | 72 | [back to README](../../README.md) | [next: Request Handler](../request-handler/index.md) 73 | -------------------------------------------------------------------------------- /docs/request-handler/index.md: -------------------------------------------------------------------------------- 1 | # Request Handler 2 | 3 | ## Table Of Contents 4 | 5 | 1. [Concept](#concept) 6 | 1. [Interface](#interface) 7 | 1. [Usage](#usage) 8 | 9 | ## Concept 10 | 11 | Request handlers are responsible for turning a JSON API request into a JSON API response, which contains the requested resources (in a document) and all headers. 12 | 13 | The document in the response will be normalized by the json api server and could be serialized by `JsonApiServer::createResponsebBody` to return it as http response. 14 | 15 | ## Interface 16 | 17 | | Method | Return Type | Description | 18 | |----------------------------------------------------|-------------------|----------------------------------------------------------| 19 | | fetchResource(RequestInterface $request) | ResponseInterface | Fetch a single resource | 20 | | fetchResources(RequestInterface $request) | ResponseInterface | Fetch a resource collection | 21 | | fetchRelationship(RequestInterface $request) | ResponseInterface | Fetch a relationship | 22 | | createResource(RequestInterface $request) | ResponseInterface | Create a new resource | 23 | | patchResource(RequestInterface $request) | ResponseInterface | Modify an existing resource | 24 | | deleteResource(RequestInterface $request) | ResponseInterface | Delete a resource | 25 | | addRelatedResources(RequestInterface $request) | ResponseInterface | Add resources to a relationship | 26 | | replaceRelatedResources(RequestInterface $request) | ResponseInterface | Replace resources of a relationship with other resources | 27 | | removeRelatedResources(RequestInterface $request) | ResponseInterface | Remove resources from a relationship | 28 | 29 | ## Usage 30 | 31 | You can use one of the following traits if you does not need all features: 32 | 33 | * NoResourceFetchTrait 34 | * NoRelationshipFetchTrait 35 | * NoResourceModificationTrait 36 | * NoResourceDeletionTrait 37 | * NoRelationshipModificationTrait 38 | 39 | The traits implement the methods of `RequestHandlerInterface` with throwing a NotAllowedException for the respective action. 40 | 41 | ***** 42 | 43 | [prev: JSON API Server](../json-api-server/index.md) | [back to README](../../README.md) 44 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | tests 13 | 14 | 15 | 16 | 17 | src 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/JsonApiServer.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class JsonApiServer 24 | { 25 | use JsonApiTrait; 26 | 27 | /** 28 | * @var DocumentDeserializerInterface 29 | */ 30 | private $deserializer; 31 | 32 | /** 33 | * @var DocumentSerializerInterface 34 | */ 35 | private $serializer; 36 | 37 | /** 38 | * @var RequestHandlerInterface[] 39 | */ 40 | private $handlers = []; 41 | 42 | /** 43 | * @param DocumentDeserializerInterface|null $deserializer 44 | * @param DocumentSerializerInterface|null $serializer 45 | */ 46 | public function __construct( 47 | ?DocumentDeserializerInterface $deserializer = null, 48 | ?DocumentSerializerInterface $serializer = null 49 | ) { 50 | $this->deserializer = $deserializer; 51 | $this->serializer = $serializer; 52 | } 53 | 54 | /** 55 | * @param string|null $requestBody 56 | * @return DocumentInterface|null 57 | */ 58 | public function createRequestBody(?string $requestBody): ?DocumentInterface 59 | { 60 | return (string)$requestBody !== '' ? 61 | $this->deserializer->deserializeDocument(json_decode($requestBody, true)) : null; 62 | } 63 | 64 | /** 65 | * Adds a request handler 66 | * 67 | * @param string $type 68 | * @param RequestHandlerInterface $handler 69 | */ 70 | public function addHandler(string $type, RequestHandlerInterface $handler): void 71 | { 72 | $this->handlers[$type] = $handler; 73 | } 74 | 75 | /** 76 | * @param RequestInterface $request 77 | * @return ResponseInterface 78 | * @throws BadRequestException 79 | * @throws UnsupportedTypeException 80 | * @throws UnsupportedMediaTypeException 81 | */ 82 | public function handleRequest(RequestInterface $request): ResponseInterface 83 | { 84 | if ($request->headers()->getRequired('Content-Type') !== 'application/vnd.api+json') { 85 | throw new UnsupportedMediaTypeException($request->headers()->getRequired('Content-Type')); 86 | } 87 | 88 | switch ($request->method()) { 89 | case 'GET': 90 | if ($request->id()) { 91 | if ($request->relationship()) { 92 | $response = $this->getHandler($request->type())->fetchRelationship($request); 93 | break; 94 | } 95 | $response = $this->getHandler($request->type())->fetchResource($request); 96 | break; 97 | } 98 | $response = $this->getHandler($request->type())->fetchResources($request); 99 | break; 100 | case 'POST': 101 | if ($request->relationship()) { 102 | $response = $this->getHandler($request->type())->addRelatedResources($request); 103 | break; 104 | } 105 | $response = $this->getHandler($request->type())->createResource($request); 106 | break; 107 | case 'PATCH': 108 | if ($request->relationship()) { 109 | $response = $this->getHandler($request->type())->replaceRelatedResources($request); 110 | break; 111 | } 112 | $response = $this->getHandler($request->type())->patchResource($request); 113 | break; 114 | case 'DELETE': 115 | if ($request->relationship()) { 116 | $response = $this->getHandler($request->type())->removeRelatedResources($request); 117 | break; 118 | } 119 | $response = $this->getHandler($request->type())->deleteResource($request); 120 | break; 121 | default: 122 | throw new BadRequestException('Something was wrong...'); 123 | } 124 | 125 | $document = $response->document(); 126 | if ($document) { 127 | foreach ($document->data()->all() as $resource) { 128 | $this->includeRelated($document, $resource, $request); 129 | $this->cleanUpResource($resource, $request); 130 | } 131 | 132 | } 133 | 134 | return $response; 135 | } 136 | 137 | /** 138 | * @param ResponseInterface $response 139 | * @return string 140 | */ 141 | public function createResponseBody(ResponseInterface $response): string 142 | { 143 | return $response->document() ? json_encode($this->serializer->serializeDocument($response->document())) : ''; 144 | } 145 | 146 | /** 147 | * @param \Throwable $throwable 148 | * @param bool $debug 149 | * 150 | * @return ResponseInterface 151 | */ 152 | public function handleException(\Throwable $throwable, bool $debug = false): ResponseInterface 153 | { 154 | $apiError = Error::createFrom($throwable, $debug); 155 | 156 | $document = $this->singleResourceDocument(); 157 | $document->errors()->add($apiError); 158 | 159 | 160 | return new DocumentResponse($document, null, $apiError->status()); 161 | } 162 | 163 | /** 164 | * @param string $type 165 | * @return RequestHandlerInterface 166 | * @throws UnsupportedTypeException 167 | */ 168 | private function getHandler(string $type): RequestHandlerInterface 169 | { 170 | if (!array_key_exists($type, $this->handlers)) { 171 | throw new UnsupportedTypeException($type); 172 | } 173 | 174 | return $this->handlers[$type]; 175 | } 176 | 177 | 178 | /** 179 | * @param DocumentInterface $document 180 | * @param ResourceInterface $resource 181 | * @param RequestInterface $request 182 | * 183 | * @return void 184 | */ 185 | protected function includeRelated( 186 | DocumentInterface $document, 187 | ResourceInterface $resource, 188 | RequestInterface $request 189 | ): void { 190 | foreach ($resource->relationships()->all() as $relationship) { 191 | $shouldIncludeRelationship = $request->requestsInclude($relationship->name()); 192 | $subRequest = $request->createSubRequest($relationship->name(), $resource); 193 | foreach ($relationship->related()->all() as $related) { 194 | if ($shouldIncludeRelationship) { 195 | $document->included()->merge($related); 196 | $this->cleanUpResource($document->included()->get($related->type(), $related->id()), $subRequest); 197 | } 198 | $this->includeRelated($document, $related, $subRequest); 199 | } 200 | } 201 | } 202 | 203 | /** 204 | * @param ResourceInterface $resource 205 | * @param RequestInterface $request 206 | */ 207 | protected function cleanUpResource(ResourceInterface $resource, RequestInterface $request): void 208 | { 209 | foreach ($resource->attributes()->all() as $key => $value) { 210 | if (!$request->requestsAttributes() || !$request->requestsField($resource->type(), $key)) { 211 | $resource->attributes()->remove($key); 212 | } 213 | } 214 | 215 | if (!$request->requestsRelationships()) { 216 | foreach ($resource->relationships()->all() as $relationship) { 217 | $resource->relationships()->removeElement($relationship); 218 | } 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/RequestHandler/NoRelationshipFetchTrait.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | trait NoRelationshipFetchTrait 14 | { 15 | /** 16 | * @param RequestInterface $request 17 | * @return ResponseInterface 18 | * @throws NotAllowedException 19 | */ 20 | public function fetchRelationship(RequestInterface $request): ResponseInterface 21 | { 22 | throw new NotAllowedException('You are not allowed to fetch the relationship ' . $request->relationship()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/RequestHandler/NoRelationshipModificationTrait.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | trait NoRelationshipModificationTrait 14 | { 15 | /** 16 | * @param RequestInterface $request 17 | * @return ResponseInterface 18 | * @throws NotAllowedException 19 | */ 20 | public function addRelatedResources(RequestInterface $request): ResponseInterface 21 | { 22 | throw new NotAllowedException('You are not allowed to modify the relationship ' . $request->relationship()); 23 | } 24 | 25 | /** 26 | * @param RequestInterface $request 27 | * @return ResponseInterface 28 | * @throws NotAllowedException 29 | */ 30 | public function replaceRelatedResources(RequestInterface $request): ResponseInterface 31 | { 32 | throw new NotAllowedException('You are not allowed to modify the relationship ' . $request->relationship()); 33 | } 34 | 35 | /** 36 | * @param RequestInterface $request 37 | * @return ResponseInterface 38 | * @throws NotAllowedException 39 | */ 40 | public function removeRelatedResources(RequestInterface $request): ResponseInterface 41 | { 42 | throw new NotAllowedException('You are not allowed to modify the relationship ' . $request->relationship()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/RequestHandler/NoResourceDeletionTrait.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | trait NoResourceDeletionTrait 14 | { 15 | /** 16 | * @param RequestInterface $request 17 | * @return ResponseInterface 18 | * @throws NotAllowedException 19 | */ 20 | public function deleteResource(RequestInterface $request): ResponseInterface 21 | { 22 | throw new NotAllowedException('You are not allowed to delete a resource of type ' . $request->type()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/RequestHandler/NoResourceFetchTrait.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | trait NoResourceFetchTrait 14 | { 15 | /** 16 | * @param RequestInterface $request 17 | * @return ResponseInterface 18 | * @throws NotAllowedException 19 | */ 20 | public function fetchResource(RequestInterface $request): ResponseInterface 21 | { 22 | throw new NotAllowedException('You are not allowed to fetch a resource of type ' . $request->type()); 23 | } 24 | 25 | /** 26 | * @param RequestInterface $request 27 | * @return ResponseInterface 28 | * @throws NotAllowedException 29 | */ 30 | public function fetchResources(RequestInterface $request): ResponseInterface 31 | { 32 | throw new NotAllowedException('You are not allowed to fetch a resource of type ' . $request->type()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/RequestHandler/NoResourceModificationTrait.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | trait NoResourceModificationTrait 14 | { 15 | /** 16 | * @param RequestInterface $request 17 | * @return ResponseInterface 18 | * @throws NotAllowedException 19 | */ 20 | public function createResource(RequestInterface $request): ResponseInterface 21 | { 22 | throw new NotAllowedException('You are not allowed to create a resource of type ' . $request->type()); 23 | } 24 | 25 | /** 26 | * @param RequestInterface $request 27 | * @return ResponseInterface 28 | * @throws NotAllowedException 29 | */ 30 | public function patchResource(RequestInterface $request): ResponseInterface 31 | { 32 | throw new NotAllowedException('You are not allowed to modify a resource of type ' . $request->type()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/RequestHandler/RequestHandlerInterface.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | interface RequestHandlerInterface 13 | { 14 | /** 15 | * @param RequestInterface $request 16 | * @return ResponseInterface 17 | */ 18 | public function fetchResource(RequestInterface $request): ResponseInterface; 19 | 20 | /** 21 | * @param RequestInterface $request 22 | * @return ResponseInterface 23 | */ 24 | public function fetchResources(RequestInterface $request): ResponseInterface; 25 | 26 | /** 27 | * @param RequestInterface $request 28 | * @return ResponseInterface 29 | */ 30 | public function fetchRelationship(RequestInterface $request): ResponseInterface; 31 | 32 | /** 33 | * @param RequestInterface $request 34 | * @return ResponseInterface 35 | */ 36 | public function createResource(RequestInterface $request): ResponseInterface; 37 | 38 | /** 39 | * @param RequestInterface $request 40 | * @return ResponseInterface 41 | */ 42 | public function patchResource(RequestInterface $request): ResponseInterface; 43 | 44 | /** 45 | * @param RequestInterface $request 46 | * @return ResponseInterface 47 | */ 48 | public function deleteResource(RequestInterface $request): ResponseInterface; 49 | 50 | /** 51 | * @param RequestInterface $request 52 | * @return ResponseInterface 53 | */ 54 | public function addRelatedResources(RequestInterface $request): ResponseInterface; 55 | 56 | /** 57 | * @param RequestInterface $request 58 | * @return ResponseInterface 59 | */ 60 | public function replaceRelatedResources(RequestInterface $request): ResponseInterface; 61 | 62 | /** 63 | * @param RequestInterface $request 64 | * @return ResponseInterface 65 | */ 66 | public function removeRelatedResources(RequestInterface $request): ResponseInterface; 67 | } 68 | -------------------------------------------------------------------------------- /tests/JsonApiServerTest.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class JsonApiServerTest extends TestCase 16 | { 17 | public function testCreateEmptyRequestBody(): void 18 | { 19 | $body = $this->createJsonApiServer()->createRequestBody(null); 20 | $this->assertNull($body); 21 | } 22 | 23 | public function testCreateRequestBody(): void 24 | { 25 | $body = $this->createJsonApiServer()->createRequestBody('{"data":[]}'); 26 | $this->assertInstanceOf(DocumentInterface::class, $body); 27 | } 28 | 29 | public function testHandleException(): void 30 | { 31 | $api = $this->createJsonApiServer(); 32 | $e = $api->handleException(new \Exception('Test')); 33 | 34 | $this->assertEquals(500, $e->status()); 35 | $this->assertEquals('application/vnd.api+json', $e->headers()->getOptional('Content-Type')); 36 | $this->assertEquals(1, $e->document()->errors()->count()); 37 | $this->assertEquals('Test', $e->document()->errors()->all()[0]->title()); 38 | } 39 | 40 | /** 41 | * @return JsonApiServer 42 | */ 43 | protected function createJsonApiServer(): JsonApiServer 44 | { 45 | /** @var DocumentDeserializerInterface $deserializer */ 46 | $deserializer = $this->createMock(DocumentDeserializerInterface::class); 47 | /** @var DocumentSerializerInterface $serializer */ 48 | $serializer = $this->createMock(DocumentSerializerInterface::class); 49 | 50 | return new JsonApiServer($deserializer, $serializer); 51 | } 52 | } 53 | --------------------------------------------------------------------------------