├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── adr.yml ├── bin └── phpadr ├── composer.json ├── composer.lock ├── docs └── arch │ ├── 0001-documenting-architecture-decisions.md │ ├── 0002-develop-tool-to-manage-adrs.md │ ├── 0003-php-as-scripting-language.md │ ├── 0004-composer-as-dependency-management.md │ ├── 0005-phpunit-as-testing-framework.md │ └── 0006-yaml-as-configuration-file.md ├── phpunit.xml ├── src ├── Command │ ├── MakeDecisionCommand.php │ ├── WorkspaceCountCommand.php │ └── WorkspaceListCommand.php ├── Domain │ ├── DecisionContent.php │ ├── DecisionRecord.php │ └── Sequence.php └── Filesystem │ ├── Config.php │ └── Workspace.php ├── template └── skeleton.md └── tests ├── Command ├── MakeDecisionCommandTest.php ├── WorkspaceCountCommandTest.php └── WorkspaceListCommandTest.php ├── Domain ├── DecisionContentTest.php ├── DecisionRecordTest.php └── SequenceTest.php └── Filesystem ├── ConfigTest.php └── WorkspaceTest.php /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | coverage_clover: build/logs/clover.xml 3 | json_path: build/logs/coveralls-upload.json -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Composer 2 | /vendor/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.2.5 5 | 6 | before_install: 7 | - composer self-update 8 | - composer install 9 | 10 | before_script: 11 | - mkdir -p build/logs 12 | - ls -la 13 | 14 | script: 15 | - ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml 16 | 17 | after_success: 18 | - ./vendor/bin/php-coveralls -v 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 José Carlos Soares de Souza 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 | # ADR Tools in PHP 2 | 3 | [![Build Status](https://travis-ci.com/globtec/phpadr.svg?branch=master)](https://travis-ci.com/globtec/phpadr) 4 | [![Coverage Status](https://coveralls.io/repos/github/globtec/phpadr/badge.svg?branch=master)](https://coveralls.io/github/globtec/phpadr?branch=master) 5 | 6 | A PHP based command-line interface tool for working with Architecture Decision Records (ADR). 7 | 8 | ## About ADR 9 | 10 | Architecture Decision Records (ADR) is a technique for capturing important architectural decisions, along with their context and consequences as described by [Michael Nygard](https://twitter.com/mtnygard) in his article: [Documenting Architecture Decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). 11 | 12 | ## Requirements 13 | 14 | * Requires PHP version 7.1.3 or newer 15 | * Multibyte String extension 16 | 17 | ## Installation 18 | 19 | You can install this tool using the [Composer](https://getcomposer.org/), execute the following command. 20 | 21 | ``` 22 | composer require globtec/phpadr --dev 23 | ``` 24 | 25 | ## Usage 26 | 27 | After of install this project you may execute the binary `phpadr` in your terminal: 28 | 29 | ``` 30 | ./vendor/bin/phpadr 31 | ``` 32 | 33 | If to execute the command above, it will be showd a list of all avaliable tool commands. 34 | 35 | By default the records will be stored in `docs/arch`, to change this workspace use the option `--config` with the path of the config file. 36 | 37 | ### Create a new ADR 38 | 39 | You may use the `make:decision` command: 40 | 41 | ``` 42 | ./vendor/bin/phpadr make:decision [<status="Accepted">] [--config="adr.yml"] 43 | ``` 44 | 45 | ### Count the ADRs 46 | 47 | You may use the `workspace:count` command: 48 | 49 | ``` 50 | ./vendor/bin/phpadr workspace:count [--config="adr.yml"] 51 | ``` 52 | 53 | ### List the ADRs 54 | 55 | You may use the `workspace:list` command: 56 | 57 | ``` 58 | ./vendor/bin/phpadr workspace:list [--config="adr.yml"] 59 | ``` 60 | 61 | ### Help 62 | 63 | For more help execute the following command: 64 | 65 | ``` 66 | ./vendor/bin/phpadr <command> --help 67 | ``` -------------------------------------------------------------------------------- /adr.yml: -------------------------------------------------------------------------------- 1 | directory: docs/arch 2 | template: 3 | decision-record: vendor/globtec/phpadr/template/skeleton.md -------------------------------------------------------------------------------- /bin/phpadr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | <?php 3 | 4 | $autoloaders = [ 5 | __DIR__ . '/../../../autoload.php', 6 | __DIR__ . '/../vendor/autoload.php', 7 | __DIR__ . '/vendor/autoload.php', 8 | ]; 9 | 10 | foreach ($autoloaders as $filename) { 11 | if (file_exists($filename)) { 12 | define('AUTOLOAD', $filename); 13 | break; 14 | } 15 | } 16 | 17 | unset($filename); 18 | 19 | if (! defined('AUTOLOAD')) { 20 | fwrite(STDERR, 'Dependencies not found, be sure to run `composer install`. See https://getcomposer.org to get Composer'); 21 | die(1); 22 | } 23 | 24 | require AUTOLOAD; 25 | 26 | use Symfony\Component\Console\Application; 27 | use ADR\Command\MakeDecisionCommand; 28 | use ADR\Command\WorkspaceCountCommand; 29 | use ADR\Command\WorkspaceListCommand; 30 | 31 | $app = new Application('PHP ADR Tools'); 32 | 33 | $app->add(new MakeDecisionCommand()); 34 | $app->add(new WorkspaceCountCommand()); 35 | $app->add(new WorkspaceListCommand()); 36 | 37 | $app->run(); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "globtec/phpadr", 3 | "description": "A PHP based command-line interface tool for working with ADR", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": [ 7 | "adr", 8 | "architecture-decision-records", 9 | "architecture-decision", 10 | "documentation", 11 | "agility" 12 | ], 13 | "authors": [ 14 | { 15 | "name": "José Carlos", 16 | "email": "josecarlos@globtec.com.br" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.2.5 || ^8.0", 21 | "ext-mbstring": "*", 22 | "symfony/console": "^4.4|^5.0|^6.0", 23 | "symfony/yaml": "^4.4|^5.0|^6.0" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "^8.0.0", 27 | "mikey179/vfsstream": "^1.6", 28 | "php-coveralls/php-coveralls" : "v2.0.0" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "ADR\\": "src/" 33 | } 34 | }, 35 | "bin": [ 36 | "bin/phpadr" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /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": "5ecf4d4537463436346a0b6192099bed", 8 | "packages": [ 9 | { 10 | "name": "psr/container", 11 | "version": "1.0.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/php-fig/container.git", 15 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 20 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "type": "library", 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "1.0.x-dev" 30 | } 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Psr\\Container\\": "src/" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "PHP-FIG", 44 | "homepage": "http://www.php-fig.org/" 45 | } 46 | ], 47 | "description": "Common Container Interface (PHP FIG PSR-11)", 48 | "homepage": "https://github.com/php-fig/container", 49 | "keywords": [ 50 | "PSR-11", 51 | "container", 52 | "container-interface", 53 | "container-interop", 54 | "psr" 55 | ], 56 | "support": { 57 | "issues": "https://github.com/php-fig/container/issues", 58 | "source": "https://github.com/php-fig/container/tree/master" 59 | }, 60 | "time": "2017-02-14T16:28:37+00:00" 61 | }, 62 | { 63 | "name": "symfony/console", 64 | "version": "4.4.x-dev", 65 | "source": { 66 | "type": "git", 67 | "url": "https://github.com/symfony/console.git", 68 | "reference": "2ac7479be375201150cff3363ee35dfda9c57c7b" 69 | }, 70 | "dist": { 71 | "type": "zip", 72 | "url": "https://api.github.com/repos/symfony/console/zipball/2ac7479be375201150cff3363ee35dfda9c57c7b", 73 | "reference": "2ac7479be375201150cff3363ee35dfda9c57c7b", 74 | "shasum": "" 75 | }, 76 | "require": { 77 | "php": ">=7.1.3", 78 | "symfony/polyfill-mbstring": "~1.0", 79 | "symfony/polyfill-php73": "^1.8", 80 | "symfony/polyfill-php80": "^1.15", 81 | "symfony/service-contracts": "^1.1|^2" 82 | }, 83 | "conflict": { 84 | "symfony/dependency-injection": "<3.4", 85 | "symfony/event-dispatcher": "<4.3|>=5", 86 | "symfony/lock": "<4.4", 87 | "symfony/process": "<3.3" 88 | }, 89 | "provide": { 90 | "psr/log-implementation": "1.0" 91 | }, 92 | "require-dev": { 93 | "psr/log": "~1.0", 94 | "symfony/config": "^3.4|^4.0|^5.0", 95 | "symfony/dependency-injection": "^3.4|^4.0|^5.0", 96 | "symfony/event-dispatcher": "^4.3", 97 | "symfony/lock": "^4.4|^5.0", 98 | "symfony/process": "^3.4|^4.0|^5.0", 99 | "symfony/var-dumper": "^4.3|^5.0" 100 | }, 101 | "suggest": { 102 | "psr/log": "For using the console logger", 103 | "symfony/event-dispatcher": "", 104 | "symfony/lock": "", 105 | "symfony/process": "" 106 | }, 107 | "type": "library", 108 | "autoload": { 109 | "psr-4": { 110 | "Symfony\\Component\\Console\\": "" 111 | }, 112 | "exclude-from-classmap": [ 113 | "/Tests/" 114 | ] 115 | }, 116 | "notification-url": "https://packagist.org/downloads/", 117 | "license": [ 118 | "MIT" 119 | ], 120 | "authors": [ 121 | { 122 | "name": "Fabien Potencier", 123 | "email": "fabien@symfony.com" 124 | }, 125 | { 126 | "name": "Symfony Community", 127 | "homepage": "https://symfony.com/contributors" 128 | } 129 | ], 130 | "description": "Eases the creation of beautiful and testable command line interfaces", 131 | "homepage": "https://symfony.com", 132 | "support": { 133 | "source": "https://github.com/symfony/console/tree/4.4" 134 | }, 135 | "funding": [ 136 | { 137 | "url": "https://symfony.com/sponsor", 138 | "type": "custom" 139 | }, 140 | { 141 | "url": "https://github.com/fabpot", 142 | "type": "github" 143 | }, 144 | { 145 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 146 | "type": "tidelift" 147 | } 148 | ], 149 | "time": "2021-04-08T07:40:10+00:00" 150 | }, 151 | { 152 | "name": "symfony/polyfill-ctype", 153 | "version": "dev-main", 154 | "source": { 155 | "type": "git", 156 | "url": "https://github.com/symfony/polyfill-ctype.git", 157 | "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" 158 | }, 159 | "dist": { 160 | "type": "zip", 161 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", 162 | "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", 163 | "shasum": "" 164 | }, 165 | "require": { 166 | "php": ">=7.1" 167 | }, 168 | "suggest": { 169 | "ext-ctype": "For best performance" 170 | }, 171 | "default-branch": true, 172 | "type": "library", 173 | "extra": { 174 | "branch-alias": { 175 | "dev-main": "1.22-dev" 176 | }, 177 | "thanks": { 178 | "name": "symfony/polyfill", 179 | "url": "https://github.com/symfony/polyfill" 180 | } 181 | }, 182 | "autoload": { 183 | "psr-4": { 184 | "Symfony\\Polyfill\\Ctype\\": "" 185 | }, 186 | "files": [ 187 | "bootstrap.php" 188 | ] 189 | }, 190 | "notification-url": "https://packagist.org/downloads/", 191 | "license": [ 192 | "MIT" 193 | ], 194 | "authors": [ 195 | { 196 | "name": "Gert de Pagter", 197 | "email": "BackEndTea@gmail.com" 198 | }, 199 | { 200 | "name": "Symfony Community", 201 | "homepage": "https://symfony.com/contributors" 202 | } 203 | ], 204 | "description": "Symfony polyfill for ctype functions", 205 | "homepage": "https://symfony.com", 206 | "keywords": [ 207 | "compatibility", 208 | "ctype", 209 | "polyfill", 210 | "portable" 211 | ], 212 | "support": { 213 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" 214 | }, 215 | "funding": [ 216 | { 217 | "url": "https://symfony.com/sponsor", 218 | "type": "custom" 219 | }, 220 | { 221 | "url": "https://github.com/fabpot", 222 | "type": "github" 223 | }, 224 | { 225 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 226 | "type": "tidelift" 227 | } 228 | ], 229 | "time": "2021-01-07T16:49:33+00:00" 230 | }, 231 | { 232 | "name": "symfony/polyfill-mbstring", 233 | "version": "dev-main", 234 | "source": { 235 | "type": "git", 236 | "url": "https://github.com/symfony/polyfill-mbstring.git", 237 | "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" 238 | }, 239 | "dist": { 240 | "type": "zip", 241 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", 242 | "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", 243 | "shasum": "" 244 | }, 245 | "require": { 246 | "php": ">=7.1" 247 | }, 248 | "suggest": { 249 | "ext-mbstring": "For best performance" 250 | }, 251 | "default-branch": true, 252 | "type": "library", 253 | "extra": { 254 | "branch-alias": { 255 | "dev-main": "1.22-dev" 256 | }, 257 | "thanks": { 258 | "name": "symfony/polyfill", 259 | "url": "https://github.com/symfony/polyfill" 260 | } 261 | }, 262 | "autoload": { 263 | "psr-4": { 264 | "Symfony\\Polyfill\\Mbstring\\": "" 265 | }, 266 | "files": [ 267 | "bootstrap.php" 268 | ] 269 | }, 270 | "notification-url": "https://packagist.org/downloads/", 271 | "license": [ 272 | "MIT" 273 | ], 274 | "authors": [ 275 | { 276 | "name": "Nicolas Grekas", 277 | "email": "p@tchwork.com" 278 | }, 279 | { 280 | "name": "Symfony Community", 281 | "homepage": "https://symfony.com/contributors" 282 | } 283 | ], 284 | "description": "Symfony polyfill for the Mbstring extension", 285 | "homepage": "https://symfony.com", 286 | "keywords": [ 287 | "compatibility", 288 | "mbstring", 289 | "polyfill", 290 | "portable", 291 | "shim" 292 | ], 293 | "support": { 294 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" 295 | }, 296 | "funding": [ 297 | { 298 | "url": "https://symfony.com/sponsor", 299 | "type": "custom" 300 | }, 301 | { 302 | "url": "https://github.com/fabpot", 303 | "type": "github" 304 | }, 305 | { 306 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 307 | "type": "tidelift" 308 | } 309 | ], 310 | "time": "2021-01-22T09:19:47+00:00" 311 | }, 312 | { 313 | "name": "symfony/polyfill-php73", 314 | "version": "dev-main", 315 | "source": { 316 | "type": "git", 317 | "url": "https://github.com/symfony/polyfill-php73.git", 318 | "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" 319 | }, 320 | "dist": { 321 | "type": "zip", 322 | "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", 323 | "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", 324 | "shasum": "" 325 | }, 326 | "require": { 327 | "php": ">=7.1" 328 | }, 329 | "default-branch": true, 330 | "type": "library", 331 | "extra": { 332 | "branch-alias": { 333 | "dev-main": "1.22-dev" 334 | }, 335 | "thanks": { 336 | "name": "symfony/polyfill", 337 | "url": "https://github.com/symfony/polyfill" 338 | } 339 | }, 340 | "autoload": { 341 | "psr-4": { 342 | "Symfony\\Polyfill\\Php73\\": "" 343 | }, 344 | "files": [ 345 | "bootstrap.php" 346 | ], 347 | "classmap": [ 348 | "Resources/stubs" 349 | ] 350 | }, 351 | "notification-url": "https://packagist.org/downloads/", 352 | "license": [ 353 | "MIT" 354 | ], 355 | "authors": [ 356 | { 357 | "name": "Nicolas Grekas", 358 | "email": "p@tchwork.com" 359 | }, 360 | { 361 | "name": "Symfony Community", 362 | "homepage": "https://symfony.com/contributors" 363 | } 364 | ], 365 | "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 366 | "homepage": "https://symfony.com", 367 | "keywords": [ 368 | "compatibility", 369 | "polyfill", 370 | "portable", 371 | "shim" 372 | ], 373 | "support": { 374 | "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" 375 | }, 376 | "funding": [ 377 | { 378 | "url": "https://symfony.com/sponsor", 379 | "type": "custom" 380 | }, 381 | { 382 | "url": "https://github.com/fabpot", 383 | "type": "github" 384 | }, 385 | { 386 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 387 | "type": "tidelift" 388 | } 389 | ], 390 | "time": "2021-01-07T16:49:33+00:00" 391 | }, 392 | { 393 | "name": "symfony/polyfill-php80", 394 | "version": "dev-main", 395 | "source": { 396 | "type": "git", 397 | "url": "https://github.com/symfony/polyfill-php80.git", 398 | "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" 399 | }, 400 | "dist": { 401 | "type": "zip", 402 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", 403 | "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", 404 | "shasum": "" 405 | }, 406 | "require": { 407 | "php": ">=7.1" 408 | }, 409 | "default-branch": true, 410 | "type": "library", 411 | "extra": { 412 | "branch-alias": { 413 | "dev-main": "1.22-dev" 414 | }, 415 | "thanks": { 416 | "name": "symfony/polyfill", 417 | "url": "https://github.com/symfony/polyfill" 418 | } 419 | }, 420 | "autoload": { 421 | "psr-4": { 422 | "Symfony\\Polyfill\\Php80\\": "" 423 | }, 424 | "files": [ 425 | "bootstrap.php" 426 | ], 427 | "classmap": [ 428 | "Resources/stubs" 429 | ] 430 | }, 431 | "notification-url": "https://packagist.org/downloads/", 432 | "license": [ 433 | "MIT" 434 | ], 435 | "authors": [ 436 | { 437 | "name": "Ion Bazan", 438 | "email": "ion.bazan@gmail.com" 439 | }, 440 | { 441 | "name": "Nicolas Grekas", 442 | "email": "p@tchwork.com" 443 | }, 444 | { 445 | "name": "Symfony Community", 446 | "homepage": "https://symfony.com/contributors" 447 | } 448 | ], 449 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 450 | "homepage": "https://symfony.com", 451 | "keywords": [ 452 | "compatibility", 453 | "polyfill", 454 | "portable", 455 | "shim" 456 | ], 457 | "support": { 458 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" 459 | }, 460 | "funding": [ 461 | { 462 | "url": "https://symfony.com/sponsor", 463 | "type": "custom" 464 | }, 465 | { 466 | "url": "https://github.com/fabpot", 467 | "type": "github" 468 | }, 469 | { 470 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 471 | "type": "tidelift" 472 | } 473 | ], 474 | "time": "2021-01-07T16:49:33+00:00" 475 | }, 476 | { 477 | "name": "symfony/service-contracts", 478 | "version": "v1.1.9", 479 | "source": { 480 | "type": "git", 481 | "url": "https://github.com/symfony/service-contracts.git", 482 | "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26" 483 | }, 484 | "dist": { 485 | "type": "zip", 486 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26", 487 | "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26", 488 | "shasum": "" 489 | }, 490 | "require": { 491 | "php": ">=7.1.3", 492 | "psr/container": "^1.0" 493 | }, 494 | "suggest": { 495 | "symfony/service-implementation": "" 496 | }, 497 | "type": "library", 498 | "extra": { 499 | "branch-alias": { 500 | "dev-master": "1.1-dev" 501 | }, 502 | "thanks": { 503 | "name": "symfony/contracts", 504 | "url": "https://github.com/symfony/contracts" 505 | } 506 | }, 507 | "autoload": { 508 | "psr-4": { 509 | "Symfony\\Contracts\\Service\\": "" 510 | } 511 | }, 512 | "notification-url": "https://packagist.org/downloads/", 513 | "license": [ 514 | "MIT" 515 | ], 516 | "authors": [ 517 | { 518 | "name": "Nicolas Grekas", 519 | "email": "p@tchwork.com" 520 | }, 521 | { 522 | "name": "Symfony Community", 523 | "homepage": "https://symfony.com/contributors" 524 | } 525 | ], 526 | "description": "Generic abstractions related to writing services", 527 | "homepage": "https://symfony.com", 528 | "keywords": [ 529 | "abstractions", 530 | "contracts", 531 | "decoupling", 532 | "interfaces", 533 | "interoperability", 534 | "standards" 535 | ], 536 | "support": { 537 | "source": "https://github.com/symfony/service-contracts/tree/v1.1.9" 538 | }, 539 | "funding": [ 540 | { 541 | "url": "https://symfony.com/sponsor", 542 | "type": "custom" 543 | }, 544 | { 545 | "url": "https://github.com/fabpot", 546 | "type": "github" 547 | }, 548 | { 549 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 550 | "type": "tidelift" 551 | } 552 | ], 553 | "time": "2020-07-06T13:19:58+00:00" 554 | }, 555 | { 556 | "name": "symfony/yaml", 557 | "version": "4.4.x-dev", 558 | "source": { 559 | "type": "git", 560 | "url": "https://github.com/symfony/yaml.git", 561 | "reference": "bcef62d61d629075af009351a8270dca9480090f" 562 | }, 563 | "dist": { 564 | "type": "zip", 565 | "url": "https://api.github.com/repos/symfony/yaml/zipball/bcef62d61d629075af009351a8270dca9480090f", 566 | "reference": "bcef62d61d629075af009351a8270dca9480090f", 567 | "shasum": "" 568 | }, 569 | "require": { 570 | "php": ">=7.1.3", 571 | "symfony/polyfill-ctype": "~1.8" 572 | }, 573 | "conflict": { 574 | "symfony/console": "<3.4" 575 | }, 576 | "require-dev": { 577 | "symfony/console": "^3.4|^4.0|^5.0" 578 | }, 579 | "suggest": { 580 | "symfony/console": "For validating YAML files using the lint command" 581 | }, 582 | "type": "library", 583 | "autoload": { 584 | "psr-4": { 585 | "Symfony\\Component\\Yaml\\": "" 586 | }, 587 | "exclude-from-classmap": [ 588 | "/Tests/" 589 | ] 590 | }, 591 | "notification-url": "https://packagist.org/downloads/", 592 | "license": [ 593 | "MIT" 594 | ], 595 | "authors": [ 596 | { 597 | "name": "Fabien Potencier", 598 | "email": "fabien@symfony.com" 599 | }, 600 | { 601 | "name": "Symfony Community", 602 | "homepage": "https://symfony.com/contributors" 603 | } 604 | ], 605 | "description": "Loads and dumps YAML files", 606 | "homepage": "https://symfony.com", 607 | "support": { 608 | "source": "https://github.com/symfony/yaml/tree/4.4" 609 | }, 610 | "funding": [ 611 | { 612 | "url": "https://symfony.com/sponsor", 613 | "type": "custom" 614 | }, 615 | { 616 | "url": "https://github.com/fabpot", 617 | "type": "github" 618 | }, 619 | { 620 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 621 | "type": "tidelift" 622 | } 623 | ], 624 | "time": "2021-04-05T10:16:45+00:00" 625 | } 626 | ], 627 | "packages-dev": [ 628 | { 629 | "name": "doctrine/instantiator", 630 | "version": "1.5.x-dev", 631 | "source": { 632 | "type": "git", 633 | "url": "https://github.com/doctrine/instantiator.git", 634 | "reference": "6410c4b8352cb64218641457cef64997e6b784fb" 635 | }, 636 | "dist": { 637 | "type": "zip", 638 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/6410c4b8352cb64218641457cef64997e6b784fb", 639 | "reference": "6410c4b8352cb64218641457cef64997e6b784fb", 640 | "shasum": "" 641 | }, 642 | "require": { 643 | "php": "^7.1 || ^8.0" 644 | }, 645 | "require-dev": { 646 | "doctrine/coding-standard": "^8.0", 647 | "ext-pdo": "*", 648 | "ext-phar": "*", 649 | "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", 650 | "phpstan/phpstan": "^0.12", 651 | "phpstan/phpstan-phpunit": "^0.12", 652 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" 653 | }, 654 | "type": "library", 655 | "autoload": { 656 | "psr-4": { 657 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 658 | } 659 | }, 660 | "notification-url": "https://packagist.org/downloads/", 661 | "license": [ 662 | "MIT" 663 | ], 664 | "authors": [ 665 | { 666 | "name": "Marco Pivetta", 667 | "email": "ocramius@gmail.com", 668 | "homepage": "https://ocramius.github.io/" 669 | } 670 | ], 671 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 672 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 673 | "keywords": [ 674 | "constructor", 675 | "instantiate" 676 | ], 677 | "support": { 678 | "issues": "https://github.com/doctrine/instantiator/issues", 679 | "source": "https://github.com/doctrine/instantiator/tree/1.4.x" 680 | }, 681 | "funding": [ 682 | { 683 | "url": "https://www.doctrine-project.org/sponsorship.html", 684 | "type": "custom" 685 | }, 686 | { 687 | "url": "https://www.patreon.com/phpdoctrine", 688 | "type": "patreon" 689 | }, 690 | { 691 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 692 | "type": "tidelift" 693 | } 694 | ], 695 | "time": "2020-11-10T19:05:51+00:00" 696 | }, 697 | { 698 | "name": "guzzlehttp/guzzle", 699 | "version": "6.5.x-dev", 700 | "source": { 701 | "type": "git", 702 | "url": "https://github.com/guzzle/guzzle.git", 703 | "reference": "e8ed4dbf49b260ff129ff0e0400718c3269971bf" 704 | }, 705 | "dist": { 706 | "type": "zip", 707 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/e8ed4dbf49b260ff129ff0e0400718c3269971bf", 708 | "reference": "e8ed4dbf49b260ff129ff0e0400718c3269971bf", 709 | "shasum": "" 710 | }, 711 | "require": { 712 | "ext-json": "*", 713 | "guzzlehttp/promises": "^1.0", 714 | "guzzlehttp/psr7": "^1.6.1", 715 | "php": ">=5.5", 716 | "symfony/polyfill-intl-idn": "^1.17.0" 717 | }, 718 | "require-dev": { 719 | "ext-curl": "*", 720 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", 721 | "psr/log": "^1.1" 722 | }, 723 | "suggest": { 724 | "psr/log": "Required for using the Log middleware" 725 | }, 726 | "type": "library", 727 | "extra": { 728 | "branch-alias": { 729 | "dev-master": "6.5-dev" 730 | } 731 | }, 732 | "autoload": { 733 | "psr-4": { 734 | "GuzzleHttp\\": "src/" 735 | }, 736 | "files": [ 737 | "src/functions_include.php" 738 | ] 739 | }, 740 | "notification-url": "https://packagist.org/downloads/", 741 | "license": [ 742 | "MIT" 743 | ], 744 | "authors": [ 745 | { 746 | "name": "Michael Dowling", 747 | "email": "mtdowling@gmail.com", 748 | "homepage": "https://github.com/mtdowling" 749 | } 750 | ], 751 | "description": "Guzzle is a PHP HTTP client library", 752 | "homepage": "http://guzzlephp.org/", 753 | "keywords": [ 754 | "client", 755 | "curl", 756 | "framework", 757 | "http", 758 | "http client", 759 | "rest", 760 | "web service" 761 | ], 762 | "support": { 763 | "issues": "https://github.com/guzzle/guzzle/issues", 764 | "source": "https://github.com/guzzle/guzzle/tree/6.5" 765 | }, 766 | "funding": [ 767 | { 768 | "url": "https://github.com/GrahamCampbell", 769 | "type": "github" 770 | }, 771 | { 772 | "url": "https://github.com/Nyholm", 773 | "type": "github" 774 | }, 775 | { 776 | "url": "https://github.com/alexeyshockov", 777 | "type": "github" 778 | }, 779 | { 780 | "url": "https://github.com/gmponos", 781 | "type": "github" 782 | }, 783 | { 784 | "url": "https://github.com/sagikazarmark", 785 | "type": "github" 786 | } 787 | ], 788 | "time": "2020-07-02T06:52:04+00:00" 789 | }, 790 | { 791 | "name": "guzzlehttp/promises", 792 | "version": "dev-master", 793 | "source": { 794 | "type": "git", 795 | "url": "https://github.com/guzzle/promises.git", 796 | "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" 797 | }, 798 | "dist": { 799 | "type": "zip", 800 | "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", 801 | "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", 802 | "shasum": "" 803 | }, 804 | "require": { 805 | "php": ">=5.5" 806 | }, 807 | "require-dev": { 808 | "symfony/phpunit-bridge": "^4.4 || ^5.1" 809 | }, 810 | "default-branch": true, 811 | "type": "library", 812 | "extra": { 813 | "branch-alias": { 814 | "dev-master": "1.4-dev" 815 | } 816 | }, 817 | "autoload": { 818 | "psr-4": { 819 | "GuzzleHttp\\Promise\\": "src/" 820 | }, 821 | "files": [ 822 | "src/functions_include.php" 823 | ] 824 | }, 825 | "notification-url": "https://packagist.org/downloads/", 826 | "license": [ 827 | "MIT" 828 | ], 829 | "authors": [ 830 | { 831 | "name": "Michael Dowling", 832 | "email": "mtdowling@gmail.com", 833 | "homepage": "https://github.com/mtdowling" 834 | } 835 | ], 836 | "description": "Guzzle promises library", 837 | "keywords": [ 838 | "promise" 839 | ], 840 | "support": { 841 | "issues": "https://github.com/guzzle/promises/issues", 842 | "source": "https://github.com/guzzle/promises/tree/1.4.1" 843 | }, 844 | "time": "2021-03-07T09:25:29+00:00" 845 | }, 846 | { 847 | "name": "guzzlehttp/psr7", 848 | "version": "1.x-dev", 849 | "source": { 850 | "type": "git", 851 | "url": "https://github.com/guzzle/psr7.git", 852 | "reference": "359b1f4a6fc1f3b97f8ea8ffd5a576f4f5151cff" 853 | }, 854 | "dist": { 855 | "type": "zip", 856 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/359b1f4a6fc1f3b97f8ea8ffd5a576f4f5151cff", 857 | "reference": "359b1f4a6fc1f3b97f8ea8ffd5a576f4f5151cff", 858 | "shasum": "" 859 | }, 860 | "require": { 861 | "php": ">=5.4.0", 862 | "psr/http-message": "~1.0", 863 | "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" 864 | }, 865 | "provide": { 866 | "psr/http-message-implementation": "1.0" 867 | }, 868 | "require-dev": { 869 | "ext-zlib": "*", 870 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" 871 | }, 872 | "suggest": { 873 | "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" 874 | }, 875 | "type": "library", 876 | "extra": { 877 | "branch-alias": { 878 | "dev-master": "1.7-dev" 879 | } 880 | }, 881 | "autoload": { 882 | "psr-4": { 883 | "GuzzleHttp\\Psr7\\": "src/" 884 | }, 885 | "files": [ 886 | "src/functions_include.php" 887 | ] 888 | }, 889 | "notification-url": "https://packagist.org/downloads/", 890 | "license": [ 891 | "MIT" 892 | ], 893 | "authors": [ 894 | { 895 | "name": "Michael Dowling", 896 | "email": "mtdowling@gmail.com", 897 | "homepage": "https://github.com/mtdowling" 898 | }, 899 | { 900 | "name": "Tobias Schultze", 901 | "homepage": "https://github.com/Tobion" 902 | } 903 | ], 904 | "description": "PSR-7 message implementation that also provides common utility methods", 905 | "keywords": [ 906 | "http", 907 | "message", 908 | "psr-7", 909 | "request", 910 | "response", 911 | "stream", 912 | "uri", 913 | "url" 914 | ], 915 | "support": { 916 | "issues": "https://github.com/guzzle/psr7/issues", 917 | "source": "https://github.com/guzzle/psr7/tree/1.x" 918 | }, 919 | "time": "2021-03-21T17:21:22+00:00" 920 | }, 921 | { 922 | "name": "mikey179/vfsstream", 923 | "version": "v1.x-dev", 924 | "source": { 925 | "type": "git", 926 | "url": "https://github.com/bovigo/vfsStream.git", 927 | "reference": "7299174faac59e63a59c20cd775fbbb4f559d04e" 928 | }, 929 | "dist": { 930 | "type": "zip", 931 | "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/7299174faac59e63a59c20cd775fbbb4f559d04e", 932 | "reference": "7299174faac59e63a59c20cd775fbbb4f559d04e", 933 | "shasum": "" 934 | }, 935 | "require": { 936 | "php": ">=5.6.0" 937 | }, 938 | "require-dev": { 939 | "phpunit/phpunit": "^4.5|^5.0" 940 | }, 941 | "type": "library", 942 | "extra": { 943 | "branch-alias": { 944 | "dev-master": "1.7.x-dev" 945 | } 946 | }, 947 | "autoload": { 948 | "psr-4": { 949 | "bovigo\\vfs\\": "src", 950 | "org\\bovigo\\vfs\\": "org/bovigo/vfs" 951 | } 952 | }, 953 | "notification-url": "https://packagist.org/downloads/", 954 | "license": [ 955 | "BSD-3-Clause" 956 | ], 957 | "authors": [ 958 | { 959 | "name": "Frank Kleine", 960 | "homepage": "http://frankkleine.de/", 961 | "role": "Developer" 962 | } 963 | ], 964 | "description": "Virtual file system to mock the real file system in unit tests.", 965 | "homepage": "http://vfs.bovigo.org/", 966 | "support": { 967 | "issues": "https://github.com/bovigo/vfsStream/issues", 968 | "source": "https://github.com/bovigo/vfsStream/tree/master", 969 | "wiki": "https://github.com/bovigo/vfsStream/wiki" 970 | }, 971 | "time": "2020-07-03T22:14:34+00:00" 972 | }, 973 | { 974 | "name": "myclabs/deep-copy", 975 | "version": "1.x-dev", 976 | "source": { 977 | "type": "git", 978 | "url": "https://github.com/myclabs/DeepCopy.git", 979 | "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" 980 | }, 981 | "dist": { 982 | "type": "zip", 983 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", 984 | "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", 985 | "shasum": "" 986 | }, 987 | "require": { 988 | "php": "^7.1 || ^8.0" 989 | }, 990 | "replace": { 991 | "myclabs/deep-copy": "self.version" 992 | }, 993 | "require-dev": { 994 | "doctrine/collections": "^1.0", 995 | "doctrine/common": "^2.6", 996 | "phpunit/phpunit": "^7.1" 997 | }, 998 | "default-branch": true, 999 | "type": "library", 1000 | "autoload": { 1001 | "psr-4": { 1002 | "DeepCopy\\": "src/DeepCopy/" 1003 | }, 1004 | "files": [ 1005 | "src/DeepCopy/deep_copy.php" 1006 | ] 1007 | }, 1008 | "notification-url": "https://packagist.org/downloads/", 1009 | "license": [ 1010 | "MIT" 1011 | ], 1012 | "description": "Create deep copies (clones) of your objects", 1013 | "keywords": [ 1014 | "clone", 1015 | "copy", 1016 | "duplicate", 1017 | "object", 1018 | "object graph" 1019 | ], 1020 | "support": { 1021 | "issues": "https://github.com/myclabs/DeepCopy/issues", 1022 | "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" 1023 | }, 1024 | "funding": [ 1025 | { 1026 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 1027 | "type": "tidelift" 1028 | } 1029 | ], 1030 | "time": "2020-11-13T09:40:50+00:00" 1031 | }, 1032 | { 1033 | "name": "phar-io/manifest", 1034 | "version": "1.0.3", 1035 | "source": { 1036 | "type": "git", 1037 | "url": "https://github.com/phar-io/manifest.git", 1038 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" 1039 | }, 1040 | "dist": { 1041 | "type": "zip", 1042 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 1043 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 1044 | "shasum": "" 1045 | }, 1046 | "require": { 1047 | "ext-dom": "*", 1048 | "ext-phar": "*", 1049 | "phar-io/version": "^2.0", 1050 | "php": "^5.6 || ^7.0" 1051 | }, 1052 | "type": "library", 1053 | "extra": { 1054 | "branch-alias": { 1055 | "dev-master": "1.0.x-dev" 1056 | } 1057 | }, 1058 | "autoload": { 1059 | "classmap": [ 1060 | "src/" 1061 | ] 1062 | }, 1063 | "notification-url": "https://packagist.org/downloads/", 1064 | "license": [ 1065 | "BSD-3-Clause" 1066 | ], 1067 | "authors": [ 1068 | { 1069 | "name": "Arne Blankerts", 1070 | "email": "arne@blankerts.de", 1071 | "role": "Developer" 1072 | }, 1073 | { 1074 | "name": "Sebastian Heuer", 1075 | "email": "sebastian@phpeople.de", 1076 | "role": "Developer" 1077 | }, 1078 | { 1079 | "name": "Sebastian Bergmann", 1080 | "email": "sebastian@phpunit.de", 1081 | "role": "Developer" 1082 | } 1083 | ], 1084 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 1085 | "support": { 1086 | "issues": "https://github.com/phar-io/manifest/issues", 1087 | "source": "https://github.com/phar-io/manifest/tree/master" 1088 | }, 1089 | "time": "2018-07-08T19:23:20+00:00" 1090 | }, 1091 | { 1092 | "name": "phar-io/version", 1093 | "version": "2.0.1", 1094 | "source": { 1095 | "type": "git", 1096 | "url": "https://github.com/phar-io/version.git", 1097 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" 1098 | }, 1099 | "dist": { 1100 | "type": "zip", 1101 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", 1102 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", 1103 | "shasum": "" 1104 | }, 1105 | "require": { 1106 | "php": "^5.6 || ^7.0" 1107 | }, 1108 | "type": "library", 1109 | "autoload": { 1110 | "classmap": [ 1111 | "src/" 1112 | ] 1113 | }, 1114 | "notification-url": "https://packagist.org/downloads/", 1115 | "license": [ 1116 | "BSD-3-Clause" 1117 | ], 1118 | "authors": [ 1119 | { 1120 | "name": "Arne Blankerts", 1121 | "email": "arne@blankerts.de", 1122 | "role": "Developer" 1123 | }, 1124 | { 1125 | "name": "Sebastian Heuer", 1126 | "email": "sebastian@phpeople.de", 1127 | "role": "Developer" 1128 | }, 1129 | { 1130 | "name": "Sebastian Bergmann", 1131 | "email": "sebastian@phpunit.de", 1132 | "role": "Developer" 1133 | } 1134 | ], 1135 | "description": "Library for handling version information and constraints", 1136 | "support": { 1137 | "issues": "https://github.com/phar-io/version/issues", 1138 | "source": "https://github.com/phar-io/version/tree/master" 1139 | }, 1140 | "time": "2018-07-08T19:19:57+00:00" 1141 | }, 1142 | { 1143 | "name": "php-coveralls/php-coveralls", 1144 | "version": "v2.0.0", 1145 | "source": { 1146 | "type": "git", 1147 | "url": "https://github.com/php-coveralls/php-coveralls.git", 1148 | "reference": "3eaf7eb689cdf6b86801a3843940d974dc657068" 1149 | }, 1150 | "dist": { 1151 | "type": "zip", 1152 | "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/3eaf7eb689cdf6b86801a3843940d974dc657068", 1153 | "reference": "3eaf7eb689cdf6b86801a3843940d974dc657068", 1154 | "shasum": "" 1155 | }, 1156 | "require": { 1157 | "ext-json": "*", 1158 | "ext-simplexml": "*", 1159 | "guzzlehttp/guzzle": "^6.0", 1160 | "php": "^5.5 || ^7.0", 1161 | "psr/log": "^1.0", 1162 | "symfony/config": "^2.1 || ^3.0 || ^4.0", 1163 | "symfony/console": "^2.1 || ^3.0 || ^4.0", 1164 | "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0", 1165 | "symfony/yaml": "^2.0 || ^3.0 || ^4.0" 1166 | }, 1167 | "require-dev": { 1168 | "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0" 1169 | }, 1170 | "suggest": { 1171 | "symfony/http-kernel": "Allows Symfony integration" 1172 | }, 1173 | "bin": [ 1174 | "bin/php-coveralls" 1175 | ], 1176 | "type": "library", 1177 | "extra": { 1178 | "branch-alias": { 1179 | "dev-master": "2.0-dev" 1180 | } 1181 | }, 1182 | "autoload": { 1183 | "psr-4": { 1184 | "PhpCoveralls\\": "src/" 1185 | } 1186 | }, 1187 | "notification-url": "https://packagist.org/downloads/", 1188 | "license": [ 1189 | "MIT" 1190 | ], 1191 | "authors": [ 1192 | { 1193 | "name": "Kitamura Satoshi", 1194 | "email": "with.no.parachute@gmail.com", 1195 | "homepage": "https://www.facebook.com/satooshi.jp", 1196 | "role": "Original creator" 1197 | }, 1198 | { 1199 | "name": "Takashi Matsuo", 1200 | "email": "tmatsuo@google.com" 1201 | }, 1202 | { 1203 | "name": "Google Inc" 1204 | }, 1205 | { 1206 | "name": "Dariusz Ruminski", 1207 | "email": "dariusz.ruminski@gmail.com", 1208 | "homepage": "https://github.com/keradus" 1209 | }, 1210 | { 1211 | "name": "Contributors", 1212 | "homepage": "https://github.com/php-coveralls/php-coveralls/graphs/contributors" 1213 | } 1214 | ], 1215 | "description": "PHP client library for Coveralls API", 1216 | "homepage": "https://github.com/php-coveralls/php-coveralls", 1217 | "keywords": [ 1218 | "ci", 1219 | "coverage", 1220 | "github", 1221 | "test" 1222 | ], 1223 | "support": { 1224 | "issues": "https://github.com/php-coveralls/php-coveralls/issues", 1225 | "source": "https://github.com/php-coveralls/php-coveralls/tree/2.0" 1226 | }, 1227 | "time": "2017-12-08T14:28:16+00:00" 1228 | }, 1229 | { 1230 | "name": "phpdocumentor/reflection-common", 1231 | "version": "2.0.0", 1232 | "source": { 1233 | "type": "git", 1234 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 1235 | "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" 1236 | }, 1237 | "dist": { 1238 | "type": "zip", 1239 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", 1240 | "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", 1241 | "shasum": "" 1242 | }, 1243 | "require": { 1244 | "php": ">=7.1" 1245 | }, 1246 | "require-dev": { 1247 | "phpunit/phpunit": "~6" 1248 | }, 1249 | "type": "library", 1250 | "extra": { 1251 | "branch-alias": { 1252 | "dev-master": "2.x-dev" 1253 | } 1254 | }, 1255 | "autoload": { 1256 | "psr-4": { 1257 | "phpDocumentor\\Reflection\\": "src/" 1258 | } 1259 | }, 1260 | "notification-url": "https://packagist.org/downloads/", 1261 | "license": [ 1262 | "MIT" 1263 | ], 1264 | "authors": [ 1265 | { 1266 | "name": "Jaap van Otterdijk", 1267 | "email": "opensource@ijaap.nl" 1268 | } 1269 | ], 1270 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 1271 | "homepage": "http://www.phpdoc.org", 1272 | "keywords": [ 1273 | "FQSEN", 1274 | "phpDocumentor", 1275 | "phpdoc", 1276 | "reflection", 1277 | "static analysis" 1278 | ], 1279 | "support": { 1280 | "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", 1281 | "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.0.0" 1282 | }, 1283 | "time": "2018-08-07T13:53:10+00:00" 1284 | }, 1285 | { 1286 | "name": "phpdocumentor/reflection-docblock", 1287 | "version": "5.0.0-alpha5", 1288 | "source": { 1289 | "type": "git", 1290 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 1291 | "reference": "8fcadfe5f85c38705151c9ab23b4781f23e6a70e" 1292 | }, 1293 | "dist": { 1294 | "type": "zip", 1295 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8fcadfe5f85c38705151c9ab23b4781f23e6a70e", 1296 | "reference": "8fcadfe5f85c38705151c9ab23b4781f23e6a70e", 1297 | "shasum": "" 1298 | }, 1299 | "require": { 1300 | "php": ">=7.1", 1301 | "phpdocumentor/type-resolver": "^0", 1302 | "webmozart/assert": "^1" 1303 | }, 1304 | "require-dev": { 1305 | "doctrine/instantiator": "^1", 1306 | "mockery/mockery": "^1" 1307 | }, 1308 | "type": "library", 1309 | "extra": { 1310 | "branch-alias": { 1311 | "dev-master": "5.x-dev" 1312 | } 1313 | }, 1314 | "autoload": { 1315 | "psr-4": { 1316 | "phpDocumentor\\Reflection\\": "src" 1317 | } 1318 | }, 1319 | "notification-url": "https://packagist.org/downloads/", 1320 | "license": [ 1321 | "MIT" 1322 | ], 1323 | "authors": [ 1324 | { 1325 | "name": "Mike van Riel", 1326 | "email": "me@mikevanriel.com" 1327 | } 1328 | ], 1329 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 1330 | "support": { 1331 | "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", 1332 | "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" 1333 | }, 1334 | "time": "2019-06-15T20:45:01+00:00" 1335 | }, 1336 | { 1337 | "name": "phpdocumentor/type-resolver", 1338 | "version": "0.7.x-dev", 1339 | "source": { 1340 | "type": "git", 1341 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 1342 | "reference": "e424cdf9bbab5bccdede5f123f706083b11bcef9" 1343 | }, 1344 | "dist": { 1345 | "type": "zip", 1346 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e424cdf9bbab5bccdede5f123f706083b11bcef9", 1347 | "reference": "e424cdf9bbab5bccdede5f123f706083b11bcef9", 1348 | "shasum": "" 1349 | }, 1350 | "require": { 1351 | "php": ">=7.1", 1352 | "phpdocumentor/reflection-common": "~2.0.0-beta1" 1353 | }, 1354 | "require-dev": { 1355 | "mockery/mockery": "~1", 1356 | "phpunit/phpunit": "~6" 1357 | }, 1358 | "type": "library", 1359 | "extra": { 1360 | "branch-alias": { 1361 | "dev-master": "0.x-dev" 1362 | } 1363 | }, 1364 | "autoload": { 1365 | "psr-4": { 1366 | "phpDocumentor\\Reflection\\": "src" 1367 | } 1368 | }, 1369 | "notification-url": "https://packagist.org/downloads/", 1370 | "license": [ 1371 | "MIT" 1372 | ], 1373 | "authors": [ 1374 | { 1375 | "name": "Mike van Riel", 1376 | "email": "me@mikevanriel.com" 1377 | } 1378 | ], 1379 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", 1380 | "support": { 1381 | "issues": "https://github.com/phpDocumentor/TypeResolver/issues", 1382 | "source": "https://github.com/phpDocumentor/TypeResolver/tree/0.7.x" 1383 | }, 1384 | "time": "2019-12-20T14:34:12+00:00" 1385 | }, 1386 | { 1387 | "name": "phpspec/prophecy", 1388 | "version": "v1.10.3", 1389 | "source": { 1390 | "type": "git", 1391 | "url": "https://github.com/phpspec/prophecy.git", 1392 | "reference": "451c3cd1418cf640de218914901e51b064abb093" 1393 | }, 1394 | "dist": { 1395 | "type": "zip", 1396 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", 1397 | "reference": "451c3cd1418cf640de218914901e51b064abb093", 1398 | "shasum": "" 1399 | }, 1400 | "require": { 1401 | "doctrine/instantiator": "^1.0.2", 1402 | "php": "^5.3|^7.0", 1403 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", 1404 | "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", 1405 | "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" 1406 | }, 1407 | "require-dev": { 1408 | "phpspec/phpspec": "^2.5 || ^3.2", 1409 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 1410 | }, 1411 | "type": "library", 1412 | "extra": { 1413 | "branch-alias": { 1414 | "dev-master": "1.10.x-dev" 1415 | } 1416 | }, 1417 | "autoload": { 1418 | "psr-4": { 1419 | "Prophecy\\": "src/Prophecy" 1420 | } 1421 | }, 1422 | "notification-url": "https://packagist.org/downloads/", 1423 | "license": [ 1424 | "MIT" 1425 | ], 1426 | "authors": [ 1427 | { 1428 | "name": "Konstantin Kudryashov", 1429 | "email": "ever.zet@gmail.com", 1430 | "homepage": "http://everzet.com" 1431 | }, 1432 | { 1433 | "name": "Marcello Duarte", 1434 | "email": "marcello.duarte@gmail.com" 1435 | } 1436 | ], 1437 | "description": "Highly opinionated mocking framework for PHP 5.3+", 1438 | "homepage": "https://github.com/phpspec/prophecy", 1439 | "keywords": [ 1440 | "Double", 1441 | "Dummy", 1442 | "fake", 1443 | "mock", 1444 | "spy", 1445 | "stub" 1446 | ], 1447 | "support": { 1448 | "issues": "https://github.com/phpspec/prophecy/issues", 1449 | "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" 1450 | }, 1451 | "time": "2020-03-05T15:02:03+00:00" 1452 | }, 1453 | { 1454 | "name": "phpunit/php-code-coverage", 1455 | "version": "6.1.4", 1456 | "source": { 1457 | "type": "git", 1458 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 1459 | "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" 1460 | }, 1461 | "dist": { 1462 | "type": "zip", 1463 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", 1464 | "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", 1465 | "shasum": "" 1466 | }, 1467 | "require": { 1468 | "ext-dom": "*", 1469 | "ext-xmlwriter": "*", 1470 | "php": "^7.1", 1471 | "phpunit/php-file-iterator": "^2.0", 1472 | "phpunit/php-text-template": "^1.2.1", 1473 | "phpunit/php-token-stream": "^3.0", 1474 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 1475 | "sebastian/environment": "^3.1 || ^4.0", 1476 | "sebastian/version": "^2.0.1", 1477 | "theseer/tokenizer": "^1.1" 1478 | }, 1479 | "require-dev": { 1480 | "phpunit/phpunit": "^7.0" 1481 | }, 1482 | "suggest": { 1483 | "ext-xdebug": "^2.6.0" 1484 | }, 1485 | "type": "library", 1486 | "extra": { 1487 | "branch-alias": { 1488 | "dev-master": "6.1-dev" 1489 | } 1490 | }, 1491 | "autoload": { 1492 | "classmap": [ 1493 | "src/" 1494 | ] 1495 | }, 1496 | "notification-url": "https://packagist.org/downloads/", 1497 | "license": [ 1498 | "BSD-3-Clause" 1499 | ], 1500 | "authors": [ 1501 | { 1502 | "name": "Sebastian Bergmann", 1503 | "email": "sebastian@phpunit.de", 1504 | "role": "lead" 1505 | } 1506 | ], 1507 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 1508 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 1509 | "keywords": [ 1510 | "coverage", 1511 | "testing", 1512 | "xunit" 1513 | ], 1514 | "support": { 1515 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 1516 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/master" 1517 | }, 1518 | "time": "2018-10-31T16:06:48+00:00" 1519 | }, 1520 | { 1521 | "name": "phpunit/php-file-iterator", 1522 | "version": "2.0.x-dev", 1523 | "source": { 1524 | "type": "git", 1525 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 1526 | "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357" 1527 | }, 1528 | "dist": { 1529 | "type": "zip", 1530 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357", 1531 | "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357", 1532 | "shasum": "" 1533 | }, 1534 | "require": { 1535 | "php": ">=7.1" 1536 | }, 1537 | "require-dev": { 1538 | "phpunit/phpunit": "^8.5" 1539 | }, 1540 | "type": "library", 1541 | "extra": { 1542 | "branch-alias": { 1543 | "dev-master": "2.0.x-dev" 1544 | } 1545 | }, 1546 | "autoload": { 1547 | "classmap": [ 1548 | "src/" 1549 | ] 1550 | }, 1551 | "notification-url": "https://packagist.org/downloads/", 1552 | "license": [ 1553 | "BSD-3-Clause" 1554 | ], 1555 | "authors": [ 1556 | { 1557 | "name": "Sebastian Bergmann", 1558 | "email": "sebastian@phpunit.de", 1559 | "role": "lead" 1560 | } 1561 | ], 1562 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 1563 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 1564 | "keywords": [ 1565 | "filesystem", 1566 | "iterator" 1567 | ], 1568 | "support": { 1569 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 1570 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0" 1571 | }, 1572 | "funding": [ 1573 | { 1574 | "url": "https://github.com/sebastianbergmann", 1575 | "type": "github" 1576 | } 1577 | ], 1578 | "time": "2020-11-30T08:25:21+00:00" 1579 | }, 1580 | { 1581 | "name": "phpunit/php-text-template", 1582 | "version": "1.2.1", 1583 | "source": { 1584 | "type": "git", 1585 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 1586 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 1587 | }, 1588 | "dist": { 1589 | "type": "zip", 1590 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 1591 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 1592 | "shasum": "" 1593 | }, 1594 | "require": { 1595 | "php": ">=5.3.3" 1596 | }, 1597 | "type": "library", 1598 | "autoload": { 1599 | "classmap": [ 1600 | "src/" 1601 | ] 1602 | }, 1603 | "notification-url": "https://packagist.org/downloads/", 1604 | "license": [ 1605 | "BSD-3-Clause" 1606 | ], 1607 | "authors": [ 1608 | { 1609 | "name": "Sebastian Bergmann", 1610 | "email": "sebastian@phpunit.de", 1611 | "role": "lead" 1612 | } 1613 | ], 1614 | "description": "Simple template engine.", 1615 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 1616 | "keywords": [ 1617 | "template" 1618 | ], 1619 | "support": { 1620 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 1621 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" 1622 | }, 1623 | "time": "2015-06-21T13:50:34+00:00" 1624 | }, 1625 | { 1626 | "name": "phpunit/php-timer", 1627 | "version": "2.1.x-dev", 1628 | "source": { 1629 | "type": "git", 1630 | "url": "https://github.com/sebastianbergmann/php-timer.git", 1631 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" 1632 | }, 1633 | "dist": { 1634 | "type": "zip", 1635 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", 1636 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", 1637 | "shasum": "" 1638 | }, 1639 | "require": { 1640 | "php": ">=7.1" 1641 | }, 1642 | "require-dev": { 1643 | "phpunit/phpunit": "^8.5" 1644 | }, 1645 | "type": "library", 1646 | "extra": { 1647 | "branch-alias": { 1648 | "dev-master": "2.1-dev" 1649 | } 1650 | }, 1651 | "autoload": { 1652 | "classmap": [ 1653 | "src/" 1654 | ] 1655 | }, 1656 | "notification-url": "https://packagist.org/downloads/", 1657 | "license": [ 1658 | "BSD-3-Clause" 1659 | ], 1660 | "authors": [ 1661 | { 1662 | "name": "Sebastian Bergmann", 1663 | "email": "sebastian@phpunit.de", 1664 | "role": "lead" 1665 | } 1666 | ], 1667 | "description": "Utility class for timing", 1668 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1669 | "keywords": [ 1670 | "timer" 1671 | ], 1672 | "support": { 1673 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 1674 | "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1" 1675 | }, 1676 | "funding": [ 1677 | { 1678 | "url": "https://github.com/sebastianbergmann", 1679 | "type": "github" 1680 | } 1681 | ], 1682 | "time": "2020-11-30T08:20:02+00:00" 1683 | }, 1684 | { 1685 | "name": "phpunit/php-token-stream", 1686 | "version": "3.1.x-dev", 1687 | "source": { 1688 | "type": "git", 1689 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 1690 | "reference": "472b687829041c24b25f475e14c2f38a09edf1c2" 1691 | }, 1692 | "dist": { 1693 | "type": "zip", 1694 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2", 1695 | "reference": "472b687829041c24b25f475e14c2f38a09edf1c2", 1696 | "shasum": "" 1697 | }, 1698 | "require": { 1699 | "ext-tokenizer": "*", 1700 | "php": ">=7.1" 1701 | }, 1702 | "require-dev": { 1703 | "phpunit/phpunit": "^7.0" 1704 | }, 1705 | "type": "library", 1706 | "extra": { 1707 | "branch-alias": { 1708 | "dev-master": "3.1-dev" 1709 | } 1710 | }, 1711 | "autoload": { 1712 | "classmap": [ 1713 | "src/" 1714 | ] 1715 | }, 1716 | "notification-url": "https://packagist.org/downloads/", 1717 | "license": [ 1718 | "BSD-3-Clause" 1719 | ], 1720 | "authors": [ 1721 | { 1722 | "name": "Sebastian Bergmann", 1723 | "email": "sebastian@phpunit.de" 1724 | } 1725 | ], 1726 | "description": "Wrapper around PHP's tokenizer extension.", 1727 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 1728 | "keywords": [ 1729 | "tokenizer" 1730 | ], 1731 | "support": { 1732 | "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", 1733 | "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1" 1734 | }, 1735 | "funding": [ 1736 | { 1737 | "url": "https://github.com/sebastianbergmann", 1738 | "type": "github" 1739 | } 1740 | ], 1741 | "abandoned": true, 1742 | "time": "2020-11-30T08:38:46+00:00" 1743 | }, 1744 | { 1745 | "name": "phpunit/phpunit", 1746 | "version": "7.5.20", 1747 | "source": { 1748 | "type": "git", 1749 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1750 | "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" 1751 | }, 1752 | "dist": { 1753 | "type": "zip", 1754 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", 1755 | "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", 1756 | "shasum": "" 1757 | }, 1758 | "require": { 1759 | "doctrine/instantiator": "^1.1", 1760 | "ext-dom": "*", 1761 | "ext-json": "*", 1762 | "ext-libxml": "*", 1763 | "ext-mbstring": "*", 1764 | "ext-xml": "*", 1765 | "myclabs/deep-copy": "^1.7", 1766 | "phar-io/manifest": "^1.0.2", 1767 | "phar-io/version": "^2.0", 1768 | "php": "^7.1", 1769 | "phpspec/prophecy": "^1.7", 1770 | "phpunit/php-code-coverage": "^6.0.7", 1771 | "phpunit/php-file-iterator": "^2.0.1", 1772 | "phpunit/php-text-template": "^1.2.1", 1773 | "phpunit/php-timer": "^2.1", 1774 | "sebastian/comparator": "^3.0", 1775 | "sebastian/diff": "^3.0", 1776 | "sebastian/environment": "^4.0", 1777 | "sebastian/exporter": "^3.1", 1778 | "sebastian/global-state": "^2.0", 1779 | "sebastian/object-enumerator": "^3.0.3", 1780 | "sebastian/resource-operations": "^2.0", 1781 | "sebastian/version": "^2.0.1" 1782 | }, 1783 | "conflict": { 1784 | "phpunit/phpunit-mock-objects": "*" 1785 | }, 1786 | "require-dev": { 1787 | "ext-pdo": "*" 1788 | }, 1789 | "suggest": { 1790 | "ext-soap": "*", 1791 | "ext-xdebug": "*", 1792 | "phpunit/php-invoker": "^2.0" 1793 | }, 1794 | "bin": [ 1795 | "phpunit" 1796 | ], 1797 | "type": "library", 1798 | "extra": { 1799 | "branch-alias": { 1800 | "dev-master": "7.5-dev" 1801 | } 1802 | }, 1803 | "autoload": { 1804 | "classmap": [ 1805 | "src/" 1806 | ] 1807 | }, 1808 | "notification-url": "https://packagist.org/downloads/", 1809 | "license": [ 1810 | "BSD-3-Clause" 1811 | ], 1812 | "authors": [ 1813 | { 1814 | "name": "Sebastian Bergmann", 1815 | "email": "sebastian@phpunit.de", 1816 | "role": "lead" 1817 | } 1818 | ], 1819 | "description": "The PHP Unit Testing framework.", 1820 | "homepage": "https://phpunit.de/", 1821 | "keywords": [ 1822 | "phpunit", 1823 | "testing", 1824 | "xunit" 1825 | ], 1826 | "support": { 1827 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 1828 | "source": "https://github.com/sebastianbergmann/phpunit/tree/7.5.20" 1829 | }, 1830 | "time": "2020-01-08T08:45:45+00:00" 1831 | }, 1832 | { 1833 | "name": "psr/http-message", 1834 | "version": "dev-master", 1835 | "source": { 1836 | "type": "git", 1837 | "url": "https://github.com/php-fig/http-message.git", 1838 | "reference": "efd67d1dc14a7ef4fc4e518e7dee91c271d524e4" 1839 | }, 1840 | "dist": { 1841 | "type": "zip", 1842 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/efd67d1dc14a7ef4fc4e518e7dee91c271d524e4", 1843 | "reference": "efd67d1dc14a7ef4fc4e518e7dee91c271d524e4", 1844 | "shasum": "" 1845 | }, 1846 | "require": { 1847 | "php": ">=5.3.0" 1848 | }, 1849 | "default-branch": true, 1850 | "type": "library", 1851 | "extra": { 1852 | "branch-alias": { 1853 | "dev-master": "1.0.x-dev" 1854 | } 1855 | }, 1856 | "autoload": { 1857 | "psr-4": { 1858 | "Psr\\Http\\Message\\": "src/" 1859 | } 1860 | }, 1861 | "notification-url": "https://packagist.org/downloads/", 1862 | "license": [ 1863 | "MIT" 1864 | ], 1865 | "authors": [ 1866 | { 1867 | "name": "PHP-FIG", 1868 | "homepage": "http://www.php-fig.org/" 1869 | } 1870 | ], 1871 | "description": "Common interface for HTTP messages", 1872 | "homepage": "https://github.com/php-fig/http-message", 1873 | "keywords": [ 1874 | "http", 1875 | "http-message", 1876 | "psr", 1877 | "psr-7", 1878 | "request", 1879 | "response" 1880 | ], 1881 | "support": { 1882 | "source": "https://github.com/php-fig/http-message/tree/master" 1883 | }, 1884 | "time": "2019-08-29T13:16:46+00:00" 1885 | }, 1886 | { 1887 | "name": "psr/log", 1888 | "version": "dev-master", 1889 | "source": { 1890 | "type": "git", 1891 | "url": "https://github.com/php-fig/log.git", 1892 | "reference": "a18c1e692e02b84abbafe4856c3cd7cc6903908c" 1893 | }, 1894 | "dist": { 1895 | "type": "zip", 1896 | "url": "https://api.github.com/repos/php-fig/log/zipball/a18c1e692e02b84abbafe4856c3cd7cc6903908c", 1897 | "reference": "a18c1e692e02b84abbafe4856c3cd7cc6903908c", 1898 | "shasum": "" 1899 | }, 1900 | "require": { 1901 | "php": ">=5.3.0" 1902 | }, 1903 | "default-branch": true, 1904 | "type": "library", 1905 | "extra": { 1906 | "branch-alias": { 1907 | "dev-master": "1.1.x-dev" 1908 | } 1909 | }, 1910 | "autoload": { 1911 | "psr-4": { 1912 | "Psr\\Log\\": "Psr/Log/" 1913 | } 1914 | }, 1915 | "notification-url": "https://packagist.org/downloads/", 1916 | "license": [ 1917 | "MIT" 1918 | ], 1919 | "authors": [ 1920 | { 1921 | "name": "PHP-FIG", 1922 | "homepage": "https://www.php-fig.org/" 1923 | } 1924 | ], 1925 | "description": "Common interface for logging libraries", 1926 | "homepage": "https://github.com/php-fig/log", 1927 | "keywords": [ 1928 | "log", 1929 | "psr", 1930 | "psr-3" 1931 | ], 1932 | "support": { 1933 | "source": "https://github.com/php-fig/log/tree/master" 1934 | }, 1935 | "time": "2021-03-02T15:02:34+00:00" 1936 | }, 1937 | { 1938 | "name": "ralouphie/getallheaders", 1939 | "version": "3.0.3", 1940 | "source": { 1941 | "type": "git", 1942 | "url": "https://github.com/ralouphie/getallheaders.git", 1943 | "reference": "120b605dfeb996808c31b6477290a714d356e822" 1944 | }, 1945 | "dist": { 1946 | "type": "zip", 1947 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", 1948 | "reference": "120b605dfeb996808c31b6477290a714d356e822", 1949 | "shasum": "" 1950 | }, 1951 | "require": { 1952 | "php": ">=5.6" 1953 | }, 1954 | "require-dev": { 1955 | "php-coveralls/php-coveralls": "^2.1", 1956 | "phpunit/phpunit": "^5 || ^6.5" 1957 | }, 1958 | "type": "library", 1959 | "autoload": { 1960 | "files": [ 1961 | "src/getallheaders.php" 1962 | ] 1963 | }, 1964 | "notification-url": "https://packagist.org/downloads/", 1965 | "license": [ 1966 | "MIT" 1967 | ], 1968 | "authors": [ 1969 | { 1970 | "name": "Ralph Khattar", 1971 | "email": "ralph.khattar@gmail.com" 1972 | } 1973 | ], 1974 | "description": "A polyfill for getallheaders.", 1975 | "support": { 1976 | "issues": "https://github.com/ralouphie/getallheaders/issues", 1977 | "source": "https://github.com/ralouphie/getallheaders/tree/develop" 1978 | }, 1979 | "time": "2019-03-08T08:55:37+00:00" 1980 | }, 1981 | { 1982 | "name": "sebastian/code-unit-reverse-lookup", 1983 | "version": "1.0.x-dev", 1984 | "source": { 1985 | "type": "git", 1986 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1987 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" 1988 | }, 1989 | "dist": { 1990 | "type": "zip", 1991 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", 1992 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", 1993 | "shasum": "" 1994 | }, 1995 | "require": { 1996 | "php": ">=5.6" 1997 | }, 1998 | "require-dev": { 1999 | "phpunit/phpunit": "^8.5" 2000 | }, 2001 | "type": "library", 2002 | "extra": { 2003 | "branch-alias": { 2004 | "dev-master": "1.0.x-dev" 2005 | } 2006 | }, 2007 | "autoload": { 2008 | "classmap": [ 2009 | "src/" 2010 | ] 2011 | }, 2012 | "notification-url": "https://packagist.org/downloads/", 2013 | "license": [ 2014 | "BSD-3-Clause" 2015 | ], 2016 | "authors": [ 2017 | { 2018 | "name": "Sebastian Bergmann", 2019 | "email": "sebastian@phpunit.de" 2020 | } 2021 | ], 2022 | "description": "Looks up which function or method a line of code belongs to", 2023 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 2024 | "support": { 2025 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 2026 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0" 2027 | }, 2028 | "funding": [ 2029 | { 2030 | "url": "https://github.com/sebastianbergmann", 2031 | "type": "github" 2032 | } 2033 | ], 2034 | "time": "2020-11-30T08:15:22+00:00" 2035 | }, 2036 | { 2037 | "name": "sebastian/comparator", 2038 | "version": "3.0.x-dev", 2039 | "source": { 2040 | "type": "git", 2041 | "url": "https://github.com/sebastianbergmann/comparator.git", 2042 | "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" 2043 | }, 2044 | "dist": { 2045 | "type": "zip", 2046 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", 2047 | "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", 2048 | "shasum": "" 2049 | }, 2050 | "require": { 2051 | "php": ">=7.1", 2052 | "sebastian/diff": "^3.0", 2053 | "sebastian/exporter": "^3.1" 2054 | }, 2055 | "require-dev": { 2056 | "phpunit/phpunit": "^8.5" 2057 | }, 2058 | "type": "library", 2059 | "extra": { 2060 | "branch-alias": { 2061 | "dev-master": "3.0-dev" 2062 | } 2063 | }, 2064 | "autoload": { 2065 | "classmap": [ 2066 | "src/" 2067 | ] 2068 | }, 2069 | "notification-url": "https://packagist.org/downloads/", 2070 | "license": [ 2071 | "BSD-3-Clause" 2072 | ], 2073 | "authors": [ 2074 | { 2075 | "name": "Sebastian Bergmann", 2076 | "email": "sebastian@phpunit.de" 2077 | }, 2078 | { 2079 | "name": "Jeff Welch", 2080 | "email": "whatthejeff@gmail.com" 2081 | }, 2082 | { 2083 | "name": "Volker Dusch", 2084 | "email": "github@wallbash.com" 2085 | }, 2086 | { 2087 | "name": "Bernhard Schussek", 2088 | "email": "bschussek@2bepublished.at" 2089 | } 2090 | ], 2091 | "description": "Provides the functionality to compare PHP values for equality", 2092 | "homepage": "https://github.com/sebastianbergmann/comparator", 2093 | "keywords": [ 2094 | "comparator", 2095 | "compare", 2096 | "equality" 2097 | ], 2098 | "support": { 2099 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 2100 | "source": "https://github.com/sebastianbergmann/comparator/tree/3.0" 2101 | }, 2102 | "funding": [ 2103 | { 2104 | "url": "https://github.com/sebastianbergmann", 2105 | "type": "github" 2106 | } 2107 | ], 2108 | "time": "2020-11-30T08:04:30+00:00" 2109 | }, 2110 | { 2111 | "name": "sebastian/diff", 2112 | "version": "3.0.x-dev", 2113 | "source": { 2114 | "type": "git", 2115 | "url": "https://github.com/sebastianbergmann/diff.git", 2116 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" 2117 | }, 2118 | "dist": { 2119 | "type": "zip", 2120 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", 2121 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", 2122 | "shasum": "" 2123 | }, 2124 | "require": { 2125 | "php": ">=7.1" 2126 | }, 2127 | "require-dev": { 2128 | "phpunit/phpunit": "^7.5 || ^8.0", 2129 | "symfony/process": "^2 || ^3.3 || ^4" 2130 | }, 2131 | "type": "library", 2132 | "extra": { 2133 | "branch-alias": { 2134 | "dev-master": "3.0-dev" 2135 | } 2136 | }, 2137 | "autoload": { 2138 | "classmap": [ 2139 | "src/" 2140 | ] 2141 | }, 2142 | "notification-url": "https://packagist.org/downloads/", 2143 | "license": [ 2144 | "BSD-3-Clause" 2145 | ], 2146 | "authors": [ 2147 | { 2148 | "name": "Sebastian Bergmann", 2149 | "email": "sebastian@phpunit.de" 2150 | }, 2151 | { 2152 | "name": "Kore Nordmann", 2153 | "email": "mail@kore-nordmann.de" 2154 | } 2155 | ], 2156 | "description": "Diff implementation", 2157 | "homepage": "https://github.com/sebastianbergmann/diff", 2158 | "keywords": [ 2159 | "diff", 2160 | "udiff", 2161 | "unidiff", 2162 | "unified diff" 2163 | ], 2164 | "support": { 2165 | "issues": "https://github.com/sebastianbergmann/diff/issues", 2166 | "source": "https://github.com/sebastianbergmann/diff/tree/3.0" 2167 | }, 2168 | "funding": [ 2169 | { 2170 | "url": "https://github.com/sebastianbergmann", 2171 | "type": "github" 2172 | } 2173 | ], 2174 | "time": "2020-11-30T07:59:04+00:00" 2175 | }, 2176 | { 2177 | "name": "sebastian/environment", 2178 | "version": "4.2.x-dev", 2179 | "source": { 2180 | "type": "git", 2181 | "url": "https://github.com/sebastianbergmann/environment.git", 2182 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" 2183 | }, 2184 | "dist": { 2185 | "type": "zip", 2186 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", 2187 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", 2188 | "shasum": "" 2189 | }, 2190 | "require": { 2191 | "php": ">=7.1" 2192 | }, 2193 | "require-dev": { 2194 | "phpunit/phpunit": "^7.5" 2195 | }, 2196 | "suggest": { 2197 | "ext-posix": "*" 2198 | }, 2199 | "type": "library", 2200 | "extra": { 2201 | "branch-alias": { 2202 | "dev-master": "4.2-dev" 2203 | } 2204 | }, 2205 | "autoload": { 2206 | "classmap": [ 2207 | "src/" 2208 | ] 2209 | }, 2210 | "notification-url": "https://packagist.org/downloads/", 2211 | "license": [ 2212 | "BSD-3-Clause" 2213 | ], 2214 | "authors": [ 2215 | { 2216 | "name": "Sebastian Bergmann", 2217 | "email": "sebastian@phpunit.de" 2218 | } 2219 | ], 2220 | "description": "Provides functionality to handle HHVM/PHP environments", 2221 | "homepage": "http://www.github.com/sebastianbergmann/environment", 2222 | "keywords": [ 2223 | "Xdebug", 2224 | "environment", 2225 | "hhvm" 2226 | ], 2227 | "support": { 2228 | "issues": "https://github.com/sebastianbergmann/environment/issues", 2229 | "source": "https://github.com/sebastianbergmann/environment/tree/4.2" 2230 | }, 2231 | "funding": [ 2232 | { 2233 | "url": "https://github.com/sebastianbergmann", 2234 | "type": "github" 2235 | } 2236 | ], 2237 | "time": "2020-11-30T07:53:42+00:00" 2238 | }, 2239 | { 2240 | "name": "sebastian/exporter", 2241 | "version": "3.1.x-dev", 2242 | "source": { 2243 | "type": "git", 2244 | "url": "https://github.com/sebastianbergmann/exporter.git", 2245 | "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" 2246 | }, 2247 | "dist": { 2248 | "type": "zip", 2249 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", 2250 | "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", 2251 | "shasum": "" 2252 | }, 2253 | "require": { 2254 | "php": ">=7.0", 2255 | "sebastian/recursion-context": "^3.0" 2256 | }, 2257 | "require-dev": { 2258 | "ext-mbstring": "*", 2259 | "phpunit/phpunit": "^6.0" 2260 | }, 2261 | "type": "library", 2262 | "extra": { 2263 | "branch-alias": { 2264 | "dev-master": "3.1.x-dev" 2265 | } 2266 | }, 2267 | "autoload": { 2268 | "classmap": [ 2269 | "src/" 2270 | ] 2271 | }, 2272 | "notification-url": "https://packagist.org/downloads/", 2273 | "license": [ 2274 | "BSD-3-Clause" 2275 | ], 2276 | "authors": [ 2277 | { 2278 | "name": "Sebastian Bergmann", 2279 | "email": "sebastian@phpunit.de" 2280 | }, 2281 | { 2282 | "name": "Jeff Welch", 2283 | "email": "whatthejeff@gmail.com" 2284 | }, 2285 | { 2286 | "name": "Volker Dusch", 2287 | "email": "github@wallbash.com" 2288 | }, 2289 | { 2290 | "name": "Adam Harvey", 2291 | "email": "aharvey@php.net" 2292 | }, 2293 | { 2294 | "name": "Bernhard Schussek", 2295 | "email": "bschussek@gmail.com" 2296 | } 2297 | ], 2298 | "description": "Provides the functionality to export PHP variables for visualization", 2299 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 2300 | "keywords": [ 2301 | "export", 2302 | "exporter" 2303 | ], 2304 | "support": { 2305 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 2306 | "source": "https://github.com/sebastianbergmann/exporter/tree/3.1" 2307 | }, 2308 | "funding": [ 2309 | { 2310 | "url": "https://github.com/sebastianbergmann", 2311 | "type": "github" 2312 | } 2313 | ], 2314 | "time": "2020-11-30T07:47:53+00:00" 2315 | }, 2316 | { 2317 | "name": "sebastian/global-state", 2318 | "version": "2.0.0", 2319 | "source": { 2320 | "type": "git", 2321 | "url": "https://github.com/sebastianbergmann/global-state.git", 2322 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" 2323 | }, 2324 | "dist": { 2325 | "type": "zip", 2326 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 2327 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 2328 | "shasum": "" 2329 | }, 2330 | "require": { 2331 | "php": "^7.0" 2332 | }, 2333 | "require-dev": { 2334 | "phpunit/phpunit": "^6.0" 2335 | }, 2336 | "suggest": { 2337 | "ext-uopz": "*" 2338 | }, 2339 | "type": "library", 2340 | "extra": { 2341 | "branch-alias": { 2342 | "dev-master": "2.0-dev" 2343 | } 2344 | }, 2345 | "autoload": { 2346 | "classmap": [ 2347 | "src/" 2348 | ] 2349 | }, 2350 | "notification-url": "https://packagist.org/downloads/", 2351 | "license": [ 2352 | "BSD-3-Clause" 2353 | ], 2354 | "authors": [ 2355 | { 2356 | "name": "Sebastian Bergmann", 2357 | "email": "sebastian@phpunit.de" 2358 | } 2359 | ], 2360 | "description": "Snapshotting of global state", 2361 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 2362 | "keywords": [ 2363 | "global state" 2364 | ], 2365 | "support": { 2366 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 2367 | "source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0" 2368 | }, 2369 | "time": "2017-04-27T15:39:26+00:00" 2370 | }, 2371 | { 2372 | "name": "sebastian/object-enumerator", 2373 | "version": "3.0.x-dev", 2374 | "source": { 2375 | "type": "git", 2376 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 2377 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" 2378 | }, 2379 | "dist": { 2380 | "type": "zip", 2381 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", 2382 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", 2383 | "shasum": "" 2384 | }, 2385 | "require": { 2386 | "php": ">=7.0", 2387 | "sebastian/object-reflector": "^1.1.1", 2388 | "sebastian/recursion-context": "^3.0" 2389 | }, 2390 | "require-dev": { 2391 | "phpunit/phpunit": "^6.0" 2392 | }, 2393 | "type": "library", 2394 | "extra": { 2395 | "branch-alias": { 2396 | "dev-master": "3.0.x-dev" 2397 | } 2398 | }, 2399 | "autoload": { 2400 | "classmap": [ 2401 | "src/" 2402 | ] 2403 | }, 2404 | "notification-url": "https://packagist.org/downloads/", 2405 | "license": [ 2406 | "BSD-3-Clause" 2407 | ], 2408 | "authors": [ 2409 | { 2410 | "name": "Sebastian Bergmann", 2411 | "email": "sebastian@phpunit.de" 2412 | } 2413 | ], 2414 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 2415 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 2416 | "support": { 2417 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 2418 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0" 2419 | }, 2420 | "funding": [ 2421 | { 2422 | "url": "https://github.com/sebastianbergmann", 2423 | "type": "github" 2424 | } 2425 | ], 2426 | "time": "2020-11-30T07:40:27+00:00" 2427 | }, 2428 | { 2429 | "name": "sebastian/object-reflector", 2430 | "version": "1.1.x-dev", 2431 | "source": { 2432 | "type": "git", 2433 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 2434 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" 2435 | }, 2436 | "dist": { 2437 | "type": "zip", 2438 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", 2439 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", 2440 | "shasum": "" 2441 | }, 2442 | "require": { 2443 | "php": ">=7.0" 2444 | }, 2445 | "require-dev": { 2446 | "phpunit/phpunit": "^6.0" 2447 | }, 2448 | "type": "library", 2449 | "extra": { 2450 | "branch-alias": { 2451 | "dev-master": "1.1-dev" 2452 | } 2453 | }, 2454 | "autoload": { 2455 | "classmap": [ 2456 | "src/" 2457 | ] 2458 | }, 2459 | "notification-url": "https://packagist.org/downloads/", 2460 | "license": [ 2461 | "BSD-3-Clause" 2462 | ], 2463 | "authors": [ 2464 | { 2465 | "name": "Sebastian Bergmann", 2466 | "email": "sebastian@phpunit.de" 2467 | } 2468 | ], 2469 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 2470 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 2471 | "support": { 2472 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 2473 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1" 2474 | }, 2475 | "funding": [ 2476 | { 2477 | "url": "https://github.com/sebastianbergmann", 2478 | "type": "github" 2479 | } 2480 | ], 2481 | "time": "2020-11-30T07:37:18+00:00" 2482 | }, 2483 | { 2484 | "name": "sebastian/recursion-context", 2485 | "version": "3.0.x-dev", 2486 | "source": { 2487 | "type": "git", 2488 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 2489 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" 2490 | }, 2491 | "dist": { 2492 | "type": "zip", 2493 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", 2494 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", 2495 | "shasum": "" 2496 | }, 2497 | "require": { 2498 | "php": ">=7.0" 2499 | }, 2500 | "require-dev": { 2501 | "phpunit/phpunit": "^6.0" 2502 | }, 2503 | "type": "library", 2504 | "extra": { 2505 | "branch-alias": { 2506 | "dev-master": "3.0.x-dev" 2507 | } 2508 | }, 2509 | "autoload": { 2510 | "classmap": [ 2511 | "src/" 2512 | ] 2513 | }, 2514 | "notification-url": "https://packagist.org/downloads/", 2515 | "license": [ 2516 | "BSD-3-Clause" 2517 | ], 2518 | "authors": [ 2519 | { 2520 | "name": "Sebastian Bergmann", 2521 | "email": "sebastian@phpunit.de" 2522 | }, 2523 | { 2524 | "name": "Jeff Welch", 2525 | "email": "whatthejeff@gmail.com" 2526 | }, 2527 | { 2528 | "name": "Adam Harvey", 2529 | "email": "aharvey@php.net" 2530 | } 2531 | ], 2532 | "description": "Provides functionality to recursively process PHP variables", 2533 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 2534 | "support": { 2535 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 2536 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0" 2537 | }, 2538 | "funding": [ 2539 | { 2540 | "url": "https://github.com/sebastianbergmann", 2541 | "type": "github" 2542 | } 2543 | ], 2544 | "time": "2020-11-30T07:34:24+00:00" 2545 | }, 2546 | { 2547 | "name": "sebastian/resource-operations", 2548 | "version": "2.0.x-dev", 2549 | "source": { 2550 | "type": "git", 2551 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 2552 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" 2553 | }, 2554 | "dist": { 2555 | "type": "zip", 2556 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", 2557 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", 2558 | "shasum": "" 2559 | }, 2560 | "require": { 2561 | "php": ">=7.1" 2562 | }, 2563 | "type": "library", 2564 | "extra": { 2565 | "branch-alias": { 2566 | "dev-master": "2.0-dev" 2567 | } 2568 | }, 2569 | "autoload": { 2570 | "classmap": [ 2571 | "src/" 2572 | ] 2573 | }, 2574 | "notification-url": "https://packagist.org/downloads/", 2575 | "license": [ 2576 | "BSD-3-Clause" 2577 | ], 2578 | "authors": [ 2579 | { 2580 | "name": "Sebastian Bergmann", 2581 | "email": "sebastian@phpunit.de" 2582 | } 2583 | ], 2584 | "description": "Provides a list of PHP built-in functions that operate on resources", 2585 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 2586 | "support": { 2587 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues", 2588 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0" 2589 | }, 2590 | "funding": [ 2591 | { 2592 | "url": "https://github.com/sebastianbergmann", 2593 | "type": "github" 2594 | } 2595 | ], 2596 | "time": "2020-11-30T07:30:19+00:00" 2597 | }, 2598 | { 2599 | "name": "sebastian/version", 2600 | "version": "2.0.1", 2601 | "source": { 2602 | "type": "git", 2603 | "url": "https://github.com/sebastianbergmann/version.git", 2604 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 2605 | }, 2606 | "dist": { 2607 | "type": "zip", 2608 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 2609 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 2610 | "shasum": "" 2611 | }, 2612 | "require": { 2613 | "php": ">=5.6" 2614 | }, 2615 | "type": "library", 2616 | "extra": { 2617 | "branch-alias": { 2618 | "dev-master": "2.0.x-dev" 2619 | } 2620 | }, 2621 | "autoload": { 2622 | "classmap": [ 2623 | "src/" 2624 | ] 2625 | }, 2626 | "notification-url": "https://packagist.org/downloads/", 2627 | "license": [ 2628 | "BSD-3-Clause" 2629 | ], 2630 | "authors": [ 2631 | { 2632 | "name": "Sebastian Bergmann", 2633 | "email": "sebastian@phpunit.de", 2634 | "role": "lead" 2635 | } 2636 | ], 2637 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 2638 | "homepage": "https://github.com/sebastianbergmann/version", 2639 | "support": { 2640 | "issues": "https://github.com/sebastianbergmann/version/issues", 2641 | "source": "https://github.com/sebastianbergmann/version/tree/master" 2642 | }, 2643 | "time": "2016-10-03T07:35:21+00:00" 2644 | }, 2645 | { 2646 | "name": "symfony/config", 2647 | "version": "4.4.x-dev", 2648 | "source": { 2649 | "type": "git", 2650 | "url": "https://github.com/symfony/config.git", 2651 | "reference": "f6d8318c14e4be81525ae47b30e618f0bed4c7b3" 2652 | }, 2653 | "dist": { 2654 | "type": "zip", 2655 | "url": "https://api.github.com/repos/symfony/config/zipball/f6d8318c14e4be81525ae47b30e618f0bed4c7b3", 2656 | "reference": "f6d8318c14e4be81525ae47b30e618f0bed4c7b3", 2657 | "shasum": "" 2658 | }, 2659 | "require": { 2660 | "php": ">=7.1.3", 2661 | "symfony/filesystem": "^3.4|^4.0|^5.0", 2662 | "symfony/polyfill-ctype": "~1.8" 2663 | }, 2664 | "conflict": { 2665 | "symfony/finder": "<3.4" 2666 | }, 2667 | "require-dev": { 2668 | "symfony/event-dispatcher": "^3.4|^4.0|^5.0", 2669 | "symfony/finder": "^3.4|^4.0|^5.0", 2670 | "symfony/messenger": "^4.1|^5.0", 2671 | "symfony/service-contracts": "^1.1|^2", 2672 | "symfony/yaml": "^3.4|^4.0|^5.0" 2673 | }, 2674 | "suggest": { 2675 | "symfony/yaml": "To use the yaml reference dumper" 2676 | }, 2677 | "type": "library", 2678 | "autoload": { 2679 | "psr-4": { 2680 | "Symfony\\Component\\Config\\": "" 2681 | }, 2682 | "exclude-from-classmap": [ 2683 | "/Tests/" 2684 | ] 2685 | }, 2686 | "notification-url": "https://packagist.org/downloads/", 2687 | "license": [ 2688 | "MIT" 2689 | ], 2690 | "authors": [ 2691 | { 2692 | "name": "Fabien Potencier", 2693 | "email": "fabien@symfony.com" 2694 | }, 2695 | { 2696 | "name": "Symfony Community", 2697 | "homepage": "https://symfony.com/contributors" 2698 | } 2699 | ], 2700 | "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", 2701 | "homepage": "https://symfony.com", 2702 | "support": { 2703 | "source": "https://github.com/symfony/config/tree/4.4" 2704 | }, 2705 | "funding": [ 2706 | { 2707 | "url": "https://symfony.com/sponsor", 2708 | "type": "custom" 2709 | }, 2710 | { 2711 | "url": "https://github.com/fabpot", 2712 | "type": "github" 2713 | }, 2714 | { 2715 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2716 | "type": "tidelift" 2717 | } 2718 | ], 2719 | "time": "2021-04-07T15:47:03+00:00" 2720 | }, 2721 | { 2722 | "name": "symfony/filesystem", 2723 | "version": "4.4.x-dev", 2724 | "source": { 2725 | "type": "git", 2726 | "url": "https://github.com/symfony/filesystem.git", 2727 | "reference": "f0f06656a18304cdeacb2c4c0113a2b78a2b4c2a" 2728 | }, 2729 | "dist": { 2730 | "type": "zip", 2731 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/f0f06656a18304cdeacb2c4c0113a2b78a2b4c2a", 2732 | "reference": "f0f06656a18304cdeacb2c4c0113a2b78a2b4c2a", 2733 | "shasum": "" 2734 | }, 2735 | "require": { 2736 | "php": ">=7.1.3", 2737 | "symfony/polyfill-ctype": "~1.8" 2738 | }, 2739 | "type": "library", 2740 | "autoload": { 2741 | "psr-4": { 2742 | "Symfony\\Component\\Filesystem\\": "" 2743 | }, 2744 | "exclude-from-classmap": [ 2745 | "/Tests/" 2746 | ] 2747 | }, 2748 | "notification-url": "https://packagist.org/downloads/", 2749 | "license": [ 2750 | "MIT" 2751 | ], 2752 | "authors": [ 2753 | { 2754 | "name": "Fabien Potencier", 2755 | "email": "fabien@symfony.com" 2756 | }, 2757 | { 2758 | "name": "Symfony Community", 2759 | "homepage": "https://symfony.com/contributors" 2760 | } 2761 | ], 2762 | "description": "Provides basic utilities for the filesystem", 2763 | "homepage": "https://symfony.com", 2764 | "support": { 2765 | "source": "https://github.com/symfony/filesystem/tree/4.4" 2766 | }, 2767 | "funding": [ 2768 | { 2769 | "url": "https://symfony.com/sponsor", 2770 | "type": "custom" 2771 | }, 2772 | { 2773 | "url": "https://github.com/fabpot", 2774 | "type": "github" 2775 | }, 2776 | { 2777 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2778 | "type": "tidelift" 2779 | } 2780 | ], 2781 | "time": "2021-04-01T10:24:12+00:00" 2782 | }, 2783 | { 2784 | "name": "symfony/polyfill-intl-idn", 2785 | "version": "dev-main", 2786 | "source": { 2787 | "type": "git", 2788 | "url": "https://github.com/symfony/polyfill-intl-idn.git", 2789 | "reference": "3709eb82b37c8d6eb23cf10ef19b27b3312d1632" 2790 | }, 2791 | "dist": { 2792 | "type": "zip", 2793 | "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3709eb82b37c8d6eb23cf10ef19b27b3312d1632", 2794 | "reference": "3709eb82b37c8d6eb23cf10ef19b27b3312d1632", 2795 | "shasum": "" 2796 | }, 2797 | "require": { 2798 | "php": ">=7.1", 2799 | "symfony/polyfill-intl-normalizer": "^1.10", 2800 | "symfony/polyfill-php72": "^1.10" 2801 | }, 2802 | "suggest": { 2803 | "ext-intl": "For best performance" 2804 | }, 2805 | "default-branch": true, 2806 | "type": "library", 2807 | "extra": { 2808 | "branch-alias": { 2809 | "dev-main": "1.22-dev" 2810 | }, 2811 | "thanks": { 2812 | "name": "symfony/polyfill", 2813 | "url": "https://github.com/symfony/polyfill" 2814 | } 2815 | }, 2816 | "autoload": { 2817 | "psr-4": { 2818 | "Symfony\\Polyfill\\Intl\\Idn\\": "" 2819 | }, 2820 | "files": [ 2821 | "bootstrap.php" 2822 | ] 2823 | }, 2824 | "notification-url": "https://packagist.org/downloads/", 2825 | "license": [ 2826 | "MIT" 2827 | ], 2828 | "authors": [ 2829 | { 2830 | "name": "Laurent Bassin", 2831 | "email": "laurent@bassin.info" 2832 | }, 2833 | { 2834 | "name": "Trevor Rowbotham", 2835 | "email": "trevor.rowbotham@pm.me" 2836 | }, 2837 | { 2838 | "name": "Symfony Community", 2839 | "homepage": "https://symfony.com/contributors" 2840 | } 2841 | ], 2842 | "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", 2843 | "homepage": "https://symfony.com", 2844 | "keywords": [ 2845 | "compatibility", 2846 | "idn", 2847 | "intl", 2848 | "polyfill", 2849 | "portable", 2850 | "shim" 2851 | ], 2852 | "support": { 2853 | "source": "https://github.com/symfony/polyfill-intl-idn/tree/main" 2854 | }, 2855 | "funding": [ 2856 | { 2857 | "url": "https://symfony.com/sponsor", 2858 | "type": "custom" 2859 | }, 2860 | { 2861 | "url": "https://github.com/fabpot", 2862 | "type": "github" 2863 | }, 2864 | { 2865 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2866 | "type": "tidelift" 2867 | } 2868 | ], 2869 | "time": "2021-02-19T11:00:07+00:00" 2870 | }, 2871 | { 2872 | "name": "symfony/polyfill-intl-normalizer", 2873 | "version": "dev-main", 2874 | "source": { 2875 | "type": "git", 2876 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 2877 | "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" 2878 | }, 2879 | "dist": { 2880 | "type": "zip", 2881 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", 2882 | "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", 2883 | "shasum": "" 2884 | }, 2885 | "require": { 2886 | "php": ">=7.1" 2887 | }, 2888 | "suggest": { 2889 | "ext-intl": "For best performance" 2890 | }, 2891 | "default-branch": true, 2892 | "type": "library", 2893 | "extra": { 2894 | "branch-alias": { 2895 | "dev-main": "1.22-dev" 2896 | }, 2897 | "thanks": { 2898 | "name": "symfony/polyfill", 2899 | "url": "https://github.com/symfony/polyfill" 2900 | } 2901 | }, 2902 | "autoload": { 2903 | "psr-4": { 2904 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 2905 | }, 2906 | "files": [ 2907 | "bootstrap.php" 2908 | ], 2909 | "classmap": [ 2910 | "Resources/stubs" 2911 | ] 2912 | }, 2913 | "notification-url": "https://packagist.org/downloads/", 2914 | "license": [ 2915 | "MIT" 2916 | ], 2917 | "authors": [ 2918 | { 2919 | "name": "Nicolas Grekas", 2920 | "email": "p@tchwork.com" 2921 | }, 2922 | { 2923 | "name": "Symfony Community", 2924 | "homepage": "https://symfony.com/contributors" 2925 | } 2926 | ], 2927 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 2928 | "homepage": "https://symfony.com", 2929 | "keywords": [ 2930 | "compatibility", 2931 | "intl", 2932 | "normalizer", 2933 | "polyfill", 2934 | "portable", 2935 | "shim" 2936 | ], 2937 | "support": { 2938 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" 2939 | }, 2940 | "funding": [ 2941 | { 2942 | "url": "https://symfony.com/sponsor", 2943 | "type": "custom" 2944 | }, 2945 | { 2946 | "url": "https://github.com/fabpot", 2947 | "type": "github" 2948 | }, 2949 | { 2950 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2951 | "type": "tidelift" 2952 | } 2953 | ], 2954 | "time": "2021-01-22T09:19:47+00:00" 2955 | }, 2956 | { 2957 | "name": "symfony/polyfill-php72", 2958 | "version": "dev-main", 2959 | "source": { 2960 | "type": "git", 2961 | "url": "https://github.com/symfony/polyfill-php72.git", 2962 | "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" 2963 | }, 2964 | "dist": { 2965 | "type": "zip", 2966 | "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", 2967 | "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", 2968 | "shasum": "" 2969 | }, 2970 | "require": { 2971 | "php": ">=7.1" 2972 | }, 2973 | "default-branch": true, 2974 | "type": "library", 2975 | "extra": { 2976 | "branch-alias": { 2977 | "dev-main": "1.22-dev" 2978 | }, 2979 | "thanks": { 2980 | "name": "symfony/polyfill", 2981 | "url": "https://github.com/symfony/polyfill" 2982 | } 2983 | }, 2984 | "autoload": { 2985 | "psr-4": { 2986 | "Symfony\\Polyfill\\Php72\\": "" 2987 | }, 2988 | "files": [ 2989 | "bootstrap.php" 2990 | ] 2991 | }, 2992 | "notification-url": "https://packagist.org/downloads/", 2993 | "license": [ 2994 | "MIT" 2995 | ], 2996 | "authors": [ 2997 | { 2998 | "name": "Nicolas Grekas", 2999 | "email": "p@tchwork.com" 3000 | }, 3001 | { 3002 | "name": "Symfony Community", 3003 | "homepage": "https://symfony.com/contributors" 3004 | } 3005 | ], 3006 | "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", 3007 | "homepage": "https://symfony.com", 3008 | "keywords": [ 3009 | "compatibility", 3010 | "polyfill", 3011 | "portable", 3012 | "shim" 3013 | ], 3014 | "support": { 3015 | "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" 3016 | }, 3017 | "funding": [ 3018 | { 3019 | "url": "https://symfony.com/sponsor", 3020 | "type": "custom" 3021 | }, 3022 | { 3023 | "url": "https://github.com/fabpot", 3024 | "type": "github" 3025 | }, 3026 | { 3027 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3028 | "type": "tidelift" 3029 | } 3030 | ], 3031 | "time": "2021-01-07T16:49:33+00:00" 3032 | }, 3033 | { 3034 | "name": "symfony/stopwatch", 3035 | "version": "4.4.x-dev", 3036 | "source": { 3037 | "type": "git", 3038 | "url": "https://github.com/symfony/stopwatch.git", 3039 | "reference": "c5572f6494fc20668a73b77684d8dc77e534d8cf" 3040 | }, 3041 | "dist": { 3042 | "type": "zip", 3043 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/c5572f6494fc20668a73b77684d8dc77e534d8cf", 3044 | "reference": "c5572f6494fc20668a73b77684d8dc77e534d8cf", 3045 | "shasum": "" 3046 | }, 3047 | "require": { 3048 | "php": ">=7.1.3", 3049 | "symfony/service-contracts": "^1.0|^2" 3050 | }, 3051 | "type": "library", 3052 | "autoload": { 3053 | "psr-4": { 3054 | "Symfony\\Component\\Stopwatch\\": "" 3055 | }, 3056 | "exclude-from-classmap": [ 3057 | "/Tests/" 3058 | ] 3059 | }, 3060 | "notification-url": "https://packagist.org/downloads/", 3061 | "license": [ 3062 | "MIT" 3063 | ], 3064 | "authors": [ 3065 | { 3066 | "name": "Fabien Potencier", 3067 | "email": "fabien@symfony.com" 3068 | }, 3069 | { 3070 | "name": "Symfony Community", 3071 | "homepage": "https://symfony.com/contributors" 3072 | } 3073 | ], 3074 | "description": "Provides a way to profile code", 3075 | "homepage": "https://symfony.com", 3076 | "support": { 3077 | "source": "https://github.com/symfony/stopwatch/tree/4.4" 3078 | }, 3079 | "funding": [ 3080 | { 3081 | "url": "https://symfony.com/sponsor", 3082 | "type": "custom" 3083 | }, 3084 | { 3085 | "url": "https://github.com/fabpot", 3086 | "type": "github" 3087 | }, 3088 | { 3089 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3090 | "type": "tidelift" 3091 | } 3092 | ], 3093 | "time": "2021-01-27T09:09:26+00:00" 3094 | }, 3095 | { 3096 | "name": "theseer/tokenizer", 3097 | "version": "1.1.3", 3098 | "source": { 3099 | "type": "git", 3100 | "url": "https://github.com/theseer/tokenizer.git", 3101 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" 3102 | }, 3103 | "dist": { 3104 | "type": "zip", 3105 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 3106 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 3107 | "shasum": "" 3108 | }, 3109 | "require": { 3110 | "ext-dom": "*", 3111 | "ext-tokenizer": "*", 3112 | "ext-xmlwriter": "*", 3113 | "php": "^7.0" 3114 | }, 3115 | "type": "library", 3116 | "autoload": { 3117 | "classmap": [ 3118 | "src/" 3119 | ] 3120 | }, 3121 | "notification-url": "https://packagist.org/downloads/", 3122 | "license": [ 3123 | "BSD-3-Clause" 3124 | ], 3125 | "authors": [ 3126 | { 3127 | "name": "Arne Blankerts", 3128 | "email": "arne@blankerts.de", 3129 | "role": "Developer" 3130 | } 3131 | ], 3132 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 3133 | "support": { 3134 | "issues": "https://github.com/theseer/tokenizer/issues", 3135 | "source": "https://github.com/theseer/tokenizer/tree/master" 3136 | }, 3137 | "time": "2019-06-13T22:48:21+00:00" 3138 | }, 3139 | { 3140 | "name": "webmozart/assert", 3141 | "version": "1.9.1", 3142 | "source": { 3143 | "type": "git", 3144 | "url": "https://github.com/webmozarts/assert.git", 3145 | "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" 3146 | }, 3147 | "dist": { 3148 | "type": "zip", 3149 | "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", 3150 | "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", 3151 | "shasum": "" 3152 | }, 3153 | "require": { 3154 | "php": "^5.3.3 || ^7.0 || ^8.0", 3155 | "symfony/polyfill-ctype": "^1.8" 3156 | }, 3157 | "conflict": { 3158 | "phpstan/phpstan": "<0.12.20", 3159 | "vimeo/psalm": "<3.9.1" 3160 | }, 3161 | "require-dev": { 3162 | "phpunit/phpunit": "^4.8.36 || ^7.5.13" 3163 | }, 3164 | "type": "library", 3165 | "autoload": { 3166 | "psr-4": { 3167 | "Webmozart\\Assert\\": "src/" 3168 | } 3169 | }, 3170 | "notification-url": "https://packagist.org/downloads/", 3171 | "license": [ 3172 | "MIT" 3173 | ], 3174 | "authors": [ 3175 | { 3176 | "name": "Bernhard Schussek", 3177 | "email": "bschussek@gmail.com" 3178 | } 3179 | ], 3180 | "description": "Assertions to validate method input/output with nice error messages.", 3181 | "keywords": [ 3182 | "assert", 3183 | "check", 3184 | "validate" 3185 | ], 3186 | "support": { 3187 | "issues": "https://github.com/webmozarts/assert/issues", 3188 | "source": "https://github.com/webmozarts/assert/tree/1.9.1" 3189 | }, 3190 | "time": "2020-07-08T17:02:28+00:00" 3191 | } 3192 | ], 3193 | "aliases": [], 3194 | "minimum-stability": "dev", 3195 | "stability-flags": { 3196 | "symfony/yaml": 20 3197 | }, 3198 | "prefer-stable": false, 3199 | "prefer-lowest": false, 3200 | "platform": { 3201 | "php": "^7.1.3", 3202 | "ext-mbstring": "*" 3203 | }, 3204 | "platform-dev": [], 3205 | "plugin-api-version": "2.0.0" 3206 | } -------------------------------------------------------------------------------- /docs/arch/0001-documenting-architecture-decisions.md: -------------------------------------------------------------------------------- 1 | # 1. Documenting architecture decisions 2 | 3 | Date: 2018-02-11 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Record certain design decisions for the benefit of future team members as well as for external oversight. 12 | 13 | ## Decision 14 | 15 | Use Architecture Decision Records (ADR), that is a technique for capturing important architectural decisions, along with their context and consequences as described by [Michael Nygard](https://twitter.com/mtnygard) in his article: [Documenting Architecture Decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). 16 | 17 | ## Consequences 18 | 19 | See Michael Nygard's article, linked above. -------------------------------------------------------------------------------- /docs/arch/0002-develop-tool-to-manage-adrs.md: -------------------------------------------------------------------------------- 1 | # 2. Develop tool to manage ADRs 2 | 3 | Date: 2018-02-12 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Encourage and facilitate the use of documentation in agile projects in world of evolutionary architecture using the technique Architecture Decision Records (ADR). 12 | 13 | ## Decision 14 | 15 | Develop a command-line tool to manage the Architecture Decision Records (ADR) that will be stored in version control along with the project source code. 16 | 17 | ## Consequences 18 | 19 | It is very easy for the development team to use. 20 | 21 | Project managers, client stakeholders, and others who don't work in version control like the development team, they will can to have difficulty using the tool or not using. 22 | 23 | But these people can help to manage Architecture Decision Records (ADR), because the records can be created or edited by a web-based version control repository hosting service, for example: Github, Bitbucket or others. -------------------------------------------------------------------------------- /docs/arch/0003-php-as-scripting-language.md: -------------------------------------------------------------------------------- 1 | # 3. PHP as scripting language 2 | 3 | Date: 2018-02-12 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | The tool must be cross-platform and developed using a open source programming language in command-line interface to be used in any project independent of the tech stack. 12 | 13 | ## Decision 14 | 15 | Develop using [PHP](http://php.net/) (PHP Hypertext Preprocessor) to work command line scripting. 16 | 17 | ## Consequences 18 | 19 | Is need to have PHP installed even if the project's technology stack is different. -------------------------------------------------------------------------------- /docs/arch/0004-composer-as-dependency-management.md: -------------------------------------------------------------------------------- 1 | # 4. Composer as dependency management 2 | 3 | Date: 2018-02-13 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Managing dependencies manually in any programming language is hard work, then we will need to use a dependency management tool for that project. 12 | 13 | ## Decision 14 | 15 | It will be used the [Composer](https://getcomposer.org/) as tool for dependency management. 16 | 17 | This project can also be installed with Composer using the following command: 18 | 19 | ``` 20 | composer require globtec/phpadr --dev dev-master 21 | ``` 22 | 23 | ## Consequences 24 | 25 | You must have the Composer tool installed. 26 | 27 | The Composer allows you to declare the libraries your projects depends on and it will manage (install/update) them for you. -------------------------------------------------------------------------------- /docs/arch/0005-phpunit-as-testing-framework.md: -------------------------------------------------------------------------------- 1 | # 5. PHPUnit as testing framework 2 | 3 | Date: 2018-02-13 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Ensure good code quality with ease for change, integration and error correction. 12 | 13 | ## Decision 14 | 15 | It will be used the [PHPUnit](https://phpunit.de/) as testing framework. 16 | 17 | ## Consequences 18 | 19 | Use unit testing for ensure that the code is working as expected and that changes don't break existing functionality. 20 | 21 | Allow to use Test-Driven Development (TDD) that is an evolutionary approach to development which combines test-first development. -------------------------------------------------------------------------------- /docs/arch/0006-yaml-as-configuration-file.md: -------------------------------------------------------------------------------- 1 | # 6. YAML as configuration file 2 | 3 | Date: 2018-06-30 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | In order to use a custom ADR template, it must be possible to configure the path to it. The same template must be used so that all ADR`s are structured in the same way. 12 | 13 | ## Decision 14 | 15 | The template path can be defined via a [YAML](http://yaml.org/) configuration file. 16 | 17 | ## Consequences 18 | 19 | We need to add [symfony/yaml](https://github.com/symfony/yaml) as a dependency to the project. 20 | 21 | To have a single place of truth the "directory" option must be removed from all console commands, because the directory can be defined in the configuration file. -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <phpunit colors="true" bootstrap="vendor/autoload.php"> 3 | <testsuites> 4 | <testsuite name="Package Test Suite"> 5 | <directory suffix="Test.php">tests/</directory> 6 | </testsuite> 7 | </testsuites> 8 | 9 | <filter> 10 | <whitelist addUncoveredFilesFromWhitelist="true" processUncoveredFilesFromWhitelist="true"> 11 | <directory suffix=".php">src/</directory> 12 | </whitelist> 13 | </filter> 14 | </phpunit> -------------------------------------------------------------------------------- /src/Command/MakeDecisionCommand.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Command; 4 | 5 | use ADR\Domain\DecisionContent; 6 | use ADR\Domain\DecisionRecord; 7 | use ADR\Domain\Sequence; 8 | use ADR\Filesystem\Config; 9 | use ADR\Filesystem\Workspace; 10 | use Symfony\Component\Console\Command\Command; 11 | use Symfony\Component\Console\Input\InputArgument; 12 | use Symfony\Component\Console\Input\InputInterface; 13 | use Symfony\Component\Console\Input\InputOption; 14 | use Symfony\Component\Console\Output\OutputInterface; 15 | 16 | /** 17 | * Command to make ADRs 18 | * 19 | * @author José Carlos <josecarlos@globtec.com.br> 20 | */ 21 | class MakeDecisionCommand extends Command 22 | { 23 | /** 24 | * Configures the command 25 | */ 26 | protected function configure() 27 | { 28 | $options = [ 29 | DecisionContent::STATUS_PROPOSED, 30 | DecisionContent::STATUS_ACCEPTED, 31 | DecisionContent::STATUS_REJECTED, 32 | DecisionContent::STATUS_DEPRECATED, 33 | ]; 34 | 35 | $this 36 | ->setName('make:decision') 37 | ->setDescription('Creates a new ADR') 38 | ->setHelp('This command allows you to create a new ADR') 39 | ->addArgument( 40 | 'title', 41 | InputArgument::REQUIRED, 42 | 'The title of the ADR' 43 | ) 44 | ->addArgument( 45 | 'status', 46 | InputArgument::OPTIONAL, 47 | sprintf( 48 | 'The status of the ADR, available options: [%s]', 49 | implode(', ', $options) 50 | ), 51 | DecisionContent::STATUS_ACCEPTED 52 | ) 53 | ->addOption( 54 | 'config', 55 | null, 56 | InputOption::VALUE_REQUIRED, 57 | 'Config file', 58 | 'vendor/globtec/phpadr/adr.yml' 59 | ); 60 | } 61 | 62 | /** 63 | * Execute the command 64 | * 65 | * @param InputInterface $input 66 | * @param OutputInterface $output 67 | */ 68 | protected function execute(InputInterface $input, OutputInterface $output) 69 | { 70 | $config = new Config($input->getOption('config')); 71 | $workspace = new Workspace($config->directory()); 72 | $sequence = new Sequence($workspace); 73 | $content = new DecisionContent($sequence->next(), $input->getArgument('title'), $input->getArgument('status')); 74 | $record = new DecisionRecord($content, $config); 75 | 76 | $workspace->add($record); 77 | 78 | $output->writeln('<info>ADR created successfully</info>'); 79 | 80 | return 0; 81 | } 82 | } -------------------------------------------------------------------------------- /src/Command/WorkspaceCountCommand.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Command; 4 | 5 | use ADR\Filesystem\Config; 6 | use ADR\Filesystem\Workspace; 7 | use Symfony\Component\Console\Command\Command; 8 | use Symfony\Component\Console\Input\InputInterface; 9 | use Symfony\Component\Console\Input\InputOption; 10 | use Symfony\Component\Console\Output\OutputInterface; 11 | use Symfony\Component\Console\Style\SymfonyStyle; 12 | 13 | /** 14 | * Command to count ADRs in workspace 15 | * 16 | * @author José Carlos <josecarlos@globtec.com.br> 17 | */ 18 | class WorkspaceCountCommand extends Command 19 | { 20 | /** 21 | * Configures the command 22 | */ 23 | protected function configure() 24 | { 25 | $this 26 | ->setName('workspace:count') 27 | ->setDescription('Count the ADRs') 28 | ->setHelp('This command allows you count the ADRs') 29 | ->addOption( 30 | 'config', 31 | null, 32 | InputOption::VALUE_REQUIRED, 33 | 'Config file', 34 | 'vendor/globtec/phpadr/adr.yml' 35 | ); 36 | } 37 | 38 | /** 39 | * Execute the command 40 | * 41 | * @param InputInterface $input 42 | * @param OutputInterface $output 43 | */ 44 | protected function execute(InputInterface $input, OutputInterface $output) 45 | { 46 | $config = new Config($input->getOption('config')); 47 | $style = new SymfonyStyle($input, $output); 48 | $workspace = new Workspace($config->directory()); 49 | 50 | $style->table(['Count'], [[$workspace->count()]]); 51 | 52 | return 0; 53 | } 54 | } -------------------------------------------------------------------------------- /src/Command/WorkspaceListCommand.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Command; 4 | 5 | use ADR\Filesystem\Config; 6 | use ADR\Filesystem\Workspace; 7 | use Symfony\Component\Console\Command\Command; 8 | use Symfony\Component\Console\Input\InputInterface; 9 | use Symfony\Component\Console\Input\InputOption; 10 | use Symfony\Component\Console\Output\OutputInterface; 11 | use Symfony\Component\Console\Style\SymfonyStyle; 12 | 13 | /** 14 | * Command to list ADRs in workspace 15 | * 16 | * @author José Carlos <josecarlos@globtec.com.br> 17 | */ 18 | class WorkspaceListCommand extends Command 19 | { 20 | /** 21 | * Configures the command 22 | */ 23 | protected function configure() 24 | { 25 | $this 26 | ->setName('workspace:list') 27 | ->setDescription('List the ADRs') 28 | ->setHelp('This command allows you list the ADRs') 29 | ->addOption( 30 | 'config', 31 | null, 32 | InputOption::VALUE_REQUIRED, 33 | 'Config file', 34 | 'vendor/globtec/phpadr/adr.yml' 35 | ); 36 | } 37 | 38 | /** 39 | * Execute the command 40 | * 41 | * @param InputInterface $input 42 | * @param OutputInterface $output 43 | */ 44 | protected function execute(InputInterface $input, OutputInterface $output) 45 | { 46 | $config = new Config($input->getOption('config')); 47 | $workspace = new Workspace($config->directory()); 48 | 49 | $records = $workspace->records(); 50 | 51 | asort($records); 52 | 53 | if (empty($records)) { 54 | $output->writeln('<info>Workspace is empty</info>'); 55 | } else { 56 | $style = new SymfonyStyle($input, $output); 57 | $style->table( 58 | ['Filename'], 59 | array_map(function ($record) { 60 | return [$record]; 61 | }, $records) 62 | ); 63 | } 64 | 65 | return 0; 66 | } 67 | } -------------------------------------------------------------------------------- /src/Domain/DecisionContent.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Domain; 4 | 5 | use Symfony\Component\Console\Exception\InvalidArgumentException; 6 | 7 | /** 8 | * Represents value object of the architecture decision 9 | * 10 | * @author José Carlos <josecarlos@globtec.com.br> 11 | */ 12 | class DecisionContent 13 | { 14 | /** 15 | * @var integer 16 | */ 17 | const TITLE_MAX_LENGTH = 100; 18 | 19 | /** 20 | * If the project stakeholders haven't agreed with it yet 21 | * 22 | * @var string 23 | */ 24 | const STATUS_PROPOSED = 'Proposed'; 25 | 26 | /** 27 | * Once it is agreed 28 | * 29 | * @var string 30 | */ 31 | const STATUS_ACCEPTED = 'Accepted'; 32 | 33 | /** 34 | * If a later ADR changes or reverses a decision to declined 35 | * 36 | * @var string 37 | */ 38 | const STATUS_REJECTED = 'Rejected'; 39 | 40 | /** 41 | * If a later ADR changes or reverses a decision to deprecated 42 | * 43 | * @var string 44 | */ 45 | const STATUS_DEPRECATED = 'Deprecated'; 46 | 47 | /** 48 | * @var int 49 | */ 50 | private $id; 51 | 52 | /** 53 | * @var string 54 | */ 55 | private $title; 56 | 57 | /** 58 | * @var string 59 | */ 60 | private $status; 61 | 62 | /** 63 | * @param int $id 64 | * @param string $title 65 | * @param string $status 66 | * 67 | * @throws InvalidArgumentException 68 | */ 69 | public function __construct(int $id, string $title, string $status = self::STATUS_ACCEPTED) 70 | { 71 | $this->id = $id; 72 | 73 | $this->setTitle($title); 74 | $this->setStatus($status); 75 | } 76 | 77 | /** 78 | * Returns the number of the ADR 79 | * 80 | * @return int ID value 81 | */ 82 | public function getId(): int 83 | { 84 | return $this->id; 85 | } 86 | 87 | /** 88 | * Returns the title 89 | * 90 | * @return string The title 91 | */ 92 | public function getTitle(): string 93 | { 94 | return $this->title; 95 | } 96 | 97 | /** 98 | * Returns the status 99 | * 100 | * @return string The status 101 | */ 102 | public function getStatus(): string 103 | { 104 | return $this->status; 105 | } 106 | 107 | /** 108 | * Set title 109 | * 110 | * @param string $title Must be short noun phrases 111 | * 112 | * @throws InvalidArgumentException 113 | */ 114 | private function setTitle(string $title): void 115 | { 116 | if ($this->isGreaterThanTitleMaxLenght($title)) { 117 | $message = sprintf( 118 | 'The title must be less than or equal to %d characters', 119 | self::TITLE_MAX_LENGTH 120 | ); 121 | 122 | throw new InvalidArgumentException($message); 123 | } 124 | 125 | $this->title = $title; 126 | } 127 | 128 | /** 129 | * Set status 130 | * 131 | * @param string $status Decision status 132 | * 133 | * @throws InvalidArgumentException 134 | */ 135 | private function setStatus(string $status): void 136 | { 137 | $statuses = [ 138 | self::STATUS_PROPOSED, 139 | self::STATUS_ACCEPTED, 140 | self::STATUS_REJECTED, 141 | self::STATUS_DEPRECATED, 142 | ]; 143 | 144 | $key = array_search(strtolower($status), array_map('strtolower', $statuses)); 145 | 146 | if (false === $key) { 147 | $message = sprintf( 148 | 'Invalid status "%s". Available status: [%s]', 149 | $status, 150 | implode(', ', $statuses) 151 | ); 152 | 153 | throw new InvalidArgumentException($message); 154 | } 155 | 156 | $this->status = $statuses[$key]; 157 | } 158 | 159 | /** 160 | * Determines whether the title is greater than maximum length set 161 | * 162 | * @param string $title 163 | * 164 | * @return bool 165 | */ 166 | private function isGreaterThanTitleMaxLenght(string $title): bool 167 | { 168 | return mb_strlen($title) > self::TITLE_MAX_LENGTH; 169 | } 170 | } -------------------------------------------------------------------------------- /src/Domain/DecisionRecord.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Domain; 4 | 5 | use ADR\Filesystem\Config; 6 | 7 | /** 8 | * Represents the architecture decision using text formatting language like Markdown 9 | * 10 | * @author José Carlos <josecarlos@globtec.com.br> 11 | */ 12 | class DecisionRecord 13 | { 14 | /** 15 | * Suffix to the name 16 | * 17 | * @var string 18 | */ 19 | const EXTENSION = '.md'; 20 | 21 | /** 22 | * Length of ADRs name number 23 | * 24 | * @var int 25 | */ 26 | const NUMBER_LENGTH = 4; 27 | 28 | /** 29 | * @var DecisionContent 30 | */ 31 | private $content; 32 | 33 | /** 34 | * @var Config 35 | */ 36 | private $config; 37 | 38 | /** 39 | * @param DecisionContent $content 40 | * @param Config $config 41 | */ 42 | public function __construct(DecisionContent $content, Config $config) 43 | { 44 | $this->content = $content; 45 | $this->config = $config; 46 | } 47 | 48 | /** 49 | * Returns the record file name 50 | * 51 | * @return string The file name 52 | */ 53 | public function filename(): string 54 | { 55 | return $this->sequence() . '-' . $this->slug() . self::EXTENSION; 56 | } 57 | 58 | /** 59 | * Returns the content of the ADR 60 | * 61 | * @return string The content 62 | */ 63 | public function output(): string 64 | { 65 | $vars = [ 66 | '<sequence>' => $this->content->getId(), 67 | '<title>' => $this->content->getTitle(), 68 | '<date>' => date('Y-m-d'), 69 | '<status>' => $this->content->getStatus(), 70 | ]; 71 | 72 | return str_replace(array_keys($vars), array_values($vars), $this->template()); 73 | } 74 | 75 | /** 76 | * Returns the number sequentially 77 | * 78 | * @return string The number sequentially 79 | */ 80 | private function sequence(): string 81 | { 82 | return str_pad($this->content->getId(), self::NUMBER_LENGTH, '0', STR_PAD_LEFT); 83 | } 84 | 85 | /** 86 | * Returns the title slugged 87 | * 88 | * @return string 89 | */ 90 | private function slug(): string 91 | { 92 | $slugged = iconv('UTF-8', 'ASCII//TRANSLIT', $this->content->getTitle()); 93 | $slugged = preg_replace('/[^-\/+|\w ]/', '', $slugged); 94 | $slugged = strtolower(trim($slugged)); 95 | $slugged = preg_replace('/[\/_|+ -]+/', '-', $slugged); 96 | 97 | return trim($slugged, '-'); 98 | } 99 | 100 | /** 101 | * Returns template for ADR 102 | * 103 | * @return string 104 | */ 105 | private function template(): string 106 | { 107 | ob_start(); 108 | 109 | require realpath($this->config->decisionRecordTemplateFile()); 110 | 111 | return ob_get_clean(); 112 | } 113 | } -------------------------------------------------------------------------------- /src/Domain/Sequence.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Domain; 4 | 5 | use ADR\Filesystem\Workspace; 6 | use Symfony\Component\Console\Exception\LogicException; 7 | 8 | /** 9 | * Represents the numbered sequentially and monotonically 10 | * 11 | * @author José Carlos <josecarlos@globtec.com.br> 12 | */ 13 | class Sequence 14 | { 15 | /** 16 | * Max value to sequence 17 | * 18 | * @var integer 19 | */ 20 | const MAX_VALUE = 9999; 21 | 22 | /** 23 | * @var Workspace 24 | */ 25 | private $workspace; 26 | 27 | /** 28 | * @param Workspace $workspace 29 | */ 30 | public function __construct(Workspace $workspace) 31 | { 32 | $this->workspace = $workspace; 33 | } 34 | 35 | /** 36 | * Create sequence monotonically, numbers will not be reused 37 | * 38 | * @throws LogicException 39 | * 40 | * @return int The next value in a sequence 41 | */ 42 | public function next(): int 43 | { 44 | $sequence = $this->workspace->count() + 1; 45 | 46 | if ($sequence > self::MAX_VALUE) { 47 | throw new LogicException('Next value exceeds the max value and cannot be used'); 48 | } 49 | 50 | return $sequence; 51 | } 52 | } -------------------------------------------------------------------------------- /src/Filesystem/Config.php: -------------------------------------------------------------------------------- 1 | <?php declare(strict_types=1); 2 | 3 | namespace ADR\Filesystem; 4 | 5 | use RuntimeException; 6 | use Symfony\Component\Yaml\Yaml; 7 | 8 | /** 9 | * Represents the configuration of this project. 10 | * 11 | * @author Alexander Bachmann <a.t.bachmann@gmail.com> 12 | */ 13 | class Config 14 | { 15 | /** 16 | * @var array 17 | */ 18 | private $data; 19 | 20 | /** 21 | * @param string $file 22 | * 23 | * @throws RuntimeException 24 | */ 25 | public function __construct(string $file) 26 | { 27 | $this->parseFile($file); 28 | } 29 | 30 | /** 31 | * Returns the ADR workspace directory 32 | * 33 | * @return string 34 | */ 35 | public function directory(): string 36 | { 37 | return $this->data['directory']; 38 | } 39 | 40 | /** 41 | * Returns the path to the decision record template 42 | * 43 | * @return string 44 | */ 45 | public function decisionRecordTemplateFile(): string 46 | { 47 | return $this->data['template']['decision-record']; 48 | } 49 | 50 | /** 51 | * @param string $file 52 | * 53 | * @throws RuntimeException If the file can not be processed 54 | */ 55 | private function parseFile(string $file): void 56 | { 57 | if (! file_exists($file)) { 58 | throw new RuntimeException("The config file does not exist: $file"); 59 | } 60 | 61 | if (! is_readable($file)) { 62 | throw new RuntimeException("The config file isn't readable: $file"); 63 | } 64 | 65 | $this->data = Yaml::parseFile($file); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Filesystem/Workspace.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Filesystem; 4 | 5 | use ADR\Domain\DecisionRecord; 6 | use FilesystemIterator; 7 | use SplFileInfo; 8 | use Symfony\Component\Console\Exception\RuntimeException; 9 | 10 | /** 11 | * Represents the workspace that store the ADRs 12 | * 13 | * @author José Carlos <josecarlos@globtec.com.br> 14 | */ 15 | class Workspace 16 | { 17 | /** 18 | * @var string 19 | */ 20 | private $directory; 21 | 22 | /** 23 | * @param string $directory The directory name 24 | * 25 | * @throws RuntimeException 26 | */ 27 | public function __construct(string $directory) 28 | { 29 | $this->set($directory); 30 | } 31 | 32 | /** 33 | * Returns directory name 34 | * 35 | * @return string The directory name 36 | */ 37 | public function get(): string 38 | { 39 | return $this->directory; 40 | } 41 | 42 | /** 43 | * Count the number of ADRs in workspace 44 | * 45 | * @return int The number of ADRs in workspace 46 | */ 47 | public function count(): int 48 | { 49 | return count($this->records()); 50 | } 51 | 52 | /** 53 | * Write a Architecture Decision to a markdown file 54 | * 55 | * @param DecisionRecord $record 56 | */ 57 | public function add(DecisionRecord $record): void 58 | { 59 | file_put_contents($this->filename($record), $record->output()); 60 | } 61 | 62 | /** 63 | * Returns array containing name of the ADRs 64 | * 65 | * @return array 66 | */ 67 | public function records(): array 68 | { 69 | $records = []; 70 | 71 | $iterator = new FilesystemIterator($this->get(), FilesystemIterator::SKIP_DOTS); 72 | 73 | /* @var $fileInfo SplFileInfo */ 74 | foreach ($iterator as $fileInfo) { 75 | if ($this->isValidFilename($fileInfo)) { 76 | array_push($records, $fileInfo->getFilename()); 77 | } 78 | } 79 | 80 | return $records; 81 | } 82 | 83 | /** 84 | * Sets workspace that store the ADRs 85 | * 86 | * @param string $directory The workspace that store the ADRs 87 | * 88 | * @throws RuntimeException 89 | */ 90 | private function set(string $directory): void 91 | { 92 | if (! file_exists($directory)) { 93 | $this->make($directory); 94 | } 95 | 96 | $this->directory = $directory; 97 | } 98 | 99 | /** 100 | * Makes directory 101 | * 102 | * @param string $directory The directory path 103 | * 104 | * @throws RuntimeException When the permissions prevent creating the directory 105 | */ 106 | private function make(string $directory): void 107 | { 108 | $parent = dirname($directory); 109 | 110 | while (! is_dir($parent)) { 111 | $parent = dirname($parent); 112 | } 113 | 114 | if (! is_writable($parent)) { 115 | throw new RuntimeException("The parent directory isn't writable: $parent"); 116 | } 117 | 118 | if (! mkdir($directory, 0777, true)) { 119 | throw new RuntimeException("Illegal directory: $directory"); 120 | } 121 | } 122 | 123 | /** 124 | * Returns filename 125 | * 126 | * @param DecisionRecord $record 127 | * 128 | * @return string The filename 129 | */ 130 | private function filename(DecisionRecord $record): string 131 | { 132 | return $this->get() . DIRECTORY_SEPARATOR . $record->filename(); 133 | } 134 | 135 | /** 136 | * Check if filename is valid to ADR 137 | * 138 | * @param SplFileInfo $fileInfo 139 | * 140 | * @return bool 141 | */ 142 | private function isValidFilename(SplFileInfo $fileInfo): bool 143 | { 144 | if (! $fileInfo->isFile()) { 145 | return false; 146 | } 147 | 148 | $pattern = '/^\d{' . DecisionRecord::NUMBER_LENGTH . '}-[a-z\d\-]+\\' . DecisionRecord::EXTENSION . '$/'; 149 | 150 | return (boolean) preg_match($pattern, $fileInfo->getFilename()); 151 | } 152 | } -------------------------------------------------------------------------------- /template/skeleton.md: -------------------------------------------------------------------------------- 1 | # <sequence>. <title> 2 | 3 | Date: <date> 4 | 5 | ## Status 6 | 7 | <status> 8 | 9 | ## Context 10 | 11 | ## Decision 12 | 13 | ## Consequences 14 | -------------------------------------------------------------------------------- /tests/Command/MakeDecisionCommandTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Command; 4 | 5 | use org\bovigo\vfs\vfsStream; 6 | use PHPUnit\Framework\TestCase; 7 | use Symfony\Component\Console\Application; 8 | use Symfony\Component\Console\Command\Command; 9 | use Symfony\Component\Console\Tester\CommandTester; 10 | 11 | class MakeDecisionCommandTest extends TestCase 12 | { 13 | /** 14 | * @var MakeDecisionCommand 15 | */ 16 | private $command; 17 | 18 | public function setUp() 19 | { 20 | $this->command = new MakeDecisionCommand(); 21 | } 22 | 23 | public function testInstanceOfCommand() 24 | { 25 | $this->assertInstanceOf(Command::class, $this->command); 26 | } 27 | 28 | public function testName() 29 | { 30 | $this->assertEquals('make:decision', $this->command->getName()); 31 | } 32 | 33 | public function testDescription() 34 | { 35 | $this->assertEquals('Creates a new ADR', $this->command->getDescription()); 36 | } 37 | 38 | public function testHelp() 39 | { 40 | $this->assertEquals('This command allows you to create a new ADR', $this->command->getHelp()); 41 | } 42 | 43 | public function testArguments() 44 | { 45 | $this->assertTrue($this->command->getDefinition()->hasArgument('title')); 46 | $this->assertTrue($this->command->getDefinition()->hasArgument('status')); 47 | 48 | $this->assertCount(2, $this->command->getDefinition()->getArguments()); 49 | } 50 | 51 | public function testArgumentTitle() 52 | { 53 | $argument = $this->command->getDefinition()->getArgument('title'); 54 | 55 | $this->assertTrue($argument->isRequired()); 56 | $this->assertEquals('The title of the ADR', $argument->getDescription()); 57 | $this->assertNull($argument->getDefault()); 58 | } 59 | 60 | public function testArgumentStatus() 61 | { 62 | $argument = $this->command->getDefinition()->getArgument('status'); 63 | 64 | $this->assertFalse($argument->isRequired()); 65 | $this->assertEquals('The status of the ADR, available options: [Proposed, Accepted, Rejected, Deprecated]', 66 | $argument->getDescription()); 67 | $this->assertEquals('Accepted', $argument->getDefault()); 68 | } 69 | 70 | public function testOptions() 71 | { 72 | $this->assertTrue($this->command->getDefinition()->hasOption('config')); 73 | 74 | $this->assertCount(1, $this->command->getDefinition()->getOptions()); 75 | } 76 | 77 | public function testOptionConfig() 78 | { 79 | $option = $this->command->getDefinition()->getOption('config'); 80 | 81 | $this->assertNull($option->getShortcut()); 82 | $this->assertTrue($option->isValueRequired()); 83 | $this->assertEquals('Config file', $option->getDescription()); 84 | $this->assertEquals('vendor/globtec/phpadr/adr.yml', $option->getDefault()); 85 | } 86 | 87 | public function testExecute() 88 | { 89 | $vfs = vfsStream::setup(); 90 | $configContent = file_get_contents('adr.yml'); 91 | $configContent = str_replace('docs/arch', $vfs->url(), $configContent); 92 | $configContent = str_replace('vendor/globtec/phpadr/', '', $configContent); 93 | $configFile = vfsStream::newFile('adr.yml')->at($vfs)->setContent($configContent)->url(); 94 | 95 | (new Application())->add($this->command); 96 | 97 | $tester = new CommandTester($this->command); 98 | 99 | $tester->execute([ 100 | 'command' => $this->command->getName(), 101 | 'title' => 'Foo', 102 | '--config' => $configFile, 103 | ]); 104 | 105 | $this->assertRegexp('/ADR created successfully/', $tester->getDisplay()); 106 | } 107 | } -------------------------------------------------------------------------------- /tests/Command/WorkspaceCountCommandTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Command; 4 | 5 | use org\bovigo\vfs\vfsStream; 6 | use PHPUnit\Framework\TestCase; 7 | use Symfony\Component\Console\Application; 8 | use Symfony\Component\Console\Command\Command; 9 | use Symfony\Component\Console\Tester\CommandTester; 10 | 11 | class WorkspaceCountCommandTest extends TestCase 12 | { 13 | /** 14 | * @var WorkspaceCountCommand 15 | */ 16 | private $command; 17 | 18 | public function setUp() 19 | { 20 | $this->command = new WorkspaceCountCommand(); 21 | } 22 | 23 | public function testInstanceOfCommand() 24 | { 25 | $this->assertInstanceOf(Command::class, $this->command); 26 | } 27 | 28 | public function testName() 29 | { 30 | $this->assertEquals('workspace:count', $this->command->getName()); 31 | } 32 | 33 | public function testDescription() 34 | { 35 | $this->assertEquals('Count the ADRs', $this->command->getDescription()); 36 | } 37 | 38 | public function testHelp() 39 | { 40 | $this->assertEquals('This command allows you count the ADRs', $this->command->getHelp()); 41 | } 42 | 43 | public function testOptions() 44 | { 45 | $this->assertTrue($this->command->getDefinition()->hasOption('config')); 46 | 47 | $this->assertCount(1, $this->command->getDefinition()->getOptions()); 48 | } 49 | 50 | public function testOptionConfig() 51 | { 52 | $option = $this->command->getDefinition()->getOption('config'); 53 | 54 | $this->assertNull($option->getShortcut()); 55 | $this->assertTrue($option->isValueRequired()); 56 | $this->assertEquals('Config file', $option->getDescription()); 57 | $this->assertEquals('vendor/globtec/phpadr/adr.yml', $option->getDefault()); 58 | } 59 | 60 | public function testExecute() 61 | { 62 | $vfs = vfsStream::setup(); 63 | $configContent = file_get_contents('adr.yml'); 64 | $configContent = str_replace('docs/arch', $vfs->url(), $configContent); 65 | $configContent = str_replace('vendor/globtec/phpadr/', '', $configContent); 66 | $configFile = vfsStream::newFile('adr.yml')->at($vfs)->setContent($configContent)->url(); 67 | 68 | $input = [ 69 | 'command' => $this->command->getName(), 70 | '--config' => $configFile, 71 | ]; 72 | 73 | (new Application())->add($this->command); 74 | 75 | $tester = new CommandTester($this->command); 76 | $tester->execute($input); 77 | 78 | $this->assertRegexp('/0/', $tester->getDisplay()); 79 | 80 | $vfs->addChild(vfsStream::newFile('0001-foo.md')); 81 | $vfs->addChild(vfsStream::newFile('0002-bar.md')); 82 | 83 | $tester->execute($input); 84 | 85 | $this->assertRegexp('/2/', $tester->getDisplay()); 86 | } 87 | } -------------------------------------------------------------------------------- /tests/Command/WorkspaceListCommandTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Command; 4 | 5 | use org\bovigo\vfs\vfsStream; 6 | use PHPUnit\Framework\TestCase; 7 | use Symfony\Component\Console\Application; 8 | use Symfony\Component\Console\Command\Command; 9 | use Symfony\Component\Console\Tester\CommandTester; 10 | 11 | class WorkspaceListCommandTest extends TestCase 12 | { 13 | /** 14 | * @var WorkspaceListCommand 15 | */ 16 | private $command; 17 | 18 | public function setUp() 19 | { 20 | $this->command = new WorkspaceListCommand(); 21 | } 22 | 23 | public function testInstanceOfCommand() 24 | { 25 | $this->assertInstanceOf(Command::class, $this->command); 26 | } 27 | 28 | public function testName() 29 | { 30 | $this->assertEquals('workspace:list', $this->command->getName()); 31 | } 32 | 33 | public function testDescription() 34 | { 35 | $this->assertEquals('List the ADRs', $this->command->getDescription()); 36 | } 37 | 38 | public function testHelp() 39 | { 40 | $this->assertEquals('This command allows you list the ADRs', $this->command->getHelp()); 41 | } 42 | 43 | public function testOptions() 44 | { 45 | $this->assertTrue($this->command->getDefinition()->hasOption('config')); 46 | 47 | $this->assertCount(1, $this->command->getDefinition()->getOptions()); 48 | } 49 | 50 | public function testOptionConfig() 51 | { 52 | $option = $this->command->getDefinition()->getOption('config'); 53 | 54 | $this->assertNull($option->getShortcut()); 55 | $this->assertTrue($option->isValueRequired()); 56 | $this->assertEquals('Config file', $option->getDescription()); 57 | $this->assertEquals('vendor/globtec/phpadr/adr.yml', $option->getDefault()); 58 | } 59 | 60 | public function testExecute() 61 | { 62 | $vfs = vfsStream::setup(); 63 | $configContent = file_get_contents('adr.yml'); 64 | $configContent = str_replace('docs/arch', $vfs->url(), $configContent); 65 | $configContent = str_replace('vendor/globtec/phpadr/', '', $configContent); 66 | $configFile = vfsStream::newFile('adr.yml')->at($vfs)->setContent($configContent)->url(); 67 | 68 | $input = [ 69 | 'command' => $this->command->getName(), 70 | '--config' => $configFile, 71 | ]; 72 | 73 | (new Application())->add($this->command); 74 | 75 | $tester = new CommandTester($this->command); 76 | $tester->execute($input); 77 | 78 | $this->assertRegexp('/Workspace is empty/', $tester->getDisplay()); 79 | 80 | $vfs->addChild(vfsStream::newFile('0001-foo.md')); 81 | $vfs->addChild(vfsStream::newFile('0002-bar.md')); 82 | 83 | $tester->execute($input); 84 | 85 | $this->assertContains('0001-foo.md', $tester->getDisplay()); 86 | $this->assertContains('0002-bar.md', $tester->getDisplay()); 87 | } 88 | } -------------------------------------------------------------------------------- /tests/Domain/DecisionContentTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Domain; 4 | 5 | use InvalidArgumentException; 6 | use PHPUnit\Framework\TestCase; 7 | 8 | class DecisionContentTest extends TestCase 9 | { 10 | /** 11 | * @dataProvider providerTestInstanceSuccessfully 12 | */ 13 | public function testInstanceSuccessfully($input, $output) 14 | { 15 | $content = new DecisionContent(1, 'Foo', $input); 16 | 17 | $this->assertEquals(1, $content->getId()); 18 | $this->assertEquals('Foo', $content->getTitle()); 19 | $this->assertEquals($output, $content->getStatus()); 20 | } 21 | 22 | /** 23 | * @expectedException InvalidArgumentException 24 | */ 25 | public function testInstanceFailureWithTitle() 26 | { 27 | new DecisionContent(1, 28 | 'A very large title should not be used in arquitecture decision record because this attribute must be short noun phrases'); 29 | } 30 | 31 | /** 32 | * @expectedException InvalidArgumentException 33 | */ 34 | public function testInstanceFailureWithStatus() 35 | { 36 | new DecisionContent(1, 'Foo', 'Superseded'); 37 | } 38 | 39 | public function providerTestInstanceSuccessfully() 40 | { 41 | return [ 42 | ['Proposed', 'Proposed'], 43 | ['proposed', 'Proposed'], 44 | ['Accepted', 'Accepted'], 45 | ['accepted', 'Accepted'], 46 | ['Rejected', 'Rejected'], 47 | ['rejected', 'Rejected'], 48 | ['Deprecated', 'Deprecated'], 49 | ['deprecated', 'Deprecated'], 50 | ]; 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /tests/Domain/DecisionRecordTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Domain; 4 | 5 | use ADR\Filesystem\Config; 6 | use PHPUnit\Framework\MockObject\MockObject; 7 | use PHPUnit\Framework\TestCase; 8 | 9 | class DecisionRecordTest extends TestCase 10 | { 11 | /** 12 | * @var DecisionContent|MockObject 13 | */ 14 | private $content; 15 | 16 | /** 17 | * @var Config|MockObject 18 | */ 19 | private $config; 20 | 21 | public function setUp() 22 | { 23 | $this->content = $this->getMockBuilder(DecisionContent::class) 24 | ->disableOriginalConstructor() 25 | ->getMock(); 26 | 27 | $this->config = $this->getMockBuilder(Config::class) 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | } 31 | 32 | public function testFilename() 33 | { 34 | $this->content->expects($this->once())->method('getId')->willReturn(1); 35 | $this->content->expects($this->once())->method('getTitle')->willReturn('Foo'); 36 | 37 | $record = new DecisionRecord($this->content, $this->config); 38 | 39 | $this->assertEquals('0001-foo.md', $record->filename()); 40 | } 41 | 42 | public function testOutput() 43 | { 44 | $this->content->expects($this->once())->method('getId')->willReturn(1); 45 | $this->content->expects($this->once())->method('getTitle')->willReturn('Foo'); 46 | $this->content->expects($this->once())->method('getStatus')->willReturn('Accepted'); 47 | 48 | $this->config->expects($this->any())->method('decisionRecordTemplateFile')->willReturn('template/skeleton.md'); 49 | 50 | $record = new DecisionRecord($this->content, $this->config); 51 | 52 | $output = $record->output(); 53 | 54 | $this->assertContains('# 1. Foo', $output); 55 | $this->assertContains(date('Y-m-d'), $output); 56 | $this->assertContains('Accepted', $output); 57 | } 58 | } -------------------------------------------------------------------------------- /tests/Domain/SequenceTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Domain; 4 | 5 | use ADR\Filesystem\Workspace; 6 | use LogicException; 7 | use PHPUnit\Framework\MockObject\MockObject; 8 | use PHPUnit\Framework\TestCase; 9 | 10 | class SequenceTest extends TestCase 11 | { 12 | /** 13 | * @var Workspace|MockObject 14 | */ 15 | private $workspace; 16 | 17 | public function setUp() 18 | { 19 | $this->workspace = $this->getMockBuilder(Workspace::class) 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | } 23 | 24 | /** 25 | * @dataProvider providerTestNextSuccessfully 26 | */ 27 | public function testNextSuccessfully($count, $expected) 28 | { 29 | $sequence = new Sequence($this->workspace); 30 | 31 | $this->workspace->expects($this->once())->method('count')->willReturn($count); 32 | 33 | $this->assertEquals($expected, $sequence->next()); 34 | } 35 | 36 | public function testNextFailure() 37 | { 38 | $this->expectException(LogicException::class); 39 | 40 | $this->workspace->expects($this->once())->method('count')->willReturn(Sequence::MAX_VALUE); 41 | 42 | $sequence = new Sequence($this->workspace); 43 | $sequence->next(); 44 | } 45 | 46 | public function providerTestNextSuccessfully() 47 | { 48 | return [ 49 | [0, 1], 50 | [10, 11], 51 | [100, 101], 52 | [999, 1000], 53 | [1050, 1051], 54 | ]; 55 | } 56 | } -------------------------------------------------------------------------------- /tests/Filesystem/ConfigTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Filesystem; 4 | 5 | use org\bovigo\vfs\vfsStream; 6 | use PHPUnit\Framework\TestCase; 7 | use RuntimeException; 8 | 9 | class ConfigTest extends TestCase 10 | { 11 | public function testInstanceSuccessfully() 12 | { 13 | $config = new Config(__DIR__ . '/../../adr.yml'); 14 | 15 | $this->assertEquals('docs/arch', $config->directory()); 16 | $this->assertEquals('vendor/globtec/phpadr/template/skeleton.md', $config->decisionRecordTemplateFile()); 17 | } 18 | 19 | public function testInstanceNotExistingFailure() 20 | { 21 | $this->expectException(RuntimeException::class); 22 | $this->expectExceptionMessage('The config file does not exist: invalid.yml'); 23 | 24 | new Config('invalid.yml'); 25 | } 26 | 27 | public function testInstanceNotReadableFailure() 28 | { 29 | $vfs = vfsStream::setup(); 30 | $configFile = vfsStream::newFile('adr.yml')->at($vfs)->chmod(0)->url(); 31 | 32 | $this->expectException(RuntimeException::class); 33 | $this->expectExceptionMessage("The config file isn't readable: $configFile"); 34 | 35 | new Config($configFile); 36 | } 37 | } -------------------------------------------------------------------------------- /tests/Filesystem/WorkspaceTest.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace ADR\Filesystem; 4 | 5 | use ADR\Domain\DecisionRecord; 6 | use org\bovigo\vfs\vfsStream; 7 | use PHPUnit\Framework\TestCase; 8 | use RuntimeException; 9 | 10 | class WorkspaceTest extends TestCase 11 | { 12 | public function testSetSuccessfully() 13 | { 14 | $directory = vfsStream::setup()->url(); 15 | 16 | $workspace = new Workspace($directory); 17 | 18 | $this->assertEquals($directory, $workspace->get()); 19 | $this->assertDirectoryIsWritable($workspace->get()); 20 | } 21 | 22 | public function testSetFailure() 23 | { 24 | $directory = vfsStream::setup('docs', 00)->url(); 25 | 26 | $this->expectException(RuntimeException::class); 27 | $this->expectExceptionMessage("The parent directory isn't writable: $directory"); 28 | 29 | new Workspace($directory . '/arch'); 30 | } 31 | 32 | public function testCount() 33 | { 34 | $vfs = vfsStream::setup(); 35 | 36 | $workspace = new Workspace($vfs->url()); 37 | 38 | $this->assertEquals(0, $workspace->count()); 39 | 40 | $vfs->addChild(vfsStream::newFile('0001-foo.md')); 41 | $vfs->addChild(vfsStream::newFile('0002-bar.md')); 42 | $vfs->addChild(vfsStream::newFile('0003-baz-2.md')); 43 | 44 | $vfs->addChild(vfsStream::newFile('0004-snake_case.md')); 45 | $vfs->addChild(vfsStream::newFile('0005-UPPERCASE.md')); 46 | $vfs->addChild(vfsStream::newFile('0006-CamelCase.md')); 47 | $vfs->addChild(vfsStream::newFile('any-file.md')); 48 | $vfs->addChild(vfsStream::newDirectory('any-directory')); 49 | 50 | $this->assertEquals(3, $workspace->count()); 51 | } 52 | 53 | public function testAdd() 54 | { 55 | $vfs = vfsStream::setup(); 56 | 57 | $record = $this->getMockBuilder(DecisionRecord::class) 58 | ->disableOriginalConstructor() 59 | ->getMock(); 60 | 61 | $record->expects($this->once())->method('filename')->willReturn('0001-foo.md'); 62 | $record->expects($this->once())->method('output'); 63 | 64 | $workspace = new Workspace($vfs->url()); 65 | $workspace->add($record); 66 | 67 | $this->assertFileExists($vfs->url() . '/0001-foo.md'); 68 | } 69 | } --------------------------------------------------------------------------------