[
  {
    "path": ".coveralls.yml",
    "content": "json_path: coveralls-upload.json\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.{yml,yaml}]\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Enforce Unix newlines\n* text=auto eol=lf\n\n# Exclude unused files\n# see: https://redd.it/2jzp6k\n/.coveralls.yml     export-ignore\n/.editorconfig      export-ignore\n/.gitattributes     export-ignore\n/.github            export-ignore\n/.gitignore         export-ignore\n/CODE_OF_CONDUCT.md export-ignore\n/CONTRIBUTING.md    export-ignore\n/MAINTAINERS.md     export-ignore\n/README.md          export-ignore\n/UPGRADING.md       export-ignore\n/phpcs.xml.dist     export-ignore\n/phpstan.neon.dist  export-ignore\n/phpunit.xml.dist   export-ignore\n/psalm.xml          export-ignore\n/tests              export-ignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: slimphp\ntidelift: \"packagist/slim/slim\"\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n- package-ecosystem: \"github-actions\"\n  directory: \"/\"\n  schedule:\n    interval: \"monthly\"\n- package-ecosystem: composer\n  directory: \"/\"\n  schedule:\n    interval: monthly\n  open-pull-requests-limit: 10\n  versioning-strategy: increase\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\n\npermissions:\n  contents: read\n\non: [push, pull_request]\n\njobs:\n  tests:\n    name: Tests PHP ${{ matrix.php }}\n    runs-on: ubuntu-latest\n    continue-on-error: ${{ matrix.experimental }}\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [7.4, 8.0, 8.1, 8.2, 8.3, 8.4]\n        experimental: [false]\n        composer-options: ['']\n        include:\n          - php: 8.2\n            analysis: true\n          - php: 8.5\n            experimental: true\n            composer-options: '--ignore-platform-req=php+'\n          - php: nightly\n            experimental: true\n            composer-options: '--ignore-platform-req=php+'\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Set up PHP ${{ matrix.php }}\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          coverage: xdebug\n          tools: composer:2.8\n\n      - name: Install dependencies with Composer\n        run: composer update --prefer-dist --no-progress --no-interaction --ansi ${{ matrix.composer-options }}\n\n      - name: Coding standards\n        if: matrix.analysis\n        run: vendor/bin/phpcs\n\n      - name: Static analysis\n        if: matrix.analysis\n        run: vendor/bin/phpstan\n\n      - name: Tests\n        run: vendor/bin/phpunit ${{ matrix.analysis && '--coverage-clover clover.xml' || '--no-coverage'  }}\n\n      - name: Upload coverage results to Coveralls\n        if: matrix.analysis\n        uses: coverallsapp/github-action@v2\n        with:\n          flag-name: php-${{ matrix.php }}\n          files: clover.xml\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.idea\n.phpunit.result.cache\n.phpunit.cache/\ncomposer.lock\nphpunit.xml\nclover.xml\nvendor\ncoverage\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n### Added\n\n### Changed\n\n### Removed\n\n## 4.15.1 - 2025-11-21\n\n## Fixed\n\n- Allow PHPUnit 10, 11 and 12 when testing Slim itself (#3411)\n\n### Added\n\n- Add support for PHP 8.5 (#3415)\n\n**Full Changelog**: https://github.com/slimphp/Slim/compare/4.15.0...4.15.1\n\n## 4.15.0 - 2025-08-24\n\n### Fixed\n\n- Fix DocBlocks for callable route handlers (#3389)\n- Change class keyword to lowercase (#3346)\n- Fix tests for PHP 8.3\n- Fixes the build status badge in Readme (#3331)\n- Fix text and eol attributes for * selector in .gitattributes (#3391)\n- Deprecate setArgument/s (#3383)\n\n### Added\n\n- Add support for PHP 8.4\n- Add phpstan v2\n\n### Changed\n\n- Update http urls in composer.json (#3399)\n\n**Full Changelog**: https://github.com/slimphp/Slim/compare/4.14.0...4.15.0\n\n## 4.14.0 - 2024-06-13\n\n### Changed\n\n- Do not HTML entity encode in PlainTextErrorRenderer by @akrabat in https://github.com/slimphp/Slim/pull/3319\n- Only render tip to error log if plain text renderer is used by @akrabat in https://github.com/slimphp/Slim/pull/3321\n- Add template generics for PSR-11 implementations in PHPStan and Psalm by @limarkxx in https://github.com/slimphp/Slim/pull/3322\n- Update squizlabs/php_codesniffer requirement from ^3.9 to ^3.10 by @dependabot in https://github.com/slimphp/Slim/pull/3324\n- Update phpstan/phpstan requirement from ^1.10 to ^1.11 by @dependabot in https://github.com/slimphp/Slim/pull/3325\n- Update psr/http-factory requirement from ^1.0 to ^1.1 by @dependabot in https://github.com/slimphp/Slim/pull/3326\n\n#### Type hinting with template generics\n\nWith the introduction of template generics, if you type-hint `Slim\\App` instance variable using `/** @var \\Slim\\App $app */`, then you will need to change it to either:\n\n*  `/** @var \\Slim\\App<null> $app */` if you are not using a DI container, or\n* `/** @var \\Slim\\App<\\Psr\\Container\\ContainerInterface> $app */` if you are\n\nYou can also type-hint to the concrete instance of the container you are using too. For example, if you are using [PHP-DI](https://php-di.org), then you can use: `/** @var \\Slim\\App<DI\\Container> $app */`.\n\n### New Contributors\n\n* @limarkxx made their first contribution in https://github.com/slimphp/Slim/pull/3322\n\n**Full Changelog**: https://github.com/slimphp/Slim/compare/4.13.0...4.14.0\n\n# 4.13.0 - 2024-03-03\n\n- [3277: Create HttpTooManyRequestsException.php](https://github.com/slimphp/Slim/pull/3277) thanks to @flavioheleno\n- [3278: Remove HttpGoneException executable flag](https://github.com/slimphp/Slim/pull/3278) thanks to @flavioheleno\n- [3285: Update guzzlehttp/psr7 requirement from ^2.5 to ^2.6](https://github.com/slimphp/Slim/pull/3285) thanks to @dependabot[bot]\n- [3290: Bump actions/checkout from 3 to 4](https://github.com/slimphp/Slim/pull/3290) thanks to @dependabot[bot]\n- [3291: Fix line length](https://github.com/slimphp/Slim/pull/3291) thanks to @l0gicgate\n- [3296: PSR 7 http-message version requirement](https://github.com/slimphp/Slim/issues/3296) thanks to @rotexdegba\n- [3297: Allow Diactoros 3](https://github.com/slimphp/Slim/pull/3297) thanks to @derrabus\n- [3299: Update tests and add PHP 8.3 to the CI matrix](https://github.com/slimphp/Slim/pull/3299) thanks to @akrabat\n- [3301: Update nyholm/psr7-server requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3301) thanks to @dependabot[bot]\n- [3302: Add support for psr/http-message ^2.0](https://github.com/slimphp/Slim/pull/3302) thanks to @rotexdegba\n- [3305: Update phpspec/prophecy-phpunit requirement from ^2.0 to ^2.1](https://github.com/slimphp/Slim/pull/3305) thanks to @dependabot[bot]\n- [3306: Update phpspec/prophecy requirement from ^1.17 to ^1.18](https://github.com/slimphp/Slim/pull/3306) thanks to @dependabot[bot]\n- [3308: Update squizlabs/php&#95;codesniffer requirement from ^3.7 to ^3.8](https://github.com/slimphp/Slim/pull/3308) thanks to @dependabot[bot]\n- [3313: Bump ramsey/composer-install from 2 to 3](https://github.com/slimphp/Slim/pull/3313) thanks to @dependabot[bot]\n- [3314: Update phpspec/prophecy requirement from ^1.18 to ^1.19](https://github.com/slimphp/Slim/pull/3314) thanks to @dependabot[bot]\n- [3315: Update squizlabs/php&#95;codesniffer requirement from ^3.8 to ^3.9](https://github.com/slimphp/Slim/pull/3315) thanks to @dependabot[bot]\n\n# 4.12.0 - 2023-07-23\n\n- [3220: Refactor](https://github.com/slimphp/Slim/pull/3220) thanks to @amirkhodabande\n- [3237: Update phpstan/phpstan requirement from ^1.8 to ^1.9](https://github.com/slimphp/Slim/pull/3237) thanks to @dependabot[bot]\n- [3238: Update slim/http requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/3238) thanks to @dependabot[bot]\n- [3239: Update slim/psr7 requirement from ^1.5 to ^1.6](https://github.com/slimphp/Slim/pull/3239) thanks to @dependabot[bot]\n- [3240: Update phpspec/prophecy requirement from ^1.15 to ^1.16](https://github.com/slimphp/Slim/pull/3240) thanks to @dependabot[bot]\n- [3241: Update adriansuter/php-autoload-override requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3241) thanks to @dependabot[bot]\n- [3245: New ability to override RouteGroupInterface in the Route class](https://github.com/slimphp/Slim/pull/3245) thanks to @githubjeka\n- [3253: Fix HttpBadRequestException description](https://github.com/slimphp/Slim/pull/3253) thanks to @jsanahuja\n- [3254: Update phpunit/phpunit requirement from ^9.5 to ^9.6](https://github.com/slimphp/Slim/pull/3254) thanks to @dependabot[bot]\n- [3255: Update phpstan/phpstan requirement from ^1.9 to ^1.10](https://github.com/slimphp/Slim/pull/3255) thanks to @dependabot[bot]\n- [3256: Update phpspec/prophecy requirement from ^1.16 to ^1.17](https://github.com/slimphp/Slim/pull/3256) thanks to @dependabot[bot]\n- [3264: Update psr/http-message requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3264) thanks to @dependabot[bot]\n- [3265: Update nyholm/psr7 requirement from ^1.5 to ^1.7](https://github.com/slimphp/Slim/pull/3265) thanks to @dependabot[bot]\n- [3266: Update guzzlehttp/psr7 requirement from ^2.4 to ^2.5](https://github.com/slimphp/Slim/pull/3266) thanks to @dependabot[bot]\n- [3267: Update nyholm/psr7 requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3267) thanks to @dependabot[bot]\n- [3269: Update httpsoft/http-server-request requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3269) thanks to @dependabot[bot]\n- [3270: Update httpsoft/http-message requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3270) thanks to @dependabot[bot]\n- [3271: prevent multiple entries of same methode in FastRouteDispatcher](https://github.com/slimphp/Slim/pull/3271) thanks to @papparazzo\n\n## 4.11.0 - 2022-11-06\n\n- [3180: Declare types](https://github.com/slimphp/Slim/pull/3180) thanks to @nbayramberdiyev\n- [3181: Update laminas/laminas-diactoros requirement from ^2.8 to ^2.9](https://github.com/slimphp/Slim/pull/3181) thanks to @dependabot[bot]\n- [3182: Update guzzlehttp/psr7 requirement from ^2.1 to ^2.2](https://github.com/slimphp/Slim/pull/3182) thanks to @dependabot[bot]\n- [3183: Update phpstan/phpstan requirement from ^1.4 to ^1.5](https://github.com/slimphp/Slim/pull/3183) thanks to @dependabot[bot]\n- [3184: Update adriansuter/php-autoload-override requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/3184) thanks to @dependabot[bot]\n- [3189: Update phpstan/phpstan requirement from ^1.5 to ^1.6](https://github.com/slimphp/Slim/pull/3189) thanks to @dependabot[bot]\n- [3191: Adding property types to Middleware classes](https://github.com/slimphp/Slim/pull/3191) thanks to @ashleycoles\n- [3193: Handlers types](https://github.com/slimphp/Slim/pull/3193) thanks to @ashleycoles\n- [3194: Adding types to AbstractErrorRenderer](https://github.com/slimphp/Slim/pull/3194) thanks to @ashleycoles\n- [3195: Adding prop types for Exception classes](https://github.com/slimphp/Slim/pull/3195) thanks to @ashleycoles\n- [3196: Adding property type declarations for Factory classes](https://github.com/slimphp/Slim/pull/3196) thanks to @ashleycoles\n- [3197: Remove redundant docblock types](https://github.com/slimphp/Slim/pull/3197) thanks to @theodorejb\n- [3199: Update laminas/laminas-diactoros requirement from ^2.9 to ^2.11](https://github.com/slimphp/Slim/pull/3199) thanks to @dependabot[bot]\n- [3200: Update phpstan/phpstan requirement from ^1.6 to ^1.7](https://github.com/slimphp/Slim/pull/3200) thanks to @dependabot[bot]\n- [3205: Update guzzlehttp/psr7 requirement from ^2.2 to ^2.4](https://github.com/slimphp/Slim/pull/3205) thanks to @dependabot[bot]\n- [3206: Update squizlabs/php_codesniffer requirement from ^3.6 to ^3.7](https://github.com/slimphp/Slim/pull/3206) thanks to @dependabot[bot]\n- [3207: Update phpstan/phpstan requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3207) thanks to @dependabot[bot]\n- [3211: Assign null coalescing to coalesce equal](https://github.com/slimphp/Slim/pull/3211) thanks to @MathiasReker\n- [3213: Void return](https://github.com/slimphp/Slim/pull/3213) thanks to @MathiasReker\n- [3214: Is null](https://github.com/slimphp/Slim/pull/3214) thanks to @MathiasReker\n- [3216: Refactor](https://github.com/slimphp/Slim/pull/3216) thanks to @mehdihasanpour\n- [3218: Refactor some code](https://github.com/slimphp/Slim/pull/3218) thanks to @mehdihasanpour\n- [3221: Cleanup](https://github.com/slimphp/Slim/pull/3221) thanks to @mehdihasanpour\n- [3225: Update laminas/laminas-diactoros requirement from ^2.11 to ^2.14](https://github.com/slimphp/Slim/pull/3225) thanks to @dependabot[bot]\n- [3228: Using assertSame to let assert equal be restricted](https://github.com/slimphp/Slim/pull/3228) thanks to @peter279k\n- [3229: Update laminas/laminas-diactoros requirement from ^2.14 to ^2.17](https://github.com/slimphp/Slim/pull/3229) thanks to @dependabot[bot]\n- [3235: Persist routes indexed by name in RouteCollector for improved performance.](https://github.com/slimphp/Slim/pull/3235) thanks to @BusterNeece\n\n## 4.10.0 - 2022-03-14\n\n- [3120: Add a new PSR-17 factory to Psr17FactoryProvider](https://github.com/slimphp/Slim/pull/3120) thanks to @solventt\n- [3123: Replace deprecated setMethods() in tests](https://github.com/slimphp/Slim/pull/3123) thanks to @solventt\n- [3126: Update guzzlehttp/psr7 requirement from ^2.0 to ^2.1](https://github.com/slimphp/Slim/pull/3126) thanks to @dependabot[bot]\n- [3127: PHPStan v1.0](https://github.com/slimphp/Slim/pull/3127) thanks to @t0mmy742\n- [3128: Update phpstan/phpstan requirement from ^1.0 to ^1.2](https://github.com/slimphp/Slim/pull/3128) thanks to @dependabot[bot]\n- [3129: Deprecate PHP 7.3](https://github.com/slimphp/Slim/pull/3129) thanks to @l0gicgate\n- [3130: Removed double defined PHP 7.4](https://github.com/slimphp/Slim/pull/3130) thanks to @flangofas\n- [3132: Add new `RequestResponseNamedArgs` route strategy](https://github.com/slimphp/Slim/pull/3132) thanks to @adoy\n- [3133: Improve typehinting for `RouteParserInterface`](https://github.com/slimphp/Slim/pull/3133) thanks to @jerowork\n- [3135: Update phpstan/phpstan requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/3135) thanks to @dependabot[bot]\n- [3137: Update phpspec/prophecy requirement from ^1.14 to ^1.15](https://github.com/slimphp/Slim/pull/3137) thanks to @dependabot[bot]\n- [3138: Update license year](https://github.com/slimphp/Slim/pull/3138) thanks to @Awilum\n- [3139: Fixed #1730 (reintroduced in 4.x)](https://github.com/slimphp/Slim/pull/3139) thanks to @adoy\n- [3145: Update phpstan/phpstan requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3145) thanks to @dependabot[bot]\n- [3146: Inherit HttpException from RuntimeException](https://github.com/slimphp/Slim/pull/3146) thanks to @nbayramberdiyev\n- [3148: Upgrade to HTML5](https://github.com/slimphp/Slim/pull/3148) thanks to @nbayramberdiyev\n- [3172: Update nyholm/psr7 requirement from ^1.4 to ^1.5](https://github.com/slimphp/Slim/pull/3172) thanks to @dependabot[bot]\n\n## 4.9.0 - 2021-10-05\n\n- [3058: Implement exception class for Gone Http error](https://github.com/slimphp/Slim/pull/3058) thanks to @TheKernelPanic\n- [3086: Update slim/psr7 requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3086) thanks to @dependabot[bot]\n- [3087: Update nyholm/psr7-server requirement from ^1.0.1 to ^1.0.2](https://github.com/slimphp/Slim/pull/3087) thanks to @dependabot[bot]\n- [3093: Update phpstan/phpstan requirement from ^0.12.85 to ^0.12.90](https://github.com/slimphp/Slim/pull/3093) thanks to @dependabot[bot]\n- [3099: Allow updated psr log](https://github.com/slimphp/Slim/pull/3099) thanks to @t0mmy742\n- [3104: Drop php7.2](https://github.com/slimphp/Slim/pull/3104) thanks to @t0mmy742\n- [3106: Use PSR-17 factory from Guzzle/psr7 2.0](https://github.com/slimphp/Slim/pull/3106) thanks to @t0mmy742\n- [3108: Update README file](https://github.com/slimphp/Slim/pull/3108) thanks to @t0mmy742\n- [3112: Update laminas/laminas-diactoros requirement from ^2.6 to ^2.8](https://github.com/slimphp/Slim/pull/3112) thanks to @dependabot[bot]\n- [3114: Update slim/psr7 requirement from ^1.4 to ^1.5](https://github.com/slimphp/Slim/pull/3114) thanks to @dependabot[bot]\n- [3115: Update phpstan/phpstan requirement from ^0.12.96 to ^0.12.99](https://github.com/slimphp/Slim/pull/3115) thanks to @dependabot[bot]\n- [3116: Remove Zend Diactoros references](https://github.com/slimphp/Slim/pull/3116) thanks to @l0gicgate\n\n## 4.8.0 - 2021-05-19\n\n- [3034: Fix phpunit dependency version](https://github.com/slimphp/Slim/pull/3034) thanks to @l0gicgate\n- [3037: Replace Travis by GitHub Actions](https://github.com/slimphp/Slim/pull/3037) thanks to @t0mmy742\n- [3043: Cover App creation from AppFactory with empty Container](https://github.com/slimphp/Slim/pull/3043) thanks to @t0mmy742\n- [3045: Update phpstan/phpstan requirement from ^0.12.58 to ^0.12.64](https://github.com/slimphp/Slim/pull/3045) thanks to @dependabot-preview[bot]\n- [3047: documentation: min php 7.2 required](https://github.com/slimphp/Slim/pull/3047) thanks to @Rotzbua\n- [3054: Update phpstan/phpstan requirement from ^0.12.64 to ^0.12.70](https://github.com/slimphp/Slim/pull/3054) thanks to @dependabot-preview[bot]\n- [3056: Fix docblock in ErrorMiddleware](https://github.com/slimphp/Slim/pull/3056) thanks to @piotr-cz\n- [3060: Update phpstan/phpstan requirement from ^0.12.70 to ^0.12.80](https://github.com/slimphp/Slim/pull/3060) thanks to @dependabot-preview[bot]\n- [3061: Update nyholm/psr7 requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3061) thanks to @dependabot-preview[bot]\n- [3063: Allow ^1.0 || ^2.0 in psr/container](https://github.com/slimphp/Slim/pull/3063) thanks to @Ayesh\n- [3069: Classname/Method Callable Arrays](https://github.com/slimphp/Slim/pull/3069) thanks to @ddrv\n- [3078: Update squizlabs/php&#95;codesniffer requirement from ^3.5 to ^3.6](https://github.com/slimphp/Slim/pull/3078) thanks to @dependabot[bot]\n- [3079: Update phpspec/prophecy requirement from ^1.12 to ^1.13](https://github.com/slimphp/Slim/pull/3079) thanks to @dependabot[bot]\n- [3080: Update guzzlehttp/psr7 requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3080) thanks to @dependabot[bot]\n- [3082: Update phpstan/phpstan requirement from ^0.12.80 to ^0.12.85](https://github.com/slimphp/Slim/pull/3082) thanks to @dependabot[bot]\n\n## 4.7.0 - 2020-11-30\n\n### Fixed\n- [3027: Fix: FastRoute dispatcher and data generator should match](https://github.com/slimphp/Slim/pull/3027) thanks to @edudobay\n\n### Added\n- [3015: PHP 8 support](https://github.com/slimphp/Slim/pull/3015) thanks to @edudobay\n\n### Optimizations\n- [3024: Randomize tests](https://github.com/slimphp/Slim/pull/3024) thanks to @pawel-slowik\n\n## 4.6.0 - 2020-11-15\n\n### Fixed\n- [2942: Fix PHPdoc for error handlers in ErrorMiddleware ](https://github.com/slimphp/Slim/pull/2942) thanks to @TiMESPLiNTER\n- [2944: Remove unused function in ErrorHandler](https://github.com/slimphp/Slim/pull/2944) thanks to @l0gicgate\n- [2960: Fix phpstan 0.12 errors](https://github.com/slimphp/Slim/pull/2960) thanks to @adriansuter\n- [2982: Removing cloning statements in tests](https://github.com/slimphp/Slim/pull/2982) thanks to @l0gicgate\n- [3017: Fix request creator factory test](https://github.com/slimphp/Slim/pull/3017) thanks to @pawel-slowik\n- [3022: Ensure RouteParser Always Present After Routing](https://github.com/slimphp/Slim/pull/3022) thanks to @l0gicgate\n\n### Added\n- [2949: Add the support in composer.json](https://github.com/slimphp/Slim/pull/2949) thanks to @ddrv\n- [2958: Strict empty string content type checking in BodyParsingMiddleware::getMediaType](https://github.com/slimphp/Slim/pull/2958) thanks to @Ayesh\n- [2997: Add hints to methods](https://github.com/slimphp/Slim/pull/2997) thanks to @evgsavosin - [3000: Fix route controller test](https://github.com/slimphp/Slim/pull/3000) thanks to @pawel-slowik\n- [3001: Add missing `$strategy` parameter in a Route test](https://github.com/slimphp/Slim/pull/3001) thanks to @pawel-slowik\n\n### Optimizations\n- [2951: Minor optimizations in if() blocks](https://github.com/slimphp/Slim/pull/2951) thanks to @Ayesh\n- [2959: Micro optimization: Declare closures in BodyParsingMiddleware as static](https://github.com/slimphp/Slim/pull/2959) thanks to @Ayesh\n- [2978: Split the routing results to its own function.](https://github.com/slimphp/Slim/pull/2978) thanks to @dlundgren\n\n### Dependencies Updated\n- [2953: Update nyholm/psr7-server requirement from ^0.4.1](https://github.com/slimphp/Slim/pull/2953) thanks to @dependabot-preview[bot]\n- [2954: Update laminas/laminas-diactoros requirement from ^2.1 to ^2.3](https://github.com/slimphp/Slim/pull/2954) thanks to @dependabot-preview[bot]\n- [2955: Update guzzlehttp/psr7 requirement from ^1.5 to ^1.6](https://github.com/slimphp/Slim/pull/2955) thanks to @dependabot-preview[bot]\n- [2956: Update slim/psr7 requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/2956) thanks to @dependabot-preview[bot]\n- [2957: Update nyholm/psr7 requirement from ^1.1 to ^1.2](https://github.com/slimphp/Slim/pull/2957) thanks to @dependabot-preview[bot]\n- [2963: Update phpstan/phpstan requirement from ^0.12.23 to ^0.12.25](https://github.com/slimphp/Slim/pull/2963) thanks to @dependabot-preview[bot]\n- [2965: Update adriansuter/php-autoload-override requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/2965) thanks to @dependabot-preview[bot]\n- [2967: Update nyholm/psr7 requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/2967) thanks to @dependabot-preview[bot]\n- [2969: Update nyholm/psr7-server requirement from ^0.4.1 to ^1.0.0](https://github.com/slimphp/Slim/pull/2969) thanks to @dependabot-preview[bot]\n- [2970: Update phpstan/phpstan requirement from ^0.12.25 to ^0.12.26](https://github.com/slimphp/Slim/pull/2970) thanks to @dependabot-preview[bot]\n- [2971: Update phpstan/phpstan requirement from ^0.12.26 to ^0.12.27](https://github.com/slimphp/Slim/pull/2971) thanks to @dependabot-preview[bot]\n- [2972: Update phpstan/phpstan requirement from ^0.12.27 to ^0.12.28](https://github.com/slimphp/Slim/pull/2972) thanks to @dependabot-preview[bot]\n- [2973: Update phpstan/phpstan requirement from ^0.12.28 to ^0.12.29](https://github.com/slimphp/Slim/pull/2973) thanks to @dependabot-preview[bot]\n- [2975: Update phpstan/phpstan requirement from ^0.12.29 to ^0.12.30](https://github.com/slimphp/Slim/pull/2975) thanks to @dependabot-preview[bot]\n- [2976: Update phpstan/phpstan requirement from ^0.12.30 to ^0.12.31](https://github.com/slimphp/Slim/pull/2976) thanks to @dependabot-preview[bot]\n- [2980: Update phpstan/phpstan requirement from ^0.12.31 to ^0.12.32](https://github.com/slimphp/Slim/pull/2980) thanks to @dependabot-preview[bot]\n- [2981: Update phpspec/prophecy requirement from ^1.10 to ^1.11](https://github.com/slimphp/Slim/pull/2981) thanks to @dependabot-preview[bot]\n- [2986: Update phpstan/phpstan requirement from ^0.12.32 to ^0.12.33](https://github.com/slimphp/Slim/pull/2986) thanks to @dependabot-preview[bot]\n- [2990: Update phpstan/phpstan requirement from ^0.12.33 to ^0.12.34](https://github.com/slimphp/Slim/pull/2990) thanks to @dependabot-preview[bot]\n- [2991: Update phpstan/phpstan requirement from ^0.12.34 to ^0.12.35](https://github.com/slimphp/Slim/pull/2991) thanks to @dependabot-preview[bot]\n- [2993: Update phpstan/phpstan requirement from ^0.12.35 to ^0.12.36](https://github.com/slimphp/Slim/pull/2993) thanks to @dependabot-preview[bot]\n- [2995: Update phpstan/phpstan requirement from ^0.12.36 to ^0.12.37](https://github.com/slimphp/Slim/pull/2995) thanks to @dependabot-preview[bot]\n- [3010: Update guzzlehttp/psr7 requirement from ^1.6 to ^1.7](https://github.com/slimphp/Slim/pull/3010) thanks to @dependabot-preview[bot]\n- [3011: Update phpspec/prophecy requirement from ^1.11 to ^1.12](https://github.com/slimphp/Slim/pull/3011) thanks to @dependabot-preview[bot]\n- [3012: Update slim/http requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3012) thanks to @dependabot-preview[bot]\n- [3013: Update slim/psr7 requirement from ^1.1 to ^1.2](https://github.com/slimphp/Slim/pull/3013) thanks to @dependabot-preview[bot]\n- [3014: Update laminas/laminas-diactoros requirement from ^2.3 to ^2.4](https://github.com/slimphp/Slim/pull/3014) thanks to @dependabot-preview[bot]\n- [3018: Update phpstan/phpstan requirement from ^0.12.37 to ^0.12.54](https://github.com/slimphp/Slim/pull/3018) thanks to @dependabot-preview[bot]\n\n## 4.5.0 - 2020-04-14\n\n### Added\n- [2928](https://github.com/slimphp/Slim/pull/2928) Test against PHP 7.4\n- [2937](https://github.com/slimphp/Slim/pull/2937) Add support for PSR-3\n\n### Fixed\n- [2916](https://github.com/slimphp/Slim/pull/2916) Rename phpcs.xml to phpcs.xml.dist\n- [2917](https://github.com/slimphp/Slim/pull/2917) Update .editorconfig\n- [2925](https://github.com/slimphp/Slim/pull/2925) ResponseEmitter: Don't remove Content-Type and Content-Length when body is empt\n- [2932](https://github.com/slimphp/Slim/pull/2932) Update the Tidelift enterprise language\n- [2938](https://github.com/slimphp/Slim/pull/2938) Modify usage of deprecated expectExceptionMessageRegExp() method\n\n## 4.4.0 - 2020-01-04\n\n### Added\n- [2862](https://github.com/slimphp/Slim/pull/2862) Optionally handle subclasses of exceptions in custom error handler\n- [2869](https://github.com/slimphp/Slim/pull/2869) php-di/php-di added in composer suggestion\n- [2874](https://github.com/slimphp/Slim/pull/2874) Add `null` to param type-hints\n- [2889](https://github.com/slimphp/Slim/pull/2889) Make `RouteContext` attributes customizable and change default to use private names\n- [2907](https://github.com/slimphp/Slim/pull/2907) Migrate to PSR-12 convention\n- [2910](https://github.com/slimphp/Slim/pull/2910) Migrate Zend to Laminas\n- [2912](https://github.com/slimphp/Slim/pull/2912) Add Laminas PSR17 Factory\n- [2913](https://github.com/slimphp/Slim/pull/2913) Update php-autoload-override version\n- [2914](https://github.com/slimphp/Slim/pull/2914) Added ability to add handled exceptions as an array\n\n### Fixed\n- [2864](https://github.com/slimphp/Slim/pull/2864) Optimize error message in error handling if displayErrorDetails was not set\n- [2876](https://github.com/slimphp/Slim/pull/2876) Update links from http to https\n- [2877](https://github.com/slimphp/Slim/pull/2877) Fix docblock for `Slim\\Routing\\RouteCollector::cacheFile`\n- [2878](https://github.com/slimphp/Slim/pull/2878) check body is writable only on ouput buffering append\n- [2896](https://github.com/slimphp/Slim/pull/2896) Render errors uniformly\n- [2902](https://github.com/slimphp/Slim/pull/2902) Fix prophecies\n- [2908](https://github.com/slimphp/Slim/pull/2908) Use autoload-dev for `Slim\\Tests` namespace\n\n### Removed\n- [2871](https://github.com/slimphp/Slim/pull/2871) Remove explicit type-hint\n- [2872](https://github.com/slimphp/Slim/pull/2872) Remove type-hint\n\n## 4.3.0 - 2019-10-05\n\n### Added\n- [2819](https://github.com/slimphp/Slim/pull/2819) Added description to addRoutingMiddleware()\n- [2820](https://github.com/slimphp/Slim/pull/2820) Update link to homepage in composer.json\n- [2828](https://github.com/slimphp/Slim/pull/2828) Allow URIs with leading multi-slashes\n- [2832](https://github.com/slimphp/Slim/pull/2832) Refactor `FastRouteDispatcher`\n- [2835](https://github.com/slimphp/Slim/pull/2835) Rename `pathFor` to `urlFor` in docblock\n- [2846](https://github.com/slimphp/Slim/pull/2846) Correcting the branch name as per issue-2843\n- [2849](https://github.com/slimphp/Slim/pull/2849) Create class alias for FastRoute\\RouteCollector\n- [2855](https://github.com/slimphp/Slim/pull/2855) Add list of allowed methods to HttpMethodNotAllowedException\n- [2860](https://github.com/slimphp/Slim/pull/2860) Add base path to `$request` and use `RouteContext` to read\n\n### Fixed\n- [2839](https://github.com/slimphp/Slim/pull/2839) Fix description for handler signature in phpdocs\n- [2844](https://github.com/slimphp/Slim/pull/2844) Handle base path by routeCollector instead of RouteCollectorProxy\n- [2845](https://github.com/slimphp/Slim/pull/2845) Fix composer scripts\n- [2851](https://github.com/slimphp/Slim/pull/2851) Fix example of 'Hello World' app\n- [2854](https://github.com/slimphp/Slim/pull/2854) Fix undefined property in tests\n\n### Removed\n- [2853](https://github.com/slimphp/Slim/pull/2853) Remove unused classes\n\n## 4.2.0 - 2019-08-20\n\n### Added\n- [2787](https://github.com/slimphp/Slim/pull/2787) Add an advanced callable resolver\n- [2791](https://github.com/slimphp/Slim/pull/2791) Add `inferPrivatePropertyTypeFromConstructor` to phpstan\n- [2793](https://github.com/slimphp/Slim/pull/2793) Add ability to configure application via a container in `AppFactory`\n- [2798](https://github.com/slimphp/Slim/pull/2798) Add PSR-7 Agnostic Body Parsing Middleware\n- [2801](https://github.com/slimphp/Slim/pull/2801) Add `setLogErrorRenderer()` method to `ErrorHandler`\n- [2807](https://github.com/slimphp/Slim/pull/2807) Add check for Slim callable notation if no resolver given\n- [2803](https://github.com/slimphp/Slim/pull/2803) Add ability to emit non seekable streams in `ResponseEmitter`\n- [2817](https://github.com/slimphp/Slim/pull/2817) Add the ability to pass in a custom `MiddlewareDispatcherInterface` to the `App`\n\n### Fixed\n- [2789](https://github.com/slimphp/Slim/pull/2789) Fix Cookie header detection in `ResponseEmitter`\n- [2796](https://github.com/slimphp/Slim/pull/2796) Fix http message format\n- [2800](https://github.com/slimphp/Slim/pull/2800) Fix null comparisons more clear in `ErrorHandler`\n- [2802](https://github.com/slimphp/Slim/pull/2802) Fix incorrect search of a header in stack\n- [2806](https://github.com/slimphp/Slim/pull/2806) Simplify `Route::prepare()` method argument preparation\n- [2809](https://github.com/slimphp/Slim/pull/2809) Eliminate a duplicate code via HOF in `MiddlewareDispatcher`\n- [2816](https://github.com/slimphp/Slim/pull/2816) Fix RouteCollectorProxy::redirect() bug\n\n### Removed\n- [2811](https://github.com/slimphp/Slim/pull/2811) Remove `DeferredCallable`\n\n## 4.1.0 - 2019-08-06\n\n### Added\n- [#2779](https://github.com/slimphp/Slim/pull/2774) Add support for Slim callables `Class:method` resolution & Container Closure auto-binding in `MiddlewareDispatcher`\n- [#2774](https://github.com/slimphp/Slim/pull/2774) Add possibility for custom `RequestHandler` invocation strategies\n\n### Fixed\n- [#2776](https://github.com/slimphp/Slim/pull/2774) Fix group middleware on multiple nested groups\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\nThe Slim Framework code of conduct is derived from the Ruby code of conduct. This document provides community guidelines for a safe, respectful, productive, and collaborative place for any person who is willing to contribute to the Slim Framework community. It applies to all “collaborative space”, which is defined as community communications channels (such as mailing lists, IRC, Slack, forums, submitted patches, commit comments, etc.). Any violations of the code of conduct may be reported by contacting one or more of the project maintainers either directly or via email to maintainers@slimframework.com.\n\n* Participants will be tolerant of opposing views.\n* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.\n* When interpreting the words and actions of others, participants should always assume good intentions.\n* Behaviour that the project maintainers consider to be harassment will not be tolerated.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute\n\n## Pull Requests\n\n1. Fork the Slim Framework repository\n2. Create a new branch for each feature or improvement\n3. Send a pull request from each feature branch to the **4.x** branch\n\nIt is very important to separate new features or improvements into separate feature branches, and to send a\npull request for each branch. This allows me to review and pull in new features or improvements individually.\n\n## Style Guide\n\nAll pull requests must adhere to the [PSR-12 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md).\n\n## Unit Testing\n\nAll pull requests must be accompanied by passing unit tests and complete code coverage. The Slim Framework uses phpunit for testing.\n\n[Learn about PHPUnit](https://github.com/sebastianbergmann/phpunit/)\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2011-2022 Josh Lockhart\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "MAINTAINERS.md",
    "content": "# Maintainers\n\nThere aren't many rules for maintainers of Slim to remember; what we have is listed here.\n\n## We don't merge our own PRs\n\nOur code is better if more than one set of eyes looks at it. Therefore we do not merge our own pull requests unless there is an exceptional circumstance. This helps to spot errors in the patch and also enables us to share information about the project around the maintainer team.\n\n## PRs tagged `WIP` are not ready to be merged\n\nSometimes it's helpful to collaborate on a patch before it's ready to be merged. We use the text `WIP` (for _Work in Progress_) in the title to mark these PRs. \n\nIf a PR has `WIP` in its title, then it is not to be merged. The person who raised the PR will remove the `WIP` text when they are ready for a full review and merge.\n\n## Assign a merged PR to a milestone\n\nBy ensuring that all merged PRs are assigned to a milestone, we can easily find which PRs were in which release.\n"
  },
  {
    "path": "README.md",
    "content": "# Slim Framework\n\n[![Build Status](https://github.com/slimphp/Slim/actions/workflows/tests.yml/badge.svg?branch=4.x)](https://github.com/slimphp/Slim/actions/workflows/tests.yml?query=branch:4.x)\n[![Coverage Status](https://coveralls.io/repos/github/slimphp/Slim/badge.svg?branch=4.x)](https://coveralls.io/github/slimphp/Slim?branch=4.x)\n[![Total Downloads](https://poser.pugx.org/slim/slim/downloads)](https://packagist.org/packages/slim/slim)\n[![License](https://poser.pugx.org/slim/slim/license)](https://packagist.org/packages/slim/slim)\n\nSlim is a PHP micro-framework that helps you quickly write simple yet powerful web applications and APIs.\n\n## Installation\n\nIt's recommended that you use [Composer](https://getcomposer.org/) to install Slim.\n\n```bash\n$ composer require slim/slim\n```\n\nThis will install Slim and all required dependencies. Slim requires PHP 7.4 or newer.\n\n## Choose a PSR-7 Implementation & ServerRequest Creator\n\nBefore you can get up and running with Slim you will need to choose a PSR-7 implementation that best fits your application. A few notable ones:\n- [Slim-Psr7](https://github.com/slimphp/Slim-Psr7) - This is the Slim Framework PSR-7 implementation\n- [httpsoft/http-message](https://github.com/httpsoft/http-message) & [httpsoft/http-server-request](https://github.com/httpsoft/http-server-request) - This is the fastest, strictest and most lightweight implementation available\n- [Nyholm/psr7](https://github.com/Nyholm/psr7) & [Nyholm/psr7-server](https://github.com/Nyholm/psr7-server) - Performance is almost the same as the HttpSoft implementation\n- [Guzzle/psr7](https://github.com/guzzle/psr7) - This is the implementation used by the Guzzle Client, featuring extra functionality for stream and file handling\n- [laminas-diactoros](https://github.com/laminas/laminas-diactoros) - This is the Laminas (Zend) PSR-7 implementation\n\n\n## Slim-Http Decorators\n\n[Slim-Http](https://github.com/slimphp/Slim-Http) is a set of decorators for any PSR-7 implementation that we recommend is used with Slim Framework.\nTo install the Slim-Http library simply run the following command:\n\n```bash\ncomposer require slim/http\n```\n\nThe `ServerRequest` and `Response` object decorators are automatically detected and applied by the internal factories. If you have installed Slim-Http and wish to turn off automatic object decoration then you can use the following statements:\n```php\n<?php\n\nuse Slim\\Factory\\AppFactory;\nuse Slim\\Factory\\ServerRequestCreatorFactory;\n\nAppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\nServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n\n$app = AppFactory::create();\n\n// ...\n```\n\n## Hello World using AppFactory with PSR-7 auto-detection\nIn order for auto-detection to work and enable you to use `AppFactory::create()` and `App::run()` without having to manually create a `ServerRequest` you need to install one of the following implementations:\n- [Slim-Psr7](https://github.com/slimphp/Slim-Psr7) - Install using `composer require slim/psr7`\n- [httpsoft/http-message](https://github.com/httpsoft/http-message) & [httpsoft/http-server-request](https://github.com/httpsoft/http-server-request) - Install using:\n`composer require httpsoft/http-message httpsoft/http-server-request`\n- [Nyholm/psr7](https://github.com/Nyholm/psr7) & [Nyholm/psr7-server](https://github.com/Nyholm/psr7-server) - Install using `composer require nyholm/psr7 nyholm/psr7-server`\n- [Guzzle/psr7](https://github.com/guzzle/psr7) - Install using `composer require guzzlehttp/psr7`\n- [laminas-diactoros](https://github.com/laminas/laminas-diactoros) - Install using `composer require laminas/laminas-diactoros`\n\nThen create file _public/index.php_.\n\n```php\n<?php\nuse Psr\\Http\\Message\\ResponseInterface as Response;\nuse Psr\\Http\\Message\\ServerRequestInterface as Request;\nuse Slim\\Factory\\AppFactory;\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// Instantiate App\n$app = AppFactory::create();\n\n// Add error middleware\n$app->addErrorMiddleware(true, true, true);\n\n// Add routes\n$app->get('/', function (Request $request, Response $response) {\n    $response->getBody()->write('<a href=\"/hello/world\">Try /hello/world</a>');\n    return $response;\n});\n\n$app->get('/hello/{name}', function (Request $request, Response $response, $args) {\n    $name = $args['name'];\n    $response->getBody()->write(\"Hello, $name\");\n    return $response;\n});\n\n$app->run();\n```\n\nYou may quickly test this using the built-in PHP server:\n```bash\n$ php -S localhost:8000 -t public\n```\n\nGoing to http://localhost:8000/hello/world will now display \"Hello, world\".\n\nFor more information on how to configure your web server, see the [Documentation](https://www.slimframework.com/docs/v4/start/web-servers.html).\n\n## Tests\nTo execute the test suite, you'll need to install all development dependencies.\n\n```bash\n$ git clone https://github.com/slimphp/Slim\n$ composer install\n$ composer test\n```\n\n## Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) for details.\n\n## Learn More\n\nLearn more at these links:\n\n- [Website](https://www.slimframework.com)\n- [Documentation](https://www.slimframework.com/docs/v4/start/installation.html)\n- [Slack](https://slimphp.slack.com)\n- [Support Forum](https://discourse.slimframework.com)\n- [Twitter](https://twitter.com/slimphp)\n- [Resources](https://github.com/xssc/awesome-slim)\n\n## Security\n\nIf you discover security related issues, please email security@slimframework.com instead of using the issue tracker.\n\n## For enterprise\n\nAvailable as part of the Tidelift Subscription.\n\nThe maintainers of `Slim` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-slim-slim?utm_source=packagist-slim-slim&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).\n<a href=\"https://github.com/slimphp/Slim/graphs/contributors\">\n    <img src=\"https://opencollective.com/slimphp/contributors.svg?width=890&button=false\" />\n</a>\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [Contribute](https://opencollective.com/slimphp/contribute)\n\n#### Individuals\n\n<a href=\"https://opencollective.com/slimphp\"><img src=\"https://opencollective.com/slimphp/individuals.svg?width=890\"></a>\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [Contribute](https://opencollective.com/slimphp/contribute)\n\n<a href=\"https://opencollective.com/slimphp/organization/0/website\"><img src=\"https://opencollective.com/slimphp/organization/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/1/website\"><img src=\"https://opencollective.com/slimphp/organization/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/2/website\"><img src=\"https://opencollective.com/slimphp/organization/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/3/website\"><img src=\"https://opencollective.com/slimphp/organization/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/4/website\"><img src=\"https://opencollective.com/slimphp/organization/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/5/website\"><img src=\"https://opencollective.com/slimphp/organization/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/6/website\"><img src=\"https://opencollective.com/slimphp/organization/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/7/website\"><img src=\"https://opencollective.com/slimphp/organization/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/8/website\"><img src=\"https://opencollective.com/slimphp/organization/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/slimphp/organization/9/website\"><img src=\"https://opencollective.com/slimphp/organization/9/avatar.svg\"></a>\n\n## License\n\nThe Slim Framework is licensed under the MIT license. See [License File](LICENSE.md) for more information.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n### Supported Versions\n\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 3.x.x   | :white_check_mark: |\n| 4.x.x   | :white_check_mark: |\n\n\n### Reporting a Vulnerability\n\nTo report a vulnerability please send an email to security@slimframework.com\n"
  },
  {
    "path": "Slim/App.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Slim\\Factory\\ServerRequestCreatorFactory;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\MiddlewareDispatcherInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\nuse Slim\\Middleware\\BodyParsingMiddleware;\nuse Slim\\Middleware\\ErrorMiddleware;\nuse Slim\\Middleware\\RoutingMiddleware;\nuse Slim\\Routing\\RouteCollectorProxy;\nuse Slim\\Routing\\RouteResolver;\nuse Slim\\Routing\\RouteRunner;\n\nuse function strtoupper;\n\n/**\n * @api\n * @template TContainerInterface of (ContainerInterface|null)\n * @template-extends RouteCollectorProxy<TContainerInterface>\n */\nclass App extends RouteCollectorProxy implements RequestHandlerInterface\n{\n    /**\n     * Current version\n     *\n     * @var string\n     */\n    public const VERSION = '4.15.1';\n\n    protected RouteResolverInterface $routeResolver;\n\n    protected MiddlewareDispatcherInterface $middlewareDispatcher;\n\n    /**\n     * @param TContainerInterface $container\n     */\n    public function __construct(\n        ResponseFactoryInterface $responseFactory,\n        ?ContainerInterface $container = null,\n        ?CallableResolverInterface $callableResolver = null,\n        ?RouteCollectorInterface $routeCollector = null,\n        ?RouteResolverInterface $routeResolver = null,\n        ?MiddlewareDispatcherInterface $middlewareDispatcher = null\n    ) {\n        parent::__construct(\n            $responseFactory,\n            $callableResolver ?? new CallableResolver($container),\n            $container,\n            $routeCollector\n        );\n\n        $this->routeResolver = $routeResolver ?? new RouteResolver($this->routeCollector);\n        $routeRunner = new RouteRunner($this->routeResolver, $this->routeCollector->getRouteParser(), $this);\n\n        if (!$middlewareDispatcher) {\n            $middlewareDispatcher = new MiddlewareDispatcher($routeRunner, $this->callableResolver, $container);\n        } else {\n            $middlewareDispatcher->seedMiddlewareStack($routeRunner);\n        }\n\n        $this->middlewareDispatcher = $middlewareDispatcher;\n    }\n\n    /**\n     * @return RouteResolverInterface\n     */\n    public function getRouteResolver(): RouteResolverInterface\n    {\n        return $this->routeResolver;\n    }\n\n    /**\n     * @return MiddlewareDispatcherInterface\n     */\n    public function getMiddlewareDispatcher(): MiddlewareDispatcherInterface\n    {\n        return $this->middlewareDispatcher;\n    }\n\n    /**\n     * @param MiddlewareInterface|string|callable $middleware\n     * @return App<TContainerInterface>\n     */\n    public function add($middleware): self\n    {\n        $this->middlewareDispatcher->add($middleware);\n        return $this;\n    }\n\n    /**\n     * @param MiddlewareInterface $middleware\n     * @return App<TContainerInterface>\n     */\n    public function addMiddleware(MiddlewareInterface $middleware): self\n    {\n        $this->middlewareDispatcher->addMiddleware($middleware);\n        return $this;\n    }\n\n    /**\n     * Add the Slim built-in routing middleware to the app middleware stack\n     *\n     * This method can be used to control middleware order and is not required for default routing operation.\n     *\n     * @return RoutingMiddleware\n     */\n    public function addRoutingMiddleware(): RoutingMiddleware\n    {\n        $routingMiddleware = new RoutingMiddleware(\n            $this->getRouteResolver(),\n            $this->getRouteCollector()->getRouteParser()\n        );\n        $this->add($routingMiddleware);\n        return $routingMiddleware;\n    }\n\n    /**\n     * Add the Slim built-in error middleware to the app middleware stack\n     *\n     * @param bool                 $displayErrorDetails\n     * @param bool                 $logErrors\n     * @param bool                 $logErrorDetails\n     * @param LoggerInterface|null $logger\n     *\n     * @return ErrorMiddleware\n     */\n    public function addErrorMiddleware(\n        bool $displayErrorDetails,\n        bool $logErrors,\n        bool $logErrorDetails,\n        ?LoggerInterface $logger = null\n    ): ErrorMiddleware {\n        $errorMiddleware = new ErrorMiddleware(\n            $this->getCallableResolver(),\n            $this->getResponseFactory(),\n            $displayErrorDetails,\n            $logErrors,\n            $logErrorDetails,\n            $logger\n        );\n        $this->add($errorMiddleware);\n        return $errorMiddleware;\n    }\n\n    /**\n     * Add the Slim body parsing middleware to the app middleware stack\n     *\n     * @param callable[] $bodyParsers\n     *\n     * @return BodyParsingMiddleware\n     */\n    public function addBodyParsingMiddleware(array $bodyParsers = []): BodyParsingMiddleware\n    {\n        $bodyParsingMiddleware = new BodyParsingMiddleware($bodyParsers);\n        $this->add($bodyParsingMiddleware);\n        return $bodyParsingMiddleware;\n    }\n\n    /**\n     * Run application\n     *\n     * This method traverses the application middleware stack and then sends the\n     * resultant Response object to the HTTP client.\n     *\n     * @param ServerRequestInterface|null $request\n     * @return void\n     */\n    public function run(?ServerRequestInterface $request = null): void\n    {\n        if (!$request) {\n            $serverRequestCreator = ServerRequestCreatorFactory::create();\n            $request = $serverRequestCreator->createServerRequestFromGlobals();\n        }\n\n        $response = $this->handle($request);\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n    }\n\n    /**\n     * Handle a request\n     *\n     * This method traverses the application middleware stack and then returns the\n     * resultant Response object.\n     *\n     * @param ServerRequestInterface $request\n     * @return ResponseInterface\n     */\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n        $response = $this->middlewareDispatcher->handle($request);\n\n        /**\n         * This is to be in compliance with RFC 2616, Section 9.\n         * If the incoming request method is HEAD, we need to ensure that the response body\n         * is empty as the request may fall back on a GET route handler due to FastRoute's\n         * routing logic which could potentially append content to the response body\n         * https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4\n         */\n        $method = strtoupper($request->getMethod());\n        if ($method === 'HEAD') {\n            $emptyBody = $this->responseFactory->createResponse()->getBody();\n            return $response->withBody($emptyBody);\n        }\n\n        return $response;\n    }\n}\n"
  },
  {
    "path": "Slim/CallableResolver.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim;\n\nuse Closure;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\AdvancedCallableResolverInterface;\n\nuse function class_exists;\nuse function is_array;\nuse function is_callable;\nuse function is_object;\nuse function is_string;\nuse function json_encode;\nuse function preg_match;\nuse function sprintf;\n\n/**\n * @template TContainerInterface of (ContainerInterface|null)\n */\nfinal class CallableResolver implements AdvancedCallableResolverInterface\n{\n    public static string $callablePattern = '!^([^\\:]+)\\:([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)$!';\n\n    /** @var TContainerInterface $container */\n    private ?ContainerInterface $container;\n\n    /**\n     * @param TContainerInterface $container\n     */\n    public function __construct(?ContainerInterface $container = null)\n    {\n        $this->container = $container;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function resolve($toResolve): callable\n    {\n        $toResolve = $this->prepareToResolve($toResolve);\n        if (is_callable($toResolve)) {\n            return $this->bindToContainer($toResolve);\n        }\n        $resolved = $toResolve;\n        if (is_string($toResolve)) {\n            $resolved = $this->resolveSlimNotation($toResolve);\n            $resolved[1] ??= '__invoke';\n        }\n        $callable = $this->assertCallable($resolved, $toResolve);\n        return $this->bindToContainer($callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function resolveRoute($toResolve): callable\n    {\n        return $this->resolveByPredicate($toResolve, [$this, 'isRoute'], 'handle');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function resolveMiddleware($toResolve): callable\n    {\n        return $this->resolveByPredicate($toResolve, [$this, 'isMiddleware'], 'process');\n    }\n\n    /**\n     * @param callable|array{class-string, string}|string $toResolve\n     *\n     * @throws RuntimeException\n     */\n    private function resolveByPredicate($toResolve, callable $predicate, string $defaultMethod): callable\n    {\n        $toResolve = $this->prepareToResolve($toResolve);\n        if (is_callable($toResolve)) {\n            return $this->bindToContainer($toResolve);\n        }\n        $resolved = $toResolve;\n        if ($predicate($toResolve)) {\n            $resolved = [$toResolve, $defaultMethod];\n        }\n        if (is_string($toResolve)) {\n            [$instance, $method] = $this->resolveSlimNotation($toResolve);\n            if ($method === null && $predicate($instance)) {\n                $method = $defaultMethod;\n            }\n            $resolved = [$instance, $method ?? '__invoke'];\n        }\n        $callable = $this->assertCallable($resolved, $toResolve);\n        return $this->bindToContainer($callable);\n    }\n\n    /**\n     * @param mixed $toResolve\n     */\n    private function isRoute($toResolve): bool\n    {\n        return $toResolve instanceof RequestHandlerInterface;\n    }\n\n    /**\n     * @param mixed $toResolve\n     */\n    private function isMiddleware($toResolve): bool\n    {\n        return $toResolve instanceof MiddlewareInterface;\n    }\n\n    /**\n     * @throws RuntimeException\n     *\n     * @return array{object, string|null} [Instance, Method Name]\n     */\n    private function resolveSlimNotation(string $toResolve): array\n    {\n        /** @psalm-suppress ArgumentTypeCoercion */\n        preg_match(CallableResolver::$callablePattern, $toResolve, $matches);\n        [$class, $method] = $matches ? [$matches[1], $matches[2]] : [$toResolve, null];\n\n        if ($this->container && $this->container->has($class)) {\n            $instance = $this->container->get($class);\n            if (!is_object($instance)) {\n                throw new RuntimeException(sprintf('%s container entry is not an object', $class));\n            }\n        } else {\n            if (!class_exists($class)) {\n                if ($method) {\n                    $class .= '::' . $method . '()';\n                }\n                throw new RuntimeException(sprintf('Callable %s does not exist', $class));\n            }\n            $instance = new $class($this->container);\n        }\n        return [$instance, $method];\n    }\n\n    /**\n     * @param mixed $resolved\n     * @param mixed $toResolve\n     *\n     * @throws RuntimeException\n     */\n    private function assertCallable($resolved, $toResolve): callable\n    {\n        if (!is_callable($resolved)) {\n            if (is_callable($toResolve) || is_object($toResolve) || is_array($toResolve)) {\n                $formatedToResolve = ($toResolveJson = json_encode($toResolve)) !== false ? $toResolveJson : '';\n            } else {\n                $formatedToResolve = is_string($toResolve) ? $toResolve : '';\n            }\n            throw new RuntimeException(sprintf('%s is not resolvable', $formatedToResolve));\n        }\n        return $resolved;\n    }\n\n    private function bindToContainer(callable $callable): callable\n    {\n        if (is_array($callable) && $callable[0] instanceof Closure) {\n            $callable = $callable[0];\n        }\n        if ($this->container && $callable instanceof Closure) {\n            /** @var Closure $callable */\n            $callable = $callable->bindTo($this->container);\n        }\n        return $callable;\n    }\n\n    /**\n     * @param callable|string|array{class-string, string}|mixed $toResolve\n     *\n     * @return callable|string|array{class-string, string}|mixed\n     */\n    private function prepareToResolve($toResolve)\n    {\n        if (!is_array($toResolve)) {\n            return $toResolve;\n        }\n        $candidate = $toResolve;\n        $class = array_shift($candidate);\n        $method = array_shift($candidate);\n        if (is_string($class) && is_string($method)) {\n            return $class . ':' . $method;\n        }\n        return $toResolve;\n    }\n}\n"
  },
  {
    "path": "Slim/Error/AbstractErrorRenderer.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Error;\n\nuse Slim\\Exception\\HttpException;\nuse Slim\\Interfaces\\ErrorRendererInterface;\nuse Throwable;\n\n/**\n * Abstract Slim application error renderer\n *\n * It outputs the error message and diagnostic information in one of the following formats:\n * JSON, XML, Plain Text or HTML\n */\nabstract class AbstractErrorRenderer implements ErrorRendererInterface\n{\n    protected string $defaultErrorTitle = 'Slim Application Error';\n\n    protected string $defaultErrorDescription = 'A website error has occurred. Sorry for the temporary inconvenience.';\n\n    protected function getErrorTitle(Throwable $exception): string\n    {\n        if ($exception instanceof HttpException) {\n            return $exception->getTitle();\n        }\n\n        return $this->defaultErrorTitle;\n    }\n\n    protected function getErrorDescription(Throwable $exception): string\n    {\n        if ($exception instanceof HttpException) {\n            return $exception->getDescription();\n        }\n\n        return $this->defaultErrorDescription;\n    }\n}\n"
  },
  {
    "path": "Slim/Error/Renderers/HtmlErrorRenderer.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Error\\Renderers;\n\nuse Slim\\Error\\AbstractErrorRenderer;\nuse Throwable;\n\nuse function get_class;\nuse function htmlentities;\nuse function sprintf;\n\n/**\n * Default Slim application HTML Error Renderer\n */\nclass HtmlErrorRenderer extends AbstractErrorRenderer\n{\n    public function __invoke(Throwable $exception, bool $displayErrorDetails): string\n    {\n        if ($displayErrorDetails) {\n            $html = '<p>The application could not run because of the following error:</p>';\n            $html .= '<h2>Details</h2>';\n            $html .= $this->renderExceptionFragment($exception);\n        } else {\n            $html = \"<p>{$this->getErrorDescription($exception)}</p>\";\n        }\n\n        return $this->renderHtmlBody($this->getErrorTitle($exception), $html);\n    }\n\n    private function renderExceptionFragment(Throwable $exception): string\n    {\n        $html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception));\n\n        $code = $exception->getCode();\n        $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code);\n\n        $html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($exception->getMessage()));\n\n        $html .= sprintf('<div><strong>File:</strong> %s</div>', $exception->getFile());\n\n        $html .= sprintf('<div><strong>Line:</strong> %s</div>', $exception->getLine());\n\n        $html .= '<h2>Trace</h2>';\n        $html .= sprintf('<pre>%s</pre>', htmlentities($exception->getTraceAsString()));\n\n        return $html;\n    }\n\n    public function renderHtmlBody(string $title = '', string $html = ''): string\n    {\n        return sprintf(\n            '<!doctype html>' .\n            '<html lang=\"en\">' .\n            '    <head>' .\n            '        <meta charset=\"utf-8\">' .\n            '        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">' .\n            '        <title>%s</title>' .\n            '        <style>' .\n            '            body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif}' .\n            '            h1{margin:0;font-size:48px;font-weight:normal;line-height:48px}' .\n            '            strong{display:inline-block;width:65px}' .\n            '        </style>' .\n            '    </head>' .\n            '    <body>' .\n            '        <h1>%s</h1>' .\n            '        <div>%s</div>' .\n            '        <a href=\"#\" onclick=\"window.history.go(-1)\">Go Back</a>' .\n            '    </body>' .\n            '</html>',\n            $title,\n            $title,\n            $html\n        );\n    }\n}\n"
  },
  {
    "path": "Slim/Error/Renderers/JsonErrorRenderer.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Error\\Renderers;\n\nuse Slim\\Error\\AbstractErrorRenderer;\nuse Throwable;\n\nuse function get_class;\nuse function json_encode;\n\nuse const JSON_PRETTY_PRINT;\nuse const JSON_UNESCAPED_SLASHES;\n\n/**\n * Default Slim application JSON Error Renderer\n */\nclass JsonErrorRenderer extends AbstractErrorRenderer\n{\n    public function __invoke(Throwable $exception, bool $displayErrorDetails): string\n    {\n        $error = ['message' => $this->getErrorTitle($exception)];\n\n        if ($displayErrorDetails) {\n            $error['exception'] = [];\n            do {\n                $error['exception'][] = $this->formatExceptionFragment($exception);\n            } while ($exception = $exception->getPrevious());\n        }\n\n        return (string) json_encode($error, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);\n    }\n\n    /**\n     * @return array<string|int>\n     */\n    private function formatExceptionFragment(Throwable $exception): array\n    {\n        $code = $exception->getCode();\n        return [\n            'type' => get_class($exception),\n            'code' => $code,\n            'message' => $exception->getMessage(),\n            'file' => $exception->getFile(),\n            'line' => $exception->getLine(),\n        ];\n    }\n}\n"
  },
  {
    "path": "Slim/Error/Renderers/PlainTextErrorRenderer.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Error\\Renderers;\n\nuse Slim\\Error\\AbstractErrorRenderer;\nuse Throwable;\n\nuse function get_class;\nuse function htmlentities;\nuse function sprintf;\n\n/**\n * Default Slim application Plain Text Error Renderer\n */\nclass PlainTextErrorRenderer extends AbstractErrorRenderer\n{\n    public function __invoke(Throwable $exception, bool $displayErrorDetails): string\n    {\n        $text = \"{$this->getErrorTitle($exception)}\\n\";\n\n        if ($displayErrorDetails) {\n            $text .= $this->formatExceptionFragment($exception);\n\n            while ($exception = $exception->getPrevious()) {\n                $text .= \"\\nPrevious Error:\\n\";\n                $text .= $this->formatExceptionFragment($exception);\n            }\n        }\n\n        return $text;\n    }\n\n    private function formatExceptionFragment(Throwable $exception): string\n    {\n        $text = sprintf(\"Type: %s\\n\", get_class($exception));\n\n        $code = $exception->getCode();\n\n        $text .= sprintf(\"Code: %s\\n\", $code);\n\n        $text .= sprintf(\"Message: %s\\n\", $exception->getMessage());\n\n        $text .= sprintf(\"File: %s\\n\", $exception->getFile());\n\n        $text .= sprintf(\"Line: %s\\n\", $exception->getLine());\n\n        $text .= sprintf('Trace: %s', $exception->getTraceAsString());\n\n        return $text;\n    }\n}\n"
  },
  {
    "path": "Slim/Error/Renderers/XmlErrorRenderer.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Error\\Renderers;\n\nuse Slim\\Error\\AbstractErrorRenderer;\nuse Throwable;\n\nuse function get_class;\nuse function sprintf;\nuse function str_replace;\n\n/**\n * Default Slim application XML Error Renderer\n */\nclass XmlErrorRenderer extends AbstractErrorRenderer\n{\n    public function __invoke(Throwable $exception, bool $displayErrorDetails): string\n    {\n        $xml = '<' . '?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?' . \">\\n\";\n        $xml .= \"<error>\\n  <message>\" . $this->createCdataSection($this->getErrorTitle($exception)) . \"</message>\\n\";\n\n        if ($displayErrorDetails) {\n            do {\n                $xml .= \"  <exception>\\n\";\n                $xml .= '    <type>' . get_class($exception) . \"</type>\\n\";\n                $xml .= '    <code>' . $exception->getCode() . \"</code>\\n\";\n                $xml .= '    <message>' . $this->createCdataSection($exception->getMessage()) . \"</message>\\n\";\n                $xml .= '    <file>' . $exception->getFile() . \"</file>\\n\";\n                $xml .= '    <line>' . $exception->getLine() . \"</line>\\n\";\n                $xml .= \"  </exception>\\n\";\n            } while ($exception = $exception->getPrevious());\n        }\n\n        $xml .= '</error>';\n\n        return $xml;\n    }\n\n    /**\n     * Returns a CDATA section with the given content.\n     */\n    private function createCdataSection(string $content): string\n    {\n        return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content));\n    }\n}\n"
  },
  {
    "path": "Slim/Exception/HttpBadRequestException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpBadRequestException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 400;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Bad request.';\n\n    protected string $title = '400 Bad Request';\n    protected string $description = 'The server cannot or will not process ' .\n        'the request due to an apparent client error.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse RuntimeException;\nuse Throwable;\n\n/**\n * @api\n * @method int getCode()\n */\nclass HttpException extends RuntimeException\n{\n    protected ServerRequestInterface $request;\n\n    protected string $title = '';\n\n    protected string $description = '';\n\n    public function __construct(\n        ServerRequestInterface $request,\n        string $message = '',\n        int $code = 0,\n        ?Throwable $previous = null\n    ) {\n        parent::__construct($message, $code, $previous);\n        $this->request = $request;\n    }\n\n    public function getRequest(): ServerRequestInterface\n    {\n        return $this->request;\n    }\n\n    public function getTitle(): string\n    {\n        return $this->title;\n    }\n\n    public function setTitle(string $title): self\n    {\n        $this->title = $title;\n        return $this;\n    }\n\n    public function getDescription(): string\n    {\n        return $this->description;\n    }\n\n    public function setDescription(string $description): self\n    {\n        $this->description = $description;\n        return $this;\n    }\n}\n"
  },
  {
    "path": "Slim/Exception/HttpForbiddenException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpForbiddenException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 403;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Forbidden.';\n\n    protected string $title = '403 Forbidden';\n    protected string $description = 'You are not permitted to perform the requested operation.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpGoneException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpGoneException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 410;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Gone.';\n\n    protected string $title = '410 Gone';\n    protected string $description = 'The target resource is no longer available at the origin server.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpInternalServerErrorException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpInternalServerErrorException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 500;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Internal server error.';\n\n    protected string $title = '500 Internal Server Error';\n    protected string $description = 'Unexpected condition encountered preventing server from fulfilling request.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpMethodNotAllowedException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\nuse function implode;\n\nclass HttpMethodNotAllowedException extends HttpSpecializedException\n{\n    /**\n     * @var string[]\n     */\n    protected array $allowedMethods = [];\n\n    /**\n     * @var int\n     */\n    protected $code = 405;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Method not allowed.';\n\n    protected string $title = '405 Method Not Allowed';\n    protected string $description = 'The request method is not supported for the requested resource.';\n\n    /**\n     * @return string[]\n     */\n    public function getAllowedMethods(): array\n    {\n        return $this->allowedMethods;\n    }\n\n    /**\n     * @param string[] $methods\n     */\n    public function setAllowedMethods(array $methods): self\n    {\n        $this->allowedMethods = $methods;\n        $this->message = 'Method not allowed. Must be one of: ' . implode(', ', $methods);\n        return $this;\n    }\n}\n"
  },
  {
    "path": "Slim/Exception/HttpNotFoundException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\nclass HttpNotFoundException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 404;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Not found.';\n\n    protected string $title = '404 Not Found';\n    protected string $description = 'The requested resource could not be found. Please verify the URI and try again.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpNotImplementedException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpNotImplementedException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 501;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Not implemented.';\n\n    protected string $title = '501 Not Implemented';\n    protected string $description = 'The server does not support the functionality required to fulfill the request.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpSpecializedException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Throwable;\n\nabstract class HttpSpecializedException extends HttpException\n{\n    /**\n     * @param ServerRequestInterface $request\n     * @param string|null $message\n     * @param Throwable|null $previous\n     */\n    public function __construct(ServerRequestInterface $request, ?string $message = null, ?Throwable $previous = null)\n    {\n        if ($message !== null) {\n            $this->message = $message;\n        }\n\n        // @phpstan-ignore-next-line\n        parent::__construct($request, $this->message, $this->code, $previous);\n    }\n}\n"
  },
  {
    "path": "Slim/Exception/HttpTooManyRequestsException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpTooManyRequestsException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 429;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Too many requests.';\n\n    protected string $title = '429 Too Many Requests';\n    protected string $description = 'The client application has surpassed its rate limit, ' .\n                                    'or number of requests they can send in a given period of time.';\n}\n"
  },
  {
    "path": "Slim/Exception/HttpUnauthorizedException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Exception;\n\n/** @api */\nclass HttpUnauthorizedException extends HttpSpecializedException\n{\n    /**\n     * @var int\n     */\n    protected $code = 401;\n\n    /**\n     * @var string\n     */\n    protected $message = 'Unauthorized.';\n\n    protected string $title = '401 Unauthorized';\n    protected string $description = 'The request requires valid user authentication.';\n}\n"
  },
  {
    "path": "Slim/Factory/AppFactory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse RuntimeException;\nuse Slim\\App;\nuse Slim\\Factory\\Psr17\\Psr17Factory;\nuse Slim\\Factory\\Psr17\\Psr17FactoryProvider;\nuse Slim\\Factory\\Psr17\\SlimHttpPsr17Factory;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\MiddlewareDispatcherInterface;\nuse Slim\\Interfaces\\Psr17FactoryProviderInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\n\n/** @api */\nclass AppFactory\n{\n    protected static ?Psr17FactoryProviderInterface $psr17FactoryProvider = null;\n\n    protected static ?ResponseFactoryInterface $responseFactory = null;\n\n    protected static ?StreamFactoryInterface $streamFactory = null;\n\n    protected static ?ContainerInterface $container = null;\n\n    protected static ?CallableResolverInterface $callableResolver = null;\n\n    protected static ?RouteCollectorInterface $routeCollector = null;\n\n    protected static ?RouteResolverInterface $routeResolver = null;\n\n    protected static ?MiddlewareDispatcherInterface $middlewareDispatcher = null;\n\n    protected static bool $slimHttpDecoratorsAutomaticDetectionEnabled = true;\n\n    /**\n     * @template TContainerInterface of (ContainerInterface|null)\n     * @param TContainerInterface $container\n     * @return (TContainerInterface is ContainerInterface ? App<TContainerInterface> : App<ContainerInterface|null>)\n     */\n    public static function create(\n        ?ResponseFactoryInterface $responseFactory = null,\n        ?ContainerInterface $container = null,\n        ?CallableResolverInterface $callableResolver = null,\n        ?RouteCollectorInterface $routeCollector = null,\n        ?RouteResolverInterface $routeResolver = null,\n        ?MiddlewareDispatcherInterface $middlewareDispatcher = null\n    ): App {\n        static::$responseFactory = $responseFactory ?? static::$responseFactory;\n        return new App(\n            self::determineResponseFactory(),\n            $container ?? static::$container,\n            $callableResolver ?? static::$callableResolver,\n            $routeCollector ?? static::$routeCollector,\n            $routeResolver ?? static::$routeResolver,\n            $middlewareDispatcher ?? static::$middlewareDispatcher\n        );\n    }\n\n    /**\n     * @template TContainerInterface of (ContainerInterface)\n     * @param TContainerInterface $container\n     * @return App<TContainerInterface>\n     */\n    public static function createFromContainer(ContainerInterface $container): App\n    {\n        $responseFactory = $container->has(ResponseFactoryInterface::class)\n        && (\n            $responseFactoryFromContainer = $container->get(ResponseFactoryInterface::class)\n        ) instanceof ResponseFactoryInterface\n            ? $responseFactoryFromContainer\n            : self::determineResponseFactory();\n\n        $callableResolver = $container->has(CallableResolverInterface::class)\n        && (\n            $callableResolverFromContainer = $container->get(CallableResolverInterface::class)\n        ) instanceof CallableResolverInterface\n            ? $callableResolverFromContainer\n            : null;\n\n        $routeCollector = $container->has(RouteCollectorInterface::class)\n        && (\n            $routeCollectorFromContainer = $container->get(RouteCollectorInterface::class)\n        ) instanceof RouteCollectorInterface\n            ? $routeCollectorFromContainer\n            : null;\n\n        $routeResolver = $container->has(RouteResolverInterface::class)\n        && (\n            $routeResolverFromContainer = $container->get(RouteResolverInterface::class)\n        ) instanceof RouteResolverInterface\n            ? $routeResolverFromContainer\n            : null;\n\n        $middlewareDispatcher = $container->has(MiddlewareDispatcherInterface::class)\n        && (\n            $middlewareDispatcherFromContainer = $container->get(MiddlewareDispatcherInterface::class)\n        ) instanceof MiddlewareDispatcherInterface\n            ? $middlewareDispatcherFromContainer\n            : null;\n\n        return new App(\n            $responseFactory,\n            $container,\n            $callableResolver,\n            $routeCollector,\n            $routeResolver,\n            $middlewareDispatcher\n        );\n    }\n\n    /**\n     * @throws RuntimeException\n     */\n    public static function determineResponseFactory(): ResponseFactoryInterface\n    {\n        if (static::$responseFactory) {\n            if (static::$streamFactory) {\n                return static::attemptResponseFactoryDecoration(static::$responseFactory, static::$streamFactory);\n            }\n            return static::$responseFactory;\n        }\n\n        $psr17FactoryProvider = static::$psr17FactoryProvider ?? new Psr17FactoryProvider();\n\n        /** @var Psr17Factory $psr17factory */\n        foreach ($psr17FactoryProvider->getFactories() as $psr17factory) {\n            if ($psr17factory::isResponseFactoryAvailable()) {\n                $responseFactory = $psr17factory::getResponseFactory();\n\n                if (static::$streamFactory || $psr17factory::isStreamFactoryAvailable()) {\n                    $streamFactory = static::$streamFactory ?? $psr17factory::getStreamFactory();\n                    return static::attemptResponseFactoryDecoration($responseFactory, $streamFactory);\n                }\n\n                return $responseFactory;\n            }\n        }\n\n        throw new RuntimeException(\n            \"Could not detect any PSR-17 ResponseFactory implementations. \" .\n            \"Please install a supported implementation in order to use `AppFactory::create()`. \" .\n            \"See https://github.com/slimphp/Slim/blob/4.x/README.md for a list of supported implementations.\"\n        );\n    }\n\n    protected static function attemptResponseFactoryDecoration(\n        ResponseFactoryInterface $responseFactory,\n        StreamFactoryInterface $streamFactory\n    ): ResponseFactoryInterface {\n        if (\n            static::$slimHttpDecoratorsAutomaticDetectionEnabled\n            && SlimHttpPsr17Factory::isResponseFactoryAvailable()\n        ) {\n            return SlimHttpPsr17Factory::createDecoratedResponseFactory($responseFactory, $streamFactory);\n        }\n\n        return $responseFactory;\n    }\n\n    public static function setPsr17FactoryProvider(Psr17FactoryProviderInterface $psr17FactoryProvider): void\n    {\n        static::$psr17FactoryProvider = $psr17FactoryProvider;\n    }\n\n    public static function setResponseFactory(ResponseFactoryInterface $responseFactory): void\n    {\n        static::$responseFactory = $responseFactory;\n    }\n\n    public static function setStreamFactory(StreamFactoryInterface $streamFactory): void\n    {\n        static::$streamFactory = $streamFactory;\n    }\n\n    public static function setContainer(ContainerInterface $container): void\n    {\n        static::$container = $container;\n    }\n\n    public static function setCallableResolver(CallableResolverInterface $callableResolver): void\n    {\n        static::$callableResolver = $callableResolver;\n    }\n\n    public static function setRouteCollector(RouteCollectorInterface $routeCollector): void\n    {\n        static::$routeCollector = $routeCollector;\n    }\n\n    public static function setRouteResolver(RouteResolverInterface $routeResolver): void\n    {\n        static::$routeResolver = $routeResolver;\n    }\n\n    public static function setMiddlewareDispatcher(MiddlewareDispatcherInterface $middlewareDispatcher): void\n    {\n        static::$middlewareDispatcher = $middlewareDispatcher;\n    }\n\n    public static function setSlimHttpDecoratorsAutomaticDetection(bool $enabled): void\n    {\n        static::$slimHttpDecoratorsAutomaticDetectionEnabled = $enabled;\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/GuzzlePsr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nclass GuzzlePsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'GuzzleHttp\\Psr7\\HttpFactory';\n    protected static string $streamFactoryClass = 'GuzzleHttp\\Psr7\\HttpFactory';\n    protected static string $serverRequestCreatorClass = 'GuzzleHttp\\Psr7\\ServerRequest';\n    protected static string $serverRequestCreatorMethod = 'fromGlobals';\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/HttpSoftPsr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nclass HttpSoftPsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'HttpSoft\\Message\\ResponseFactory';\n    protected static string $streamFactoryClass = 'HttpSoft\\Message\\StreamFactory';\n    protected static string $serverRequestCreatorClass = 'HttpSoft\\ServerRequest\\ServerRequestCreator';\n    protected static string $serverRequestCreatorMethod = 'createFromGlobals';\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/LaminasDiactorosPsr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nclass LaminasDiactorosPsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'Laminas\\Diactoros\\ResponseFactory';\n    protected static string $streamFactoryClass = 'Laminas\\Diactoros\\StreamFactory';\n    protected static string $serverRequestCreatorClass = 'Laminas\\Diactoros\\ServerRequestFactory';\n    protected static string $serverRequestCreatorMethod = 'fromGlobals';\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/NyholmPsr17Factory.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\n\nclass NyholmPsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'Nyholm\\Psr7\\Factory\\Psr17Factory';\n    protected static string $streamFactoryClass = 'Nyholm\\Psr7\\Factory\\Psr17Factory';\n    protected static string $serverRequestCreatorClass = 'Nyholm\\Psr7Server\\ServerRequestCreator';\n    protected static string $serverRequestCreatorMethod = 'fromGlobals';\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function getServerRequestCreator(): ServerRequestCreatorInterface\n    {\n        /*\n         * Nyholm Psr17Factory implements all factories in one unified\n         * factory which implements all of the PSR-17 factory interfaces\n         */\n        $psr17Factory = new static::$responseFactoryClass();\n\n        $serverRequestCreator = new static::$serverRequestCreatorClass(\n            $psr17Factory,\n            $psr17Factory,\n            $psr17Factory,\n            $psr17Factory\n        );\n\n        return new ServerRequestCreator($serverRequestCreator, static::$serverRequestCreatorMethod);\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/Psr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\Psr17FactoryInterface;\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\n\nuse function class_exists;\nuse function get_called_class;\n\nabstract class Psr17Factory implements Psr17FactoryInterface\n{\n    protected static string $responseFactoryClass;\n\n    protected static string $streamFactoryClass;\n\n    protected static string $serverRequestCreatorClass;\n\n    protected static string $serverRequestCreatorMethod;\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function getResponseFactory(): ResponseFactoryInterface\n    {\n        if (\n            !static::isResponseFactoryAvailable()\n            || !(($responseFactory = new static::$responseFactoryClass()) instanceof ResponseFactoryInterface)\n        ) {\n            throw new RuntimeException(get_called_class() . ' could not instantiate a response factory.');\n        }\n\n        return $responseFactory;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function getStreamFactory(): StreamFactoryInterface\n    {\n        if (\n            !static::isStreamFactoryAvailable()\n            || !(($streamFactory = new static::$streamFactoryClass()) instanceof StreamFactoryInterface)\n        ) {\n            throw new RuntimeException(get_called_class() . ' could not instantiate a stream factory.');\n        }\n\n        return $streamFactory;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function getServerRequestCreator(): ServerRequestCreatorInterface\n    {\n        if (!static::isServerRequestCreatorAvailable()) {\n            throw new RuntimeException(get_called_class() . ' could not instantiate a server request creator.');\n        }\n\n        return new ServerRequestCreator(static::$serverRequestCreatorClass, static::$serverRequestCreatorMethod);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function isResponseFactoryAvailable(): bool\n    {\n        return static::$responseFactoryClass && class_exists(static::$responseFactoryClass);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function isStreamFactoryAvailable(): bool\n    {\n        return static::$streamFactoryClass && class_exists(static::$streamFactoryClass);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function isServerRequestCreatorAvailable(): bool\n    {\n        return (\n            static::$serverRequestCreatorClass\n            && static::$serverRequestCreatorMethod\n            && class_exists(static::$serverRequestCreatorClass)\n        );\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/Psr17FactoryProvider.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nuse Slim\\Interfaces\\Psr17FactoryProviderInterface;\n\nuse function array_unshift;\n\nclass Psr17FactoryProvider implements Psr17FactoryProviderInterface\n{\n    /**\n     * @var string[]\n     */\n    protected static array $factories = [\n        SlimPsr17Factory::class,\n        HttpSoftPsr17Factory::class,\n        NyholmPsr17Factory::class,\n        LaminasDiactorosPsr17Factory::class,\n        GuzzlePsr17Factory::class,\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function getFactories(): array\n    {\n        return static::$factories;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function setFactories(array $factories): void\n    {\n        static::$factories = $factories;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function addFactory(string $factory): void\n    {\n        array_unshift(static::$factories, $factory);\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/ServerRequestCreator.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nuse Closure;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\n\nclass ServerRequestCreator implements ServerRequestCreatorInterface\n{\n    /**\n     * @var object|string\n     */\n    protected $serverRequestCreator;\n\n    protected string $serverRequestCreatorMethod;\n\n    /**\n     * @param object|string $serverRequestCreator\n     */\n    public function __construct($serverRequestCreator, string $serverRequestCreatorMethod)\n    {\n        $this->serverRequestCreator = $serverRequestCreator;\n        $this->serverRequestCreatorMethod = $serverRequestCreatorMethod;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createServerRequestFromGlobals(): ServerRequestInterface\n    {\n        /** @var callable $callable */\n        $callable = [$this->serverRequestCreator, $this->serverRequestCreatorMethod];\n\n        /** @var ServerRequestInterface */\n        return (Closure::fromCallable($callable))();\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/SlimHttpPsr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse RuntimeException;\n\nclass SlimHttpPsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'Slim\\Http\\Factory\\DecoratedResponseFactory';\n\n    /**\n     * @throws RuntimeException when the factory could not be instantiated\n     */\n    public static function createDecoratedResponseFactory(\n        ResponseFactoryInterface $responseFactory,\n        StreamFactoryInterface $streamFactory\n    ): ResponseFactoryInterface {\n        if (\n            !((\n                $decoratedResponseFactory = new static::$responseFactoryClass($responseFactory, $streamFactory)\n                ) instanceof ResponseFactoryInterface\n            )\n        ) {\n            throw new RuntimeException(get_called_class() . ' could not instantiate a decorated response factory.');\n        }\n\n        return $decoratedResponseFactory;\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/SlimHttpServerRequestCreator.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\n\nuse function class_exists;\n\nclass SlimHttpServerRequestCreator implements ServerRequestCreatorInterface\n{\n    protected ServerRequestCreatorInterface $serverRequestCreator;\n\n    protected static string $serverRequestDecoratorClass = 'Slim\\Http\\ServerRequest';\n\n    public function __construct(ServerRequestCreatorInterface $serverRequestCreator)\n    {\n        $this->serverRequestCreator = $serverRequestCreator;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createServerRequestFromGlobals(): ServerRequestInterface\n    {\n        if (!static::isServerRequestDecoratorAvailable()) {\n            throw new RuntimeException('The Slim-Http ServerRequest decorator is not available.');\n        }\n\n        $request = $this->serverRequestCreator->createServerRequestFromGlobals();\n\n        if (\n            !((\n                $decoratedServerRequest = new static::$serverRequestDecoratorClass($request)\n                ) instanceof ServerRequestInterface)\n        ) {\n            throw new RuntimeException(get_called_class() . ' could not instantiate a decorated server request.');\n        }\n\n        return $decoratedServerRequest;\n    }\n\n    public static function isServerRequestDecoratorAvailable(): bool\n    {\n        return class_exists(static::$serverRequestDecoratorClass);\n    }\n}\n"
  },
  {
    "path": "Slim/Factory/Psr17/SlimPsr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory\\Psr17;\n\nclass SlimPsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'Slim\\Psr7\\Factory\\ResponseFactory';\n    protected static string $streamFactoryClass = 'Slim\\Psr7\\Factory\\StreamFactory';\n    protected static string $serverRequestCreatorClass = 'Slim\\Psr7\\Factory\\ServerRequestFactory';\n    protected static string $serverRequestCreatorMethod = 'createFromGlobals';\n}\n"
  },
  {
    "path": "Slim/Factory/ServerRequestCreatorFactory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Factory;\n\nuse RuntimeException;\nuse Slim\\Factory\\Psr17\\Psr17Factory;\nuse Slim\\Factory\\Psr17\\Psr17FactoryProvider;\nuse Slim\\Factory\\Psr17\\SlimHttpServerRequestCreator;\nuse Slim\\Interfaces\\Psr17FactoryProviderInterface;\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\n\n/** @api */\nclass ServerRequestCreatorFactory\n{\n    protected static ?Psr17FactoryProviderInterface $psr17FactoryProvider = null;\n\n    protected static ?ServerRequestCreatorInterface $serverRequestCreator = null;\n\n    protected static bool $slimHttpDecoratorsAutomaticDetectionEnabled = true;\n\n    public static function create(): ServerRequestCreatorInterface\n    {\n        return static::determineServerRequestCreator();\n    }\n\n    /**\n     * @throws RuntimeException\n     */\n    public static function determineServerRequestCreator(): ServerRequestCreatorInterface\n    {\n        if (static::$serverRequestCreator) {\n            return static::attemptServerRequestCreatorDecoration(static::$serverRequestCreator);\n        }\n\n        $psr17FactoryProvider = static::$psr17FactoryProvider ?? new Psr17FactoryProvider();\n\n        /** @var Psr17Factory $psr17Factory */\n        foreach ($psr17FactoryProvider->getFactories() as $psr17Factory) {\n            if ($psr17Factory::isServerRequestCreatorAvailable()) {\n                $serverRequestCreator = $psr17Factory::getServerRequestCreator();\n                return static::attemptServerRequestCreatorDecoration($serverRequestCreator);\n            }\n        }\n\n        throw new RuntimeException(\n            \"Could not detect any ServerRequest creator implementations. \" .\n            \"Please install a supported implementation in order to use `App::run()` \" .\n            \"without having to pass in a `ServerRequest` object. \" .\n            \"See https://github.com/slimphp/Slim/blob/4.x/README.md for a list of supported implementations.\"\n        );\n    }\n\n    protected static function attemptServerRequestCreatorDecoration(\n        ServerRequestCreatorInterface $serverRequestCreator\n    ): ServerRequestCreatorInterface {\n        if (\n            static::$slimHttpDecoratorsAutomaticDetectionEnabled\n            && SlimHttpServerRequestCreator::isServerRequestDecoratorAvailable()\n        ) {\n            return new SlimHttpServerRequestCreator($serverRequestCreator);\n        }\n\n        return $serverRequestCreator;\n    }\n\n    public static function setPsr17FactoryProvider(Psr17FactoryProviderInterface $psr17FactoryProvider): void\n    {\n        static::$psr17FactoryProvider = $psr17FactoryProvider;\n    }\n\n    public static function setServerRequestCreator(ServerRequestCreatorInterface $serverRequestCreator): void\n    {\n        self::$serverRequestCreator = $serverRequestCreator;\n    }\n\n    public static function setSlimHttpDecoratorsAutomaticDetection(bool $enabled): void\n    {\n        static::$slimHttpDecoratorsAutomaticDetectionEnabled = $enabled;\n    }\n}\n"
  },
  {
    "path": "Slim/Handlers/ErrorHandler.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Handlers;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Log\\LoggerInterface;\nuse RuntimeException;\nuse Slim\\Error\\Renderers\\HtmlErrorRenderer;\nuse Slim\\Error\\Renderers\\JsonErrorRenderer;\nuse Slim\\Error\\Renderers\\PlainTextErrorRenderer;\nuse Slim\\Error\\Renderers\\XmlErrorRenderer;\nuse Slim\\Exception\\HttpException;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\ErrorHandlerInterface;\nuse Slim\\Interfaces\\ErrorRendererInterface;\nuse Slim\\Logger;\nuse Throwable;\n\nuse function array_intersect;\nuse function array_key_exists;\nuse function array_keys;\nuse function call_user_func;\nuse function count;\nuse function current;\nuse function explode;\nuse function implode;\nuse function next;\nuse function preg_match;\n\n/**\n * Default Slim application error handler\n *\n * It outputs the error message and diagnostic information in one of the following formats:\n * JSON, XML, Plain Text or HTML based on the Accept header.\n * @api\n */\nclass ErrorHandler implements ErrorHandlerInterface\n{\n    protected string $defaultErrorRendererContentType = 'text/html';\n\n    /**\n     * @var ErrorRendererInterface|string|callable\n     */\n    protected $defaultErrorRenderer = HtmlErrorRenderer::class;\n\n    /**\n     * @var ErrorRendererInterface|string|callable\n     */\n    protected $logErrorRenderer = PlainTextErrorRenderer::class;\n\n    /**\n     * @var array<string|callable>\n     */\n    protected array $errorRenderers = [\n        'application/json' => JsonErrorRenderer::class,\n        'application/xml' => XmlErrorRenderer::class,\n        'text/xml' => XmlErrorRenderer::class,\n        'text/html' => HtmlErrorRenderer::class,\n        'text/plain' => PlainTextErrorRenderer::class,\n    ];\n\n    protected bool $displayErrorDetails = false;\n\n    protected bool $logErrors;\n\n    protected bool $logErrorDetails = false;\n\n    protected ?string $contentType = null;\n\n    protected ?string $method = null;\n\n    protected ServerRequestInterface $request;\n\n    protected Throwable $exception;\n\n    protected int $statusCode;\n\n    protected CallableResolverInterface $callableResolver;\n\n    protected ResponseFactoryInterface $responseFactory;\n\n    protected LoggerInterface $logger;\n\n    public function __construct(\n        CallableResolverInterface $callableResolver,\n        ResponseFactoryInterface $responseFactory,\n        ?LoggerInterface $logger = null\n    ) {\n        $this->callableResolver = $callableResolver;\n        $this->responseFactory = $responseFactory;\n        $this->logger = $logger ?: $this->getDefaultLogger();\n    }\n\n    /**\n     * Invoke error handler\n     *\n     * @param ServerRequestInterface $request             The most recent Request object\n     * @param Throwable              $exception           The caught Exception object\n     * @param bool                   $displayErrorDetails Whether or not to display the error details\n     * @param bool                   $logErrors           Whether or not to log errors\n     * @param bool                   $logErrorDetails     Whether or not to log error details\n     */\n    public function __invoke(\n        ServerRequestInterface $request,\n        Throwable $exception,\n        bool $displayErrorDetails,\n        bool $logErrors,\n        bool $logErrorDetails\n    ): ResponseInterface {\n        $this->displayErrorDetails = $displayErrorDetails;\n        $this->logErrors = $logErrors;\n        $this->logErrorDetails = $logErrorDetails;\n        $this->request = $request;\n        $this->exception = $exception;\n        $this->method = $request->getMethod();\n        $this->statusCode = $this->determineStatusCode();\n        if ($this->contentType === null) {\n            $this->contentType = $this->determineContentType($request);\n        }\n\n        if ($logErrors) {\n            $this->writeToErrorLog();\n        }\n\n        return $this->respond();\n    }\n\n    /**\n     * Force the content type for all error handler responses.\n     *\n     * @param string|null $contentType The content type\n     */\n    public function forceContentType(?string $contentType): void\n    {\n        $this->contentType = $contentType;\n    }\n\n    protected function determineStatusCode(): int\n    {\n        if ($this->method === 'OPTIONS') {\n            return 200;\n        }\n\n        if ($this->exception instanceof HttpException) {\n            return $this->exception->getCode();\n        }\n\n        return 500;\n    }\n\n    /**\n     * Determine which content type we know about is wanted using Accept header\n     *\n     * Note: This method is a bare-bones implementation designed specifically for\n     * Slim's error handling requirements. Consider a fully-feature solution such\n     * as willdurand/negotiation for any other situation.\n     */\n    protected function determineContentType(ServerRequestInterface $request): ?string\n    {\n        $acceptHeader = $request->getHeaderLine('Accept');\n        $selectedContentTypes = array_intersect(\n            explode(',', $acceptHeader),\n            array_keys($this->errorRenderers)\n        );\n        $count = count($selectedContentTypes);\n\n        if ($count) {\n            $current = current($selectedContentTypes);\n\n            /**\n             * Ensure other supported content types take precedence over text/plain\n             * when multiple content types are provided via Accept header.\n             */\n            if ($current === 'text/plain' && $count > 1) {\n                $next = next($selectedContentTypes);\n                if (is_string($next)) {\n                    return $next;\n                }\n            }\n\n            // @phpstan-ignore-next-line\n            if (is_string($current)) {\n                return $current;\n            }\n        }\n\n        if (preg_match('/\\+(json|xml)/', $acceptHeader, $matches)) {\n            $mediaType = 'application/' . $matches[1];\n            if (array_key_exists($mediaType, $this->errorRenderers)) {\n                return $mediaType;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Determine which renderer to use based on content type\n     *\n     * @throws RuntimeException\n     */\n    protected function determineRenderer(): callable\n    {\n        if ($this->contentType !== null && array_key_exists($this->contentType, $this->errorRenderers)) {\n            $renderer = $this->errorRenderers[$this->contentType];\n        } else {\n            $renderer = $this->defaultErrorRenderer;\n        }\n\n        return $this->callableResolver->resolve($renderer);\n    }\n\n    /**\n     * Register an error renderer for a specific content-type\n     *\n     * @param string  $contentType  The content-type this renderer should be registered to\n     * @param ErrorRendererInterface|string|callable $errorRenderer The error renderer\n     */\n    public function registerErrorRenderer(string $contentType, $errorRenderer): void\n    {\n        $this->errorRenderers[$contentType] = $errorRenderer;\n    }\n\n    /**\n     * Set the default error renderer\n     *\n     * @param string                                 $contentType   The content type of the default error renderer\n     * @param ErrorRendererInterface|string|callable $errorRenderer The default error renderer\n     */\n    public function setDefaultErrorRenderer(string $contentType, $errorRenderer): void\n    {\n        $this->defaultErrorRendererContentType = $contentType;\n        $this->defaultErrorRenderer = $errorRenderer;\n    }\n\n    /**\n     * Set the renderer for the error logger\n     *\n     * @param ErrorRendererInterface|string|callable $logErrorRenderer\n     */\n    public function setLogErrorRenderer($logErrorRenderer): void\n    {\n        $this->logErrorRenderer = $logErrorRenderer;\n    }\n\n    /**\n     * Write to the error log if $logErrors has been set to true\n     */\n    protected function writeToErrorLog(): void\n    {\n        $renderer = $this->callableResolver->resolve($this->logErrorRenderer);\n\n        /** @var string $error */\n        $error = $renderer($this->exception, $this->logErrorDetails);\n\n        if ($this->logErrorRenderer === PlainTextErrorRenderer::class && !$this->displayErrorDetails) {\n            $error .= \"\\nTips: To display error details in HTTP response \";\n            $error .= 'set \"displayErrorDetails\" to true in the ErrorHandler constructor.';\n        }\n\n        $this->logError($error);\n    }\n\n    /**\n     * Wraps the error_log function so that this can be easily tested\n     */\n    protected function logError(string $error): void\n    {\n        $this->logger->error($error);\n    }\n\n    /**\n     * Returns a default logger implementation.\n     */\n    protected function getDefaultLogger(): LoggerInterface\n    {\n        return new Logger();\n    }\n\n    protected function respond(): ResponseInterface\n    {\n        $response = $this->responseFactory->createResponse($this->statusCode);\n        if ($this->contentType !== null && array_key_exists($this->contentType, $this->errorRenderers)) {\n            $response = $response->withHeader('Content-type', $this->contentType);\n        } else {\n            $response = $response->withHeader('Content-type', $this->defaultErrorRendererContentType);\n        }\n\n        if ($this->exception instanceof HttpMethodNotAllowedException) {\n            $allowedMethods = implode(', ', $this->exception->getAllowedMethods());\n            $response = $response->withHeader('Allow', $allowedMethods);\n        }\n\n        $renderer = $this->determineRenderer();\n        $body = call_user_func($renderer, $this->exception, $this->displayErrorDetails);\n        if ($body !== false) {\n            /** @var string $body */\n            $response->getBody()->write($body);\n        }\n\n        return $response;\n    }\n}\n"
  },
  {
    "path": "Slim/Handlers/Strategies/RequestHandler.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Handlers\\Strategies;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\RequestHandlerInvocationStrategyInterface;\n\n/**\n * PSR-15 RequestHandler invocation strategy\n */\nclass RequestHandler implements RequestHandlerInvocationStrategyInterface\n{\n    protected bool $appendRouteArgumentsToRequestAttributes;\n\n    public function __construct(bool $appendRouteArgumentsToRequestAttributes = false)\n    {\n        $this->appendRouteArgumentsToRequestAttributes = $appendRouteArgumentsToRequestAttributes;\n    }\n\n    /**\n     * Invoke a route callable that implements RequestHandlerInterface\n     *\n     * @param array<string, string>  $routeArguments\n     */\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface {\n        if ($this->appendRouteArgumentsToRequestAttributes) {\n            foreach ($routeArguments as $k => $v) {\n                $request = $request->withAttribute($k, $v);\n            }\n        }\n\n        /** @var ResponseInterface */\n        return $callable($request);\n    }\n}\n"
  },
  {
    "path": "Slim/Handlers/Strategies/RequestResponse.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Handlers\\Strategies;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\n\n/**\n * Default route callback strategy with route parameters as an array of arguments.\n */\nclass RequestResponse implements InvocationStrategyInterface\n{\n    /**\n     * Invoke a route callable with request, response, and all route parameters\n     * as an array of arguments.\n     *\n     * @param array<string, string>  $routeArguments\n     */\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface {\n        foreach ($routeArguments as $k => $v) {\n            $request = $request->withAttribute($k, $v);\n        }\n\n        /** @var ResponseInterface */\n        return $callable($request, $response, $routeArguments);\n    }\n}\n"
  },
  {
    "path": "Slim/Handlers/Strategies/RequestResponseArgs.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Handlers\\Strategies;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\n\nuse function array_values;\n\n/**\n * Route callback strategy with route parameters as individual arguments.\n * @api\n */\nclass RequestResponseArgs implements InvocationStrategyInterface\n{\n    /**\n     * Invoke a route callable with request, response and all route parameters\n     * as individual arguments.\n     *\n     * @param array<string, string>  $routeArguments\n     */\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface {\n        /** @var ResponseInterface */\n        return $callable($request, $response, ...array_values($routeArguments));\n    }\n}\n"
  },
  {
    "path": "Slim/Handlers/Strategies/RequestResponseNamedArgs.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Handlers\\Strategies;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\nuse RuntimeException;\n\n/**\n * Route callback strategy with route parameters as individual arguments.\n * @api\n */\nclass RequestResponseNamedArgs implements InvocationStrategyInterface\n{\n    /**\n     * Invoke a route callable with request, response and all route parameters\n     * as individual arguments.\n     *\n     * @param array<string, string>  $routeArguments\n     */\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface {\n        /** @var ResponseInterface */\n        return $callable($request, $response, ...$routeArguments);\n    }\n}\n"
  },
  {
    "path": "Slim/Interfaces/AdvancedCallableResolverInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\ninterface AdvancedCallableResolverInterface extends CallableResolverInterface\n{\n    /**\n     * Resolve $toResolve into a callable\n     *\n     * @param callable|array{class-string, string}|string $toResolve\n     */\n    public function resolveRoute($toResolve): callable;\n\n    /**\n     * Resolve $toResolve into a callable\n     *\n     * @param callable|array{class-string, string}|string $toResolve\n     */\n    public function resolveMiddleware($toResolve): callable;\n}\n"
  },
  {
    "path": "Slim/Interfaces/CallableResolverInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\ninterface CallableResolverInterface\n{\n    /**\n     * Resolve $toResolve into a callable\n     *\n     * @param callable|array{class-string, string}|string $toResolve\n     */\n    public function resolve($toResolve): callable;\n}\n"
  },
  {
    "path": "Slim/Interfaces/DispatcherInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Slim\\Routing\\RoutingResults;\n\ninterface DispatcherInterface\n{\n    /**\n     * Get routing results for a given request method and uri\n     */\n    public function dispatch(string $method, string $uri): RoutingResults;\n\n    /**\n     * Get allowed methods for a given uri\n     *\n     * @return string[]\n     */\n    public function getAllowedMethods(string $uri): array;\n}\n"
  },
  {
    "path": "Slim/Interfaces/ErrorHandlerInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Throwable;\n\ninterface ErrorHandlerInterface\n{\n    public function __invoke(\n        ServerRequestInterface $request,\n        Throwable $exception,\n        bool $displayErrorDetails,\n        bool $logErrors,\n        bool $logErrorDetails\n    ): ResponseInterface;\n}\n"
  },
  {
    "path": "Slim/Interfaces/ErrorRendererInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Throwable;\n\ninterface ErrorRendererInterface\n{\n    public function __invoke(Throwable $exception, bool $displayErrorDetails): string;\n}\n"
  },
  {
    "path": "Slim/Interfaces/InvocationStrategyInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\n\n/**\n * Defines a contract for invoking a route callable.\n */\ninterface InvocationStrategyInterface\n{\n    /**\n     * Invoke a route callable.\n     *\n     * @param callable               $callable       The callable to invoke using the strategy.\n     * @param ServerRequestInterface $request        The request object.\n     * @param ResponseInterface      $response       The response object.\n     * @param array<string, string>  $routeArguments The route's placeholder arguments\n     *\n     * @return ResponseInterface The response from the callable.\n     */\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface;\n}\n"
  },
  {
    "path": "Slim/Interfaces/MiddlewareDispatcherInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\n/** @api */\ninterface MiddlewareDispatcherInterface extends RequestHandlerInterface\n{\n    /**\n     * Add a new middleware to the stack\n     *\n     * Middleware are organized as a stack. That means middleware\n     * that have been added before will be executed after the newly\n     * added one (last in, first out).\n     *\n     * @param MiddlewareInterface|string|callable $middleware\n     */\n    public function add($middleware): self;\n\n    /**\n     * Add a new middleware to the stack\n     *\n     * Middleware are organized as a stack. That means middleware\n     * that have been added before will be executed after the newly\n     * added one (last in, first out).\n     */\n    public function addMiddleware(MiddlewareInterface $middleware): self;\n\n    /**\n     * Seed the middleware stack with the inner request handler\n     */\n    public function seedMiddlewareStack(RequestHandlerInterface $kernel): void;\n}\n"
  },
  {
    "path": "Slim/Interfaces/Psr17FactoryInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse RuntimeException;\n\ninterface Psr17FactoryInterface\n{\n    /**\n     * @throws RuntimeException when the factory could not be instantiated\n     */\n    public static function getResponseFactory(): ResponseFactoryInterface;\n\n    /**\n     * @throws RuntimeException when the factory could not be instantiated\n     */\n    public static function getStreamFactory(): StreamFactoryInterface;\n\n    /**\n     * @throws RuntimeException when the factory could not be instantiated\n     */\n    public static function getServerRequestCreator(): ServerRequestCreatorInterface;\n\n    /**\n     * Is the PSR-17 ResponseFactory available\n     */\n    public static function isResponseFactoryAvailable(): bool;\n\n    /**\n     * Is the PSR-17 StreamFactory available\n     */\n    public static function isStreamFactoryAvailable(): bool;\n\n    /**\n     * Is the ServerRequest creator available\n     */\n    public static function isServerRequestCreatorAvailable(): bool;\n}\n"
  },
  {
    "path": "Slim/Interfaces/Psr17FactoryProviderInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\n/** @api */\ninterface Psr17FactoryProviderInterface\n{\n    /**\n     * @return string[]\n     */\n    public static function getFactories(): array;\n\n    /**\n     * @param string[] $factories\n     */\n    public static function setFactories(array $factories): void;\n\n    public static function addFactory(string $factory): void;\n}\n"
  },
  {
    "path": "Slim/Interfaces/RequestHandlerInvocationStrategyInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\ninterface RequestHandlerInvocationStrategyInterface extends InvocationStrategyInterface\n{\n}\n"
  },
  {
    "path": "Slim/Interfaces/RouteCollectorInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse InvalidArgumentException;\nuse RuntimeException;\n\n/** @api */\ninterface RouteCollectorInterface\n{\n    /**\n     * Get the route parser\n     */\n    public function getRouteParser(): RouteParserInterface;\n\n    /**\n     * Get default route invocation strategy\n     */\n    public function getDefaultInvocationStrategy(): InvocationStrategyInterface;\n\n    /**\n     * Set default route invocation strategy\n     */\n    public function setDefaultInvocationStrategy(InvocationStrategyInterface $strategy): RouteCollectorInterface;\n\n    /**\n     * Get path to FastRoute cache file\n     */\n    public function getCacheFile(): ?string;\n\n    /**\n     * Set path to FastRoute cache file\n     *\n     * @throws InvalidArgumentException\n     * @throws RuntimeException\n     */\n    public function setCacheFile(string $cacheFile): RouteCollectorInterface;\n\n    /**\n     * Get the base path used in pathFor()\n     */\n    public function getBasePath(): string;\n\n    /**\n     * Set the base path used in pathFor()\n     */\n    public function setBasePath(string $basePath): RouteCollectorInterface;\n\n    /**\n     * Get route objects\n     *\n     * @return RouteInterface[]\n     */\n    public function getRoutes(): array;\n\n    /**\n     * Get named route object\n     *\n     * @param string $name Route name\n     *\n     * @throws RuntimeException   If named route does not exist\n     */\n    public function getNamedRoute(string $name): RouteInterface;\n\n    /**\n     * Remove named route\n     *\n     * @param string $name Route name\n     *\n     * @throws RuntimeException   If named route does not exist\n     */\n    public function removeNamedRoute(string $name): RouteCollectorInterface;\n\n    /**\n     * Lookup a route via the route's unique identifier\n     *\n     * @throws RuntimeException   If route of identifier does not exist\n     */\n    public function lookupRoute(string $identifier): RouteInterface;\n\n    /**\n     * Add route group\n     * @param string|callable $callable\n     */\n    public function group(string $pattern, $callable): RouteGroupInterface;\n\n    /**\n     * Add route\n     *\n     * @param string[] $methods Array of HTTP methods\n     * @param string $pattern The route pattern\n     * @param callable|array{class-string, string}|string $handler The route callable\n     */\n    public function map(array $methods, string $pattern, $handler): RouteInterface;\n}\n"
  },
  {
    "path": "Slim/Interfaces/RouteCollectorProxyInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\UriInterface;\n\n/**\n * @api\n * @template TContainerInterface of (ContainerInterface|null)\n */\ninterface RouteCollectorProxyInterface\n{\n    public function getResponseFactory(): ResponseFactoryInterface;\n\n    public function getCallableResolver(): CallableResolverInterface;\n\n    /**\n     * @return TContainerInterface\n     */\n    public function getContainer(): ?ContainerInterface;\n\n    public function getRouteCollector(): RouteCollectorInterface;\n\n    /**\n     * Get the RouteCollectorProxy's base path\n     */\n    public function getBasePath(): string;\n\n    /**\n     * Set the RouteCollectorProxy's base path\n     * @return RouteCollectorProxyInterface<TContainerInterface>\n     */\n    public function setBasePath(string $basePath): RouteCollectorProxyInterface;\n\n    /**\n     * Add GET route\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function get(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add POST route\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function post(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add PUT route\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function put(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add PATCH route\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function patch(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add DELETE route\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function delete(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add OPTIONS route\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function options(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add route for any HTTP method\n     *\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function any(string $pattern, $callable): RouteInterface;\n\n    /**\n     * Add route with multiple methods\n     *\n     * @param string[] $methods Numeric array of HTTP method names\n     * @param string $pattern The route URI pattern\n     * @param callable|array{class-string, string}|string $callable The route callback routine\n     */\n    public function map(array $methods, string $pattern, $callable): RouteInterface;\n\n    /**\n     * Route Groups\n     *\n     * This method accepts a route pattern and a callback. All route\n     * declarations in the callback will be prepended by the group(s)\n     * that it is in.\n     * @param string|callable $callable\n     */\n    public function group(string $pattern, $callable): RouteGroupInterface;\n\n    /**\n     * Add a route that sends an HTTP redirect\n     *\n     * @param string|UriInterface $to\n     */\n    public function redirect(string $from, $to, int $status = 302): RouteInterface;\n}\n"
  },
  {
    "path": "Slim/Interfaces/RouteGroupInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Slim\\MiddlewareDispatcher;\n\n/** @api */\ninterface RouteGroupInterface\n{\n    public function collectRoutes(): RouteGroupInterface;\n\n    /**\n     * Add middleware to the route group\n     *\n     * @param MiddlewareInterface|string|callable $middleware\n     */\n    public function add($middleware): RouteGroupInterface;\n\n    /**\n     * Add middleware to the route group\n     */\n    public function addMiddleware(MiddlewareInterface $middleware): RouteGroupInterface;\n\n    /**\n     * Append the group's middleware to the MiddlewareDispatcher\n     * @param MiddlewareDispatcher<\\Psr\\Container\\ContainerInterface|null> $dispatcher\n     */\n    public function appendMiddlewareToDispatcher(MiddlewareDispatcher $dispatcher): RouteGroupInterface;\n\n    /**\n     * Get the RouteGroup's pattern\n     */\n    public function getPattern(): string;\n}\n"
  },
  {
    "path": "Slim/Interfaces/RouteInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\n\n/** @api */\ninterface RouteInterface\n{\n    /**\n     * Get route invocation strategy\n     */\n    public function getInvocationStrategy(): InvocationStrategyInterface;\n\n    /**\n     * Set route invocation strategy\n     */\n    public function setInvocationStrategy(InvocationStrategyInterface $invocationStrategy): RouteInterface;\n\n    /**\n     * Get route methods\n     *\n     * @return string[]\n     */\n    public function getMethods(): array;\n\n    /**\n     * Get route pattern\n     */\n    public function getPattern(): string;\n\n    /**\n     * Set route pattern\n     */\n    public function setPattern(string $pattern): RouteInterface;\n\n    /**\n     * Get route callable\n     *\n     * @return callable|array{class-string, string}|string\n     */\n    public function getCallable();\n\n    /**\n     * Set route callable\n     *\n     * @param callable|array{class-string, string}|string $callable\n     */\n    public function setCallable($callable): RouteInterface;\n\n    /**\n     * Get route name\n     */\n    public function getName(): ?string;\n\n    /**\n     * Set route name\n     *\n     * @return static\n     */\n    public function setName(string $name): RouteInterface;\n\n    /**\n     * Get the route's unique identifier\n     */\n    public function getIdentifier(): string;\n\n    /**\n     * Retrieve a specific route argument\n     */\n    public function getArgument(string $name, ?string $default = null): ?string;\n\n    /**\n     * Get route arguments\n     *\n     * @return array<string, string>\n     */\n    public function getArguments(): array;\n\n    /**\n     * Set a route argument\n     *\n     * @deprecated 4.14.1 Use a middleware for custom route arguments now.\n     */\n    public function setArgument(string $name, string $value): RouteInterface;\n\n    /**\n     * Replace route arguments\n     *\n     * @param array<string, string> $arguments\n     *\n     * @deprecated 4.14.1 Use a middleware for custom route arguments now.\n     */\n    public function setArguments(array $arguments): self;\n\n    /**\n     * @param MiddlewareInterface|string|callable $middleware\n     */\n    public function add($middleware): self;\n\n    public function addMiddleware(MiddlewareInterface $middleware): self;\n\n    /**\n     * Prepare the route for use\n     *\n     * @param array<string, string> $arguments\n     */\n    public function prepare(array $arguments): self;\n\n    /**\n     * Run route\n     *\n     * This method traverses the middleware stack, including the route's callable\n     * and captures the resultant HTTP response object. It then sends the response\n     * back to the Application.\n     */\n    public function run(ServerRequestInterface $request): ResponseInterface;\n}\n"
  },
  {
    "path": "Slim/Interfaces/RouteParserInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse InvalidArgumentException;\nuse Psr\\Http\\Message\\UriInterface;\nuse RuntimeException;\n\n/** @api */\ninterface RouteParserInterface\n{\n    /**\n     * Build the path for a named route excluding the base path\n     *\n     * @param string                                           $routeName   Route name\n     * @param array<string, string>                            $data        Named argument replacement data\n     * @param array<string, string | array<array-key, string>> $queryParams Optional query string parameters\n     *\n     * @throws RuntimeException         If named route does not exist\n     * @throws InvalidArgumentException If required data not provided\n     */\n    public function relativeUrlFor(string $routeName, array $data = [], array $queryParams = []): string;\n\n    /**\n     * Build the path for a named route including the base path\n     *\n     * @param string                                           $routeName   Route name\n     * @param array<string, string>                            $data        Named argument replacement data\n     * @param array<string, string | array<array-key, string>> $queryParams Optional query string parameters\n     *\n     * @throws RuntimeException         If named route does not exist\n     * @throws InvalidArgumentException If required data not provided\n     */\n    public function urlFor(string $routeName, array $data = [], array $queryParams = []): string;\n\n    /**\n     * Get fully qualified URL for named route\n     *\n     * @param UriInterface                                     $uri\n     * @param string                                           $routeName   Route name\n     * @param array<string, string>                            $data        Named argument replacement data\n     * @param array<string, string | array<array-key, string>> $queryParams Optional query string parameters\n     */\n    public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string;\n}\n"
  },
  {
    "path": "Slim/Interfaces/RouteResolverInterface.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Slim\\Routing\\RoutingResults;\n\ninterface RouteResolverInterface\n{\n    /**\n     * @param string $uri Should be ServerRequestInterface::getUri()->getPath()\n     */\n    public function computeRoutingResults(string $uri, string $method): RoutingResults;\n\n    public function resolveRoute(string $identifier): RouteInterface;\n}\n"
  },
  {
    "path": "Slim/Interfaces/ServerRequestCreatorInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Interfaces;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\n\ninterface ServerRequestCreatorInterface\n{\n    public function createServerRequestFromGlobals(): ServerRequestInterface;\n}\n"
  },
  {
    "path": "Slim/Logger.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim;\n\nuse Psr\\Log\\AbstractLogger;\nuse Psr\\Log\\InvalidArgumentException;\nuse Stringable;\n\nuse function error_log;\n\nclass Logger extends AbstractLogger\n{\n    /**\n     * @param mixed             $level\n     * @param string|Stringable $message\n     * @param array<mixed>      $context\n     *\n     * @throws InvalidArgumentException\n     */\n    public function log($level, $message, array $context = []): void\n    {\n        error_log((string) $message);\n    }\n}\n"
  },
  {
    "path": "Slim/Middleware/BodyParsingMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Middleware;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\n\nuse function count;\nuse function explode;\nuse function is_array;\nuse function is_object;\nuse function is_string;\nuse function json_decode;\nuse function libxml_clear_errors;\nuse function libxml_disable_entity_loader;\nuse function libxml_use_internal_errors;\nuse function parse_str;\nuse function simplexml_load_string;\nuse function strtolower;\nuse function trim;\n\nuse const LIBXML_VERSION;\n\n/** @api */\nclass BodyParsingMiddleware implements MiddlewareInterface\n{\n    /**\n     * @var callable[]\n     */\n    protected array $bodyParsers;\n\n    /**\n     * @param callable[] $bodyParsers list of body parsers as an associative array of mediaType => callable\n     */\n    public function __construct(array $bodyParsers = [])\n    {\n        $this->registerDefaultBodyParsers();\n\n        foreach ($bodyParsers as $mediaType => $parser) {\n            $this->registerBodyParser($mediaType, $parser);\n        }\n    }\n\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $parsedBody = $request->getParsedBody();\n\n        if (empty($parsedBody)) {\n            $parsedBody = $this->parseBody($request);\n            $request = $request->withParsedBody($parsedBody);\n        }\n\n        return $handler->handle($request);\n    }\n\n    /**\n     * @param string $mediaType A HTTP media type (excluding content-type params).\n     * @param callable $callable A callable that returns parsed contents for media type.\n     */\n    public function registerBodyParser(string $mediaType, callable $callable): self\n    {\n        $this->bodyParsers[$mediaType] = $callable;\n        return $this;\n    }\n\n    /**\n     * @param string $mediaType A HTTP media type (excluding content-type params).\n     */\n    public function hasBodyParser(string $mediaType): bool\n    {\n        return isset($this->bodyParsers[$mediaType]);\n    }\n\n    /**\n     * @param string $mediaType A HTTP media type (excluding content-type params).\n     * @throws RuntimeException\n     */\n    public function getBodyParser(string $mediaType): callable\n    {\n        if (!isset($this->bodyParsers[$mediaType])) {\n            throw new RuntimeException('No parser for type ' . $mediaType);\n        }\n        return $this->bodyParsers[$mediaType];\n    }\n\n    protected function registerDefaultBodyParsers(): void\n    {\n        $this->registerBodyParser('application/json', static function ($input) {\n            /** @var string $input */\n            $result = json_decode($input, true);\n\n            if (!is_array($result)) {\n                return null;\n            }\n\n            return $result;\n        });\n\n        $this->registerBodyParser('application/x-www-form-urlencoded', static function ($input) {\n            /** @var string $input */\n            parse_str($input, $data);\n\n            return $data;\n        });\n\n        $xmlCallable = static function ($input) {\n            /** @var string $input */\n\n            $backup = self::disableXmlEntityLoader(true);\n            $backup_errors = libxml_use_internal_errors(true);\n            $result = simplexml_load_string($input);\n\n            self::disableXmlEntityLoader($backup);\n            libxml_clear_errors();\n            libxml_use_internal_errors($backup_errors);\n\n            if ($result === false) {\n                return null;\n            }\n\n            return $result;\n        };\n\n        $this->registerBodyParser('application/xml', $xmlCallable);\n        $this->registerBodyParser('text/xml', $xmlCallable);\n    }\n\n    /**\n     * @return null|array<mixed>|object\n     */\n    protected function parseBody(ServerRequestInterface $request)\n    {\n        $mediaType = $this->getMediaType($request);\n        if ($mediaType === null) {\n            return null;\n        }\n\n        // Check if this specific media type has a parser registered first\n        if (!isset($this->bodyParsers[$mediaType])) {\n            // If not, look for a media type with a structured syntax suffix (RFC 6839)\n            $parts = explode('+', $mediaType);\n            if (count($parts) >= 2) {\n                $mediaType = 'application/' . $parts[count($parts) - 1];\n            }\n        }\n\n        if (isset($this->bodyParsers[$mediaType])) {\n            $body = (string)$request->getBody();\n            $parsed = $this->bodyParsers[$mediaType]($body);\n\n            if ($parsed !== null && !is_object($parsed) && !is_array($parsed)) {\n                throw new RuntimeException(\n                    'Request body media type parser return value must be an array, an object, or null'\n                );\n            }\n\n            return $parsed;\n        }\n\n        return null;\n    }\n\n    /**\n     * @return string|null The serverRequest media type, minus content-type params\n     */\n    protected function getMediaType(ServerRequestInterface $request): ?string\n    {\n        $contentType = $request->getHeader('Content-Type')[0] ?? null;\n\n        if (is_string($contentType) && trim($contentType) !== '') {\n            $contentTypeParts = explode(';', $contentType);\n            return strtolower(trim($contentTypeParts[0]));\n        }\n\n        return null;\n    }\n\n    protected static function disableXmlEntityLoader(bool $disable): bool\n    {\n        if (LIBXML_VERSION >= 20900) {\n            // libxml >= 2.9.0 disables entity loading by default, so it is\n            // safe to skip the real call (deprecated in PHP 8).\n            return true;\n        }\n\n        // @codeCoverageIgnoreStart\n        return libxml_disable_entity_loader($disable);\n        // @codeCoverageIgnoreEnd\n    }\n}\n"
  },
  {
    "path": "Slim/Middleware/ContentLengthMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Middleware;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\n/** @api */\nclass ContentLengthMiddleware implements MiddlewareInterface\n{\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $response = $handler->handle($request);\n\n        // Add Content-Length header if not already added\n        $size = $response->getBody()->getSize();\n        if ($size !== null && !$response->hasHeader('Content-Length')) {\n            $response = $response->withHeader('Content-Length', (string) $size);\n        }\n\n        return $response;\n    }\n}\n"
  },
  {
    "path": "Slim/Middleware/ErrorMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Middleware;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Psr\\Log\\LoggerInterface;\nuse Slim\\Exception\\HttpException;\nuse Slim\\Handlers\\ErrorHandler;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\ErrorHandlerInterface;\nuse Throwable;\n\nuse function get_class;\nuse function is_subclass_of;\n\n/** @api */\nclass ErrorMiddleware implements MiddlewareInterface\n{\n    protected CallableResolverInterface $callableResolver;\n\n    protected ResponseFactoryInterface $responseFactory;\n\n    protected bool $displayErrorDetails;\n\n    protected bool $logErrors;\n\n    protected bool $logErrorDetails;\n\n    protected ?LoggerInterface $logger = null;\n\n    /**\n     * @var ErrorHandlerInterface[]|callable[]|string[]\n     */\n    protected array $handlers = [];\n\n    /**\n     * @var ErrorHandlerInterface[]|callable[]|string[]\n     */\n    protected array $subClassHandlers = [];\n\n    /**\n     * @var ErrorHandlerInterface|callable|string|null\n     */\n    protected $defaultErrorHandler;\n\n    public function __construct(\n        CallableResolverInterface $callableResolver,\n        ResponseFactoryInterface $responseFactory,\n        bool $displayErrorDetails,\n        bool $logErrors,\n        bool $logErrorDetails,\n        ?LoggerInterface $logger = null\n    ) {\n        $this->callableResolver = $callableResolver;\n        $this->responseFactory = $responseFactory;\n        $this->displayErrorDetails = $displayErrorDetails;\n        $this->logErrors = $logErrors;\n        $this->logErrorDetails = $logErrorDetails;\n        $this->logger = $logger;\n    }\n\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        try {\n            return $handler->handle($request);\n        } catch (Throwable $e) {\n            return $this->handleException($request, $e);\n        }\n    }\n\n    public function handleException(ServerRequestInterface $request, Throwable $exception): ResponseInterface\n    {\n        if ($exception instanceof HttpException) {\n            $request = $exception->getRequest();\n        }\n\n        $exceptionType = get_class($exception);\n        $handler = $this->getErrorHandler($exceptionType);\n\n        /** @var ResponseInterface */\n        return $handler($request, $exception, $this->displayErrorDetails, $this->logErrors, $this->logErrorDetails);\n    }\n\n    /**\n     * Get callable to handle scenarios where an error\n     * occurs when processing the current request.\n     *\n     * @param string $type Exception/Throwable name. ie: RuntimeException::class\n     * @return callable|ErrorHandler\n     */\n    public function getErrorHandler(string $type)\n    {\n        if (isset($this->handlers[$type])) {\n            return $this->callableResolver->resolve($this->handlers[$type]);\n        }\n\n        if (isset($this->subClassHandlers[$type])) {\n            return $this->callableResolver->resolve($this->subClassHandlers[$type]);\n        }\n\n        foreach ($this->subClassHandlers as $class => $handler) {\n            if (is_subclass_of($type, $class)) {\n                return $this->callableResolver->resolve($handler);\n            }\n        }\n\n        return $this->getDefaultErrorHandler();\n    }\n\n    /**\n     * Get default error handler\n     *\n     * @return ErrorHandler|callable\n     */\n    public function getDefaultErrorHandler()\n    {\n        if ($this->defaultErrorHandler === null) {\n            $this->defaultErrorHandler = new ErrorHandler(\n                $this->callableResolver,\n                $this->responseFactory,\n                $this->logger\n            );\n        }\n\n        return $this->callableResolver->resolve($this->defaultErrorHandler);\n    }\n\n    /**\n     * Set callable as the default Slim application error handler.\n     *\n     * The callable signature MUST match the ErrorHandlerInterface\n     *\n     * @param string|callable|ErrorHandler $handler\n     * @see ErrorHandlerInterface\n     *\n     * 1. Instance of \\Psr\\Http\\Message\\ServerRequestInterface\n     * 2. Instance of \\Throwable\n     * 3. Boolean $displayErrorDetails\n     * 4. Boolean $logErrors\n     * 5. Boolean $logErrorDetails\n     *\n     * The callable MUST return an instance of\n     * \\Psr\\Http\\Message\\ResponseInterface.\n     *\n     */\n    public function setDefaultErrorHandler($handler): self\n    {\n        $this->defaultErrorHandler = $handler;\n        return $this;\n    }\n\n    /**\n     * Set callable to handle scenarios where an error\n     * occurs when processing the current request.\n     *\n     * The callable signature MUST match the ErrorHandlerInterface\n     *\n     * Pass true to $handleSubclasses to make the handler handle all subclasses of\n     * the type as well. Pass an array of classes to make the same function handle multiple exceptions.\n     *\n     * @param string|string[] $typeOrTypes Exception/Throwable name.\n     * ie: RuntimeException::class or an array of classes\n     * ie: [HttpNotFoundException::class, HttpMethodNotAllowedException::class]\n     * @param string|callable|ErrorHandlerInterface $handler\n     *\n     * @see ErrorHandlerInterface\n     *\n     * 1. Instance of \\Psr\\Http\\Message\\ServerRequestInterface\n     * 2. Instance of \\Throwable\n     * 3. Boolean $displayErrorDetails\n     * 4. Boolean $logErrors\n     * 5. Boolean $logErrorDetails\n     *\n     * The callable MUST return an instance of\n     * \\Psr\\Http\\Message\\ResponseInterface.\n     *\n     */\n    public function setErrorHandler($typeOrTypes, $handler, bool $handleSubclasses = false): self\n    {\n        if (is_array($typeOrTypes)) {\n            foreach ($typeOrTypes as $type) {\n                $this->addErrorHandler($type, $handler, $handleSubclasses);\n            }\n        } else {\n            $this->addErrorHandler($typeOrTypes, $handler, $handleSubclasses);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Used internally to avoid code repetition when passing multiple exceptions to setErrorHandler().\n     * @param string|callable|ErrorHandlerInterface $handler\n     */\n    private function addErrorHandler(string $type, $handler, bool $handleSubclasses): void\n    {\n        if ($handleSubclasses) {\n            $this->subClassHandlers[$type] = $handler;\n        } else {\n            $this->handlers[$type] = $handler;\n        }\n    }\n}\n"
  },
  {
    "path": "Slim/Middleware/MethodOverrideMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Middleware;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nuse function is_array;\nuse function strtoupper;\n\n/** @api */\nclass MethodOverrideMiddleware implements MiddlewareInterface\n{\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $methodHeader = $request->getHeaderLine('X-Http-Method-Override');\n\n        if ($methodHeader) {\n            $request = $request->withMethod($methodHeader);\n        } elseif (strtoupper($request->getMethod()) === 'POST') {\n            $body = $request->getParsedBody();\n\n            if (is_array($body) && !empty($body['_METHOD']) && is_string($body['_METHOD'])) {\n                $request = $request->withMethod($body['_METHOD']);\n            }\n\n            if ($request->getBody()->eof()) {\n                $request->getBody()->rewind();\n            }\n        }\n\n        return $handler->handle($request);\n    }\n}\n"
  },
  {
    "path": "Slim/Middleware/OutputBufferingMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Middleware;\n\nuse InvalidArgumentException;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Throwable;\n\nuse function in_array;\nuse function ob_end_clean;\nuse function ob_get_clean;\nuse function ob_start;\n\n/** @api */\nclass OutputBufferingMiddleware implements MiddlewareInterface\n{\n    public const APPEND = 'append';\n    public const PREPEND = 'prepend';\n\n    protected StreamFactoryInterface $streamFactory;\n\n    protected string $style;\n\n    /**\n     * @param string $style Either \"append\" or \"prepend\"\n     */\n    public function __construct(StreamFactoryInterface $streamFactory, string $style = 'append')\n    {\n        $this->streamFactory = $streamFactory;\n        $this->style = $style;\n\n        if (!in_array($style, [static::APPEND, static::PREPEND], true)) {\n            throw new InvalidArgumentException(\"Invalid style `{$style}`. Must be `append` or `prepend`\");\n        }\n    }\n\n    /**\n     * @throws Throwable\n     */\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        try {\n            ob_start();\n            $response = $handler->handle($request);\n            $output = ob_get_clean();\n        } catch (Throwable $e) {\n            ob_end_clean();\n            throw $e;\n        }\n\n        if (!empty($output)) {\n            if ($this->style === static::PREPEND) {\n                $body = $this->streamFactory->createStream();\n                $body->write($output . $response->getBody());\n                $response = $response->withBody($body);\n            } elseif ($this->style === static::APPEND && $response->getBody()->isWritable()) {\n                $response->getBody()->write($output);\n            }\n        }\n\n        return $response;\n    }\n}\n"
  },
  {
    "path": "Slim/Middleware/RoutingMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Middleware;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Interfaces\\RouteParserInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\nuse Slim\\Routing\\RouteContext;\nuse Slim\\Routing\\RoutingResults;\n\nclass RoutingMiddleware implements MiddlewareInterface\n{\n    protected RouteResolverInterface $routeResolver;\n\n    protected RouteParserInterface $routeParser;\n\n    public function __construct(RouteResolverInterface $routeResolver, RouteParserInterface $routeParser)\n    {\n        $this->routeResolver = $routeResolver;\n        $this->routeParser = $routeParser;\n    }\n\n    /**\n     * @throws HttpNotFoundException\n     * @throws HttpMethodNotAllowedException\n     * @throws RuntimeException\n     */\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $request = $this->performRouting($request);\n        return $handler->handle($request);\n    }\n\n    /**\n     * Perform routing\n     *\n     * @param  ServerRequestInterface $request PSR7 Server Request\n     *\n     * @throws HttpNotFoundException\n     * @throws HttpMethodNotAllowedException\n     * @throws RuntimeException\n     */\n    public function performRouting(ServerRequestInterface $request): ServerRequestInterface\n    {\n        $request = $request->withAttribute(RouteContext::ROUTE_PARSER, $this->routeParser);\n\n        $routingResults = $this->resolveRoutingResultsFromRequest($request);\n        $routeStatus = $routingResults->getRouteStatus();\n\n        $request = $request->withAttribute(RouteContext::ROUTING_RESULTS, $routingResults);\n\n        switch ($routeStatus) {\n            case RoutingResults::FOUND:\n                $routeArguments = $routingResults->getRouteArguments();\n                $routeIdentifier = $routingResults->getRouteIdentifier() ?? '';\n                $route = $this->routeResolver\n                    ->resolveRoute($routeIdentifier)\n                    ->prepare($routeArguments);\n                return $request->withAttribute(RouteContext::ROUTE, $route);\n\n            case RoutingResults::NOT_FOUND:\n                throw new HttpNotFoundException($request);\n\n            case RoutingResults::METHOD_NOT_ALLOWED:\n                $exception = new HttpMethodNotAllowedException($request);\n                $exception->setAllowedMethods($routingResults->getAllowedMethods());\n                throw $exception;\n\n            default:\n                throw new RuntimeException('An unexpected error occurred while performing routing.');\n        }\n    }\n\n    /**\n     * Resolves the route from the given request\n     */\n    protected function resolveRoutingResultsFromRequest(ServerRequestInterface $request): RoutingResults\n    {\n        return $this->routeResolver->computeRoutingResults(\n            $request->getUri()->getPath(),\n            $request->getMethod()\n        );\n    }\n}\n"
  },
  {
    "path": "Slim/MiddlewareDispatcher.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim;\n\nuse Closure;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\AdvancedCallableResolverInterface;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\MiddlewareDispatcherInterface;\n\nuse function class_exists;\nuse function function_exists;\nuse function is_callable;\nuse function is_string;\nuse function preg_match;\nuse function sprintf;\n\n/**\n * @api\n * @template TContainerInterface of (ContainerInterface|null)\n */\nclass MiddlewareDispatcher implements MiddlewareDispatcherInterface\n{\n    /**\n     * Tip of the middleware call stack\n     */\n    protected RequestHandlerInterface $tip;\n\n    protected ?CallableResolverInterface $callableResolver;\n\n    /** @var TContainerInterface $container */\n    protected ?ContainerInterface $container;\n\n    /**\n     * @param TContainerInterface $container\n     */\n    public function __construct(\n        RequestHandlerInterface $kernel,\n        ?CallableResolverInterface $callableResolver = null,\n        ?ContainerInterface $container = null\n    ) {\n        $this->seedMiddlewareStack($kernel);\n        $this->callableResolver = $callableResolver;\n        $this->container = $container;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function seedMiddlewareStack(RequestHandlerInterface $kernel): void\n    {\n        $this->tip = $kernel;\n    }\n\n    /**\n     * Invoke the middleware stack\n     */\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n        return $this->tip->handle($request);\n    }\n\n    /**\n     * Add a new middleware to the stack\n     *\n     * Middleware are organized as a stack. That means middleware\n     * that have been added before will be executed after the newly\n     * added one (last in, first out).\n     *\n     * @param MiddlewareInterface|string|callable $middleware\n     */\n    public function add($middleware): MiddlewareDispatcherInterface\n    {\n        if ($middleware instanceof MiddlewareInterface) {\n            return $this->addMiddleware($middleware);\n        }\n\n        if (is_string($middleware)) {\n            return $this->addDeferred($middleware);\n        }\n\n        if (is_callable($middleware)) {\n            return $this->addCallable($middleware);\n        }\n\n        /** @phpstan-ignore-next-line */\n        throw new RuntimeException(\n            'A middleware must be an object/class name referencing an implementation of ' .\n            'MiddlewareInterface or a callable with a matching signature.'\n        );\n    }\n\n    /**\n     * Add a new middleware to the stack\n     *\n     * Middleware are organized as a stack. That means middleware\n     * that have been added before will be executed after the newly\n     * added one (last in, first out).\n     */\n    public function addMiddleware(MiddlewareInterface $middleware): MiddlewareDispatcherInterface\n    {\n        $next = $this->tip;\n        $this->tip = new class ($middleware, $next) implements RequestHandlerInterface {\n            private MiddlewareInterface $middleware;\n\n            private RequestHandlerInterface $next;\n\n            public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $next)\n            {\n                $this->middleware = $middleware;\n                $this->next = $next;\n            }\n\n            public function handle(ServerRequestInterface $request): ResponseInterface\n            {\n                return $this->middleware->process($request, $this->next);\n            }\n        };\n\n        return $this;\n    }\n\n    /**\n     * Add a new middleware by class name\n     *\n     * Middleware are organized as a stack. That means middleware\n     * that have been added before will be executed after the newly\n     * added one (last in, first out).\n     * @return MiddlewareDispatcher<TContainerInterface>\n     */\n    public function addDeferred(string $middleware): self\n    {\n        $next = $this->tip;\n        $this->tip = new class (\n            $middleware,\n            $next,\n            $this->container,\n            $this->callableResolver\n        ) implements RequestHandlerInterface {\n            private string $middleware;\n\n            private RequestHandlerInterface $next;\n\n            private ?ContainerInterface $container;\n\n            private ?CallableResolverInterface $callableResolver;\n\n            public function __construct(\n                string $middleware,\n                RequestHandlerInterface $next,\n                ?ContainerInterface $container = null,\n                ?CallableResolverInterface $callableResolver = null\n            ) {\n                $this->middleware = $middleware;\n                $this->next = $next;\n                $this->container = $container;\n                $this->callableResolver = $callableResolver;\n            }\n\n            public function handle(ServerRequestInterface $request): ResponseInterface\n            {\n                if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {\n                    $callable = $this->callableResolver->resolveMiddleware($this->middleware);\n                    /** @var ResponseInterface */\n                    return $callable($request, $this->next);\n                }\n\n                $callable = null;\n\n                if ($this->callableResolver instanceof CallableResolverInterface) {\n                    try {\n                        $callable = $this->callableResolver->resolve($this->middleware);\n                    } catch (RuntimeException $e) {\n                        // Do Nothing\n                    }\n                }\n\n                if (!$callable) {\n                    $resolved = $this->middleware;\n                    $instance = null;\n                    $method = null;\n\n                    /** @psalm-suppress ArgumentTypeCoercion */\n                    // Check for Slim callable as `class:method`\n                    if (preg_match(CallableResolver::$callablePattern, $resolved, $matches)) {\n                        $resolved = $matches[1];\n                        $method = $matches[2];\n                    }\n\n                    if ($this->container && $this->container->has($resolved)) {\n                        $instance = $this->container->get($resolved);\n                        if ($instance instanceof MiddlewareInterface) {\n                            return $instance->process($request, $this->next);\n                        }\n                    } elseif (!function_exists($resolved)) {\n                        if (!class_exists($resolved)) {\n                            throw new RuntimeException(sprintf('Middleware %s does not exist', $resolved));\n                        }\n                        $instance = new $resolved($this->container);\n                    }\n\n                    if ($instance && $instance instanceof MiddlewareInterface) {\n                        return $instance->process($request, $this->next);\n                    }\n\n                    $callable = $instance ?? $resolved;\n                    if ($instance && $method) {\n                        $callable = [$instance, $method];\n                    }\n\n                    if ($this->container && $callable instanceof Closure) {\n                        $callable = $callable->bindTo($this->container);\n                    }\n                }\n\n                if (!is_callable($callable)) {\n                    throw new RuntimeException(\n                        sprintf(\n                            'Middleware %s is not resolvable',\n                            $this->middleware\n                        )\n                    );\n                }\n\n                /** @var ResponseInterface */\n                return $callable($request, $this->next);\n            }\n        };\n\n        return $this;\n    }\n\n    /**\n     * Add a (non-standard) callable middleware to the stack\n     *\n     * Middleware are organized as a stack. That means middleware\n     * that have been added before will be executed after the newly\n     * added one (last in, first out).\n     * @return MiddlewareDispatcher<TContainerInterface>\n     */\n    public function addCallable(callable $middleware): self\n    {\n        $next = $this->tip;\n\n        if ($this->container && $middleware instanceof Closure) {\n            /** @var Closure $middleware */\n            $middleware = $middleware->bindTo($this->container);\n        }\n\n        $this->tip = new class ($middleware, $next) implements RequestHandlerInterface {\n            /**\n             * @var callable\n             */\n            private $middleware;\n\n            /**\n             * @var RequestHandlerInterface\n             */\n            private $next;\n\n            public function __construct(callable $middleware, RequestHandlerInterface $next)\n            {\n                $this->middleware = $middleware;\n                $this->next = $next;\n            }\n\n            public function handle(ServerRequestInterface $request): ResponseInterface\n            {\n                /** @var ResponseInterface */\n                return ($this->middleware)($request, $this->next);\n            }\n        };\n\n        return $this;\n    }\n}\n"
  },
  {
    "path": "Slim/ResponseEmitter.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim;\n\nuse Psr\\Http\\Message\\ResponseInterface;\n\nuse function connection_status;\nuse function header;\nuse function headers_sent;\nuse function in_array;\nuse function min;\nuse function sprintf;\nuse function strlen;\nuse function strtolower;\n\nuse const CONNECTION_NORMAL;\n\nclass ResponseEmitter\n{\n    private int $responseChunkSize;\n\n    public function __construct(int $responseChunkSize = 4096)\n    {\n        $this->responseChunkSize = $responseChunkSize;\n    }\n\n    /**\n     * Send the response the client\n     */\n    public function emit(ResponseInterface $response): void\n    {\n        $isEmpty = $this->isResponseEmpty($response);\n        if (headers_sent() === false) {\n            $this->emitHeaders($response);\n\n            // Set the status _after_ the headers, because of PHP's \"helpful\" behavior with location headers.\n            // See https://github.com/slimphp/Slim/issues/1730\n\n            $this->emitStatusLine($response);\n        }\n\n        if (!$isEmpty) {\n            $this->emitBody($response);\n        }\n    }\n\n    /**\n     * Emit Response Headers\n     */\n    private function emitHeaders(ResponseInterface $response): void\n    {\n        foreach ($response->getHeaders() as $name => $values) {\n            $first = strtolower($name) !== 'set-cookie';\n            foreach ($values as $value) {\n                $header = sprintf('%s: %s', $name, $value);\n                header($header, $first);\n                $first = false;\n            }\n        }\n    }\n\n    /**\n     * Emit Status Line\n     */\n    private function emitStatusLine(ResponseInterface $response): void\n    {\n        $statusLine = sprintf(\n            'HTTP/%s %s %s',\n            $response->getProtocolVersion(),\n            $response->getStatusCode(),\n            $response->getReasonPhrase()\n        );\n        header($statusLine, true, $response->getStatusCode());\n    }\n\n    /**\n     * Emit Body\n     */\n    private function emitBody(ResponseInterface $response): void\n    {\n        $body = $response->getBody();\n        if ($body->isSeekable()) {\n            $body->rewind();\n        }\n\n        $amountToRead = (int) $response->getHeaderLine('Content-Length');\n        if (!$amountToRead) {\n            $amountToRead = $body->getSize();\n        }\n\n        if ($amountToRead) {\n            while ($amountToRead > 0 && !$body->eof()) {\n                $length = min($this->responseChunkSize, $amountToRead);\n                $data = $body->read($length);\n                echo $data;\n\n                $amountToRead -= strlen($data);\n\n                if (connection_status() !== CONNECTION_NORMAL) {\n                    break;\n                }\n            }\n        } else {\n            while (!$body->eof()) {\n                echo $body->read($this->responseChunkSize);\n                if (connection_status() !== CONNECTION_NORMAL) {\n                    break;\n                }\n            }\n        }\n    }\n\n    /**\n     * Asserts response body is empty or status code is 204, 205 or 304\n     */\n    public function isResponseEmpty(ResponseInterface $response): bool\n    {\n        if (in_array($response->getStatusCode(), [204, 205, 304], true)) {\n            return true;\n        }\n        $stream = $response->getBody();\n        $seekable = $stream->isSeekable();\n        if ($seekable) {\n            $stream->rewind();\n        }\n        return $seekable ? $stream->read(1) === '' : $stream->eof();\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/Dispatcher.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse FastRoute\\DataGenerator\\GroupCountBased;\nuse FastRoute\\RouteCollector as FastRouteCollector;\nuse FastRoute\\RouteParser\\Std;\nuse Slim\\Interfaces\\DispatcherInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\n\nclass Dispatcher implements DispatcherInterface\n{\n    private RouteCollectorInterface $routeCollector;\n\n    private ?FastRouteDispatcher $dispatcher = null;\n\n    public function __construct(RouteCollectorInterface $routeCollector)\n    {\n        $this->routeCollector = $routeCollector;\n    }\n\n    protected function createDispatcher(): FastRouteDispatcher\n    {\n        if ($this->dispatcher) {\n            return $this->dispatcher;\n        }\n\n        $routeDefinitionCallback = function (FastRouteCollector $r): void {\n            $basePath = $this->routeCollector->getBasePath();\n\n            foreach ($this->routeCollector->getRoutes() as $route) {\n                $r->addRoute($route->getMethods(), $basePath . $route->getPattern(), $route->getIdentifier());\n            }\n        };\n\n        $cacheFile = $this->routeCollector->getCacheFile();\n        if ($cacheFile) {\n            /** @var FastRouteDispatcher $dispatcher */\n            $dispatcher = \\FastRoute\\cachedDispatcher($routeDefinitionCallback, [\n                'dataGenerator' => GroupCountBased::class,\n                'dispatcher' => FastRouteDispatcher::class,\n                'routeParser' => new Std(),\n                'cacheFile' => $cacheFile,\n            ]);\n        } else {\n            /** @var FastRouteDispatcher $dispatcher */\n            $dispatcher = \\FastRoute\\simpleDispatcher($routeDefinitionCallback, [\n                'dataGenerator' => GroupCountBased::class,\n                'dispatcher' => FastRouteDispatcher::class,\n                'routeParser' => new Std(),\n            ]);\n        }\n\n        $this->dispatcher = $dispatcher;\n        return $this->dispatcher;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function dispatch(string $method, string $uri): RoutingResults\n    {\n        $dispatcher = $this->createDispatcher();\n        $results = $dispatcher->dispatch($method, $uri);\n        return new RoutingResults($this, $method, $uri, $results[0], $results[1], $results[2]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAllowedMethods(string $uri): array\n    {\n        $dispatcher = $this->createDispatcher();\n        return $dispatcher->getAllowedMethods($uri);\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/FastRouteDispatcher.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse FastRoute\\Dispatcher\\GroupCountBased;\n\nclass FastRouteDispatcher extends GroupCountBased\n{\n    /**\n     * @var string[][]\n     */\n    private array $allowedMethods = [];\n\n    /**\n     * @param string $httpMethod\n     * @param string $uri\n     *\n     * @return array{int, string|null, array<string, string>}\n     */\n    public function dispatch($httpMethod, $uri): array\n    {\n        $routingResults = $this->routingResults($httpMethod, $uri);\n        if ($routingResults[0] === self::FOUND) {\n            return $routingResults;\n        }\n\n        // For HEAD requests, attempt fallback to GET\n        if ($httpMethod === 'HEAD') {\n            $routingResults = $this->routingResults('GET', $uri);\n            if ($routingResults[0] === self::FOUND) {\n                return $routingResults;\n            }\n        }\n\n        // If nothing else matches, try fallback routes\n        $routingResults = $this->routingResults('*', $uri);\n        if ($routingResults[0] === self::FOUND) {\n            return $routingResults;\n        }\n\n        if (!empty($this->getAllowedMethods($uri))) {\n            return [self::METHOD_NOT_ALLOWED, null, []];\n        }\n\n        return [self::NOT_FOUND, null, []];\n    }\n\n    /**\n     * @param string $httpMethod\n     * @param string $uri\n     *\n     * @return array{int, string|null, array<string, string>}\n     */\n    private function routingResults(string $httpMethod, string $uri): array\n    {\n        if (isset($this->staticRouteMap[$httpMethod][$uri])) {\n            /** @var string $routeIdentifier */\n            $routeIdentifier = $this->staticRouteMap[$httpMethod][$uri];\n            return [self::FOUND, $routeIdentifier, []];\n        }\n\n        if (isset($this->variableRouteData[$httpMethod])) {\n            /** @var array{0: int, 1?: string, 2?: array<string, string>} $result */\n            $result = $this->dispatchVariableRoute($this->variableRouteData[$httpMethod], $uri);\n            if ($result[0] === self::FOUND) {\n                /** @var array{int, string, array<string, string>} $result */\n                return [self::FOUND, $result[1], $result[2]];\n            }\n        }\n\n        return [self::NOT_FOUND, null, []];\n    }\n\n    /**\n     * @param string $uri\n     *\n     * @return string[]\n     */\n    public function getAllowedMethods(string $uri): array\n    {\n        if (isset($this->allowedMethods[$uri])) {\n            return $this->allowedMethods[$uri];\n        }\n\n        $allowedMethods = [];\n        foreach ($this->staticRouteMap as $method => $uriMap) {\n            if (isset($uriMap[$uri])) {\n                $allowedMethods[$method] = true;\n            }\n        }\n\n        foreach ($this->variableRouteData as $method => $routeData) {\n            $result = $this->dispatchVariableRoute($routeData, $uri);\n            if ($result[0] === self::FOUND) {\n                $allowedMethods[$method] = true;\n            }\n        }\n\n        return $this->allowedMethods[$uri] = array_keys($allowedMethods);\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/Route.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Slim\\Handlers\\Strategies\\RequestHandler;\nuse Slim\\Handlers\\Strategies\\RequestResponse;\nuse Slim\\Interfaces\\AdvancedCallableResolverInterface;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\nuse Slim\\Interfaces\\RequestHandlerInvocationStrategyInterface;\nuse Slim\\Interfaces\\RouteGroupInterface;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\MiddlewareDispatcher;\n\nuse function array_key_exists;\nuse function array_replace;\nuse function array_reverse;\nuse function class_implements;\nuse function in_array;\nuse function is_array;\n\n/**\n * @api\n * @template TContainerInterface of (ContainerInterface|null)\n */\nclass Route implements RouteInterface, RequestHandlerInterface\n{\n    /**\n     * HTTP methods supported by this route\n     *\n     * @var string[]\n     */\n    protected array $methods = [];\n\n    /**\n     * Route identifier\n     */\n    protected string $identifier;\n\n    /**\n     * Route name\n     */\n    protected ?string $name = null;\n\n    /**\n     * Parent route groups\n     *\n     * @var RouteGroupInterface[]\n     */\n    protected array $groups;\n\n    protected InvocationStrategyInterface $invocationStrategy;\n\n    /**\n     * Route parameters\n     *\n     * @var array<string, string>\n     */\n    protected array $arguments = [];\n\n    /**\n     * Route arguments parameters\n     *\n     * @var array<string, string>\n     */\n    protected array $savedArguments = [];\n\n    /**\n     * Container\n     * @var TContainerInterface $container\n     */\n    protected ?ContainerInterface $container = null;\n\n    /** @var MiddlewareDispatcher<TContainerInterface> $middlewareDispatcher */\n    protected MiddlewareDispatcher $middlewareDispatcher;\n\n    /**\n     * Route callable\n     *\n     * @var callable|array{class-string, string}|string\n     */\n    protected $callable;\n\n    protected CallableResolverInterface $callableResolver;\n\n    protected ResponseFactoryInterface $responseFactory;\n\n    /**\n     * Route pattern\n     */\n    protected string $pattern;\n\n    protected bool $groupMiddlewareAppended = false;\n\n    /**\n     * @param string[] $methods The route HTTP methods\n     * @param string $pattern The route pattern\n     * @param callable|array{class-string, string}|string $callable The route callable\n     * @param ResponseFactoryInterface $responseFactory\n     * @param CallableResolverInterface $callableResolver\n     * @param TContainerInterface $container\n     * @param InvocationStrategyInterface|null $invocationStrategy\n     * @param RouteGroupInterface[] $groups The parent route groups\n     * @param int $identifier The route identifier\n     */\n    public function __construct(\n        array $methods,\n        string $pattern,\n        $callable,\n        ResponseFactoryInterface $responseFactory,\n        CallableResolverInterface $callableResolver,\n        ?ContainerInterface $container = null,\n        ?InvocationStrategyInterface $invocationStrategy = null,\n        array $groups = [],\n        int $identifier = 0\n    ) {\n        $this->methods = $methods;\n        $this->pattern = $pattern;\n        $this->callable = $callable;\n        $this->responseFactory = $responseFactory;\n        $this->callableResolver = $callableResolver;\n        $this->container = $container;\n        $this->invocationStrategy = $invocationStrategy ?? new RequestResponse();\n        $this->groups = $groups;\n        $this->identifier = 'route' . $identifier;\n        $this->middlewareDispatcher = new MiddlewareDispatcher($this, $callableResolver, $container);\n    }\n\n    public function getCallableResolver(): CallableResolverInterface\n    {\n        return $this->callableResolver;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getInvocationStrategy(): InvocationStrategyInterface\n    {\n        return $this->invocationStrategy;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setInvocationStrategy(InvocationStrategyInterface $invocationStrategy): RouteInterface\n    {\n        $this->invocationStrategy = $invocationStrategy;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getMethods(): array\n    {\n        return $this->methods;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPattern(): string\n    {\n        return $this->pattern;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setPattern(string $pattern): RouteInterface\n    {\n        $this->pattern = $pattern;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getCallable()\n    {\n        return $this->callable;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setCallable($callable): RouteInterface\n    {\n        $this->callable = $callable;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getName(): ?string\n    {\n        return $this->name;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setName(string $name): RouteInterface\n    {\n        $this->name = $name;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getIdentifier(): string\n    {\n        return $this->identifier;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getArgument(string $name, ?string $default = null): ?string\n    {\n        if (array_key_exists($name, $this->arguments)) {\n            return $this->arguments[$name];\n        }\n        return $default;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getArguments(): array\n    {\n        return $this->arguments;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setArguments(array $arguments, bool $includeInSavedArguments = true): RouteInterface\n    {\n        if ($includeInSavedArguments) {\n            $this->savedArguments = $arguments;\n        }\n\n        $this->arguments = $arguments;\n        return $this;\n    }\n\n    /**\n     * @return RouteGroupInterface[]\n     */\n    public function getGroups(): array\n    {\n        return $this->groups;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function add($middleware): RouteInterface\n    {\n        $this->middlewareDispatcher->add($middleware);\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addMiddleware(MiddlewareInterface $middleware): RouteInterface\n    {\n        $this->middlewareDispatcher->addMiddleware($middleware);\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function prepare(array $arguments): RouteInterface\n    {\n        $this->arguments = array_replace($this->savedArguments, $arguments);\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setArgument(string $name, string $value, bool $includeInSavedArguments = true): RouteInterface\n    {\n        if ($includeInSavedArguments) {\n            $this->savedArguments[$name] = $value;\n        }\n\n        $this->arguments[$name] = $value;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function run(ServerRequestInterface $request): ResponseInterface\n    {\n        if (!$this->groupMiddlewareAppended) {\n            $this->appendGroupMiddlewareToRoute();\n        }\n\n        return $this->middlewareDispatcher->handle($request);\n    }\n\n    /**\n     * @return void\n     */\n    protected function appendGroupMiddlewareToRoute(): void\n    {\n        $inner = $this->middlewareDispatcher;\n        $this->middlewareDispatcher = new MiddlewareDispatcher($inner, $this->callableResolver, $this->container);\n\n        foreach (array_reverse($this->groups) as $group) {\n            $group->appendMiddlewareToDispatcher($this->middlewareDispatcher);\n        }\n\n        $this->groupMiddlewareAppended = true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n        if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {\n            $callable = $this->callableResolver->resolveRoute($this->callable);\n        } else {\n            $callable = $this->callableResolver->resolve($this->callable);\n        }\n        $strategy = $this->invocationStrategy;\n\n        $strategyImplements = class_implements($strategy);\n\n        if (\n            is_array($callable)\n            && $callable[0] instanceof RequestHandlerInterface\n            && !in_array(RequestHandlerInvocationStrategyInterface::class, $strategyImplements)\n        ) {\n            $strategy = new RequestHandler();\n        }\n\n        $response = $this->responseFactory->createResponse();\n        return $strategy($callable, $request, $response, $this->arguments);\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteCollector.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse RuntimeException;\nuse Slim\\Handlers\\Strategies\\RequestResponse;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteCollectorProxyInterface;\nuse Slim\\Interfaces\\RouteGroupInterface;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\n\nuse function array_pop;\nuse function dirname;\nuse function file_exists;\nuse function is_readable;\nuse function is_writable;\nuse function sprintf;\n\n/**\n * RouteCollector is used to collect routes and route groups\n * as well as generate paths and URLs relative to its environment\n * @template TContainerInterface of (ContainerInterface|null)\n */\nclass RouteCollector implements RouteCollectorInterface\n{\n    protected RouteParserInterface $routeParser;\n\n    protected CallableResolverInterface $callableResolver;\n\n    protected ?ContainerInterface $container = null;\n\n    protected InvocationStrategyInterface $defaultInvocationStrategy;\n\n    /**\n     * Base path used in pathFor()\n     */\n    protected string $basePath = '';\n\n    /**\n     * Path to fast route cache file. Set to null to disable route caching\n     */\n    protected ?string $cacheFile = null;\n\n    /**\n     * Routes\n     *\n     * @var RouteInterface[]\n     */\n    protected array $routes = [];\n\n    /**\n     * Routes indexed by name\n     *\n     * @var RouteInterface[]\n     */\n    protected array $routesByName = [];\n\n    /**\n     * Route groups\n     *\n     * @var RouteGroupInterface[]\n     */\n    protected array $routeGroups = [];\n\n    /**\n     * Route counter incrementer\n     */\n    protected int $routeCounter = 0;\n\n    protected ResponseFactoryInterface $responseFactory;\n\n    /**\n     * @param TContainerInterface $container\n     */\n    public function __construct(\n        ResponseFactoryInterface $responseFactory,\n        CallableResolverInterface $callableResolver,\n        ?ContainerInterface $container = null,\n        ?InvocationStrategyInterface $defaultInvocationStrategy = null,\n        ?RouteParserInterface $routeParser = null,\n        ?string $cacheFile = null\n    ) {\n        $this->responseFactory = $responseFactory;\n        $this->callableResolver = $callableResolver;\n        $this->container = $container;\n        $this->defaultInvocationStrategy = $defaultInvocationStrategy ?? new RequestResponse();\n        $this->routeParser = $routeParser ?? new RouteParser($this);\n\n        if ($cacheFile) {\n            $this->setCacheFile($cacheFile);\n        }\n    }\n\n    public function getRouteParser(): RouteParserInterface\n    {\n        return $this->routeParser;\n    }\n\n    /**\n     * Get default route invocation strategy\n     */\n    public function getDefaultInvocationStrategy(): InvocationStrategyInterface\n    {\n        return $this->defaultInvocationStrategy;\n    }\n\n    public function setDefaultInvocationStrategy(InvocationStrategyInterface $strategy): RouteCollectorInterface\n    {\n        $this->defaultInvocationStrategy = $strategy;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getCacheFile(): ?string\n    {\n        return $this->cacheFile;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setCacheFile(string $cacheFile): RouteCollectorInterface\n    {\n        if (file_exists($cacheFile) && !is_readable($cacheFile)) {\n            throw new RuntimeException(\n                sprintf('Route collector cache file `%s` is not readable', $cacheFile)\n            );\n        }\n\n        if (!file_exists($cacheFile) && !is_writable(dirname($cacheFile))) {\n            throw new RuntimeException(\n                sprintf('Route collector cache file directory `%s` is not writable', dirname($cacheFile))\n            );\n        }\n\n        $this->cacheFile = $cacheFile;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getBasePath(): string\n    {\n        return $this->basePath;\n    }\n\n    /**\n     * Set the base path used in urlFor()\n     */\n    public function setBasePath(string $basePath): RouteCollectorInterface\n    {\n        $this->basePath = $basePath;\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRoutes(): array\n    {\n        return $this->routes;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeNamedRoute(string $name): RouteCollectorInterface\n    {\n        $route = $this->getNamedRoute($name);\n\n        /** @psalm-suppress PossiblyNullArrayOffset */\n        unset($this->routesByName[$route->getName()], $this->routes[$route->getIdentifier()]);\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getNamedRoute(string $name): RouteInterface\n    {\n        if (isset($this->routesByName[$name])) {\n            $route = $this->routesByName[$name];\n            if ($route->getName() === $name) {\n                return $route;\n            }\n\n            unset($this->routesByName[$name]);\n        }\n\n        foreach ($this->routes as $route) {\n            if ($name === $route->getName()) {\n                $this->routesByName[$name] = $route;\n                return $route;\n            }\n        }\n\n        throw new RuntimeException('Named route does not exist for name: ' . $name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function lookupRoute(string $identifier): RouteInterface\n    {\n        if (!isset($this->routes[$identifier])) {\n            throw new RuntimeException('Route not found, looks like your route cache is stale.');\n        }\n        return $this->routes[$identifier];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function group(string $pattern, $callable): RouteGroupInterface\n    {\n        $routeGroup = $this->createGroup($pattern, $callable);\n        $this->routeGroups[] = $routeGroup;\n\n        $routeGroup->collectRoutes();\n        array_pop($this->routeGroups);\n\n        return $routeGroup;\n    }\n\n    /**\n     * @param string|callable $callable\n     */\n    protected function createGroup(string $pattern, $callable): RouteGroupInterface\n    {\n        $routeCollectorProxy = $this->createProxy($pattern);\n        return new RouteGroup($pattern, $callable, $this->callableResolver, $routeCollectorProxy);\n    }\n\n    /**\n     * @return RouteCollectorProxyInterface<TContainerInterface>\n     */\n    protected function createProxy(string $pattern): RouteCollectorProxyInterface\n    {\n        /** @var RouteCollectorProxy<TContainerInterface> */\n        return new RouteCollectorProxy(\n            $this->responseFactory,\n            $this->callableResolver,\n            $this->container,\n            $this,\n            $pattern\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function map(array $methods, string $pattern, $handler): RouteInterface\n    {\n        $route = $this->createRoute($methods, $pattern, $handler);\n        $this->routes[$route->getIdentifier()] = $route;\n\n        $routeName = $route->getName();\n        if ($routeName !== null && !isset($this->routesByName[$routeName])) {\n            $this->routesByName[$routeName] = $route;\n        }\n\n        $this->routeCounter++;\n\n        return $route;\n    }\n\n    /**\n     * @param string[] $methods\n     * @param callable|array{class-string, string}|string $callable\n     */\n    protected function createRoute(array $methods, string $pattern, $callable): RouteInterface\n    {\n        return new Route(\n            $methods,\n            $pattern,\n            $callable,\n            $this->responseFactory,\n            $this->callableResolver,\n            $this->container,\n            $this->defaultInvocationStrategy,\n            $this->routeGroups,\n            $this->routeCounter\n        );\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteCollectorProxy.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteCollectorProxyInterface;\nuse Slim\\Interfaces\\RouteGroupInterface;\nuse Slim\\Interfaces\\RouteInterface;\n\n/**\n * @template TContainerInterface of (ContainerInterface|null)\n * @template-implements RouteCollectorProxyInterface<TContainerInterface>\n */\nclass RouteCollectorProxy implements RouteCollectorProxyInterface\n{\n    protected ResponseFactoryInterface $responseFactory;\n\n    protected CallableResolverInterface $callableResolver;\n\n    /** @var TContainerInterface */\n    protected ?ContainerInterface $container = null;\n\n    protected RouteCollectorInterface $routeCollector;\n\n    protected string $groupPattern;\n\n    /**\n     * @param TContainerInterface $container\n     */\n    public function __construct(\n        ResponseFactoryInterface $responseFactory,\n        CallableResolverInterface $callableResolver,\n        ?ContainerInterface $container = null,\n        ?RouteCollectorInterface $routeCollector = null,\n        string $groupPattern = ''\n    ) {\n        $this->responseFactory = $responseFactory;\n        $this->callableResolver = $callableResolver;\n        $this->container = $container;\n        $this->routeCollector = $routeCollector ?? new RouteCollector($responseFactory, $callableResolver, $container);\n        $this->groupPattern = $groupPattern;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getResponseFactory(): ResponseFactoryInterface\n    {\n        return $this->responseFactory;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getCallableResolver(): CallableResolverInterface\n    {\n        return $this->callableResolver;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return TContainerInterface\n     */\n    public function getContainer(): ?ContainerInterface\n    {\n        return $this->container;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRouteCollector(): RouteCollectorInterface\n    {\n        return $this->routeCollector;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getBasePath(): string\n    {\n        return $this->routeCollector->getBasePath();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setBasePath(string $basePath): RouteCollectorProxyInterface\n    {\n        $this->routeCollector->setBasePath($basePath);\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function get(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['GET'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function post(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['POST'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function put(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['PUT'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function patch(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['PATCH'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function delete(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['DELETE'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['OPTIONS'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function any(string $pattern, $callable): RouteInterface\n    {\n        return $this->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function map(array $methods, string $pattern, $callable): RouteInterface\n    {\n        $pattern = $this->groupPattern . $pattern;\n\n        return $this->routeCollector->map($methods, $pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function group(string $pattern, $callable): RouteGroupInterface\n    {\n        $pattern = $this->groupPattern . $pattern;\n\n        return $this->routeCollector->group($pattern, $callable);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function redirect(string $from, $to, int $status = 302): RouteInterface\n    {\n        $responseFactory = $this->responseFactory;\n\n        $handler = function () use ($to, $status, $responseFactory) {\n            $response = $responseFactory->createResponse($status);\n            return $response->withHeader('Location', (string) $to);\n        };\n\n        return $this->get($from, $handler);\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteContext.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\n\n/** @api */\nfinal class RouteContext\n{\n    public const ROUTE = '__route__';\n\n    public const ROUTE_PARSER = '__routeParser__';\n\n    public const ROUTING_RESULTS = '__routingResults__';\n\n    public const BASE_PATH = '__basePath__';\n\n    public static function fromRequest(ServerRequestInterface $serverRequest): self\n    {\n        $route = $serverRequest->getAttribute(self::ROUTE);\n        $routeParser = $serverRequest->getAttribute(self::ROUTE_PARSER);\n        $routingResults = $serverRequest->getAttribute(self::ROUTING_RESULTS);\n        $basePath = $serverRequest->getAttribute(self::BASE_PATH);\n\n        if ($routeParser === null || $routingResults === null) {\n            throw new RuntimeException('Cannot create RouteContext before routing has been completed');\n        }\n\n        /** @var RouteInterface|null $route */\n        /** @var RouteParserInterface $routeParser */\n        /** @var RoutingResults $routingResults */\n        /** @var string|null $basePath */\n        return new self($route, $routeParser, $routingResults, $basePath);\n    }\n\n    private ?RouteInterface $route;\n\n    private RouteParserInterface $routeParser;\n\n    private RoutingResults $routingResults;\n\n    private ?string $basePath;\n\n    private function __construct(\n        ?RouteInterface $route,\n        RouteParserInterface $routeParser,\n        RoutingResults $routingResults,\n        ?string $basePath = null\n    ) {\n        $this->route = $route;\n        $this->routeParser = $routeParser;\n        $this->routingResults = $routingResults;\n        $this->basePath = $basePath;\n    }\n\n    public function getRoute(): ?RouteInterface\n    {\n        return $this->route;\n    }\n\n    public function getRouteParser(): RouteParserInterface\n    {\n        return $this->routeParser;\n    }\n\n    public function getRoutingResults(): RoutingResults\n    {\n        return $this->routingResults;\n    }\n\n    public function getBasePath(): string\n    {\n        if ($this->basePath === null) {\n            throw new RuntimeException('No base path defined.');\n        }\n        return $this->basePath;\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteGroup.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Slim\\Interfaces\\AdvancedCallableResolverInterface;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\RouteCollectorProxyInterface;\nuse Slim\\Interfaces\\RouteGroupInterface;\nuse Slim\\MiddlewareDispatcher;\n\nclass RouteGroup implements RouteGroupInterface\n{\n    /**\n     * @var callable|string\n     */\n    protected $callable;\n\n    protected CallableResolverInterface $callableResolver;\n\n    /**\n     * @var RouteCollectorProxyInterface<\\Psr\\Container\\ContainerInterface|null>\n     */\n    protected RouteCollectorProxyInterface $routeCollectorProxy;\n\n    /**\n     * @var MiddlewareInterface[]|string[]|callable[]\n     */\n    protected array $middleware = [];\n\n    protected string $pattern;\n\n    /**\n     * @param callable|string              $callable\n     * @param RouteCollectorProxyInterface<\\Psr\\Container\\ContainerInterface|null> $routeCollectorProxy\n     */\n    public function __construct(\n        string $pattern,\n        $callable,\n        CallableResolverInterface $callableResolver,\n        RouteCollectorProxyInterface $routeCollectorProxy\n    ) {\n        $this->pattern = $pattern;\n        $this->callable = $callable;\n        $this->callableResolver = $callableResolver;\n        $this->routeCollectorProxy = $routeCollectorProxy;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function collectRoutes(): RouteGroupInterface\n    {\n        if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {\n            $callable = $this->callableResolver->resolveRoute($this->callable);\n        } else {\n            $callable = $this->callableResolver->resolve($this->callable);\n        }\n        $callable($this->routeCollectorProxy);\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function add($middleware): RouteGroupInterface\n    {\n        $this->middleware[] = $middleware;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addMiddleware(MiddlewareInterface $middleware): RouteGroupInterface\n    {\n        $this->middleware[] = $middleware;\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @param MiddlewareDispatcher<\\Psr\\Container\\ContainerInterface|null> $dispatcher\n     */\n    public function appendMiddlewareToDispatcher(MiddlewareDispatcher $dispatcher): RouteGroupInterface\n    {\n        foreach ($this->middleware as $middleware) {\n            $dispatcher->add($middleware);\n        }\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPattern(): string\n    {\n        return $this->pattern;\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteParser.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse FastRoute\\RouteParser\\Std;\nuse InvalidArgumentException;\nuse Psr\\Http\\Message\\UriInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\n\nuse function array_key_exists;\nuse function array_reverse;\nuse function http_build_query;\nuse function implode;\nuse function is_string;\n\nclass RouteParser implements RouteParserInterface\n{\n    private RouteCollectorInterface $routeCollector;\n\n    private Std $routeParser;\n\n    public function __construct(RouteCollectorInterface $routeCollector)\n    {\n        $this->routeCollector = $routeCollector;\n        $this->routeParser = new Std();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function relativeUrlFor(string $routeName, array $data = [], array $queryParams = []): string\n    {\n        $route = $this->routeCollector->getNamedRoute($routeName);\n        $pattern = $route->getPattern();\n\n        $segments = [];\n        $segmentName = '';\n\n        /*\n         * $routes is an associative array of expressions representing a route as multiple segments\n         * There is an expression for each optional parameter plus one without the optional parameters\n         * The most specific is last, hence why we reverse the array before iterating over it\n         */\n        $expressions = array_reverse($this->routeParser->parse($pattern));\n        foreach ($expressions as $expression) {\n            foreach ($expression as $segment) {\n                /*\n                 * Each $segment is either a string or an array of strings\n                 * containing optional parameters of an expression\n                 */\n                if (is_string($segment)) {\n                    $segments[] = $segment;\n                    continue;\n                }\n\n                /** @var string[] $segment */\n                /*\n                 * If we don't have a data element for this segment in the provided $data\n                 * we cancel testing to move onto the next expression with a less specific item\n                 */\n                if (!array_key_exists($segment[0], $data)) {\n                    $segments = [];\n                    $segmentName = $segment[0];\n                    break;\n                }\n\n                $segments[] = $data[$segment[0]];\n            }\n\n            /*\n             * If we get to this logic block we have found all the parameters\n             * for the provided $data which means we don't need to continue testing\n             * less specific expressions\n             */\n            if (!empty($segments)) {\n                break;\n            }\n        }\n\n        if (empty($segments)) {\n            throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);\n        }\n\n        $url = implode('', $segments);\n        if ($queryParams) {\n            $url .= '?' . http_build_query($queryParams);\n        }\n\n        return $url;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function urlFor(string $routeName, array $data = [], array $queryParams = []): string\n    {\n        $basePath = $this->routeCollector->getBasePath();\n        $url = $this->relativeUrlFor($routeName, $data, $queryParams);\n\n        if ($basePath) {\n            $url = $basePath . $url;\n        }\n\n        return $url;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string\n    {\n        $path = $this->urlFor($routeName, $data, $queryParams);\n        $scheme = $uri->getScheme();\n        $authority = $uri->getAuthority();\n        $protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : '');\n        return $protocol . $path;\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteResolver.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse RuntimeException;\nuse Slim\\Interfaces\\DispatcherInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\n\nuse function rawurldecode;\n\n/**\n * RouteResolver instantiates the FastRoute dispatcher\n * and computes the routing results of a given URI and request method\n */\nclass RouteResolver implements RouteResolverInterface\n{\n    protected RouteCollectorInterface $routeCollector;\n\n    private DispatcherInterface $dispatcher;\n\n    public function __construct(RouteCollectorInterface $routeCollector, ?DispatcherInterface $dispatcher = null)\n    {\n        $this->routeCollector = $routeCollector;\n        $this->dispatcher = $dispatcher ?? new Dispatcher($routeCollector);\n    }\n\n    /**\n     * @param string $uri Should be $request->getUri()->getPath()\n     */\n    public function computeRoutingResults(string $uri, string $method): RoutingResults\n    {\n        $uri = rawurldecode($uri);\n        if ($uri === '' || $uri[0] !== '/') {\n            $uri = '/' . $uri;\n        }\n        return $this->dispatcher->dispatch($method, $uri);\n    }\n\n    /**\n     * @throws RuntimeException\n     */\n    public function resolveRoute(string $identifier): RouteInterface\n    {\n        return $this->routeCollector->lookupRoute($identifier);\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RouteRunner.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Interfaces\\RouteCollectorProxyInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\nuse Slim\\Middleware\\RoutingMiddleware;\n\nclass RouteRunner implements RequestHandlerInterface\n{\n    private RouteResolverInterface $routeResolver;\n\n    private RouteParserInterface $routeParser;\n\n    /**\n     * @var RouteCollectorProxyInterface<\\Psr\\Container\\ContainerInterface|null>\n     */\n    private ?RouteCollectorProxyInterface $routeCollectorProxy;\n\n    /**\n     * @param RouteCollectorProxyInterface<\\Psr\\Container\\ContainerInterface|null> $routeCollectorProxy\n     */\n    public function __construct(\n        RouteResolverInterface $routeResolver,\n        RouteParserInterface $routeParser,\n        ?RouteCollectorProxyInterface $routeCollectorProxy = null\n    ) {\n        $this->routeResolver = $routeResolver;\n        $this->routeParser = $routeParser;\n        $this->routeCollectorProxy = $routeCollectorProxy;\n    }\n\n    /**\n     * This request handler is instantiated automatically in App::__construct()\n     * It is at the very tip of the middleware queue meaning it will be executed\n     * last and it detects whether or not routing has been performed in the user\n     * defined middleware stack. In the event that the user did not perform routing\n     * it is done here\n     *\n     * @throws HttpNotFoundException\n     * @throws HttpMethodNotAllowedException\n     */\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n        // If routing hasn't been done, then do it now so we can dispatch\n        if ($request->getAttribute(RouteContext::ROUTING_RESULTS) === null) {\n            $routingMiddleware = new RoutingMiddleware($this->routeResolver, $this->routeParser);\n            $request = $routingMiddleware->performRouting($request);\n        }\n\n        if ($this->routeCollectorProxy !== null) {\n            $request = $request->withAttribute(\n                RouteContext::BASE_PATH,\n                $this->routeCollectorProxy->getBasePath()\n            );\n        }\n\n        /** @var Route<\\Psr\\Container\\ContainerInterface|null> $route */\n        $route = $request->getAttribute(RouteContext::ROUTE);\n        return $route->run($request);\n    }\n}\n"
  },
  {
    "path": "Slim/Routing/RoutingResults.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Routing;\n\nuse Slim\\Interfaces\\DispatcherInterface;\n\nuse function rawurldecode;\n\n/** @api */\nclass RoutingResults\n{\n    public const NOT_FOUND = 0;\n    public const FOUND = 1;\n    public const METHOD_NOT_ALLOWED = 2;\n\n    protected DispatcherInterface $dispatcher;\n\n    protected string $method;\n\n    protected string $uri;\n\n    /**\n     * The status is one of the constants shown above\n     * NOT_FOUND = 0\n     * FOUND = 1\n     * METHOD_NOT_ALLOWED = 2\n     */\n    protected int $routeStatus;\n\n    protected ?string $routeIdentifier = null;\n\n    /**\n     * @var array<string, string>\n     */\n    protected array $routeArguments;\n\n    /**\n     * @param array<string, string> $routeArguments\n     */\n    public function __construct(\n        DispatcherInterface $dispatcher,\n        string $method,\n        string $uri,\n        int $routeStatus,\n        ?string $routeIdentifier = null,\n        array $routeArguments = []\n    ) {\n        $this->dispatcher = $dispatcher;\n        $this->method = $method;\n        $this->uri = $uri;\n        $this->routeStatus = $routeStatus;\n        $this->routeIdentifier = $routeIdentifier;\n        $this->routeArguments = $routeArguments;\n    }\n\n    public function getDispatcher(): DispatcherInterface\n    {\n        return $this->dispatcher;\n    }\n\n    public function getMethod(): string\n    {\n        return $this->method;\n    }\n\n    public function getUri(): string\n    {\n        return $this->uri;\n    }\n\n    public function getRouteStatus(): int\n    {\n        return $this->routeStatus;\n    }\n\n    public function getRouteIdentifier(): ?string\n    {\n        return $this->routeIdentifier;\n    }\n\n    /**\n     * @return array<string, string>\n     */\n    public function getRouteArguments(bool $urlDecode = true): array\n    {\n        if (!$urlDecode) {\n            return $this->routeArguments;\n        }\n\n        $routeArguments = [];\n        foreach ($this->routeArguments as $key => $value) {\n            $routeArguments[$key] = rawurldecode($value);\n        }\n\n        return $routeArguments;\n    }\n\n    /**\n     * @return string[]\n     */\n    public function getAllowedMethods(): array\n    {\n        return $this->dispatcher->getAllowedMethods($this->uri);\n    }\n}\n"
  },
  {
    "path": "UPGRADING.md",
    "content": "# How to upgrade\n\n* [2654] - `RouteParser::pathFor()` and `RouteParser::relativePathFor()` are deprecated. Use `RouteParser::urlFor()` and `RouteParser::relativeUrlFor()`\n* [2638] - `RouteCollector::pathFor()` is now deprecated. Use `RouteParser::urlFor()`\n* [2622] - `Router` has been removed. It is now split into `RouteCollector`, `RouteRunner` and `RouteParser`\n* [2555] - PSR-15 Middleware support was implemented at the cost of Double-Pass middleware being deprecated.\n* [2529] - Slim no longer ships with its own PSR-7 implementation you will need to provide your own before you can create/run an app.\n* [2507] - Method names are now case sensitive when using `App::map()`.\n* [2404] - Slim 4 requires PHP 7.1 or higher\n* [2398] - Error handling was extracted into its own middleware. Add `RoutingMiddleware` to your middleware pipeline to handle errors by default. See PR for more information.\n* [2329] - If you were overriding the HTTP method using either the custom header or the body param, you need to add the `Middleware\\MethodOverrideMiddleware` middleware to be able to override the method like before.\n* [2290] - Slim no longer ships with `Pimple` as container dependency so you need to supply your own. `App::__call()` has been deprecated.\n* [2288] - If you were using `determineRouteBeforeAppMiddleware`, you need to add the `Middleware\\RoutingMiddleware` middleware to your application just before your call `run()` to maintain the previous behaviour.\n* [2254] - You need to add the `Middleware\\ContentLengthMiddleware` middleware if you want Slim to add the Content-Length header this automatically.\n* [2166] - You need to add the `Middleware\\OutputBuffering` middleware to capture echo'd or var_dump'd output from your code.\n\n[2654]: https://github.com/slimphp/Slim/pull/2654\n[2638]: https://github.com/slimphp/Slim/pull/2638\n[2622]: https://github.com/slimphp/Slim/pull/2622\n[2555]: https://github.com/slimphp/Slim/pull/2555\n[2529]: https://github.com/slimphp/Slim/pull/2529\n[2507]: https://github.com/slimphp/Slim/pull/2507\n[2496]: https://github.com/slimphp/Slim/pull/2496\n[2404]: https://github.com/slimphp/Slim/pull/2404\n[2398]: https://github.com/slimphp/Slim/pull/2398\n[2329]: https://github.com/slimphp/Slim/pull/2329\n[2290]: https://github.com/slimphp/Slim/pull/2290\n[2288]: https://github.com/slimphp/Slim/pull/2288\n[2254]: https://github.com/slimphp/Slim/pull/2254\n[2166]: https://github.com/slimphp/Slim/pull/2166\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"slim/slim\",\n    \"type\": \"library\",\n    \"description\": \"Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs\",\n    \"keywords\": [\"framework\",\"micro\",\"api\",\"router\"],\n    \"homepage\": \"https://www.slimframework.com\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Josh Lockhart\",\n            \"email\": \"hello@joshlockhart.com\",\n            \"homepage\": \"https://joshlockhart.com\"\n        },\n        {\n            \"name\": \"Andrew Smith\",\n            \"email\": \"a.smith@silentworks.co.uk\",\n            \"homepage\": \"https://silentworks.co.uk\"\n        },\n        {\n            \"name\": \"Rob Allen\",\n            \"email\": \"rob@akrabat.com\",\n            \"homepage\": \"https://akrabat.com\"\n        },\n        {\n            \"name\": \"Pierre Berube\",\n            \"email\": \"pierre@lgse.com\",\n            \"homepage\": \"https://www.lgse.com\"\n        },\n        {\n            \"name\": \"Gabriel Manricks\",\n            \"email\": \"gmanricks@me.com\",\n            \"homepage\": \"http://gabrielmanricks.com\"\n        }\n    ],\n    \"support\": {\n        \"docs\": \"https://www.slimframework.com/docs/v4/\",\n        \"forum\": \"https://discourse.slimframework.com/\",\n        \"irc\": \"irc://irc.freenode.net:6667/slimphp\",\n        \"issues\": \"https://github.com/slimphp/Slim/issues\",\n        \"rss\": \"https://www.slimframework.com/blog/feed.rss\",\n        \"slack\": \"https://slimphp.slack.com/\",\n        \"source\": \"https://github.com/slimphp/Slim\",\n        \"wiki\": \"https://github.com/slimphp/Slim/wiki\"\n    },\n    \"require\": {\n        \"php\": \"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0\",\n        \"ext-json\": \"*\",\n        \"nikic/fast-route\": \"^1.3\",\n        \"psr/container\": \"^1.0 || ^2.0\",\n        \"psr/http-factory\": \"^1.1\",\n        \"psr/http-message\": \"^1.1 || ^2.0\",\n        \"psr/http-server-handler\": \"^1.0\",\n        \"psr/http-server-middleware\": \"^1.0\",\n        \"psr/log\": \"^1.1 || ^2.0 || ^3.0\"\n    },\n    \"require-dev\": {\n        \"ext-simplexml\": \"*\",\n        \"adriansuter/php-autoload-override\": \"^1.4 || ^2\",\n        \"guzzlehttp/psr7\": \"^2.6\",\n        \"httpsoft/http-message\": \"^1.1\",\n        \"httpsoft/http-server-request\": \"^1.1\",\n        \"laminas/laminas-diactoros\": \"^2.17 || ^3\",\n        \"nyholm/psr7\": \"^1.8\",\n        \"nyholm/psr7-server\": \"^1.1\",\n        \"phpspec/prophecy\": \"^1.19\",\n        \"phpspec/prophecy-phpunit\": \"^2.1\",\n        \"phpstan/phpstan\": \"^1 || ^2\",\n        \"phpunit/phpunit\": \"^9.6 || ^10 || ^11 || ^12\",\n        \"slim/http\": \"^1.3\",\n        \"slim/psr7\": \"^1.6\",\n        \"squizlabs/php_codesniffer\": \"^3.10\",\n        \"vimeo/psalm\": \"^5 || ^6\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Slim\\\\\": \"Slim\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Slim\\\\Tests\\\\\": \"tests\"\n        }\n    },\n    \"scripts\": {\n        \"test\": [\n            \"@phpunit\",\n            \"@phpcs\",\n            \"@phpstan\",\n            \"@psalm\"\n        ],\n        \"phpunit\": \"phpunit\",\n        \"phpcs\": \"phpcs\",\n        \"phpstan\": \"phpstan --memory-limit=-1\",\n        \"psalm\": \"psalm --no-cache\"\n    },\n    \"suggest\": {\n        \"ext-simplexml\": \"Needed to support XML format in BodyParsingMiddleware\",\n        \"ext-xml\": \"Needed to support XML format in BodyParsingMiddleware\",\n        \"slim/psr7\": \"Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information.\",\n        \"php-di/php-di\": \"PHP-DI is the recommended container library to be used with Slim\"\n    },\n    \"config\": {\n        \"sort-packages\": true\n    }\n}\n"
  },
  {
    "path": "phpcs.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<ruleset name=\"Slim coding standard\">\n    <description>Slim coding standard</description>\n\n    <!-- display progress -->\n    <arg value=\"p\"/>\n    <!-- use colors in output -->\n    <arg name=\"colors\"/>\n\n    <!-- inherit rules from: -->\n    <rule ref=\"PSR12\"/>\n    <rule ref=\"Generic.Arrays.DisallowLongArraySyntax\"/>\n\n    <!-- Paths to check -->\n    <file>Slim</file>\n    <file>tests</file>\n</ruleset>\n"
  },
  {
    "path": "phpstan.neon.dist",
    "content": "parameters:\n    level: max\n    paths:\n        - Slim\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/12.3/phpunit.xsd\"\n         beStrictAboutChangesToGlobalState=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         colors=\"true\"\n         bootstrap=\"tests/bootstrap.php\"\n         executionOrder=\"random\"\n         cacheDirectory=\".phpunit.cache\"\n>\n    <testsuites>\n        <testsuite name=\"Slim Test Suite\">\n            <directory>tests</directory>\n            <exclude>tests/Mocks</exclude>\n        </testsuite>\n    </testsuites>\n    <coverage>\n        <report>\n            <html outputDirectory=\"coverage\" lowUpperBound=\"20\" highLowerBound=\"50\"/>\n        </report>\n    </coverage>\n    <source>\n        <include>\n            <directory>Slim</directory>\n        </include>\n    </source>\n</phpunit>\n"
  },
  {
    "path": "psalm.xml",
    "content": "<?xml version=\"1.0\"?>\n<psalm\n    errorLevel=\"3\"\n    resolveFromConfigFile=\"true\"\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns=\"https://getpsalm.org/schema/config\"\n    xsi:schemaLocation=\"https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd\"\n    findUnusedBaselineEntry=\"true\"\n    findUnusedCode=\"true\"\n>\n    <projectFiles>\n        <directory name=\"Slim\" />\n        <ignoreFiles>\n            <directory name=\"vendor\" />\n        </ignoreFiles>\n    </projectFiles>\n</psalm>\n"
  },
  {
    "path": "tests/AppTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests;\n\nuse Prophecy\\Argument;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\StreamInterface;\nuse Psr\\Http\\Message\\UriInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Psr\\Log\\LoggerInterface;\nuse ReflectionClass;\nuse ReflectionProperty;\nuse RuntimeException;\nuse Slim\\App;\nuse Slim\\CallableResolver;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Handlers\\Strategies\\RequestResponseArgs;\nuse Slim\\Handlers\\Strategies\\RequestResponseNamedArgs;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\MiddlewareDispatcherInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteCollectorProxyInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\nuse Slim\\Middleware\\BodyParsingMiddleware;\nuse Slim\\Middleware\\ErrorMiddleware;\nuse Slim\\Middleware\\RoutingMiddleware;\nuse Slim\\MiddlewareDispatcher;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RouteCollectorProxy;\nuse Slim\\Routing\\RouteContext;\nuse Slim\\Tests\\Mocks\\MockAction;\nuse stdClass;\n\nuse function array_key_exists;\nuse function array_shift;\nuse function count;\nuse function ini_set;\nuse function json_encode;\nuse function strtolower;\nuse function sys_get_temp_dir;\nuse function tempnam;\n\nclass AppTest extends TestCase\n{\n    public static function setupBeforeClass(): void\n    {\n        ini_set('error_log', tempnam(sys_get_temp_dir(), 'slim'));\n    }\n\n    public function testDoesNotUseContainerAsServiceLocator(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n\n        $containerProphecy->has(Argument::type('string'))->shouldNotHaveBeenCalled();\n        $containerProphecy->get(Argument::type('string'))->shouldNotHaveBeenCalled();\n    }\n\n    /********************************************************************************\n     * Getter methods\n     *******************************************************************************/\n\n    public function testGetContainer(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n\n        $this->assertSame($containerProphecy->reveal(), $app->getContainer());\n    }\n\n    public function testGetCallableResolverReturnsInjectedInstance(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $app = new App($responseFactoryProphecy->reveal(), null, $callableResolverProphecy->reveal());\n\n        $this->assertSame($callableResolverProphecy->reveal(), $app->getCallableResolver());\n    }\n\n    public function testCreatesCallableResolverWhenNull(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal(), null);\n\n        $this->assertEquals($callableResolver, $app->getCallableResolver());\n    }\n\n    public function testGetRouteCollectorReturnsInjectedInstance(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeParserProphecy = $this->prophesize(RouteParserInterface::class);\n\n        $routeCollectorProphecy->getRouteParser()->willReturn($routeParserProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal(), null, null, $routeCollectorProphecy->reveal());\n\n        $this->assertSame($routeCollectorProphecy->reveal(), $app->getRouteCollector());\n    }\n\n    public function testCreatesRouteCollectorWhenNullWithInjectedContainer(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $routeCollector = new RouteCollector(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $containerProphecy->reveal()\n        );\n        $app = new App(\n            $responseFactoryProphecy->reveal(),\n            $containerProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n\n        $this->assertEquals($routeCollector, $app->getRouteCollector());\n    }\n\n    public function testGetMiddlewareDispatcherGetsSeededAndReturnsInjectedInstance(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n\n        $middlewareDispatcherProphecy = $this->prophesize(MiddlewareDispatcherInterface::class);\n        $middlewareDispatcherProphecy\n            ->seedMiddlewareStack(Argument::any())\n            ->shouldBeCalledOnce();\n\n        $app = new App(\n            $responseFactoryProphecy->reveal(),\n            null,\n            null,\n            null,\n            null,\n            $middlewareDispatcherProphecy->reveal()\n        );\n\n        $this->assertSame($middlewareDispatcherProphecy->reveal(), $app->getMiddlewareDispatcher());\n    }\n\n    public static function lowerCaseRequestMethodsProvider(): array\n    {\n        return [\n            ['get'],\n            ['post'],\n            ['put'],\n            ['patch'],\n            ['delete'],\n            ['options'],\n        ];\n    }\n\n    /**\n     * @param string $method\n     * @dataProvider upperCaseRequestMethodsProvider()\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('upperCaseRequestMethodsProvider')]\n    public function testGetPostPutPatchDeleteOptionsMethods(string $method): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn($method);\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $methodName = strtolower($method);\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->$methodName('/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        });\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testAnyRoute(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->any('/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        });\n\n        foreach ($this->upperCaseRequestMethodsProvider() as $methods) {\n            $method = $methods[0];\n            $uriProphecy = $this->prophesize(UriInterface::class);\n            $uriProphecy->getPath()->willReturn('/');\n\n            $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n            $requestProphecy->getMethod()->willReturn($method);\n            $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n            $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n            $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n                $this->getAttribute($args[0])->willReturn($args[1]);\n                return $this;\n            });\n\n            $response = $app->handle($requestProphecy->reveal());\n\n            $this->assertSame('Hello World', (string) $response->getBody());\n        }\n    }\n\n    /********************************************************************************\n     * Route collector proxy methods\n     *******************************************************************************/\n\n    public static function upperCaseRequestMethodsProvider(): array\n    {\n        return [\n            ['GET'],\n            ['POST'],\n            ['PUT'],\n            ['PATCH'],\n            ['DELETE'],\n            ['OPTIONS'],\n        ];\n    }\n\n    /**\n     * @param string $method\n     * @dataProvider lowerCaseRequestMethodsProvider\n     * @dataProvider upperCaseRequestMethodsProvider\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('lowerCaseRequestMethodsProvider')]\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('upperCaseRequestMethodsProvider')]\n    public function testMapRoute(string $method): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn($method);\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->map([$method], '/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        });\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testRedirectRoute(): void\n    {\n        $from = '/from';\n        $to = '/to';\n\n        $routeCreatedResponse = $this->prophesize(ResponseInterface::class);\n\n        $handlerCreatedResponse = $this->prophesize(ResponseInterface::class);\n        $handlerCreatedResponse->getStatusCode()->willReturn(301);\n        $handlerCreatedResponse->getHeaderLine('Location')->willReturn($to);\n        $handlerCreatedResponse->withHeader(\n            Argument::type('string'),\n            Argument::type('string')\n        )->will(function ($args) {\n            $this->getHeader($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($routeCreatedResponse->reveal());\n        $responseFactoryProphecy->createResponse(301)->willReturn($handlerCreatedResponse->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn($from);\n        $uriProphecy->__toString()->willReturn($to);\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->redirect($from, $to, 301);\n        $response = $app->handle($requestProphecy->reveal());\n\n        $responseFactoryProphecy->createResponse(301)->shouldHaveBeenCalled();\n        $this->assertSame(301, $response->getStatusCode());\n        $this->assertSame($to, $response->getHeaderLine('Location'));\n    }\n\n    public function testRouteWithInternationalCharacters(): void\n    {\n        $path = '/новости';\n\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get($path, function () use ($responseProphecy) {\n            return $responseProphecy->reveal();\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn($path);\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    /********************************************************************************\n     * Route Patterns\n     *******************************************************************************/\n\n    public static function routePatternsProvider(): array\n    {\n        return [\n            [''], // Empty Route\n            ['/'], // Single Slash Route\n            ['foo'], // Route That Does Not Start With A Slash\n            ['/foo'], // Route That Does Not End In A Slash\n            ['/foo/'], // Route That Ends In A Slash\n        ];\n    }\n\n    /**\n     * @param string $pattern\n     * @dataProvider routePatternsProvider\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('routePatternsProvider')]\n    public function testRoutePatterns(string $pattern): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get($pattern, function () {\n        });\n\n        $routeCollector = $app->getRouteCollector();\n        $route = $routeCollector->lookupRoute('route0');\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    /********************************************************************************\n     * Route Groups\n     *******************************************************************************/\n\n    public static function routeGroupsDataProvider(): array\n    {\n        return [\n            'empty group with empty route' => [\n                ['', ''], ''\n            ],\n            'empty group with single slash route' => [\n                ['', '/'], '/'\n            ],\n            'empty group with segment route that does not end in aSlash' => [\n                ['', '/foo'], '/foo'\n            ],\n            'empty group with segment route that ends in aSlash' => [\n                ['', '/foo/'], '/foo/'\n            ],\n            'group single slash with empty route' => [\n                ['/', ''], '/'\n            ],\n            'group single slash with single slash route' => [\n                ['/', '/'], '//'\n            ],\n            'group single slash with segment route that does not end in aSlash' => [\n                ['/', '/foo'], '//foo'\n            ],\n            'group single slash with segment route that ends in aSlash' => [\n                ['/', '/foo/'], '//foo/'\n            ],\n            'group segment with empty route' => [\n                ['/foo', ''], '/foo'\n            ],\n            'group segment with single slash route' => [\n                ['/foo', '/'], '/foo/'\n            ],\n            'group segment with segment route that does not end in aSlash' => [\n                ['/foo', '/bar'], '/foo/bar'\n            ],\n            'group segment with segment route that ends in aSlash' => [\n                ['/foo', '/bar/'], '/foo/bar/'\n            ],\n            'empty group with nested group segment with an empty route' => [\n                ['', '/foo', ''], '/foo'\n            ],\n            'empty group with nested group segment with single slash route' => [\n                ['', '/foo', '/'], '/foo/'\n            ],\n            'group single slash with empty nested group and segment route without leading slash' => [\n                ['/', '', 'foo'], '/foo'\n            ],\n            'group single slash with empty nested group and segment route' => [\n                ['/', '', '/foo'], '//foo'\n            ],\n            'group single slash with single slash group and segment route without leading slash' => [\n                ['/', '/', 'foo'], '//foo'\n            ],\n            'group single slash with single slash nested group and segment route' => [\n                ['/', '/', '/foo'], '///foo'\n            ],\n            'group single slash with nested group segment with an empty route' => [\n                ['/', '/foo', ''], '//foo'\n            ],\n            'group single slash with nested group segment with single slash route' => [\n                ['/', '/foo', '/'], '//foo/'\n            ],\n            'group single slash with nested group segment with segment route' => [\n                ['/', '/foo', '/bar'], '//foo/bar'\n            ],\n            'group single slash with nested group segment with segment route that has aTrailing slash' => [\n                ['/', '/foo', '/bar/'], '//foo/bar/'\n            ],\n            'empty group with empty nested group and segment route without leading slash' => [\n                ['', '', 'foo'], 'foo'\n            ],\n            'empty group with empty nested group and segment route' => [\n                ['', '', '/foo'], '/foo'\n            ],\n            'empty group with single slash group and segment route without leading slash' => [\n                ['', '/', 'foo'], '/foo'\n            ],\n            'empty group with single slash nested group and segment route' => [\n                ['', '/', '/foo'], '//foo'\n            ],\n            'empty group with nested group segment with segment route' => [\n                ['', '/foo', '/bar'], '/foo/bar'\n            ],\n            'empty group with nested group segment with segment route that has aTrailing slash' => [\n                ['', '/foo', '/bar/'], '/foo/bar/'\n            ],\n            'group segment with empty nested group and segment route without leading slash' => [\n                ['/foo', '', 'bar'], '/foobar'\n            ],\n            'group segment with empty nested group and segment route' => [\n                ['/foo', '', '/bar'], '/foo/bar'\n            ],\n            'group segment with single slash nested group and segment route' => [\n                ['/foo', '/', 'bar'], '/foo/bar'\n            ],\n            'group segment with single slash nested group and slash segment route' => [\n                ['/foo', '/', '/bar'], '/foo//bar'\n            ],\n            'two group segments with empty route' => [\n                ['/foo', '/bar', ''], '/foo/bar'\n            ],\n            'two group segments with single slash route' => [\n                ['/foo', '/bar', '/'], '/foo/bar/'\n            ],\n            'two group segments with segment route' => [\n                ['/foo', '/bar', '/baz'], '/foo/bar/baz'\n            ],\n            'two group segments with segment route that has aTrailing slash' => [\n                ['/foo', '/bar', '/baz/'], '/foo/bar/baz/'\n            ],\n        ];\n    }\n\n    public function testGroupClosureIsBoundToThisClass(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $app = new App($responseFactoryProphecy->reveal());\n\n        $testCase = $this;\n        $app->group('/foo', function () use ($testCase) {\n            $testCase->assertSame($testCase, $this);\n        });\n    }\n\n    /**\n     * @dataProvider routeGroupsDataProvider\n     * @param array  $sequence\n     * @param string $expectedPath\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('routeGroupsDataProvider')]\n    public function testRouteGroupCombinations(array $sequence, string $expectedPath): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $app = new App($responseFactoryProphecy->reveal());\n\n        $processSequence = function (RouteCollectorProxy $app, array $sequence, $processSequence) {\n            $path = array_shift($sequence);\n\n            /**\n             * If sequence isn't on last element we use $app->group()\n             * The very tail of the sequence uses the $app->get() method\n             */\n            if (count($sequence)) {\n                $app->group($path, function (RouteCollectorProxy $group) use (&$sequence, $processSequence) {\n                    $processSequence($group, $sequence, $processSequence);\n                });\n            } else {\n                $app->get($path, function () {\n                });\n            }\n        };\n\n        $processSequence($app, $sequence, $processSequence);\n\n        $routeCollector = $app->getRouteCollector();\n        $route = $routeCollector->lookupRoute('route0');\n\n        $this->assertSame($expectedPath, $route->getPattern());\n    }\n\n    public function testRouteGroupPattern(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n\n        /** @var ResponseFactoryInterface $responseFactoryInterface */\n        $responseFactoryInterface = $responseFactoryProphecy->reveal();\n        $app = new App($responseFactoryInterface);\n        $group = $app->group('/foo', function () {\n        });\n\n        $this->assertSame('/foo', $group->getPattern());\n    }\n\n    /********************************************************************************\n     * Middleware\n     *******************************************************************************/\n\n    public function testAddMiddleware(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(Argument::cetera())->will(function () use ($responseProphecy) {\n            return $responseProphecy->reveal();\n        });\n\n        $middlewareProphecy2 = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy2->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            return $handler->handle($request);\n        });\n\n        $app->add($middlewareProphecy->reveal());\n        $app->addMiddleware($middlewareProphecy2->reveal());\n        $app->get('/', function (ServerRequestInterface $request, $response) {\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n\n        $response = $app->handle($requestProphecy->reveal());\n        $middlewareProphecy->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->shouldHaveBeenCalled();\n        $middlewareProphecy2->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->shouldHaveBeenCalled();\n\n        $this->assertSame($responseProphecy->reveal(), $response);\n    }\n\n    public function testAddMiddlewareUsingDeferredResolution(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(Argument::cetera())->willReturn($responseProphecy->reveal());\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('middleware')->willReturn(true);\n        $containerProphecy->get('middleware')->willReturn($middlewareProphecy);\n\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n        $app->add('middleware');\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n\n        $response = $app->handle($requestProphecy->reveal());\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testAddRoutingMiddleware(): void\n    {\n        /** @var ResponseFactoryInterface $responseFactory */\n        $responseFactory = $this->prophesize(ResponseFactoryInterface::class)->reveal();\n\n        // Create the app.\n        $app = new App($responseFactory);\n\n        // Add the routing middleware.\n        $routingMiddleware = $app->addRoutingMiddleware();\n\n        // Check that the routing middleware really has been added to the tip of the app middleware stack.\n        $middlewareDispatcherProperty = new ReflectionProperty(App::class, 'middlewareDispatcher');\n        $this->setAccessible($middlewareDispatcherProperty);\n        /** @var MiddlewareDispatcher $middlewareDispatcher */\n        $middlewareDispatcher = $middlewareDispatcherProperty->getValue($app);\n\n        $tipProperty = new ReflectionProperty(MiddlewareDispatcher::class, 'tip');\n        $this->setAccessible($tipProperty);\n        /** @var RequestHandlerInterface $tip */\n        $tip = $tipProperty->getValue($middlewareDispatcher);\n\n        $reflection = new ReflectionClass($tip);\n        $middlewareProperty = $reflection->getProperty('middleware');\n        $this->setAccessible($middlewareProperty);\n\n        $this->assertSame($routingMiddleware, $middlewareProperty->getValue($tip));\n        $this->assertInstanceOf(RoutingMiddleware::class, $routingMiddleware);\n    }\n\n    public function testAddErrorMiddleware(): void\n    {\n        /** @var ResponseFactoryInterface $responseFactory */\n        $responseFactory = $this->prophesize(ResponseFactoryInterface::class)->reveal();\n\n        /** @var LoggerInterface $logger */\n        $logger = $this->prophesize(LoggerInterface::class)->reveal();\n\n        // Create the app.\n        $app = new App($responseFactory);\n\n        // Add the error middleware.\n        $errorMiddleware = $app->addErrorMiddleware(true, true, true, $logger);\n\n        // Check that the error middleware really has been added to the tip of the app middleware stack.\n        $middlewareDispatcherProperty = new ReflectionProperty(App::class, 'middlewareDispatcher');\n        $this->setAccessible($middlewareDispatcherProperty);\n        /** @var MiddlewareDispatcher $middlewareDispatcher */\n        $middlewareDispatcher = $middlewareDispatcherProperty->getValue($app);\n\n        $tipProperty = new ReflectionProperty(MiddlewareDispatcher::class, 'tip');\n        $this->setAccessible($tipProperty);\n        /** @var RequestHandlerInterface $tip */\n        $tip = $tipProperty->getValue($middlewareDispatcher);\n\n        $reflection = new ReflectionClass($tip);\n        $middlewareProperty = $reflection->getProperty('middleware');\n        $this->setAccessible($middlewareProperty);\n\n        $this->assertSame($errorMiddleware, $middlewareProperty->getValue($tip));\n        $this->assertInstanceOf(ErrorMiddleware::class, $errorMiddleware);\n    }\n\n    public function testAddBodyParsingMiddleware(): void\n    {\n        /** @var ResponseFactoryInterface $responseFactory */\n        $responseFactory = $this->prophesize(ResponseFactoryInterface::class)->reveal();\n\n        // Create the app.\n        $app = new App($responseFactory);\n\n        // Add the error middleware.\n        $bodyParsingMiddleware = $app->addBodyParsingMiddleware();\n\n        // Check that the body parsing middleware really has been added to the tip of the app middleware stack.\n        $middlewareDispatcherProperty = new ReflectionProperty(App::class, 'middlewareDispatcher');\n        $this->setAccessible($middlewareDispatcherProperty);\n        /** @var MiddlewareDispatcher $middlewareDispatcher */\n        $middlewareDispatcher = $middlewareDispatcherProperty->getValue($app);\n\n        $tipProperty = new ReflectionProperty(MiddlewareDispatcher::class, 'tip');\n        $this->setAccessible($tipProperty);\n        /** @var RequestHandlerInterface $tip */\n        $tip = $tipProperty->getValue($middlewareDispatcher);\n\n        $reflection = new ReflectionClass($tip);\n        $middlewareProperty = $reflection->getProperty('middleware');\n        $this->setAccessible($middlewareProperty);\n\n        $this->assertSame($bodyParsingMiddleware, $middlewareProperty->getValue($tip));\n        $this->assertInstanceOf(BodyParsingMiddleware::class, $bodyParsingMiddleware);\n    }\n\n    public function testAddMiddlewareOnRoute(): void\n    {\n        $output = '';\n\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In1';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out1';\n\n            return $response;\n        });\n\n        $middlewareProphecy2 = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy2->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In2';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out2';\n\n            return $response;\n        });\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) use (&$output) {\n            $output .= 'Center';\n            return $response;\n        })\n            ->add($middlewareProphecy->reveal())\n            ->addMiddleware($middlewareProphecy2->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app->handle($requestProphecy->reveal());\n\n        $this->assertSame('In2In1CenterOut1Out2', $output);\n    }\n\n    public function testAddMiddlewareOnRouteGroup(): void\n    {\n        $output = '';\n\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In1';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out1';\n\n            return $response;\n        });\n\n        $middlewareProphecy2 = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy2->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In2';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out2';\n\n            return $response;\n        });\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->group('/foo', function (RouteCollectorProxy $proxy) use (&$output) {\n            $proxy->get('/bar', function (ServerRequestInterface $request, ResponseInterface $response) use (&$output) {\n                $output .= 'Center';\n                return $response;\n            });\n        })\n            ->add($middlewareProphecy->reveal())\n            ->addMiddleware($middlewareProphecy2->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/foo/bar');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app->handle($requestProphecy->reveal());\n\n        $this->assertSame('In2In1CenterOut1Out2', $output);\n    }\n\n    public function testAddMiddlewareOnTwoRouteGroup(): void\n    {\n        $output = '';\n\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In1';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out1';\n\n            return $response;\n        });\n\n        $middlewareProphecy2 = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy2->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In2';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out2';\n\n            return $response;\n        });\n\n        $middlewareProphecy3 = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy3->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use (&$output) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            $output .= 'In3';\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n\n            $output .= 'Out3';\n\n            return $response;\n        });\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->group('/foo', function (RouteCollectorProxyInterface $group) use (\n            $middlewareProphecy2,\n            $middlewareProphecy3,\n            &$output\n        ) {\n            // ensure that more than one nested group at the same level doesn't break middleware\n            $group->group('/fizz', function (RouteCollectorProxyInterface $group) {\n                $group->get('/buzz', function (ServerRequestInterface $request, ResponseInterface $response) {\n                    return $response;\n                });\n            });\n\n            $group->group('/bar', function (RouteCollectorProxyInterface $group) use (\n                $middlewareProphecy3,\n                &$output\n            ) {\n                $group->get('/baz', function (\n                    ServerRequestInterface $request,\n                    ResponseInterface $response\n                ) use (&$output) {\n                    $output .= 'Center';\n                    return $response;\n                })->add($middlewareProphecy3->reveal());\n            })->add($middlewareProphecy2->reveal());\n        })->add($middlewareProphecy->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/foo/bar/baz');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app->handle($requestProphecy->reveal());\n\n        $this->assertSame('In1In2In3CenterOut3Out2Out1', $output);\n    }\n\n    public function testAddMiddlewareAsStringNotImplementingInterfaceThrowsException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage(\n            'A middleware must be an object/class name referencing an implementation of ' .\n            'MiddlewareInterface or a callable with a matching signature.'\n        );\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->add(new stdClass());\n    }\n\n    /********************************************************************************\n     * Runner\n     *******************************************************************************/\n\n    public function testInvokeReturnMethodNotAllowed(): void\n    {\n        $this->expectException(HttpMethodNotAllowedException::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function () {\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('POST');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app->handle($requestProphecy->reveal());\n    }\n\n    public function testInvokeWithMatchingRoute(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, $response) {\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithMatchingRouteWithSetArgument(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write(\"Hello {$args['name']}\");\n            return $response;\n        })->setArgument('name', 'World');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithMatchingRouteWithSetArguments(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write(\"{$args['greeting']} {$args['name']}\");\n            return $response;\n        })->setArguments(['greeting' => 'Hello', 'name' => 'World']);\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithMatchingRouteWithNamedParameterRequestResponseStrategy(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/Hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write(\"Hello {$args['name']}\");\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithMatchingRouteWithNamedParameterRequestResponseArgStrategy(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->getRouteCollector()->setDefaultInvocationStrategy(new RequestResponseArgs());\n        $app->get('/Hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $name) {\n            $response->getBody()->write(\"Hello {$name}\");\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithMatchingRouteWithNamedParameterRequestResponseNamedArgsStrategy(): void\n    {\n        if (PHP_VERSION_ID < 80000) {\n            $this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');\n        }\n\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->getRouteCollector()->setDefaultInvocationStrategy(new RequestResponseNamedArgs());\n        $app->get(\n            '/{greeting}/{name}',\n            function (ServerRequestInterface $request, ResponseInterface $response, $name, $greeting) {\n                $response->getBody()->write(\"{$greeting} {$name}\");\n                return $response;\n            }\n        );\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithMatchingRouteWithNamedParameterOverwritesSetArgument(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/Hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write(\"Hello {$args['name']}\");\n            return $response;\n        })->setArgument('name', 'World!');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithoutMatchingRoute(): void\n    {\n        $this->expectException(HttpNotFoundException::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $app = new App($responseFactoryProphecy->reveal());\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app->handle($requestProphecy->reveal());\n    }\n\n    public function testInvokeWithCallableRegisteredInContainer(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $handler = new class\n        {\n            public function foo(ServerRequestInterface $request, ResponseInterface $response)\n            {\n                return $response;\n            }\n        };\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('handler')->willReturn(true);\n        $containerProphecy->get('handler')->willReturn($handler);\n\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n        $app->get('/', 'handler:foo');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testInvokeWithNonExistentMethodOnCallableRegisteredInContainer(): void\n    {\n        $this->expectException(RuntimeException::class);\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $handler = new class\n        {\n        };\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('handler')->willReturn(true);\n        $containerProphecy->get('handler')->willReturn($handler);\n\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n        $app->get('/', 'handler:method_does_not_exist');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())\n            ->will(function ($args) {\n                $this->getAttribute($args[0])->willReturn($args[1]);\n                return $this;\n            });\n\n        $app->handle($requestProphecy->reveal());\n    }\n\n    public function testInvokeWithCallableInContainerViaCallMagicMethod(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $mockAction = new MockAction();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('handler')->willReturn(true);\n        $containerProphecy->get('handler')->willReturn($mockAction);\n\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n        $app->get('/', 'handler:foo');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $expectedPayload = json_encode(['name' => 'foo', 'arguments' => []]);\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame($expectedPayload, (string) $response->getBody());\n    }\n\n    public function testInvokeFunctionName(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        // @codingStandardsIgnoreStart\n        function handle($request, ResponseInterface $response)\n        {\n            $response->getBody()->write('Hello World');\n            return $response;\n        }\n\n        // @codingStandardsIgnoreEnd\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', __NAMESPACE__ . '\\handle');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testCurrentRequestAttributesAreNotLostWhenAddingRouteArguments(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/Hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write($request->getAttribute('greeting') . ' ' . $args['name']);\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal()->withAttribute('greeting', 'Hello'));\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testCurrentRequestAttributesAreNotLostWhenAddingRouteArgumentsRequestResponseArg(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->getRouteCollector()->setDefaultInvocationStrategy(new RequestResponseArgs());\n        $app->get('/Hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, $name) {\n            $response->getBody()->write($request->getAttribute('greeting') . ' ' . $name);\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal()->withAttribute('greeting', 'Hello'));\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testRun(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n        $streamProphecy->read(1)->willReturn('_');\n        $streamProphecy->read('11')->will(function () {\n            $this->eof()->willReturn(true);\n            return $this->reveal()->__toString();\n        });\n        $streamProphecy->eof()->willReturn(false);\n        $streamProphecy->isSeekable()->willReturn(true);\n        $streamProphecy->rewind()->shouldBeCalled();\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n        $responseProphecy->getStatusCode()->willReturn(200);\n        $responseProphecy->getHeaders()->willReturn(['Content-Length' => ['11']]);\n        $responseProphecy->getProtocolVersion()->willReturn('1.1');\n        $responseProphecy->getReasonPhrase()->willReturn('');\n        $responseProphecy->getHeaderLine('Content-Length')->willReturn('11');\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('Hello World');\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $app->run($requestProphecy->reveal());\n\n        $this->expectOutputString('Hello World');\n    }\n\n    public function testRunWithoutPassingInServerRequest(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n        $streamProphecy->read(1)->willReturn('_');\n        $streamProphecy->read(11)->will(function () {\n            $this->eof()->willReturn(true);\n            return $this->reveal()->__toString();\n        });\n        $streamProphecy->eof()->willReturn(false);\n        $streamProphecy->isSeekable()->willReturn(true);\n        $streamProphecy->rewind()->shouldBeCalled();\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n        $responseProphecy->getStatusCode()->willReturn(200);\n        $responseProphecy->getHeaders()->willReturn(['Content-Length' => ['11']]);\n        $responseProphecy->getProtocolVersion()->willReturn('1.1');\n        $responseProphecy->getReasonPhrase()->willReturn('');\n        $responseProphecy->getHeaderLine('Content-Length')->willReturn('11');\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('Hello World');\n            return $response;\n        });\n\n        $app->run();\n\n        $this->expectOutputString('Hello World');\n    }\n\n    public function testHandleReturnsEmptyResponseBodyWithHeadRequestMethod(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n        $responseProphecy\n            ->withBody(Argument::type(StreamInterface::class))\n            ->will(function ($args) {\n                $this->getBody()->willReturn($args[0]);\n                return $this;\n            });\n\n        $emptyStreamProphecy = $this->prophesize(StreamInterface::class);\n        $emptyStreamProphecy->__toString()->willReturn('');\n        $emptyResponseProphecy = $this->prophesize(ResponseInterface::class);\n        $emptyResponseProphecy->getBody()->willReturn($emptyStreamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn(\n            $responseProphecy->reveal(),\n            $emptyResponseProphecy->reveal()\n        );\n\n        $called = 0;\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) use (&$called) {\n            $called++;\n            $response->getBody()->write('Hello World');\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('HEAD');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertSame(1, $called);\n        $this->assertEmpty((string) $response->getBody());\n    }\n\n    public function testCanBeReExecutedRecursivelyDuringDispatch(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseHeaders = [];\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n        $responseProphecy->getStatusCode()->willReturn(200);\n        $responseProphecy->getHeader(Argument::type('string'))->will(function ($args) use (&$responseHeaders) {\n            return $responseHeaders[$args[0]];\n        });\n        $responseProphecy->withAddedHeader(\n            Argument::type('string'),\n            Argument::type('string')\n        )->will(function ($args) use (&$responseHeaders) {\n            $key = $args[0];\n            $value = $args[1];\n            if (!isset($responseHeaders[$key])) {\n                $responseHeaders[$key] = [];\n            }\n            $responseHeaders[$key][] = $value;\n            return $this;\n        });\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse(Argument::type('integer'))\n            ->will(function ($args) use ($responseProphecy) {\n                $responseProphecy->getStatusCode()->willReturn($args[0]);\n                return $responseProphecy->reveal();\n            });\n\n        $app = new App($responseFactoryProphecy->reveal());\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) use ($app, $responseFactoryProphecy) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            if ($request->hasHeader('X-NESTED')) {\n                return $responseFactoryProphecy\n                    ->reveal()\n                    ->createResponse(204)\n                    ->withAddedHeader('X-TRACE', 'nested');\n            }\n\n            /** @var ResponseInterface $response */\n            $response = $app->handle($request->withAddedHeader('X-NESTED', '1'));\n            return $response->withAddedHeader('X-TRACE', 'outer');\n        });\n\n        $middlewareProphecy2 = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy2->process(\n            Argument::type(ServerRequestInterface::class),\n            Argument::type(RequestHandlerInterface::class)\n        )->will(function ($args) {\n            /** @var ServerRequestInterface $request */\n            $request = $args[0];\n\n            /** @var RequestHandlerInterface $handler */\n            $handler = $args[1];\n\n            /** @var ResponseInterface $response */\n            $response = $handler->handle($request);\n            $response->getBody()->write('1');\n\n            return $response;\n        });\n\n        $app\n            ->add($middlewareProphecy->reveal())\n            ->add($middlewareProphecy2->reveal());\n        $app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $responseHeaders = [];\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->hasHeader(Argument::type('string'))->will(function ($args) use (&$responseHeaders) {\n            return array_key_exists($args[0], $responseHeaders);\n        });\n        $requestProphecy->withAddedHeader(\n            Argument::type('string'),\n            Argument::type('string')\n        )->will(function ($args) use (&$responseHeaders) {\n            $key = $args[0];\n            $value = $args[1];\n            if (!isset($responseHeaders[$key])) {\n                $responseHeaders[$key] = [];\n            }\n            $responseHeaders[$key][] = $value;\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertSame(204, $response->getStatusCode());\n        $this->assertSame(['nested', 'outer'], $response->getHeader('X-TRACE'));\n        $this->assertSame('11', (string) $response->getBody());\n    }\n\n    // TODO: Re-add testUnsupportedMethodWithoutRoute\n\n    // TODO: Re-add testUnsupportedMethodWithRoute\n\n    public function testContainerSetToRoute(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('Hello World');\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('handler')->willReturn(true);\n        $containerProphecy->get('handler')->willReturn(function () use ($responseProphecy) {\n            return $responseProphecy->reveal();\n        });\n\n        $app = new App($responseFactoryProphecy->reveal(), $containerProphecy->reveal());\n        $routeCollector = $app->getRouteCollector();\n        $routeCollector->map(['GET'], '/', 'handler');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n\n        $this->assertSame('Hello World', (string) $response->getBody());\n    }\n\n    public function testAppIsARequestHandler(): void\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $app = new App($responseFactoryProphecy->reveal());\n\n        $this->assertInstanceOf(RequestHandlerInterface::class, $app);\n    }\n\n    public function testInvokeSequentialProcessToAPathWithOptionalArgsAndWithoutOptionalArgs(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/Hello[/{name}]', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write((string) count($args));\n            return $response;\n        });\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n        $this->assertSame('1', (string) $response->getBody());\n\n        $uriProphecy2 = $this->prophesize(UriInterface::class);\n        $uriProphecy2->getPath()->willReturn('/Hello');\n\n        $requestProphecy2 = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy2->getMethod()->willReturn('GET');\n        $requestProphecy2->getUri()->willReturn($uriProphecy2->reveal());\n        $requestProphecy2->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy2->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $streamProphecy->__toString()->willReturn('');\n        $response = $app->handle($requestProphecy2->reveal());\n        $this->assertSame('0', (string) $response->getBody());\n    }\n\n    public function testInvokeSequentialProcessToAPathWithOptionalArgsAndWithoutOptionalArgsAndKeepSetedArgs(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $app->get('/Hello[/{name}]', function (ServerRequestInterface $request, ResponseInterface $response, $args) {\n            $response->getBody()->write((string) count($args));\n            return $response;\n        })->setArgument('extra', 'value');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n        $this->assertSame('2', (string) $response->getBody());\n\n        $uriProphecy2 = $this->prophesize(UriInterface::class);\n        $uriProphecy2->getPath()->willReturn('/Hello');\n\n        $requestProphecy2 = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy2->getMethod()->willReturn('GET');\n        $requestProphecy2->getUri()->willReturn($uriProphecy2->reveal());\n        $requestProphecy2->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy2->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $streamProphecy->__toString()->willReturn('');\n        $response = $app->handle($requestProphecy2->reveal());\n        $this->assertSame('1', (string) $response->getBody());\n    }\n\n    public function testInvokeSequentialProcessAfterAddingAnotherRouteArgument(): void\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n        $streamProphecy->__toString()->willReturn('');\n        $streamProphecy->write(Argument::type('string'))->will(function ($args) {\n            $body = $this->reveal()->__toString();\n            $body .= $args[0];\n            $this->__toString()->willReturn($body);\n            return 0;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getBody()->willReturn($streamProphecy->reveal());\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy->createResponse()->willReturn($responseProphecy->reveal());\n\n        $app = new App($responseFactoryProphecy->reveal());\n        $route = $app->get('/Hello[/{name}]', function (\n            ServerRequestInterface $request,\n            ResponseInterface $response,\n            $args\n        ) {\n            $response->getBody()->write((string) count($args));\n            return $response;\n        })->setArgument('extra', 'value');\n\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy->getPath()->willReturn('/Hello/World');\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getMethod()->willReturn('GET');\n        $requestProphecy->getUri()->willReturn($uriProphecy->reveal());\n        $requestProphecy->getAttribute(RouteContext::ROUTE)->willReturn($route);\n        $requestProphecy->getAttribute(RouteContext::ROUTING_RESULTS)->willReturn(null);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) {\n            $this->getAttribute($args[0])->willReturn($args[1]);\n            return $this;\n        });\n\n        $response = $app->handle($requestProphecy->reveal());\n        $this->assertSame('2', (string) $response->getBody());\n\n        $route->setArgument('extra2', 'value2');\n\n        $streamProphecy->__toString()->willReturn('');\n        $response = $app->handle($requestProphecy->reveal());\n        $this->assertSame('3', (string) $response->getBody());\n    }\n}\n"
  },
  {
    "path": "tests/Assets/HeaderStack.php",
    "content": "<?php\n\n/**\n * This is a direct copy of zend-diactoros/test/TestAsset/Functions.php and is used to override\n * header() and headers_sent() so we can test that they do the right thing.\n *\n * We put these into the Slim namespace, so that Slim\\App will use these versions of header() and\n * headers_sent() when we test its output.\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Assets;\n\nuse function explode;\nuse function trim;\n\n/**\n * Zend Framework (http://framework.zend.com/)\n *\n * This file exists to allow overriding the various output-related functions\n * in order to test what happens during the `Server::listen()` cycle.\n *\n * These functions include:\n *\n * - headers_sent(): we want to always return false so that headers will be\n *   emitted, and we can test to see their values.\n * - header(): we want to aggregate calls to this function.\n *\n * The HeaderStack class then aggregates that information for us, and the test\n * harness resets the values pre and post test.\n *\n * @see       http://github.com/zendframework/zend-diactoros for the canonical source repository\n * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)\n * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License\n */\n\n/**\n * Store output artifacts\n */\nclass HeaderStack\n{\n    /**\n     * @var string[][]\n     */\n    private static $data = [];\n\n    /**\n     * Reset state\n     */\n    public static function reset()\n    {\n        self::$data = [];\n    }\n\n    /**\n     * Push a header on the stack\n     *\n     * @param array $header\n     */\n    public static function push(array $header)\n    {\n        self::$data[] = $header;\n    }\n\n    /**\n     * Return the current header stack\n     *\n     * @return string[][]\n     */\n    public static function stack()\n    {\n        return self::$data;\n    }\n\n    /**\n     * Verify if there's a header line on the stack\n     *\n     * @param string $header\n     *\n     * @return bool\n     */\n    public static function has($header)\n    {\n        foreach (self::$data as $item) {\n            $components = explode(':', $item['header']);\n            if (trim($components[0]) === $header) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "tests/CallableResolverTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests;\n\nuse Prophecy\\Argument;\nuse Prophecy\\Prophecy\\ObjectProphecy;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\CallableResolver;\nuse Slim\\Tests\\Mocks\\CallableTest;\nuse Slim\\Tests\\Mocks\\InvokableTest;\nuse Slim\\Tests\\Mocks\\MiddlewareTest;\nuse Slim\\Tests\\Mocks\\RequestHandlerTest;\n\nclass CallableResolverTest extends TestCase\n{\n    private ObjectProphecy $containerProphecy;\n\n    public static function setUpBeforeClass(): void\n    {\n        function testAdvancedCallable()\n        {\n            return true;\n        }\n    }\n\n    public function setUp(): void\n    {\n        CallableTest::$CalledCount = 0;\n        InvokableTest::$CalledCount = 0;\n        RequestHandlerTest::$CalledCount = 0;\n\n        $this->containerProphecy = $this->prophesize(ContainerInterface::class);\n        $this->containerProphecy->has(Argument::type('string'))->willReturn(false);\n    }\n\n    public function testClosure(): void\n    {\n        $test = function () {\n            return true;\n        };\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve($test);\n        $callableRoute = $resolver->resolveRoute($test);\n        $callableMiddleware = $resolver->resolveMiddleware($test);\n\n        $this->assertTrue($callable());\n        $this->assertTrue($callableRoute());\n        $this->assertTrue($callableMiddleware());\n    }\n\n    public function testClosureContainer(): void\n    {\n        $this->containerProphecy->has('ultimateAnswer')->willReturn(true);\n        $this->containerProphecy->get('ultimateAnswer')->willReturn(42);\n\n        $that = $this;\n        $test = function () use ($that) {\n            $that->assertInstanceOf(ContainerInterface::class, $this);\n\n            /** @var ContainerInterface $this */\n            return $this->get('ultimateAnswer');\n        };\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $callable = $resolver->resolve($test);\n        $callableRoute = $resolver->resolveRoute($test);\n        $callableMiddleware = $resolver->resolveMiddleware($test);\n\n        $this->assertSame(42, $callable());\n        $this->assertSame(42, $callableRoute());\n        $this->assertSame(42, $callableMiddleware());\n    }\n\n    public function testFunctionName(): void\n    {\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve(__NAMESPACE__ . '\\testAdvancedCallable');\n        $callableRoute = $resolver->resolveRoute(__NAMESPACE__ . '\\testAdvancedCallable');\n        $callableMiddleware = $resolver->resolveMiddleware(__NAMESPACE__ . '\\testAdvancedCallable');\n\n        $this->assertTrue($callable());\n        $this->assertTrue($callableRoute());\n        $this->assertTrue($callableMiddleware());\n    }\n\n    public function testObjMethodArray(): void\n    {\n        $obj = new CallableTest();\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve([$obj, 'toCall']);\n        $callableRoute = $resolver->resolveRoute([$obj, 'toCall']);\n        $callableMiddleware = $resolver->resolveMiddleware([$obj, 'toCall']);\n\n        $callable();\n        $this->assertSame(1, CallableTest::$CalledCount);\n\n        $callableRoute();\n        $this->assertSame(2, CallableTest::$CalledCount);\n\n        $callableMiddleware();\n        $this->assertSame(3, CallableTest::$CalledCount);\n    }\n\n    public function testSlimCallable(): void\n    {\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve('Slim\\Tests\\Mocks\\CallableTest:toCall');\n        $callableRoute = $resolver->resolveRoute('Slim\\Tests\\Mocks\\CallableTest:toCall');\n        $callableMiddleware = $resolver->resolveMiddleware('Slim\\Tests\\Mocks\\CallableTest:toCall');\n\n        $callable();\n        $this->assertSame(1, CallableTest::$CalledCount);\n\n        $callableRoute();\n        $this->assertSame(2, CallableTest::$CalledCount);\n\n        $callableMiddleware();\n        $this->assertSame(3, CallableTest::$CalledCount);\n    }\n\n    public function testSlimCallableAsArray(): void\n    {\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve([CallableTest::class, 'toCall']);\n        $callableRoute = $resolver->resolveRoute([CallableTest::class, 'toCall']);\n        $callableMiddleware = $resolver->resolveMiddleware([CallableTest::class, 'toCall']);\n\n        $callable();\n        $this->assertSame(1, CallableTest::$CalledCount);\n\n        $callableRoute();\n        $this->assertSame(2, CallableTest::$CalledCount);\n\n        $callableMiddleware();\n        $this->assertSame(3, CallableTest::$CalledCount);\n    }\n\n    public function testSlimCallableContainer(): void\n    {\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve('Slim\\Tests\\Mocks\\CallableTest:toCall');\n        $this->assertSame($container, CallableTest::$CalledContainer);\n\n        CallableTest::$CalledContainer = null;\n        $resolver->resolveRoute('Slim\\Tests\\Mocks\\CallableTest:toCall');\n        $this->assertSame($container, CallableTest::$CalledContainer);\n\n        CallableTest::$CalledContainer = null;\n        $resolver->resolveMiddleware('Slim\\Tests\\Mocks\\CallableTest:toCall');\n        $this->assertSame($container, CallableTest::$CalledContainer);\n    }\n\n    public function testSlimCallableAsArrayContainer(): void\n    {\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve([CallableTest::class, 'toCall']);\n        $this->assertSame($container, CallableTest::$CalledContainer);\n\n        CallableTest::$CalledContainer = null;\n        $resolver->resolveRoute([CallableTest::class, 'toCall']);\n        $this->assertSame($container, CallableTest::$CalledContainer);\n\n        CallableTest::$CalledContainer = null;\n        $resolver->resolveMiddleware([CallableTest::class ,'toCall']);\n        $this->assertSame($container, CallableTest::$CalledContainer);\n    }\n\n    public function testContainer(): void\n    {\n        $this->containerProphecy->has('callable_service')->willReturn(true);\n        $this->containerProphecy->get('callable_service')->willReturn(new CallableTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n\n        $resolver = new CallableResolver($container);\n        $callable = $resolver->resolve('callable_service:toCall');\n        $callableRoute = $resolver->resolveRoute('callable_service:toCall');\n        $callableMiddleware = $resolver->resolveMiddleware('callable_service:toCall');\n\n        $callable();\n        $this->assertSame(1, CallableTest::$CalledCount);\n\n        $callableRoute();\n        $this->assertSame(2, CallableTest::$CalledCount);\n\n        $callableMiddleware();\n        $this->assertSame(3, CallableTest::$CalledCount);\n    }\n\n    public function testResolutionToAnInvokableClassInContainer(): void\n    {\n        $this->containerProphecy->has('an_invokable')->willReturn(true);\n        $this->containerProphecy->get('an_invokable')->willReturn(new InvokableTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n\n        $resolver = new CallableResolver($container);\n        $callable = $resolver->resolve('an_invokable');\n        $callableRoute = $resolver->resolveRoute('an_invokable');\n        $callableMiddleware = $resolver->resolveMiddleware('an_invokable');\n\n        $callable();\n        $this->assertSame(1, InvokableTest::$CalledCount);\n\n        $callableRoute();\n        $this->assertSame(2, InvokableTest::$CalledCount);\n\n        $callableMiddleware();\n        $this->assertSame(3, InvokableTest::$CalledCount);\n    }\n\n    public function testResolutionToAnInvokableClass(): void\n    {\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve('Slim\\Tests\\Mocks\\InvokableTest');\n        $callableRoute = $resolver->resolveRoute('Slim\\Tests\\Mocks\\InvokableTest');\n        $callableMiddleware = $resolver->resolveMiddleware('Slim\\Tests\\Mocks\\InvokableTest');\n\n        $callable();\n        $this->assertSame(1, InvokableTest::$CalledCount);\n\n        $callableRoute();\n        $this->assertSame(2, InvokableTest::$CalledCount);\n\n        $callableMiddleware();\n        $this->assertSame(3, InvokableTest::$CalledCount);\n    }\n\n    public function testResolutionToAPsrRequestHandlerClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Slim\\\\Tests\\\\Mocks\\\\RequestHandlerTest is not resolvable');\n\n        $resolver = new CallableResolver(); // No container injected\n        $resolver->resolve(RequestHandlerTest::class);\n    }\n\n    public function testRouteResolutionToAPsrRequestHandlerClass(): void\n    {\n        $request = $this->createServerRequest('/', 'GET');\n        $resolver = new CallableResolver(); // No container injected\n        $callableRoute = $resolver->resolveRoute(RequestHandlerTest::class);\n        $callableRoute($request);\n        $this->assertSame(1, RequestHandlerTest::$CalledCount);\n    }\n\n    public function testMiddlewareResolutionToAPsrRequestHandlerClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Slim\\\\Tests\\\\Mocks\\\\RequestHandlerTest is not resolvable');\n\n        $resolver = new CallableResolver(); // No container injected\n        $resolver->resolveMiddleware(RequestHandlerTest::class);\n    }\n\n    public function testObjPsrRequestHandlerClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('{} is not resolvable');\n\n        $obj = new RequestHandlerTest();\n        $resolver = new CallableResolver(); // No container injected\n        $resolver->resolve($obj);\n    }\n\n    public function testRouteObjPsrRequestHandlerClass(): void\n    {\n        $obj = new RequestHandlerTest();\n        $request = $this->createServerRequest('/', 'GET');\n        $resolver = new CallableResolver(); // No container injected\n        $callableRoute = $resolver->resolveRoute($obj);\n        $callableRoute($request);\n        $this->assertSame(1, RequestHandlerTest::$CalledCount);\n    }\n\n    public function testMiddlewareObjPsrRequestHandlerClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('{} is not resolvable');\n\n        $obj = new RequestHandlerTest();\n        $resolver = new CallableResolver(); // No container injected\n        $resolver->resolveMiddleware($obj);\n    }\n\n    public function testObjPsrRequestHandlerClassInContainer(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('a_requesthandler is not resolvable');\n\n        $this->containerProphecy->has('a_requesthandler')->willReturn(true);\n        $this->containerProphecy->get('a_requesthandler')->willReturn(new RequestHandlerTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve('a_requesthandler');\n    }\n\n    public function testRouteObjPsrRequestHandlerClassInContainer(): void\n    {\n        $this->containerProphecy->has('a_requesthandler')->willReturn(true);\n        $this->containerProphecy->get('a_requesthandler')->willReturn(new RequestHandlerTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $request = $this->createServerRequest('/', 'GET');\n        $resolver = new CallableResolver($container);\n        $callable = $resolver->resolveRoute('a_requesthandler');\n        $callable($request);\n\n        $this->assertSame(1, RequestHandlerTest::$CalledCount);\n    }\n\n    public function testMiddlewareObjPsrRequestHandlerClassInContainer(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('a_requesthandler is not resolvable');\n\n        $this->containerProphecy->has('a_requesthandler')->willReturn(true);\n        $this->containerProphecy->get('a_requesthandler')->willReturn(new RequestHandlerTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveMiddleware('a_requesthandler');\n    }\n\n    public function testResolutionToAPsrRequestHandlerClassWithCustomMethod(): void\n    {\n        $resolver = new CallableResolver(); // No container injected\n        $callable = $resolver->resolve(RequestHandlerTest::class . ':custom');\n        $callableRoute = $resolver->resolveRoute(RequestHandlerTest::class . ':custom');\n        $callableMiddleware = $resolver->resolveMiddleware(RequestHandlerTest::class . ':custom');\n\n        $this->assertIsArray($callable);\n        $this->assertInstanceOf(RequestHandlerTest::class, $callable[0]);\n        $this->assertSame('custom', $callable[1]);\n\n        $this->assertIsArray($callableRoute);\n        $this->assertInstanceOf(RequestHandlerTest::class, $callableRoute[0]);\n        $this->assertSame('custom', $callableRoute[1]);\n\n        $this->assertIsArray($callableMiddleware);\n        $this->assertInstanceOf(RequestHandlerTest::class, $callableMiddleware[0]);\n        $this->assertSame('custom', $callableMiddleware[1]);\n    }\n\n    public function testObjMiddlewareClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('{} is not resolvable');\n\n        $obj = new MiddlewareTest();\n        $resolver = new CallableResolver(); // No container injected\n        $resolver->resolve($obj);\n    }\n\n    public function testRouteObjMiddlewareClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('{} is not resolvable');\n\n        $obj = new MiddlewareTest();\n        $resolver = new CallableResolver(); // No container injected\n        $resolver->resolveRoute($obj);\n    }\n\n    public function testMiddlewareObjMiddlewareClass(): void\n    {\n        $obj = new MiddlewareTest();\n        $request = $this->createServerRequest('/', 'GET');\n        $resolver = new CallableResolver(); // No container injected\n        $callableRouteMiddleware = $resolver->resolveMiddleware($obj);\n        $callableRouteMiddleware($request, $this->createMock(RequestHandlerInterface::class));\n        $this->assertSame(1, MiddlewareTest::$CalledCount);\n    }\n\n    public function testNotObjectInContainerThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('callable_service container entry is not an object');\n\n        $this->containerProphecy->has('callable_service')->willReturn(true);\n        $this->containerProphecy->get('callable_service')->willReturn('NOT AN OBJECT');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve('callable_service');\n    }\n\n    public function testMethodNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('callable_service:notFound is not resolvable');\n\n        $this->containerProphecy->has('callable_service')->willReturn(true);\n        $this->containerProphecy->get('callable_service')->willReturn(new CallableTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve('callable_service:notFound');\n    }\n\n    public function testRouteMethodNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('callable_service:notFound is not resolvable');\n\n        $this->containerProphecy->has('callable_service')->willReturn(true);\n        $this->containerProphecy->get('callable_service')->willReturn(new CallableTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveRoute('callable_service:notFound');\n    }\n\n    public function testMiddlewareMethodNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('callable_service:notFound is not resolvable');\n\n        $this->containerProphecy->has('callable_service')->willReturn(true);\n        $this->containerProphecy->get('callable_service')->willReturn(new CallableTest());\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveMiddleware('callable_service:notFound');\n    }\n\n    public function testFunctionNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable notFound does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve('notFound');\n    }\n\n    public function testRouteFunctionNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable notFound does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveRoute('notFound');\n    }\n\n    public function testMiddlewareFunctionNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable notFound does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveMiddleware('notFound');\n    }\n\n    public function testClassNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable Unknown::notFound() does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve('Unknown:notFound');\n    }\n\n    public function testRouteClassNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable Unknown::notFound() does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveRoute('Unknown:notFound');\n    }\n\n    public function testMiddlewareClassNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable Unknown::notFound() does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveMiddleware('Unknown:notFound');\n    }\n\n    public function testCallableClassNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable Unknown::notFound() does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolve(['Unknown', 'notFound']);\n    }\n\n    public function testRouteCallableClassNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable Unknown::notFound() does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveRoute(['Unknown', 'notFound']);\n    }\n\n    public function testMiddlewareCallableClassNotFoundThrowException(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Callable Unknown::notFound() does not exist');\n\n        /** @var ContainerInterface $container */\n        $container = $this->containerProphecy->reveal();\n        $resolver = new CallableResolver($container);\n        $resolver->resolveMiddleware(['Unknown', 'notFound']);\n    }\n}\n"
  },
  {
    "path": "tests/Error/AbstractErrorRendererTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Error;\n\nuse Exception;\nuse ReflectionClass;\nuse RuntimeException;\nuse Slim\\Error\\Renderers\\HtmlErrorRenderer;\nuse Slim\\Error\\Renderers\\JsonErrorRenderer;\nuse Slim\\Error\\Renderers\\PlainTextErrorRenderer;\nuse Slim\\Error\\Renderers\\XmlErrorRenderer;\nuse Slim\\Exception\\HttpException;\nuse Slim\\Tests\\TestCase;\nuse stdClass;\n\nuse function json_decode;\nuse function json_encode;\nuse function simplexml_load_string;\n\nclass AbstractErrorRendererTest extends TestCase\n{\n    public function testHTMLErrorRendererDisplaysErrorDetails()\n    {\n        $exception = new RuntimeException('Oops..');\n        $renderer = new HtmlErrorRenderer();\n        $output = $renderer->__invoke($exception, true);\n\n        $this->assertMatchesRegularExpression(\n            '/.*The application could not run because of the following error:.*/',\n            $output\n        );\n        $this->assertStringContainsString('Oops..', $output);\n    }\n\n    public function testHTMLErrorRendererNoErrorDetails()\n    {\n        $exception = new RuntimeException('Oops..');\n        $renderer = new HtmlErrorRenderer();\n        $output = $renderer->__invoke($exception, false);\n\n        $this->assertMatchesRegularExpression(\n            '/.*A website error has occurred. Sorry for the temporary inconvenience.*/',\n            $output\n        );\n        $this->assertStringNotContainsString('Oops..', $output);\n    }\n\n    public function testHTMLErrorRendererRenderFragmentMethod()\n    {\n        $exception = new Exception('Oops..', 500);\n        $renderer = new HtmlErrorRenderer();\n        $reflectionRenderer = new ReflectionClass(HtmlErrorRenderer::class);\n\n        $method = $reflectionRenderer->getMethod('renderExceptionFragment');\n        $this->setAccessible($method);\n        $output = $method->invoke($renderer, $exception);\n\n        $this->assertMatchesRegularExpression('/.*Type:*/', $output);\n        $this->assertMatchesRegularExpression('/.*Code:*/', $output);\n        $this->assertMatchesRegularExpression('/.*Message*/', $output);\n        $this->assertMatchesRegularExpression('/.*File*/', $output);\n        $this->assertMatchesRegularExpression('/.*Line*/', $output);\n    }\n\n    public function testHTMLErrorRendererRenderHttpException()\n    {\n        $exceptionTitle = 'title';\n        $exceptionDescription = 'description';\n\n        $httpExceptionProphecy = $this->prophesize(HttpException::class);\n\n        $httpExceptionProphecy\n            ->getTitle()\n            ->willReturn($exceptionTitle)\n            ->shouldBeCalledOnce();\n\n        $httpExceptionProphecy\n            ->getDescription()\n            ->willReturn($exceptionDescription)\n            ->shouldBeCalledOnce();\n\n        $renderer = new HtmlErrorRenderer();\n        $output = $renderer->__invoke($httpExceptionProphecy->reveal(), false);\n\n        $this->assertStringContainsString($exceptionTitle, $output, 'Should contain http exception title');\n        $this->assertStringContainsString($exceptionDescription, $output, 'Should contain http exception description');\n    }\n\n    public function testJSONErrorRendererDisplaysErrorDetails()\n    {\n        $exception = new Exception('Oops..');\n        $renderer = new JsonErrorRenderer();\n        $reflectionRenderer = new ReflectionClass(JsonErrorRenderer::class);\n\n        $method = $reflectionRenderer->getMethod('formatExceptionFragment');\n        $this->setAccessible($method);\n\n        $fragment = $method->invoke($renderer, $exception);\n        $output = json_encode(json_decode($renderer->__invoke($exception, true)));\n        $expectedString = json_encode(['message' => 'Slim Application Error', 'exception' => [$fragment]]);\n\n        $this->assertSame($output, $expectedString);\n    }\n\n    public function testJSONErrorRendererDoesNotDisplayErrorDetails()\n    {\n        $exception = new Exception('Oops..');\n\n        $renderer = new JsonErrorRenderer();\n        $output = json_encode(json_decode($renderer->__invoke($exception, false)));\n\n        $this->assertSame($output, json_encode(['message' => 'Slim Application Error']));\n    }\n\n    public function testJSONErrorRendererDisplaysPreviousError()\n    {\n        $previousException = new Exception('Oh no!');\n        $exception = new Exception('Oops..', 0, $previousException);\n\n        $renderer = new JsonErrorRenderer();\n        $reflectionRenderer = new ReflectionClass(JsonErrorRenderer::class);\n        $method = $reflectionRenderer->getMethod('formatExceptionFragment');\n        $this->setAccessible($method);\n\n        $output = json_encode(json_decode($renderer->__invoke($exception, true)));\n\n        $fragments = [\n            $method->invoke($renderer, $exception),\n            $method->invoke($renderer, $previousException),\n        ];\n\n        $expectedString = json_encode(['message' => 'Slim Application Error', 'exception' => $fragments]);\n\n        $this->assertSame($output, $expectedString);\n    }\n\n    public function testJSONErrorRendererRenderHttpException()\n    {\n        $exceptionTitle = 'title';\n\n        $httpExceptionProphecy = $this->prophesize(HttpException::class);\n\n        $httpExceptionProphecy\n            ->getTitle()\n            ->willReturn($exceptionTitle)\n            ->shouldBeCalledOnce();\n\n        $renderer = new JsonErrorRenderer();\n        $output = json_encode(json_decode($renderer->__invoke($httpExceptionProphecy->reveal(), false)));\n\n        $this->assertSame(\n            $output,\n            json_encode(['message' => $exceptionTitle]),\n            'Should contain http exception title'\n        );\n    }\n\n\n    public function testXMLErrorRendererDisplaysErrorDetails()\n    {\n        $previousException = new RuntimeException('Oops..');\n        $exception = new Exception('Ooops...', 0, $previousException);\n\n        $renderer = new XmlErrorRenderer();\n\n        /** @var stdClass $output */\n        $output = simplexml_load_string($renderer->__invoke($exception, true));\n\n        $this->assertSame((string) $output->message[0], 'Slim Application Error');\n        $this->assertSame((string) $output->exception[0]->type, 'Exception');\n        $this->assertSame((string) $output->exception[0]->message, 'Ooops...');\n        $this->assertSame((string) $output->exception[1]->type, 'RuntimeException');\n        $this->assertSame((string) $output->exception[1]->message, 'Oops..');\n    }\n\n    public function testXMLErrorRendererRenderHttpException()\n    {\n        $exceptionTitle = 'title';\n\n        $httpExceptionProphecy = $this->prophesize(HttpException::class);\n\n        $httpExceptionProphecy\n            ->getTitle()\n            ->willReturn($exceptionTitle)\n            ->shouldBeCalledOnce();\n\n        $renderer = new XmlErrorRenderer();\n\n        /** @var stdClass $output */\n        $output = simplexml_load_string($renderer->__invoke($httpExceptionProphecy->reveal(), true));\n\n        $this->assertSame((string) $output->message[0], $exceptionTitle, 'Should contain http exception title');\n    }\n\n    public function testPlainTextErrorRendererFormatFragmentMethod()\n    {\n        $message = 'Oops.. <br>';\n        $exception = new Exception($message, 500);\n        $renderer = new PlainTextErrorRenderer();\n        $reflectionRenderer = new ReflectionClass(PlainTextErrorRenderer::class);\n\n        $method = $reflectionRenderer->getMethod('formatExceptionFragment');\n        $this->setAccessible($method);\n        $output = $method->invoke($renderer, $exception);\n        $this->assertIsString($output);\n\n        $this->assertMatchesRegularExpression('/.*Type:*/', $output);\n        $this->assertMatchesRegularExpression('/.*Code:*/', $output);\n        $this->assertMatchesRegularExpression('/.*Message*/', $output);\n        $this->assertMatchesRegularExpression('/.*File*/', $output);\n        $this->assertMatchesRegularExpression('/.*Line*/', $output);\n\n        // ensure the renderer doesn't reformat the message\n        $this->assertMatchesRegularExpression(\"/.*$message/\", $output);\n    }\n\n    public function testPlainTextErrorRendererDisplaysErrorDetails()\n    {\n        $previousException = new RuntimeException('Oops..');\n        $exception = new Exception('Ooops...', 0, $previousException);\n\n        $renderer = new PlainTextErrorRenderer();\n        $output = $renderer->__invoke($exception, true);\n\n        $this->assertMatchesRegularExpression('/Ooops.../', $output);\n    }\n\n    public function testPlainTextErrorRendererNotDisplaysErrorDetails()\n    {\n        $previousException = new RuntimeException('Oops..');\n        $exception = new Exception('Ooops...', 0, $previousException);\n\n        $renderer = new PlainTextErrorRenderer();\n        $output = $renderer->__invoke($exception, false);\n\n        $this->assertSame(\"Slim Application Error\\n\", $output, 'Should show only one string');\n    }\n\n    public function testPlainTextErrorRendererRenderHttpException()\n    {\n        $exceptionTitle = 'title';\n\n        $httpExceptionProphecy = $this->prophesize(HttpException::class);\n\n        $httpExceptionProphecy\n            ->getTitle()\n            ->willReturn($exceptionTitle)\n            ->shouldBeCalledOnce();\n\n        $renderer = new PlainTextErrorRenderer();\n        $output = $renderer->__invoke($httpExceptionProphecy->reveal(), true);\n\n        $this->assertStringContainsString($exceptionTitle, $output, 'Should contain http exception title');\n    }\n}\n"
  },
  {
    "path": "tests/Exception/HttpExceptionTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Exception;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Tests\\TestCase;\n\nclass HttpExceptionTest extends TestCase\n{\n    public function testHttpExceptionRequestReponseGetterSetters()\n    {\n        $request = $this->createServerRequest('/');\n        $exception = new HttpNotFoundException($request);\n\n        $this->assertInstanceOf(ServerRequestInterface::class, $exception->getRequest());\n    }\n\n    public function testHttpExceptionAttributeGettersSetters()\n    {\n        $request = $this->createServerRequest('/');\n\n        $exception = new HttpNotFoundException($request);\n        $exception->setTitle('Title');\n        $exception->setDescription('Description');\n\n        $this->assertSame('Title', $exception->getTitle());\n        $this->assertSame('Description', $exception->getDescription());\n    }\n\n    public function testHttpNotAllowedExceptionGetAllowedMethods()\n    {\n        $request = $this->createServerRequest('/');\n\n        $exception = new HttpMethodNotAllowedException($request);\n        $exception->setAllowedMethods(['GET']);\n        $this->assertSame(['GET'], $exception->getAllowedMethods());\n        $this->assertSame('Method not allowed. Must be one of: GET', $exception->getMessage());\n\n        $exception = new HttpMethodNotAllowedException($request);\n        $this->assertSame([], $exception->getAllowedMethods());\n        $this->assertSame('Method not allowed.', $exception->getMessage());\n    }\n}\n"
  },
  {
    "path": "tests/Exception/HttpUnauthorizedExceptionTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Exception;\n\nuse Slim\\Exception\\HttpUnauthorizedException;\nuse Slim\\Tests\\TestCase;\n\nclass HttpUnauthorizedExceptionTest extends TestCase\n{\n    public function testHttpUnauthorizedException()\n    {\n        $request = $this->createServerRequest('/');\n        $exception = new HttpUnauthorizedException($request);\n\n        $this->assertInstanceOf(HttpUnauthorizedException::class, $exception);\n    }\n\n    public function testHttpUnauthorizedExceptionWithMessage()\n    {\n        $request = $this->createServerRequest('/');\n        $exception = new HttpUnauthorizedException($request, 'Hello World');\n\n        $this->assertSame('Hello World', $exception->getMessage());\n    }\n}\n"
  },
  {
    "path": "tests/Factory/AppFactoryTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Factory;\n\nuse GuzzleHttp\\Psr7\\HttpFactory;\nuse Laminas\\Diactoros\\ResponseFactory as LaminasDiactorosResponseFactory;\nuse HttpSoft\\Message\\ResponseFactory as HttpSoftResponseFactory;\nuse Nyholm\\Psr7\\Factory\\Psr17Factory;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse ReflectionClass;\nuse ReflectionProperty;\nuse RuntimeException;\nuse Slim\\Factory\\AppFactory;\nuse Slim\\Factory\\Psr17\\GuzzlePsr17Factory;\nuse Slim\\Factory\\Psr17\\HttpSoftPsr17Factory;\nuse Slim\\Factory\\Psr17\\LaminasDiactorosPsr17Factory;\nuse Slim\\Factory\\Psr17\\NyholmPsr17Factory;\nuse Slim\\Factory\\Psr17\\Psr17FactoryProvider;\nuse Slim\\Factory\\Psr17\\SlimHttpPsr17Factory;\nuse Slim\\Factory\\Psr17\\SlimPsr17Factory;\nuse Slim\\Http\\Factory\\DecoratedResponseFactory;\nuse Slim\\Http\\Response as DecoratedResponse;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\MiddlewareDispatcherInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\nuse Slim\\Psr7\\Factory\\ResponseFactory as SlimResponseFactory;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Tests\\Mocks\\MockPsr17FactoryWithoutStreamFactory;\nuse Slim\\Tests\\TestCase;\nuse stdClass;\n\nclass AppFactoryTest extends TestCase\n{\n    protected function tearDown(): void\n    {\n        $reflectionClass = new ReflectionClass(SlimHttpPsr17Factory::class);\n        $reflectionClass->setStaticPropertyValue('responseFactoryClass', DecoratedResponseFactory::class);\n    }\n\n    public static function provideImplementations()\n    {\n        return [\n            [SlimPsr17Factory::class, SlimResponseFactory::class],\n            [HttpSoftPsr17Factory::class, HttpSoftResponseFactory::class],\n            [NyholmPsr17Factory::class, Psr17Factory::class],\n            [GuzzlePsr17Factory::class, HttpFactory::class],\n            [LaminasDiactorosPsr17Factory::class, LaminasDiactorosResponseFactory::class],\n        ];\n    }\n\n    /**\n     * @dataProvider provideImplementations\n     * @param string $psr17factory\n     * @param string $expectedResponseFactoryClass\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('provideImplementations')]\n    public function testCreateAppWithAllImplementations(string $psr17factory, string $expectedResponseFactoryClass)\n    {\n        Psr17FactoryProvider::setFactories([$psr17factory]);\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n\n        $app = AppFactory::create();\n\n        $routeCollector = $app->getRouteCollector();\n\n        $responseFactoryProperty = new ReflectionProperty(RouteCollector::class, 'responseFactory');\n        $this->setAccessible($responseFactoryProperty);\n\n        $responseFactory = $responseFactoryProperty->getValue($routeCollector);\n\n        $this->assertInstanceOf($expectedResponseFactoryClass, $responseFactory);\n    }\n\n    public function testDetermineResponseFactoryReturnsDecoratedFactory()\n    {\n        Psr17FactoryProvider::setFactories([SlimPsr17Factory::class]);\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(true);\n\n        $app = AppFactory::create();\n\n        $this->assertInstanceOf(DecoratedResponseFactory::class, $app->getResponseFactory());\n    }\n\n    public function testDetermineResponseFactoryThrowsRuntimeExceptionIfDecoratedNotInstanceOfResponseInterface()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage(\n            'Slim\\\\Factory\\\\Psr17\\\\SlimHttpPsr17Factory could not instantiate a decorated response factory.'\n        );\n\n        $reflectionClass = new ReflectionClass(SlimHttpPsr17Factory::class);\n        $reflectionClass->setStaticPropertyValue('responseFactoryClass', SlimHttpPsr17Factory::class);\n\n        Psr17FactoryProvider::setFactories([SlimPsr17Factory::class]);\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(true);\n\n        AppFactory::create();\n    }\n\n    /**\n     * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests\n     */\n    public function testDetermineResponseFactoryThrowsRuntimeException()\n    {\n        $this->expectException(RuntimeException::class);\n\n        Psr17FactoryProvider::setFactories([]);\n        AppFactory::create();\n    }\n\n    public function testSetPsr17FactoryProvider()\n    {\n        $psr17FactoryProvider = new Psr17FactoryProvider();\n        $psr17FactoryProvider::setFactories([SlimPsr17Factory::class]);\n\n        AppFactory::setPsr17FactoryProvider($psr17FactoryProvider);\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n\n        $this->assertInstanceOf(SlimResponseFactory::class, AppFactory::determineResponseFactory());\n    }\n\n    /**\n     * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testResponseFactoryIsStillReturnedIfStreamFactoryIsNotAvailable()\n    {\n        Psr17FactoryProvider::setFactories([MockPsr17FactoryWithoutStreamFactory::class]);\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(true);\n\n        $app = AppFactory::create();\n\n        $this->assertInstanceOf(SlimResponseFactory::class, $app->getResponseFactory());\n    }\n\n    /**\n     * @runInSeparateProcess - AppFactory::setResponseFactory breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testAppIsCreatedWithInstancesFromSetters()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeParserProphecy = $this->prophesize(RouteParserInterface::class);\n        $routeResolverProphecy = $this->prophesize(RouteResolverInterface::class);\n        $middlewareDispatcherProphecy = $this->prophesize(MiddlewareDispatcherInterface::class);\n\n        $routeCollectorProphecy->getRouteParser()->willReturn($routeParserProphecy);\n\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n        AppFactory::setResponseFactory($responseFactoryProphecy->reveal());\n        AppFactory::setContainer($containerProphecy->reveal());\n        AppFactory::setCallableResolver($callableResolverProphecy->reveal());\n        AppFactory::setRouteCollector($routeCollectorProphecy->reveal());\n        AppFactory::setRouteResolver($routeResolverProphecy->reveal());\n        AppFactory::setMiddlewareDispatcher($middlewareDispatcherProphecy->reveal());\n\n        $app = AppFactory::create();\n\n        $this->assertSame(\n            $responseFactoryProphecy->reveal(),\n            $app->getResponseFactory()\n        );\n\n        $this->assertSame(\n            $containerProphecy->reveal(),\n            $app->getContainer()\n        );\n\n        $this->assertSame(\n            $callableResolverProphecy->reveal(),\n            $app->getCallableResolver()\n        );\n\n        $this->assertSame(\n            $routeCollectorProphecy->reveal(),\n            $app->getRouteCollector()\n        );\n\n        $this->assertSame(\n            $routeResolverProphecy->reveal(),\n            $app->getRouteResolver()\n        );\n\n        $this->assertSame(\n            $middlewareDispatcherProphecy->reveal(),\n            $app->getMiddlewareDispatcher()\n        );\n    }\n\n    /**\n     * @runInSeparateProcess - AppFactory::create saves $responseFactory into static::$responseFactory,\n     *                         this breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testAppIsCreatedWithInjectedInstancesFromFunctionArguments()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeParserProphecy = $this->prophesize(RouteParserInterface::class);\n        $routeResolverProphecy = $this->prophesize(RouteResolverInterface::class);\n\n        $routeCollectorProphecy->getRouteParser()->willReturn($routeParserProphecy->reveal());\n\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n\n        $app = AppFactory::create(\n            $responseFactoryProphecy->reveal(),\n            $containerProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $routeCollectorProphecy->reveal(),\n            $routeResolverProphecy->reveal()\n        );\n\n        $this->assertSame(\n            $responseFactoryProphecy->reveal(),\n            $app->getResponseFactory()\n        );\n\n        $this->assertSame(\n            $containerProphecy->reveal(),\n            $app->getContainer()\n        );\n\n        $this->assertSame(\n            $callableResolverProphecy->reveal(),\n            $app->getCallableResolver()\n        );\n\n        $this->assertSame(\n            $routeCollectorProphecy->reveal(),\n            $app->getRouteCollector()\n        );\n\n        $this->assertSame(\n            $routeResolverProphecy->reveal(),\n            $app->getRouteResolver()\n        );\n    }\n\n    /**\n     * @runInSeparateProcess - AppFactory::setResponseFactory breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testResponseAndStreamFactoryIsBeingInjectedInDecoratedResponseFactory()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse(200, '')\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $streamFactoryProphecy = $this->prophesize(StreamFactoryInterface::class);\n\n        AppFactory::setResponseFactory($responseFactoryProphecy->reveal());\n        AppFactory::setStreamFactory($streamFactoryProphecy->reveal());\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(true);\n\n        $app = AppFactory::create();\n\n        $responseFactory = $app->getResponseFactory();\n        $response = $responseFactory->createResponse();\n\n        $streamFactoryProperty = new ReflectionProperty(DecoratedResponse::class, 'streamFactory');\n        $this->setAccessible($streamFactoryProperty);\n\n        $this->assertSame($streamFactoryProphecy->reveal(), $streamFactoryProperty->getValue($response));\n    }\n\n    public function testCreateAppWithContainerUsesContainerDependenciesWhenPresent()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $routeResolverProphecy = $this->prophesize(RouteResolverInterface::class);\n        $routeParserProphecy = $this->prophesize(RouteParserInterface::class);\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->getRouteParser()\n            ->willReturn($routeParserProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $middlewareDispatcherProphecy = $this->prophesize(MiddlewareDispatcherInterface::class);\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $containerProphecy\n            ->has(ResponseFactoryInterface::class)\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get(ResponseFactoryInterface::class)\n            ->willReturn($responseFactoryProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(CallableResolverInterface::class)\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get(CallableResolverInterface::class)\n            ->willReturn($callableResolverProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(RouteCollectorInterface::class)\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get(RouteCollectorInterface::class)\n            ->willReturn($routeCollectorProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(RouteResolverInterface::class)\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get(RouteResolverInterface::class)\n            ->willReturn($routeResolverProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(MiddlewareDispatcherInterface::class)\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get(MiddlewareDispatcherInterface::class)\n            ->willReturn($middlewareDispatcherProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n        $app = AppFactory::createFromContainer($containerProphecy->reveal());\n\n        $this->assertSame($app->getResponseFactory(), $responseFactoryProphecy->reveal());\n        $this->assertSame($app->getContainer(), $containerProphecy->reveal());\n        $this->assertSame($app->getCallableResolver(), $callableResolverProphecy->reveal());\n        $this->assertSame($app->getRouteCollector(), $routeCollectorProphecy->reveal());\n        $this->assertSame($app->getRouteResolver(), $routeResolverProphecy->reveal());\n        $this->assertSame($app->getMiddlewareDispatcher(), $middlewareDispatcherProphecy->reveal());\n    }\n\n    public function testCreateAppWithEmptyContainer()\n    {\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $containerProphecy\n            ->has(ResponseFactoryInterface::class)\n            ->willReturn(false)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(CallableResolverInterface::class)\n            ->willReturn(false)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(RouteCollectorInterface::class)\n            ->willReturn(false)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(RouteResolverInterface::class)\n            ->willReturn(false)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->has(MiddlewareDispatcherInterface::class)\n            ->willReturn(false)\n            ->shouldBeCalledOnce();\n\n        AppFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n        AppFactory::createFromContainer($containerProphecy->reveal());\n    }\n}\n"
  },
  {
    "path": "tests/Factory/Psr17/Psr17FactoryProviderTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Factory\\Psr17;\n\nuse Slim\\Factory\\Psr17\\Psr17FactoryProvider;\nuse Slim\\Tests\\TestCase;\n\nclass Psr17FactoryProviderTest extends TestCase\n{\n    /**\n     * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testGetSetFactories()\n    {\n        Psr17FactoryProvider::setFactories([]);\n\n        $this->assertSame([], Psr17FactoryProvider::getFactories());\n    }\n\n\n    /**\n     * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testAddFactory()\n    {\n        Psr17FactoryProvider::setFactories(['Factory 1']);\n        Psr17FactoryProvider::addFactory('Factory 2');\n\n        $this->assertSame(['Factory 2', 'Factory 1'], Psr17FactoryProvider::getFactories());\n    }\n}\n"
  },
  {
    "path": "tests/Factory/Psr17/Psr17FactoryTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Factory\\Psr17;\n\nuse RuntimeException;\nuse Slim\\Tests\\Mocks\\MockPsr17Factory;\nuse Slim\\Tests\\TestCase;\n\nclass Psr17FactoryTest extends TestCase\n{\n    public function testGetResponseFactoryThrowsRuntimeException()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Slim\\\\Tests\\\\Mocks\\\\MockPsr17Factory could not instantiate a response factory.');\n\n        MockPsr17Factory::getResponseFactory();\n    }\n\n    public function testGetStreamFactoryThrowsRuntimeException()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Slim\\\\Tests\\\\Mocks\\\\MockPsr17Factory could not instantiate a stream factory.');\n\n        MockPsr17Factory::getStreamFactory();\n    }\n\n    public function testGetServerRequestCreatorThrowsRuntimeException()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('Slim\\\\Tests\\\\Mocks\\\\MockPsr17Factory' .\n                                      ' could not instantiate a server request creator.');\n\n        MockPsr17Factory::getServerRequestCreator();\n    }\n}\n"
  },
  {
    "path": "tests/Factory/Psr17/SlimHttpServerRequestCreatorTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Factory\\Psr17;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse ReflectionClass;\nuse ReflectionProperty;\nuse RuntimeException;\nuse Slim\\Factory\\Psr17\\SlimHttpServerRequestCreator;\nuse Slim\\Http\\ServerRequest;\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\nuse Slim\\Tests\\TestCase;\nuse stdClass;\n\nclass SlimHttpServerRequestCreatorTest extends TestCase\n{\n    /**\n     * We need to reset the static property of SlimHttpServerRequestCreator back to its original value\n     * Otherwise other tests will fail\n     */\n    public function tearDown(): void\n    {\n        $serverRequestCreatorProphecy = $this->prophesize(ServerRequestCreatorInterface::class);\n\n        $slimHttpServerRequestCreator = new SlimHttpServerRequestCreator($serverRequestCreatorProphecy->reveal());\n\n        $serverRequestDecoratorClassProperty = new ReflectionProperty(\n            SlimHttpServerRequestCreator::class,\n            'serverRequestDecoratorClass'\n        );\n        $this->setAccessible($serverRequestDecoratorClassProperty);\n        $serverRequestDecoratorClassProperty->setValue($slimHttpServerRequestCreator, ServerRequest::class);\n    }\n\n    public function testCreateServerRequestFromGlobals()\n    {\n        $serverRequestProphecy = $this->prophesize(ServerRequestInterface::class);\n\n        $serverRequestCreatorProphecy = $this->prophesize(ServerRequestCreatorInterface::class);\n\n        $serverRequestCreatorProphecy\n            ->createServerRequestFromGlobals()\n            ->willReturn($serverRequestProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $slimHttpServerRequestCreator = new SlimHttpServerRequestCreator($serverRequestCreatorProphecy->reveal());\n\n        $this->assertInstanceOf(ServerRequest::class, $slimHttpServerRequestCreator->createServerRequestFromGlobals());\n    }\n\n    public function testCreateServerRequestFromGlobalsThrowsRuntimeException()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('The Slim-Http ServerRequest decorator is not available.');\n\n        $serverRequestCreatorProphecy = $this->prophesize(ServerRequestCreatorInterface::class);\n\n        $slimHttpServerRequestCreator = new SlimHttpServerRequestCreator($serverRequestCreatorProphecy->reveal());\n\n        $serverRequestDecoratorClassProperty = new ReflectionProperty(\n            SlimHttpServerRequestCreator::class,\n            'serverRequestDecoratorClass'\n        );\n        $this->setAccessible($serverRequestDecoratorClassProperty);\n        $serverRequestDecoratorClassProperty->setValue($slimHttpServerRequestCreator, '');\n\n        $slimHttpServerRequestCreator->createServerRequestFromGlobals();\n    }\n\n    public function testCreateServerRequestFromGlobalsThrowsRuntimeExceptionIfNotInstanceOfServerRequestInterface()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage(\n            'Slim\\\\Factory\\\\Psr17\\\\SlimHttpServerRequestCreator could not instantiate a decorated server request.'\n        );\n\n        $serverRequestProphecy = $this->prophesize(ServerRequestInterface::class);\n\n        $serverRequestCreatorProphecy = $this->prophesize(ServerRequestCreatorInterface::class);\n        $serverRequestCreatorProphecy\n            ->createServerRequestFromGlobals()\n            ->willReturn($serverRequestProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $slimHttpServerRequestCreator = new SlimHttpServerRequestCreator($serverRequestCreatorProphecy->reveal());\n\n        $reflectionClass = new ReflectionClass(SlimHttpServerRequestCreator::class);\n        $reflectionClass->setStaticPropertyValue('serverRequestDecoratorClass', stdClass::class);\n\n        $slimHttpServerRequestCreator->createServerRequestFromGlobals();\n    }\n}\n"
  },
  {
    "path": "tests/Factory/ServerRequestCreatorFactoryTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Factory;\n\nuse GuzzleHttp\\Psr7\\ServerRequest as GuzzleServerRequest;\nuse Laminas\\Diactoros\\ServerRequest as LaminasDiactorosServerRequest;\nuse Nyholm\\Psr7\\ServerRequest as NyholmServerRequest;\nuse HttpSoft\\Message\\ServerRequest as HttpSoftServerRequest;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse RuntimeException;\nuse Slim\\Factory\\Psr17\\GuzzlePsr17Factory;\nuse Slim\\Factory\\Psr17\\HttpSoftPsr17Factory;\nuse Slim\\Factory\\Psr17\\LaminasDiactorosPsr17Factory;\nuse Slim\\Factory\\Psr17\\NyholmPsr17Factory;\nuse Slim\\Factory\\Psr17\\Psr17FactoryProvider;\nuse Slim\\Factory\\Psr17\\SlimHttpServerRequestCreator;\nuse Slim\\Factory\\Psr17\\SlimPsr17Factory;\nuse Slim\\Factory\\ServerRequestCreatorFactory;\nuse Slim\\Http\\ServerRequest;\nuse Slim\\Interfaces\\ServerRequestCreatorInterface;\nuse Slim\\Psr7\\Request as SlimServerRequest;\nuse Slim\\Tests\\TestCase;\n\nclass ServerRequestCreatorFactoryTest extends TestCase\n{\n    public static function provideImplementations()\n    {\n        return [\n            [SlimPsr17Factory::class, SlimServerRequest::class],\n            [HttpSoftPsr17Factory::class, HttpSoftServerRequest::class],\n            [NyholmPsr17Factory::class, NyholmServerRequest::class],\n            [GuzzlePsr17Factory::class, GuzzleServerRequest::class],\n            [LaminasDiactorosPsr17Factory::class, LaminasDiactorosServerRequest::class],\n        ];\n    }\n\n    /**\n     * @dataProvider provideImplementations\n     * @param string $psr17factory\n     * @param string $expectedServerRequestClass\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('provideImplementations')]\n    public function testCreateAppWithAllImplementations(string $psr17factory, string $expectedServerRequestClass)\n    {\n        Psr17FactoryProvider::setFactories([$psr17factory]);\n        ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n\n        $serverRequestCreator = ServerRequestCreatorFactory::create();\n        $serverRequest = $serverRequestCreator->createServerRequestFromGlobals();\n\n        $this->assertInstanceOf($expectedServerRequestClass, $serverRequest);\n    }\n\n    public function testDetermineServerRequestCreatorReturnsDecoratedServerRequestCreator()\n    {\n        Psr17FactoryProvider::setFactories([SlimPsr17Factory::class]);\n        ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(true);\n\n        $serverRequestCreator = ServerRequestCreatorFactory::create();\n\n        $this->assertInstanceOf(SlimHttpServerRequestCreator::class, $serverRequestCreator);\n        $this->assertInstanceOf(ServerRequest::class, $serverRequestCreator->createServerRequestFromGlobals());\n    }\n\n    /**\n     * @runInSeparateProcess - Psr17FactoryProvider::setFactories breaks other tests\n     */\n     #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testDetermineServerRequestCreatorThrowsRuntimeException()\n    {\n        $this->expectException(RuntimeException::class);\n\n        Psr17FactoryProvider::setFactories([]);\n        ServerRequestCreatorFactory::create();\n    }\n\n    public function testSetPsr17FactoryProvider()\n    {\n        $psr17FactoryProvider = new Psr17FactoryProvider();\n        $psr17FactoryProvider::setFactories([SlimPsr17Factory::class]);\n\n        ServerRequestCreatorFactory::setPsr17FactoryProvider($psr17FactoryProvider);\n        ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n\n        $serverRequestCreator = ServerRequestCreatorFactory::create();\n\n        $this->assertInstanceOf(SlimServerRequest::class, $serverRequestCreator->createServerRequestFromGlobals());\n    }\n\n    /**\n     * @runInSeparateProcess - ServerRequestCreatorFactory::setServerRequestCreator breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testSetServerRequestCreatorWithoutDecorators()\n    {\n        ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);\n        $serverRequestProphecy = $this->prophesize(ServerRequestInterface::class);\n\n        $serverRequestCreatorProphecy = $this->prophesize(ServerRequestCreatorInterface::class);\n        $serverRequestCreatorProphecy\n            ->createServerRequestFromGlobals()\n            ->willReturn($serverRequestProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        ServerRequestCreatorFactory::setServerRequestCreator($serverRequestCreatorProphecy->reveal());\n\n        $serverRequestCreator = ServerRequestCreatorFactory::create();\n\n        $this->assertSame($serverRequestProphecy->reveal(), $serverRequestCreator->createServerRequestFromGlobals());\n    }\n\n    /**\n     * @runInSeparateProcess - ServerRequestCreatorFactory::setServerRequestCreator breaks other tests\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\RunInSeparateProcess]\n    public function testSetServerRequestCreatorWithDecorators()\n    {\n        ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(true);\n        $serverRequestProphecy = $this->prophesize(ServerRequestInterface::class);\n\n        $serverRequestCreatorProphecy = $this->prophesize(ServerRequestCreatorInterface::class);\n        $serverRequestCreatorProphecy\n            ->createServerRequestFromGlobals()\n            ->willReturn($serverRequestProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        ServerRequestCreatorFactory::setServerRequestCreator($serverRequestCreatorProphecy->reveal());\n\n        $serverRequestCreator = ServerRequestCreatorFactory::create();\n\n        $this->assertInstanceOf(ServerRequest::class, $serverRequestCreator->createServerRequestFromGlobals());\n    }\n}\n"
  },
  {
    "path": "tests/Handlers/ErrorHandlerTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Handlers;\n\nuse Prophecy\\Argument;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Log\\LoggerInterface;\nuse ReflectionClass;\nuse ReflectionMethod;\nuse ReflectionProperty;\nuse RuntimeException;\nuse Slim\\Error\\Renderers\\HtmlErrorRenderer;\nuse Slim\\Error\\Renderers\\JsonErrorRenderer;\nuse Slim\\Error\\Renderers\\PlainTextErrorRenderer;\nuse Slim\\Error\\Renderers\\XmlErrorRenderer;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Handlers\\ErrorHandler;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Tests\\Mocks\\MockCustomException;\nuse Slim\\Tests\\TestCase;\n\nclass ErrorHandlerTest extends TestCase\n{\n    private function getMockLogger(): LoggerInterface\n    {\n        return $this->createMock(LoggerInterface::class);\n    }\n\n    public function testDetermineRenderer()\n    {\n        $handler = $this->createMock(ErrorHandler::class);\n        $class = new ReflectionClass(ErrorHandler::class);\n\n        $callableResolverProperty = $class->getProperty('callableResolver');\n        $this->setAccessible($callableResolverProperty);\n        $callableResolverProperty->setValue($handler, $this->getCallableResolver());\n\n        $reflectionProperty = $class->getProperty('contentType');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, 'application/json');\n\n        $method = $class->getMethod('determineRenderer');\n        $this->setAccessible($method);\n\n        $renderer = $method->invoke($handler);\n        $this->assertIsCallable($renderer);\n        $this->assertInstanceOf(JsonErrorRenderer::class, $renderer[0]);\n\n        $reflectionProperty->setValue($handler, 'application/xml');\n        $renderer = $method->invoke($handler);\n        $this->assertIsCallable($renderer);\n        $this->assertInstanceOf(XmlErrorRenderer::class, $renderer[0]);\n\n        $reflectionProperty->setValue($handler, 'text/plain');\n        $renderer = $method->invoke($handler);\n        $this->assertIsCallable($renderer);\n        $this->assertInstanceOf(PlainTextErrorRenderer::class, $renderer[0]);\n\n        // Test the default error renderer\n        $reflectionProperty->setValue($handler, 'text/unknown');\n        $renderer = $method->invoke($handler);\n        $this->assertIsCallable($renderer);\n        $this->assertInstanceOf(HtmlErrorRenderer::class, $renderer[0]);\n    }\n\n    public function testDetermineStatusCode()\n    {\n        $request = $this->createServerRequest('/');\n        $handler = $this->createMock(ErrorHandler::class);\n        $class = new ReflectionClass(ErrorHandler::class);\n\n        $reflectionProperty = $class->getProperty('responseFactory');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $this->getResponseFactory());\n\n        $reflectionProperty = $class->getProperty('exception');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, new HttpNotFoundException($request));\n\n        $method = $class->getMethod('determineStatusCode');\n        $this->setAccessible($method);\n\n        $statusCode = $method->invoke($handler);\n        $this->assertSame($statusCode, 404);\n\n        $reflectionProperty->setValue($handler, new MockCustomException());\n\n        $statusCode = $method->invoke($handler);\n        $this->assertSame($statusCode, 500);\n    }\n\n    /**\n     * Test if we can force the content type of all error handler responses.\n     */\n    public function testForceContentType()\n    {\n        $request = $this\n            ->createServerRequest('/not-defined', 'GET')\n            ->withHeader('Accept', 'text/plain,text/xml');\n\n        $handler = new ErrorHandler($this->getCallableResolver(), $this->getResponseFactory());\n        $handler->forceContentType('application/json');\n\n        $exception = new HttpNotFoundException($request);\n\n        /** @var ResponseInterface $response */\n        $response = $handler->__invoke($request, $exception, false, false, false);\n\n        $this->assertSame(['application/json'], $response->getHeader('Content-Type'));\n    }\n\n    public function testHalfValidContentType()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Content-Type', 'unknown/json+');\n\n        $handler = $this->createMock(ErrorHandler::class);\n        $newErrorRenderers = [\n            'application/xml' => XmlErrorRenderer::class,\n            'text/xml' => XmlErrorRenderer::class,\n            'text/html' => HtmlErrorRenderer::class,\n        ];\n\n        $class = new ReflectionClass(ErrorHandler::class);\n\n        $reflectionProperty = $class->getProperty('responseFactory');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $this->getResponseFactory());\n\n        $reflectionProperty = $class->getProperty('errorRenderers');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $newErrorRenderers);\n\n        $method = $class->getMethod('determineContentType');\n        $this->setAccessible($method);\n\n        $contentType = $method->invoke($handler, $request);\n\n        $this->assertNull($contentType);\n    }\n\n    public function testDetermineContentTypeTextPlainMultiAcceptHeader()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Content-Type', 'text/plain')\n            ->withHeader('Accept', 'text/plain,text/xml');\n\n        $handler = $this->createMock(ErrorHandler::class);\n\n        $errorRenderers = [\n            'text/plain' => PlainTextErrorRenderer::class,\n            'text/xml' => XmlErrorRenderer::class,\n        ];\n\n        $class = new ReflectionClass(ErrorHandler::class);\n\n        $reflectionProperty = $class->getProperty('responseFactory');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $this->getResponseFactory());\n\n        $reflectionProperty = $class->getProperty('errorRenderers');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $errorRenderers);\n\n        $method = $class->getMethod('determineContentType');\n        $this->setAccessible($method);\n\n        $contentType = $method->invoke($handler, $request);\n\n        $this->assertSame('text/xml', $contentType);\n    }\n\n    public function testDetermineContentTypeApplicationJsonOrXml()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Content-Type', 'text/json')\n            ->withHeader('Accept', 'application/xhtml+xml');\n\n        $handler = $this->createMock(ErrorHandler::class);\n\n        $errorRenderers = [\n            'application/xml' => XmlErrorRenderer::class\n        ];\n\n        $class = new ReflectionClass(ErrorHandler::class);\n\n        $reflectionProperty = $class->getProperty('responseFactory');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $this->getResponseFactory());\n\n        $reflectionProperty = $class->getProperty('errorRenderers');\n        $this->setAccessible($reflectionProperty);\n        $reflectionProperty->setValue($handler, $errorRenderers);\n\n        $method = $class->getMethod('determineContentType');\n        $this->setAccessible($method);\n\n        $contentType = $method->invoke($handler, $request);\n\n        $this->assertSame('application/xml', $contentType);\n    }\n\n    /**\n     * Ensure that an acceptable media-type is found in the Accept header even\n     * if it's not the first in the list.\n     */\n    public function testAcceptableMediaTypeIsNotFirstInList()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Accept', 'text/plain,text/html');\n\n        // provide access to the determineContentType() as it's a protected method\n        $class = new ReflectionClass(ErrorHandler::class);\n        $method = $class->getMethod('determineContentType');\n        $this->setAccessible($method);\n\n        // use a mock object here as ErrorHandler cannot be directly instantiated\n        $handler = $this->createMock(ErrorHandler::class);\n\n        // call determineContentType()\n        $return = $method->invoke($handler, $request);\n\n        $this->assertSame('text/html', $return);\n    }\n\n    public function testRegisterErrorRenderer()\n    {\n        $handler = new ErrorHandler($this->getCallableResolver(), $this->getResponseFactory());\n        $handler->registerErrorRenderer('application/slim', PlainTextErrorRenderer::class);\n\n        $reflectionClass = new ReflectionClass(ErrorHandler::class);\n        $reflectionProperty = $reflectionClass->getProperty('errorRenderers');\n        $this->setAccessible($reflectionProperty);\n        $errorRenderers = $reflectionProperty->getValue($handler);\n\n        $this->assertArrayHasKey('application/slim', $errorRenderers);\n    }\n\n    public function testSetDefaultErrorRenderer()\n    {\n        $handler = new ErrorHandler($this->getCallableResolver(), $this->getResponseFactory());\n        $handler->setDefaultErrorRenderer('text/plain', PlainTextErrorRenderer::class);\n\n        $reflectionClass = new ReflectionClass(ErrorHandler::class);\n        $reflectionProperty = $reflectionClass->getProperty('defaultErrorRenderer');\n        $this->setAccessible($reflectionProperty);\n        $defaultErrorRenderer = $reflectionProperty->getValue($handler);\n\n        $defaultErrorRendererContentTypeProperty = $reflectionClass->getProperty('defaultErrorRendererContentType');\n        $this->setAccessible($defaultErrorRendererContentTypeProperty);\n        $defaultErrorRendererContentType = $defaultErrorRendererContentTypeProperty->getValue($handler);\n\n        $this->assertSame(PlainTextErrorRenderer::class, $defaultErrorRenderer);\n        $this->assertSame('text/plain', $defaultErrorRendererContentType);\n    }\n\n    public function testOptions()\n    {\n        $request = $this->createServerRequest('/', 'OPTIONS');\n        $handler = new ErrorHandler($this->getCallableResolver(), $this->getResponseFactory());\n        $exception = new HttpMethodNotAllowedException($request);\n        $exception->setAllowedMethods(['POST', 'PUT']);\n\n        /** @var ResponseInterface $res */\n        $res = $handler->__invoke($request, $exception, true, false, true);\n\n        $this->assertSame(200, $res->getStatusCode());\n        $this->assertTrue($res->hasHeader('Allow'));\n        $this->assertSame('POST, PUT', $res->getHeaderLine('Allow'));\n    }\n\n    public function testWriteToErrorLog()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Accept', 'application/json');\n\n        $logger = $this->getMockLogger();\n\n        $handler = new ErrorHandler(\n            $this->getCallableResolver(),\n            $this->getResponseFactory(),\n            $logger\n        );\n\n        $logger->expects(self::once())\n            ->method('error')\n            ->willReturnCallback(static function (string $error) {\n                self::assertStringNotContainsString(\n                    'set \"displayErrorDetails\" to true in the ErrorHandler constructor',\n                    $error\n                );\n            });\n\n        $exception = new HttpNotFoundException($request);\n        $handler->__invoke($request, $exception, true, true, true);\n    }\n\n    public function testWriteToErrorLogShowTip()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Accept', 'application/json');\n\n        $logger = $this->getMockLogger();\n\n        $handler = new ErrorHandler(\n            $this->getCallableResolver(),\n            $this->getResponseFactory(),\n            $logger\n        );\n\n        $logger->expects(self::once())\n            ->method('error')\n            ->willReturnCallback(static function (string $error) {\n                self::assertStringContainsString(\n                    'set \"displayErrorDetails\" to true in the ErrorHandler constructor',\n                    $error\n                );\n            });\n\n        $exception = new HttpNotFoundException($request);\n        $handler->__invoke($request, $exception, false, true, true);\n    }\n\n    public function testWriteToErrorLogDoesNotShowTipIfErrorLogRendererIsNotPlainText()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Accept', 'application/json');\n\n        $logger = $this->getMockLogger();\n\n        $handler = new ErrorHandler(\n            $this->getCallableResolver(),\n            $this->getResponseFactory(),\n            $logger\n        );\n\n        $handler->setLogErrorRenderer(HtmlErrorRenderer::class);\n\n        $logger->expects(self::once())\n            ->method('error')\n            ->willReturnCallback(static function (string $error) {\n                self::assertStringNotContainsString(\n                    'set \"displayErrorDetails\" to true in the ErrorHandler constructor',\n                    $error\n                );\n            });\n\n        $exception = new HttpNotFoundException($request);\n        $handler->__invoke($request, $exception, false, true, true);\n    }\n\n    public function testDefaultErrorRenderer()\n    {\n        $request = $this\n            ->createServerRequest('/', 'GET')\n            ->withHeader('Accept', 'application/unknown');\n\n        $handler = new ErrorHandler($this->getCallableResolver(), $this->getResponseFactory());\n        $exception = new RuntimeException();\n\n        /** @var ResponseInterface $res */\n        $res = $handler->__invoke($request, $exception, true, false, true);\n\n        $this->assertTrue($res->hasHeader('Content-Type'));\n        $this->assertSame('text/html', $res->getHeaderLine('Content-Type'));\n    }\n\n    public function testLogErrorRenderer()\n    {\n        $renderer = function () {\n            return '';\n        };\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $callableResolverProphecy\n            ->resolve('logErrorRenderer')\n            ->willReturn($renderer)\n            ->shouldBeCalledOnce();\n\n        $loggerProphecy = $this->prophesize(LoggerInterface::class);\n        $loggerProphecy\n            ->error(Argument::type('string'))\n            ->shouldBeCalled();\n\n        $handler = new ErrorHandler(\n            $callableResolverProphecy->reveal(),\n            $this->getResponseFactory(),\n            $loggerProphecy->reveal()\n        );\n        $handler->setLogErrorRenderer('logErrorRenderer');\n\n        $displayErrorDetailsProperty = new ReflectionProperty($handler, 'displayErrorDetails');\n        $this->setAccessible($displayErrorDetailsProperty);\n        $displayErrorDetailsProperty->setValue($handler, true);\n\n        $exception = new RuntimeException();\n        $exceptionProperty = new ReflectionProperty($handler, 'exception');\n        $this->setAccessible($exceptionProperty);\n        $exceptionProperty->setValue($handler, $exception);\n\n        $writeToErrorLogMethod = new ReflectionMethod($handler, 'writeToErrorLog');\n        $this->setAccessible($writeToErrorLogMethod);\n        $writeToErrorLogMethod->invoke($handler);\n    }\n}\n"
  },
  {
    "path": "tests/Handlers/Strategies/RequestResponseNamedArgsTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Handlers\\Strategies;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Handlers\\Strategies\\RequestResponseNamedArgs;\nuse Slim\\Tests\\TestCase;\n\nclass RequestResponseNamedArgsTest extends TestCase\n{\n    private ServerRequestInterface $request;\n    private ResponseInterface $response;\n\n    private const PHP_8_0_VERSION_ID = 80000;\n\n    public function setUp(): void\n    {\n        $this->request = $this->createMock(ServerRequestInterface::class);\n        $this->response = $this->createMock(ResponseInterface::class);\n    }\n\n    public function testCallingWithEmptyArguments()\n    {\n        if (PHP_VERSION_ID < self::PHP_8_0_VERSION_ID) {\n            $this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');\n        }\n\n        $args = [];\n        $invocationStrategy = new RequestResponseNamedArgs();\n\n        $callback = function ($request, $response) {\n            $this->assertSame($this->request, $request);\n            $this->assertSame($this->response, $response);\n\n            return $response;\n        };\n\n        $this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));\n    }\n\n    public function testCallingWithKnownArguments()\n    {\n        if (PHP_VERSION_ID < self::PHP_8_0_VERSION_ID) {\n            $this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');\n        }\n\n        $args = [\n            'name' => 'world',\n            'greeting' => 'hello',\n        ];\n\n        $invocationStrategy = new RequestResponseNamedArgs();\n\n        $callback = function ($request, $response, $greeting, $name) use ($args) {\n            $this->assertSame($this->request, $request);\n            $this->assertSame($this->response, $response);\n            $this->assertSame($greeting, $args['greeting']);\n            $this->assertSame($name, $args['name']);\n\n            return $response;\n        };\n\n        $this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));\n    }\n\n    public function testCallingWithOptionalArguments()\n    {\n        if (PHP_VERSION_ID < 80000) {\n            $this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');\n        }\n\n        $args = [\n            'name' => 'world',\n        ];\n\n        $invocationStrategy = new RequestResponseNamedArgs();\n\n        $callback = function ($request, $response, $greeting = 'Hello', $name = 'Rob') use ($args) {\n            $this->assertSame($this->request, $request);\n            $this->assertSame($this->response, $response);\n            $this->assertSame($greeting, 'Hello');\n            $this->assertSame($name, $args['name']);\n\n            return $response;\n        };\n\n        $this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));\n    }\n\n    public function testCallingWithUnknownAndVariadic()\n    {\n        if (PHP_VERSION_ID < self::PHP_8_0_VERSION_ID) {\n            $this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');\n        }\n\n        $args = [\n            'name' => 'world',\n            'greeting' => 'hello',\n        ];\n\n        $invocationStrategy = new RequestResponseNamedArgs();\n\n        $callback = function ($request, $response, ...$arguments) use ($args) {\n            $this->assertSame($this->request, $request);\n            $this->assertSame($this->response, $response);\n            $this->assertSame($args, $arguments);\n\n            return $response;\n        };\n\n        $this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));\n    }\n\n    public function testCallingWithMixedKnownAndUnknownParametersAndVariadic()\n    {\n        if (PHP_VERSION_ID < self::PHP_8_0_VERSION_ID) {\n            $this->markTestSkipped('Named arguments are not supported in PHP versions prior to 8.0');\n        }\n\n        $known = [\n            'name' => 'world',\n            'greeting' => 'hello',\n        ];\n        $unknown = [\n            'foo' => 'foo',\n            'bar' => 'bar',\n        ];\n        $args = array_merge($known, $unknown);\n        $invocationStrategy = new RequestResponseNamedArgs();\n\n        $callback = function ($request, $response, $name, $greeting, ...$arguments) use ($known, $unknown) {\n            $this->assertSame($this->request, $request);\n            $this->assertSame($this->response, $response);\n            $this->assertSame($name, $known['name']);\n            $this->assertSame($greeting, $known['greeting']);\n            $this->assertSame($unknown, $arguments);\n\n            return $response;\n        };\n\n        $this->assertSame($this->response, $invocationStrategy($callback, $this->request, $this->response, $args));\n    }\n}\n"
  },
  {
    "path": "tests/Middleware/BodyParsingMiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Middleware;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\Middleware\\BodyParsingMiddleware;\nuse Slim\\Tests\\TestCase;\n\nuse function is_string;\nuse function simplexml_load_string;\n\nclass BodyParsingMiddlewareTest extends TestCase\n{\n    /**\n     * Create a request handler that simply assigns the $request that it receives to a public property\n     * of the returned response, so that we can then inspect that request.\n     */\n    protected function createRequestHandler(): RequestHandlerInterface\n    {\n        $response = $this->createResponse();\n        return new class ($response) implements RequestHandlerInterface {\n            private $response;\n            public $request;\n\n            public function __construct(ResponseInterface $response)\n            {\n                $this->response = $response;\n            }\n\n            public function handle(ServerRequestInterface $request): ResponseInterface\n            {\n                $this->request = $request;\n                return $this->response;\n            }\n        };\n    }\n\n    /**\n     * @param string $contentType\n     * @param string $body\n     * @return ServerRequestInterface\n     */\n    protected function createRequestWithBody($contentType, $body)\n    {\n        $request = $this->createServerRequest('/', 'POST');\n        if (is_string($contentType)) {\n            $request = $request->withHeader('Content-Type', $contentType);\n        }\n        if (is_string($body)) {\n            $request = $request->withBody($this->createStream($body));\n        }\n        return $request;\n    }\n\n\n    public static function parsingProvider()\n    {\n        return [\n            'form' => [\n                'application/x-www-form-urlencoded;charset=utf8',\n                'foo=bar',\n                ['foo' => 'bar'],\n            ],\n            'json' => [\n                \"application/json\",\n                '{\"foo\":\"bar\"}',\n                ['foo' => 'bar'],\n            ],\n            'json-with-charset' => [\n                \"application/json\\t ; charset=utf8\",\n                '{\"foo\":\"bar\"}',\n                ['foo' => 'bar'],\n            ],\n            'json-suffix' => [\n                'application/vnd.api+json;charset=utf8',\n                '{\"foo\":\"bar\"}',\n                ['foo' => 'bar'],\n            ],\n            'xml' => [\n                'application/xml',\n                '<person><name>John</name></person>',\n                simplexml_load_string('<person><name>John</name></person>'),\n            ],\n            'xml-suffix' => [\n                'application/hal+xml;charset=utf8',\n                '<person><name>John</name></person>',\n                simplexml_load_string('<person><name>John</name></person>'),\n            ],\n            'text-xml' => [\n                'text/xml',\n                '<person><name>John</name></person>',\n                simplexml_load_string('<person><name>John</name></person>'),\n            ],\n            'invalid-json' => [\n                'application/json;charset=utf8',\n                '{\"foo\"}/bar',\n                null,\n            ],\n            'valid-json-but-not-an-array' => [\n                'application/json;charset=utf8',\n                '\"foo bar\"',\n                null,\n            ],\n            'unknown-contenttype' => [\n                'text/foo+bar',\n                '\"foo bar\"',\n                null,\n            ],\n            'empty-contenttype' => [\n                '',\n                '\"foo bar\"',\n                null,\n            ],\n            'no-contenttype' => [\n                null,\n                '\"foo bar\"',\n                null,\n            ],\n            'invalid-contenttype' => [\n                'foo',\n                '\"foo bar\"',\n                null,\n            ],\n            'invalid-xml' => [\n                'application/xml',\n                '<person><name>John</name></invalid>',\n                null,\n            ],\n            'invalid-textxml' => [\n                'text/xml',\n                '<person><name>John</name></invalid>',\n                null,\n            ],\n        ];\n    }\n\n    /**\n     * @dataProvider parsingProvider\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('parsingProvider')]\n    public function testParsing($contentType, $body, $expected)\n    {\n        $request = $this->createRequestWithBody($contentType, $body);\n\n        $middleware = new BodyParsingMiddleware();\n        $requestHandler = $this->createRequestHandler();\n        $middleware->process($request, $requestHandler);\n\n        $this->assertEquals($expected, $requestHandler->request->getParsedBody());\n    }\n\n    public function testParsingWithARegisteredParser()\n    {\n        $request = $this->createRequestWithBody('application/vnd.api+json', '{\"foo\":\"bar\"}');\n\n        $parsers = [\n            'application/vnd.api+json' => function ($input) {\n                return ['data' => $input];\n            },\n        ];\n        $middleware = new BodyParsingMiddleware($parsers);\n        $requestHandler = $this->createRequestHandler();\n        $middleware->process($request, $requestHandler);\n\n        $this->assertSame(['data' => '{\"foo\":\"bar\"}'], $requestHandler->request->getParsedBody());\n    }\n\n    public function testParsingFailsWhenAnInvalidTypeIsReturned()\n    {\n        $request = $this->createRequestWithBody('application/json;charset=utf8', '{\"foo\":\"bar\"}');\n\n        $parsers = [\n            'application/json' => function ($input) {\n                return 10; // invalid - should return null, array or object\n            },\n        ];\n        $middleware = new BodyParsingMiddleware($parsers);\n\n        $this->expectException(RuntimeException::class);\n        $middleware->process($request, $this->createRequestHandler());\n    }\n\n    public function testSettingAndGettingAParser()\n    {\n        $middleware = new BodyParsingMiddleware();\n        $parser = function ($input) {\n            return ['data' => $input];\n        };\n\n        $this->assertFalse($middleware->hasBodyParser('text/foo'));\n\n        $middleware->registerBodyParser('text/foo', $parser);\n        $this->assertTrue($middleware->hasBodyParser('text/foo'));\n\n        $this->assertSame($parser, $middleware->getBodyParser('text/foo'));\n    }\n\n    public function testGettingUnknownParser()\n    {\n        $middleware = new BodyParsingMiddleware();\n\n        $this->expectException(RuntimeException::class);\n        $middleware->getBodyParser('text/foo');\n    }\n}\n"
  },
  {
    "path": "tests/Middleware/ContentLengthMiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Middleware;\n\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Slim\\Middleware\\ContentLengthMiddleware;\nuse Slim\\Tests\\TestCase;\n\nclass ContentLengthMiddlewareTest extends TestCase\n{\n    public function testAddsContentLength()\n    {\n        $request = $this->createServerRequest('/');\n        $responseFactory = $this->getResponseFactory();\n\n        $mw = function ($request, $handler) use ($responseFactory) {\n            $response = $responseFactory->createResponse();\n            $response->getBody()->write('Body');\n            return $response;\n        };\n        $mw2 = new ContentLengthMiddleware();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandlerInterface::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($mw);\n        $middlewareDispatcher->addMiddleware($mw2);\n        $response = $middlewareDispatcher->handle($request);\n\n        $this->assertSame('4', $response->getHeaderLine('Content-Length'));\n    }\n}\n"
  },
  {
    "path": "tests/Middleware/ErrorMiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Middleware;\n\nuse Error;\nuse InvalidArgumentException;\nuse LogicException;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Log\\LoggerInterface;\nuse RuntimeException;\nuse Slim\\App;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Handlers\\ErrorHandler;\nuse Slim\\Middleware\\ErrorMiddleware;\nuse Slim\\Middleware\\RoutingMiddleware;\nuse Slim\\Tests\\Mocks\\MockCustomException;\nuse Slim\\Tests\\TestCase;\n\nclass ErrorMiddlewareTest extends TestCase\n{\n    private function getMockLogger(): LoggerInterface\n    {\n        return $this->createMock(LoggerInterface::class);\n    }\n\n    public function testSetErrorHandler()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $routingMiddleware = new RoutingMiddleware(\n            $app->getRouteResolver(),\n            $app->getRouteCollector()->getRouteParser()\n        );\n        $app->add($routingMiddleware);\n\n        $exception = HttpNotFoundException::class;\n        $handler = (function () {\n            $response = $this->createResponse(500);\n            $response->getBody()->write('Oops..');\n            return $response;\n        })->bindTo($this);\n\n        $errorMiddleware = new ErrorMiddleware(\n            $callableResolver,\n            $this->getResponseFactory(),\n            false,\n            false,\n            false,\n            $logger\n        );\n        $errorMiddleware->setErrorHandler($exception, $handler);\n        $app->add($errorMiddleware);\n\n        $request = $this->createServerRequest('/foo/baz/');\n        $app->run($request);\n\n        $this->expectOutputString('Oops..');\n    }\n\n    public function testSetDefaultErrorHandler()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $routingMiddleware = new RoutingMiddleware(\n            $app->getRouteResolver(),\n            $app->getRouteCollector()->getRouteParser()\n        );\n        $app->add($routingMiddleware);\n\n        $handler = (function () {\n            $response = $this->createResponse();\n            $response->getBody()->write('Oops..');\n            return $response;\n        })->bindTo($this);\n\n        $errorMiddleware = new ErrorMiddleware(\n            $callableResolver,\n            $this->getResponseFactory(),\n            false,\n            false,\n            false,\n            $logger\n        );\n        $errorMiddleware->setDefaultErrorHandler($handler);\n        $app->add($errorMiddleware);\n\n        $request = $this->createServerRequest('/foo/baz/');\n        $app->run($request);\n\n        $this->expectOutputString('Oops..');\n    }\n\n    public function testSetDefaultErrorHandlerThrowsException()\n    {\n        $this->expectException(RuntimeException::class);\n\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $errorMiddleware = new ErrorMiddleware(\n            $callableResolver,\n            $this->getResponseFactory(),\n            false,\n            false,\n            false,\n            $logger\n        );\n        $errorMiddleware->setDefaultErrorHandler('Uncallable');\n        $errorMiddleware->getDefaultErrorHandler();\n    }\n\n    public function testGetErrorHandlerWillReturnDefaultErrorHandlerForUnhandledExceptions()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $middleware = new ErrorMiddleware($callableResolver, $this->getResponseFactory(), false, false, false, $logger);\n        $exception = MockCustomException::class;\n        $handler = $middleware->getErrorHandler($exception);\n        $this->assertInstanceOf(ErrorHandler::class, $handler);\n    }\n\n    public function testSuperclassExceptionHandlerHandlesExceptionWithSubclassExactMatch()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n        $middleware = new ErrorMiddleware($callableResolver, $this->getResponseFactory(), false, false, false, $logger);\n        $app->add(function ($request, $handler) {\n            throw new LogicException('This is a LogicException...');\n        });\n        $middleware->setErrorHandler(LogicException::class, (function (ServerRequestInterface $request, $exception) {\n            $response = $this->createResponse();\n            $response->getBody()->write($exception->getMessage());\n            return $response;\n        })->bindTo($this), true); // - true; handle subclass but also LogicException explicitly\n        $middleware->setDefaultErrorHandler((function () {\n            $response = $this->createResponse();\n            $response->getBody()->write('Oops..');\n            return $response;\n        })->bindTo($this));\n        $app->add($middleware);\n        $app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('...');\n            return $response;\n        });\n        $request = $this->createServerRequest('/foo');\n        $app->run($request);\n        $this->expectOutputString('This is a LogicException...');\n    }\n\n    public function testSuperclassExceptionHandlerHandlesSubclassException()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $middleware = new ErrorMiddleware($callableResolver, $this->getResponseFactory(), false, false, false, $logger);\n\n        $app->add(function ($request, $handler) {\n            throw new InvalidArgumentException('This is a subclass of LogicException...');\n        });\n\n        $middleware->setErrorHandler(LogicException::class, (function (ServerRequestInterface $request, $exception) {\n            $response = $this->createResponse();\n            $response->getBody()->write($exception->getMessage());\n            return $response;\n        })->bindTo($this), true); // - true; handle subclass\n\n        $middleware->setDefaultErrorHandler((function () {\n            $response = $this->createResponse();\n            $response->getBody()->write('Oops..');\n            return $response;\n        })->bindTo($this));\n\n        $app->add($middleware);\n\n        $app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('...');\n            return $response;\n        });\n\n        $request = $this->createServerRequest('/foo');\n        $app->run($request);\n\n        $this->expectOutputString('This is a subclass of LogicException...');\n    }\n\n    public function testSuperclassExceptionHandlerDoesNotHandleSubclassException()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $middleware = new ErrorMiddleware($callableResolver, $this->getResponseFactory(), false, false, false, $logger);\n\n        $app->add(function ($request, $handler) {\n            throw new InvalidArgumentException('This is a subclass of LogicException...');\n        });\n\n        $middleware->setErrorHandler(LogicException::class, (function (ServerRequestInterface $request, $exception) {\n            $response = $this->createResponse();\n            $response->getBody()->write($exception->getMessage());\n            return $response;\n        })->bindTo($this), false); // - false; don't handle subclass\n\n        $middleware->setDefaultErrorHandler((function () {\n            $response = $this->createResponse();\n            $response->getBody()->write('Oops..');\n            return $response;\n        })->bindTo($this));\n\n        $app->add($middleware);\n\n        $app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('...');\n            return $response;\n        });\n\n        $request = $this->createServerRequest('/foo');\n        $app->run($request);\n\n        $this->expectOutputString('Oops..');\n    }\n\n    public function testHandleMultipleExceptionsAddedAsArray()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $middleware = new ErrorMiddleware($callableResolver, $this->getResponseFactory(), false, false, false, $logger);\n\n        $app->add(function ($request, $handler) {\n            throw new InvalidArgumentException('This is an invalid argument exception...');\n        });\n\n        $handler = (function (ServerRequestInterface $request, $exception) {\n            $response = $this->createResponse();\n            $response->getBody()->write($exception->getMessage());\n            return $response;\n        });\n\n        $middleware->setErrorHandler([LogicException::class, InvalidArgumentException::class], $handler->bindTo($this));\n\n        $middleware->setDefaultErrorHandler((function () {\n            $response = $this->createResponse();\n            $response->getBody()->write('Oops..');\n            return $response;\n        })->bindTo($this));\n\n        $app->add($middleware);\n\n        $app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('...');\n            return $response;\n        });\n\n        $request = $this->createServerRequest('/foo');\n        $app->run($request);\n\n        $this->expectOutputString('This is an invalid argument exception...');\n    }\n\n    public function testErrorHandlerHandlesThrowables()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $app = new App($responseFactory);\n        $callableResolver = $app->getCallableResolver();\n        $logger = $this->getMockLogger();\n\n        $middleware = new ErrorMiddleware($callableResolver, $this->getResponseFactory(), false, false, false, $logger);\n\n        $app->add(function ($request, $handler) {\n            throw new Error('Oops..');\n        });\n\n        $middleware->setDefaultErrorHandler((function (ServerRequestInterface $request, $exception) {\n            $response = $this->createResponse();\n            $response->getBody()->write($exception->getMessage());\n            return $response;\n        })->bindTo($this));\n\n        $app->add($middleware);\n\n        $app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('...');\n            return $response;\n        });\n\n        $request = $this->createServerRequest('/foo');\n        $app->run($request);\n\n        $this->expectOutputString('Oops..');\n    }\n}\n"
  },
  {
    "path": "tests/Middleware/MethodOverrideMiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Middleware;\n\nuse Psr\\Http\\Message\\ServerRequestInterface as Request;\nuse Psr\\Http\\Message\\StreamInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface as RequestHandler;\nuse Slim\\Middleware\\MethodOverrideMiddleware;\nuse Slim\\Tests\\TestCase;\n\nclass MethodOverrideMiddlewareTest extends TestCase\n{\n    public function testHeader()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = (function (Request $request, RequestHandler $handler) use ($responseFactory) {\n            $this->assertSame('PUT', $request->getMethod());\n            return $responseFactory->createResponse();\n        })->bindTo($this);\n        $methodOverrideMiddleware = new MethodOverrideMiddleware();\n\n        $request = $this\n            ->createServerRequest('/', 'POST')\n            ->withHeader('X-Http-Method-Override', 'PUT');\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandler::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($methodOverrideMiddleware);\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testBodyParam()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = (function (Request $request, RequestHandler $handler) use ($responseFactory) {\n            $this->assertSame('PUT', $request->getMethod());\n            return $responseFactory->createResponse();\n        })->bindTo($this);\n\n        $methodOverrideMiddleware = new MethodOverrideMiddleware();\n\n        $request = $this\n            ->createServerRequest('/', 'POST')\n            ->withParsedBody(['_METHOD' => 'PUT']);\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandler::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($methodOverrideMiddleware);\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testHeaderPreferred()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = (function (Request $request, RequestHandler $handler) use ($responseFactory) {\n            $this->assertSame('DELETE', $request->getMethod());\n            return $responseFactory->createResponse();\n        })->bindTo($this);\n\n        $methodOverrideMiddleware = new MethodOverrideMiddleware();\n\n        $request = $this\n            ->createServerRequest('/', 'POST')\n            ->withHeader('X-Http-Method-Override', 'DELETE')\n            ->withParsedBody((object) ['_METHOD' => 'PUT']);\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandler::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($methodOverrideMiddleware);\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testNoOverride()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = (function (Request $request, RequestHandler $handler) use ($responseFactory) {\n            $this->assertSame('POST', $request->getMethod());\n            return $responseFactory->createResponse();\n        })->bindTo($this);\n\n        $methodOverrideMiddleware = new MethodOverrideMiddleware();\n\n        $request = $this->createServerRequest('/', 'POST');\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandler::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($methodOverrideMiddleware);\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testNoOverrideRewindEofBodyStream()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = (function (Request $request, RequestHandler $handler) use ($responseFactory) {\n            $this->assertSame('POST', $request->getMethod());\n            return $responseFactory->createResponse();\n        })->bindTo($this);\n\n        $methodOverrideMiddleware = new MethodOverrideMiddleware();\n\n        $request = $this->createServerRequest('/', 'POST');\n\n        // Prophesize the body stream for which `eof()` returns `true` and the\n        // `rewind()` has to be called.\n        $bodyProphecy = $this->prophesize(StreamInterface::class);\n        /** @noinspection PhpUndefinedMethodInspection */\n        $bodyProphecy->eof()\n            ->willReturn(true)\n            ->shouldBeCalled();\n        /** @noinspection PhpUndefinedMethodInspection */\n        $bodyProphecy->rewind()\n            ->shouldBeCalled();\n        /** @var StreamInterface $body */\n        $body = $bodyProphecy->reveal();\n        $request = $request->withBody($body);\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandler::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($methodOverrideMiddleware);\n        $middlewareDispatcher->handle($request);\n    }\n}\n"
  },
  {
    "path": "tests/Middleware/OutputBufferingMiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Middleware;\n\nuse Exception;\nuse InvalidArgumentException;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse ReflectionProperty;\nuse Slim\\Middleware\\OutputBufferingMiddleware;\nuse Slim\\Tests\\TestCase;\n\nuse function ob_get_contents;\n\nclass OutputBufferingMiddlewareTest extends TestCase\n{\n    public function testStyleDefaultValid()\n    {\n        $middleware = new OutputBufferingMiddleware($this->getStreamFactory());\n\n        $reflectionProperty = new ReflectionProperty($middleware, 'style');\n        $this->setAccessible($reflectionProperty);\n        $value = $reflectionProperty->getValue($middleware);\n\n        $this->assertSame('append', $value);\n    }\n\n    public function testStyleCustomValid()\n    {\n        $middleware = new OutputBufferingMiddleware($this->getStreamFactory(), 'prepend');\n\n        $reflectionProperty = new ReflectionProperty($middleware, 'style');\n        $this->setAccessible($reflectionProperty);\n        $value = $reflectionProperty->getValue($middleware);\n\n        $this->assertSame('prepend', $value);\n    }\n\n    public function testStyleCustomInvalid()\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        new OutputBufferingMiddleware($this->getStreamFactory(), 'foo');\n    }\n\n    public function testAppend()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = function ($request, $handler) use ($responseFactory) {\n            $response = $responseFactory->createResponse();\n            $response->getBody()->write('Body');\n            echo 'Test';\n\n            return $response;\n        };\n        $outputBufferingMiddleware = new OutputBufferingMiddleware($this->getStreamFactory(), 'append');\n\n        $request = $this->createServerRequest('/', 'GET');\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandlerInterface::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($outputBufferingMiddleware);\n        $response = $middlewareDispatcher->handle($request);\n\n        $this->assertSame('BodyTest', (string) $response->getBody());\n    }\n\n    public function testPrepend()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = function ($request, $handler) use ($responseFactory) {\n            $response = $responseFactory->createResponse();\n            $response->getBody()->write('Body');\n            echo 'Test';\n\n            return $response;\n        };\n        $outputBufferingMiddleware = new OutputBufferingMiddleware($this->getStreamFactory(), 'prepend');\n\n        $request = $this->createServerRequest('/', 'GET');\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandlerInterface::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($outputBufferingMiddleware);\n        $response = $middlewareDispatcher->handle($request);\n\n        $this->assertSame('TestBody', (string) $response->getBody());\n    }\n\n    public function testOutputBufferIsCleanedWhenThrowableIsCaught()\n    {\n        $this->getResponseFactory();\n        $middleware = (function ($request, $handler) {\n            echo \"Test\";\n            $this->assertSame('Test', ob_get_contents());\n            throw new Exception('Oops...');\n        })->bindTo($this);\n        $outputBufferingMiddleware = new OutputBufferingMiddleware($this->getStreamFactory(), 'prepend');\n\n        $request = $this->createServerRequest('/', 'GET');\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandlerInterface::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($outputBufferingMiddleware);\n\n        try {\n            $middlewareDispatcher->handle($request);\n        } catch (Exception $e) {\n            $this->assertSame('', ob_get_contents());\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Middleware/RoutingMiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Middleware;\n\nuse FastRoute\\Dispatcher;\nuse Prophecy\\Argument;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\CallableResolver;\nuse Slim\\Exception\\HttpMethodNotAllowedException;\nuse Slim\\Exception\\HttpNotFoundException;\nuse Slim\\Interfaces\\RouteParserInterface;\nuse Slim\\Interfaces\\RouteResolverInterface;\nuse Slim\\Middleware\\RoutingMiddleware;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RouteContext;\nuse Slim\\Routing\\RouteParser;\nuse Slim\\Routing\\RouteResolver;\nuse Slim\\Routing\\RoutingResults;\nuse Slim\\Tests\\TestCase;\n\nclass RoutingMiddlewareTest extends TestCase\n{\n    protected function getRouteCollector()\n    {\n        $callableResolver = new CallableResolver();\n        $responseFactory = $this->getResponseFactory();\n        $routeCollector = new RouteCollector($responseFactory, $callableResolver);\n        $routeCollector->map(['GET'], '/hello/{name}', null);\n        return $routeCollector;\n    }\n\n    public function testRouteIsStoredOnSuccessfulMatch()\n    {\n        $responseFactory = $this->getResponseFactory();\n        $middleware = (function (ServerRequestInterface $request) use ($responseFactory) {\n            // route is available\n            $route = $request->getAttribute(RouteContext::ROUTE);\n            $this->assertNotNull($route);\n            $this->assertSame('foo', $route->getArgument('name'));\n\n            // routeParser is available\n            $routeParser = $request->getAttribute(RouteContext::ROUTE_PARSER);\n            $this->assertNotNull($routeParser);\n            $this->assertInstanceOf(RouteParserInterface::class, $routeParser);\n\n            // routingResults is available\n            $routingResults = $request->getAttribute(RouteContext::ROUTING_RESULTS);\n            $this->assertInstanceOf(RoutingResults::class, $routingResults);\n            return $responseFactory->createResponse();\n        })->bindTo($this);\n\n        $routeCollector = $this->getRouteCollector();\n        $routeParser = new RouteParser($routeCollector);\n        $routeResolver = new RouteResolver($routeCollector);\n        $routingMiddleware = new RoutingMiddleware($routeResolver, $routeParser);\n\n        $request = $this->createServerRequest('https://example.com:443/hello/foo', 'GET');\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $this->createMock(RequestHandlerInterface::class),\n            null\n        );\n        $middlewareDispatcher->addCallable($middleware);\n        $middlewareDispatcher->addMiddleware($routingMiddleware);\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testRouteIsNotStoredOnMethodNotAllowed()\n    {\n        $routeCollector = $this->getRouteCollector();\n        $routeParser = new RouteParser($routeCollector);\n        $routeResolver = new RouteResolver($routeCollector);\n        $routingMiddleware = new RoutingMiddleware($routeResolver, $routeParser);\n\n        $request = $this->createServerRequest('https://example.com:443/hello/foo', 'POST');\n        $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class);\n        /** @var RequestHandlerInterface $requestHandler */\n        $requestHandler = $requestHandlerProphecy->reveal();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($requestHandler, null);\n        $middlewareDispatcher->addMiddleware($routingMiddleware);\n\n        try {\n            $middlewareDispatcher->handle($request);\n            $this->fail('HTTP method should not have been allowed');\n        } catch (HttpMethodNotAllowedException $exception) {\n            $request = $exception->getRequest();\n\n            // route is not available\n            $route = $request->getAttribute(RouteContext::ROUTE);\n            $this->assertNull($route);\n\n            // routeParser is available\n            $routeParser = $request->getAttribute(RouteContext::ROUTE_PARSER);\n            $this->assertNotNull($routeParser);\n            $this->assertInstanceOf(RouteParserInterface::class, $routeParser);\n\n            // routingResults is available\n            $routingResults = $request->getAttribute(RouteContext::ROUTING_RESULTS);\n            $this->assertInstanceOf(RoutingResults::class, $routingResults);\n            $this->assertSame(Dispatcher::METHOD_NOT_ALLOWED, $routingResults->getRouteStatus());\n        }\n    }\n\n    public function testRouteIsNotStoredOnNotFound()\n    {\n        $routeCollector = $this->getRouteCollector();\n        $routeParser = new RouteParser($routeCollector);\n        $routeResolver = new RouteResolver($routeCollector);\n        $routingMiddleware = new RoutingMiddleware($routeResolver, $routeParser);\n\n        $request = $this->createServerRequest('https://example.com:443/goodbye', 'GET');\n        $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class);\n        /** @var RequestHandlerInterface $requestHandler */\n        $requestHandler = $requestHandlerProphecy->reveal();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($requestHandler, null);\n        $middlewareDispatcher->addMiddleware($routingMiddleware);\n\n        try {\n            $middlewareDispatcher->handle($request);\n            $this->fail('HTTP route should not have been found');\n        } catch (HttpNotFoundException $exception) {\n            $request = $exception->getRequest();\n\n            // route is not available\n            $route = $request->getAttribute(RouteContext::ROUTE);\n            $this->assertNull($route);\n\n            // routeParser is available\n            $routeParser = $request->getAttribute(RouteContext::ROUTE_PARSER);\n            $this->assertNotNull($routeParser);\n            $this->assertInstanceOf(RouteParserInterface::class, $routeParser);\n\n            // routingResults is available\n            $routingResults = $request->getAttribute(RouteContext::ROUTING_RESULTS);\n            $this->assertInstanceOf(RoutingResults::class, $routingResults);\n            $this->assertSame(Dispatcher::NOT_FOUND, $routingResults->getRouteStatus());\n        }\n    }\n\n    public function testPerformRoutingThrowsExceptionOnInvalidRoutingResultsRouteStatus()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('An unexpected error occurred while performing routing.');\n\n        // Prophesize the `RoutingResults` instance that would return an invalid route\n        // status when the method `getRouteStatus()` gets called.\n        $routingResultsProphecy = $this->prophesize(RoutingResults::class);\n        /** @noinspection PhpUndefinedMethodInspection */\n        $routingResultsProphecy->getRouteStatus()\n            ->willReturn(-1)\n            ->shouldBeCalledOnce();\n        /** @var RoutingResults $routingResults */\n        $routingResults = $routingResultsProphecy->reveal();\n\n        // Prophesize the `RouteParserInterface` instance will be created.\n        $routeParserProphecy = $this->prophesize(RouteParser::class);\n        /** @var RouteParserInterface $routeParser */\n        $routeParser = $routeParserProphecy->reveal();\n\n        // Prophesize the `RouteResolverInterface` that would return the `RoutingResults`\n        // defined above, when the method `computeRoutingResults()` gets called.\n        $routeResolverProphecy = $this->prophesize(RouteResolverInterface::class);\n        /** @noinspection PhpUndefinedMethodInspection */\n        $routeResolverProphecy->computeRoutingResults(Argument::any(), Argument::any())\n            ->willReturn($routingResults)\n            ->shouldBeCalled();\n        /** @var RouteResolverInterface $routeResolver */\n        $routeResolver = $routeResolverProphecy->reveal();\n\n        // Create the server request.\n        $request = $this->createServerRequest('https://example.com:443/hello/foo', 'GET');\n\n        // Create the routing middleware with the `RouteResolverInterface` defined\n        // above. Perform the routing, which should throw the RuntimeException.\n        $middleware = new RoutingMiddleware($routeResolver, $routeParser);\n        /** @noinspection PhpUnhandledExceptionInspection */\n        $middleware->performRouting($request);\n    }\n}\n"
  },
  {
    "path": "tests/MiddlewareDispatcherTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests;\n\nuse Prophecy\\Argument;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Tests\\Mocks\\MockMiddlewareSlimCallable;\nuse Slim\\Tests\\Mocks\\MockMiddlewareWithConstructor;\nuse Slim\\Tests\\Mocks\\MockMiddlewareWithoutConstructor;\nuse Slim\\Tests\\Mocks\\MockRequestHandler;\nuse Slim\\Tests\\Mocks\\MockSequenceMiddleware;\nuse stdClass;\n\nclass MiddlewareDispatcherTest extends TestCase\n{\n    public static function setUpBeforeClass(): void\n    {\n        function testProcessRequest(ServerRequestInterface $request, RequestHandlerInterface $handler)\n        {\n            return $handler->handle($request);\n        }\n    }\n\n    public function testAddMiddleware(): void\n    {\n        $responseFactory = $this->getResponseFactory();\n        $callable = function ($request, $handler) use ($responseFactory) {\n            return $responseFactory->createResponse();\n        };\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestHandlerProphecy = $this->prophesize(RequestHandlerInterface::class);\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($requestHandlerProphecy->reveal());\n        $middlewareDispatcher->add($callable);\n\n        $response = $middlewareDispatcher->handle($requestProphecy->reveal());\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n    }\n\n    public function testNamedFunctionIsResolved(): void\n    {\n        $handler = new MockRequestHandler();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null);\n        $middlewareDispatcher->addDeferred(__NAMESPACE__ . '\\testProcessRequest');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public function testDeferredResolvedCallable(): void\n    {\n        $callable = function (ServerRequestInterface $request, RequestHandlerInterface $handler) {\n            return $handler->handle($request);\n        };\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $containerProphecy\n            ->has('callable')\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get('callable')\n            ->willReturn($callable)\n            ->shouldBeCalledOnce();\n\n        $handler = new MockRequestHandler();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, $containerProphecy->reveal());\n        $middlewareDispatcher->addDeferred('callable');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public function testDeferredResolvedCallableWithoutContainerAndNonAdvancedCallableResolver(): void\n    {\n        $callable = function (ServerRequestInterface $request, RequestHandlerInterface $handler) {\n            return $handler->handle($request);\n        };\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $callableResolverProphecy\n            ->resolve('callable')\n            ->willReturn($callable)\n            ->shouldBeCalledOnce();\n\n        $handler = new MockRequestHandler();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null, $callableResolverProphecy->reveal());\n        $middlewareDispatcher->addDeferred('callable');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public function testDeferredResolvedCallableWithDirectConstructorCall(): void\n    {\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $callableResolverProphecy\n            ->resolve(MockMiddlewareWithoutConstructor::class)\n            ->willThrow(new RuntimeException('Callable not available from resolver'))\n            ->shouldBeCalledOnce();\n\n        $handler = new MockRequestHandler();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null, $callableResolverProphecy->reveal());\n        $middlewareDispatcher->addDeferred(MockMiddlewareWithoutConstructor::class);\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public static function deferredCallableProvider(): array\n    {\n        return [\n            [MockMiddlewareSlimCallable::class . ':custom', new MockMiddlewareSlimCallable()],\n            ['MiddlewareInstance', new MockMiddlewareWithoutConstructor()],\n            ['NamedFunction', __NAMESPACE__ . '\\testProcessRequest'],\n            ['Callable', function (ServerRequestInterface $request, RequestHandlerInterface $handler) {\n                return $handler->handle($request);\n            }],\n            ['MiddlewareInterfaceNotImplemented', 'MiddlewareInterfaceNotImplemented']\n        ];\n    }\n\n    /**\n     * @dataProvider deferredCallableProvider\n     *\n     * @param string $callable\n     * @param callable|MiddlewareInterface\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('deferredCallableProvider')]\n    public function testDeferredResolvedCallableWithContainerAndNonAdvancedCallableResolverUnableToResolveCallable(\n        $callable,\n        $result\n    ): void {\n        if ($callable === 'MiddlewareInterfaceNotImplemented') {\n            $this->expectException(RuntimeException::class);\n            $this->expectExceptionMessage('Middleware MiddlewareInterfaceNotImplemented is not resolvable');\n        }\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $callableResolverProphecy\n            ->resolve($callable)\n            ->willThrow(RuntimeException::class)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $containerProphecy\n            ->has(Argument::any())\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get(Argument::any())\n            ->willReturn($result)\n            ->shouldBeCalledOnce();\n\n        $handler = new MockRequestHandler();\n\n        $middlewareDispatcher = $this->createMiddlewareDispatcher(\n            $handler,\n            $containerProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n        $middlewareDispatcher->addDeferred($callable);\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public function testDeferredResolvedSlimCallable(): void\n    {\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null);\n        $middlewareDispatcher->addDeferred(MockMiddlewareSlimCallable::class . ':custom');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public function testDeferredResolvedClosureIsBoundToContainer(): void\n    {\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $self = $this;\n        $callable = function (\n            ServerRequestInterface $request,\n            RequestHandlerInterface $handler\n        ) use ($self) {\n            $self->assertInstanceOf(ContainerInterface::class, $this);\n            return $handler->handle($request);\n        };\n\n        $containerProphecy->has('callable')->willReturn(true);\n        $containerProphecy->get('callable')->willReturn($callable);\n\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, $containerProphecy->reveal());\n        $middlewareDispatcher->addDeferred('callable');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testAddCallableBindsClosureToContainer(): void\n    {\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $self = $this;\n        $callable = function (\n            ServerRequestInterface $request,\n            RequestHandlerInterface $handler\n        ) use (\n            $self,\n            $containerProphecy\n        ) {\n            $self->assertSame($containerProphecy->reveal(), $this);\n            return $handler->handle($request);\n        };\n\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, $containerProphecy->reveal());\n        $middlewareDispatcher->addCallable($callable);\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testResolvableReturnsInstantiatedObject(): void\n    {\n        MockMiddlewareWithoutConstructor::$CalledCount = 0;\n\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null);\n        $middlewareDispatcher->addDeferred(MockMiddlewareWithoutConstructor::class);\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n\n        $this->assertSame(1, MockMiddlewareWithoutConstructor::$CalledCount);\n        $this->assertSame(1, $handler->getCalledCount());\n    }\n\n    public function testResolveThrowsExceptionWhenResolvableDoesNotImplementMiddlewareInterface(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage('MiddlewareInterfaceNotImplemented is not resolvable');\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $containerProphecy\n            ->has('MiddlewareInterfaceNotImplemented')\n            ->willReturn(true)\n            ->shouldBeCalledOnce();\n\n        $containerProphecy\n            ->get('MiddlewareInterfaceNotImplemented')\n            ->willReturn(new stdClass())\n            ->shouldBeCalledOnce();\n\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, $containerProphecy->reveal());\n        $middlewareDispatcher->addDeferred('MiddlewareInterfaceNotImplemented');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testResolveThrowsExceptionWithoutContainerAndUnresolvableClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessageMatches('/(Middleware|Callable) Unresolvable::class does not exist/');\n\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null);\n        $middlewareDispatcher->addDeferred('Unresolvable::class');\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testResolveThrowsExceptionWithoutContainerNonAdvancedCallableResolverAndUnresolvableClass(): void\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessageMatches('/(Middleware|Callable) Unresolvable::class does not exist/');\n\n        $unresolvable = 'Unresolvable::class';\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $callableResolverProphecy\n            ->resolve($unresolvable)\n            ->willThrow(RuntimeException::class)\n            ->shouldBeCalledOnce();\n\n        $handler = new MockRequestHandler();\n        $middlewareDispatcher = $this->createMiddlewareDispatcher($handler, null, $callableResolverProphecy->reveal());\n        $middlewareDispatcher->addDeferred($unresolvable);\n\n        $request = $this->createServerRequest('/');\n        $middlewareDispatcher->handle($request);\n    }\n\n    public function testExecutesKernelWithEmptyMiddlewareStack(): void\n    {\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))->willReturn($responseProphecy->reveal());\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, null);\n\n        $response = $dispatcher->handle($requestProphecy->reveal());\n\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))->shouldHaveBeenCalled();\n        $this->assertSame($responseProphecy->reveal(), $response);\n    }\n\n    public function testExecutesMiddlewareLastInFirstOut(): void\n    {\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->getHeader(Argument::type('string'))->willReturn([]);\n        $requestProphecy->withAddedHeader(Argument::type('string'), Argument::type('string'))->will(function ($args) {\n            $headers = $this->reveal()->getHeader($args[0]);\n\n            $headers[] = $args[1];\n            $this->getHeader($args[0])->willReturn($headers);\n            $this->hasHeader($args[0])->willReturn(true);\n            return $this;\n        });\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy->getHeader(Argument::type('string'))->willReturn([]);\n        $responseProphecy->withHeader(Argument::type('string'), Argument::type('array'))->will(function ($args) {\n            $this->getHeader($args[0])->willReturn($args[1]);\n            $this->hasHeader($args[0])->willReturn(true);\n            return $this;\n        });\n        $responseProphecy->withAddedHeader(Argument::type('string'), Argument::type('string'))->will(function ($args) {\n            $headers = $this->reveal()->getHeader($args[0]);\n\n            $headers[] = $args[1];\n            $this->getHeader($args[0])->willReturn($headers);\n            $this->hasHeader($args[0])->willReturn(true);\n            return $this;\n        });\n        $responseProphecy->withStatus(Argument::type('int'))->will(function ($args) {\n            $this->getStatusCode()->willReturn($args[0]);\n            return $this;\n        });\n\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))\n            ->will(function ($args) use ($responseProphecy): ResponseInterface {\n                $request = $args[0];\n                return $responseProphecy->reveal()\n                    ->withStatus(204)\n                    ->withHeader('X-SEQ-PRE-REQ-HANDLER', $request->getHeader('X-SEQ-PRE-REQ-HANDLER'));\n            });\n\n        $middleware0Prophecy = $this->prophesize(MiddlewareInterface::class);\n        $middleware0Prophecy\n            ->process(\n                Argument::type(ServerRequestInterface::class),\n                Argument::type(RequestHandlerInterface::class)\n            )\n            ->will(function ($args): ResponseInterface {\n                return $args[1]->handle($args[0]->withAddedHeader('X-SEQ-PRE-REQ-HANDLER', '0'))\n                    ->withAddedHeader('X-SEQ-POST-REQ-HANDLER', '0');\n            });\n\n        $middleware1Prophecy = $this->prophesize(MiddlewareInterface::class);\n        $middleware1Prophecy\n            ->process(\n                Argument::type(ServerRequestInterface::class),\n                Argument::type(RequestHandlerInterface::class)\n            )\n            ->will(function ($args): ResponseInterface {\n                return $args[1]->handle($args[0]->withAddedHeader('X-SEQ-PRE-REQ-HANDLER', '1'))\n                    ->withAddedHeader('X-SEQ-POST-REQ-HANDLER', '1');\n            });\n\n        MockSequenceMiddleware::$id = '2';\n\n        $middleware3Prophecy = $this->prophesize(MiddlewareInterface::class);\n        $middleware3Prophecy\n            ->process(\n                Argument::type(ServerRequestInterface::class),\n                Argument::type(RequestHandlerInterface::class)\n            )\n            ->will(function ($args): ResponseInterface {\n                return $args[1]->handle($args[0]->withAddedHeader('X-SEQ-PRE-REQ-HANDLER', '3'))\n                    ->withAddedHeader('X-SEQ-POST-REQ-HANDLER', '3');\n            });\n\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, null);\n        $dispatcher->add($middleware0Prophecy->reveal());\n        $dispatcher->addMiddleware($middleware1Prophecy->reveal());\n        $dispatcher->addDeferred(MockSequenceMiddleware::class);\n        $dispatcher->add($middleware3Prophecy->reveal());\n\n        $response = $dispatcher->handle($requestProphecy->reveal());\n\n        $this->assertSame(['3', '2', '1', '0'], $response->getHeader('X-SEQ-PRE-REQ-HANDLER'));\n        $this->assertSame(['0', '1', '2', '3'], $response->getHeader('X-SEQ-POST-REQ-HANDLER'));\n        $this->assertSame(204, $response->getStatusCode());\n    }\n\n    public function testDoesNotInstantiateDeferredMiddlewareInCaseOfAnEarlyReturningOuterMiddleware(): void\n    {\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(Argument::cetera())->willReturn($responseProphecy->reveal());\n\n        MockSequenceMiddleware::$hasBeenInstantiated = false;\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, null);\n        $dispatcher->addDeferred(MockSequenceMiddleware::class);\n        $dispatcher->addMiddleware($middlewareProphecy->reveal());\n        $response = $dispatcher->handle($requestProphecy->reveal());\n\n        $this->assertFalse(MockSequenceMiddleware::$hasBeenInstantiated);\n        $this->assertSame($responseProphecy->reveal(), $response);\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))->shouldNotHaveBeenCalled();\n    }\n\n    public function testThrowsExceptionForDeferredNonMiddlewareInterfaceClasses(): void\n    {\n        $this->expectException(RuntimeException::class);\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, null);\n        $dispatcher->addDeferred(stdClass::class);\n        $dispatcher->handle($requestProphecy->reveal());\n\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))->shouldNotHaveBeenCalled();\n    }\n\n    public function testCanBeExecutedMultipleTimes(): void\n    {\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(Argument::cetera())->willReturn($responseProphecy->reveal());\n\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, null);\n        $dispatcher->add($middlewareProphecy->reveal());\n\n        $response1 = $dispatcher->handle($requestProphecy->reveal());\n        $response2 = $dispatcher->handle($requestProphecy->reveal());\n\n        $this->assertSame($responseProphecy->reveal(), $response1);\n        $this->assertSame($responseProphecy->reveal(), $response2);\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))->shouldNotHaveBeenCalled();\n    }\n\n    public function testCanBeReExecutedRecursivelyDuringDispatch(): void\n    {\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n\n        $requestProphecy->hasHeader('X-NESTED')->willReturn(false);\n        $requestProphecy->withAddedHeader('X-NESTED', '1')->will(function () {\n            $this->hasHeader('X-NESTED')->willReturn(true);\n            return $this;\n        });\n\n        $responseProphecy->getHeader(Argument::type('string'))->willReturn([]);\n        $responseProphecy->withAddedHeader(Argument::type('string'), Argument::type('string'))->will(function ($args) {\n            $headers = $this->reveal()->getHeader($args[0]);\n\n            $headers[] = $args[1];\n            $this->getHeader($args[0])->willReturn($headers);\n            $this->hasHeader($args[0])->willReturn(true);\n            return $this;\n        });\n\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, null);\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy\n            ->process(\n                Argument::type(ServerRequestInterface::class),\n                Argument::type(RequestHandlerInterface::class)\n            )\n            ->will(function ($args) use ($dispatcher, $responseProphecy): ResponseInterface {\n                $request = $args[0];\n                if ($request->hasHeader('X-NESTED')) {\n                    return $responseProphecy->reveal()->withAddedHeader('X-TRACE', 'nested');\n                }\n\n                $response = $dispatcher->handle($request->withAddedHeader('X-NESTED', '1'));\n\n                return $response->withAddedHeader('X-TRACE', 'outer');\n            });\n        $dispatcher->add($middlewareProphecy->reveal());\n\n        $response = $dispatcher->handle($requestProphecy->reveal());\n\n        $this->assertSame(['nested', 'outer'], $response->getHeader('X-TRACE'));\n    }\n\n    public function testFetchesMiddlewareFromContainer(): void\n    {\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $middlewareProphecy = $this->prophesize(MiddlewareInterface::class);\n        $middlewareProphecy->process(Argument::cetera())->willReturn($responseProphecy->reveal());\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('somemiddlewarename')->willReturn(true);\n        $containerProphecy->get('somemiddlewarename')->willReturn($middlewareProphecy->reveal());\n        /** @var ContainerInterface $container */\n        $container = $containerProphecy->reveal();\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, $container);\n        $dispatcher->addDeferred('somemiddlewarename');\n        $response = $dispatcher->handle($requestProphecy->reveal());\n\n        $this->assertSame($responseProphecy->reveal(), $response);\n        $kernelProphecy->handle(Argument::type(ServerRequestInterface::class))->shouldNotHaveBeenCalled();\n    }\n\n    public function testMiddlewareGetsInstantiatedWithContainer(): void\n    {\n        $kernelProphecy = $this->prophesize(RequestHandlerInterface::class);\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has(MockMiddlewareWithConstructor::class)->willReturn(false);\n        /** @var ContainerInterface $container */\n        $container = $containerProphecy->reveal();\n        /** @var RequestHandlerInterface $kernel */\n        $kernel = $kernelProphecy->reveal();\n        $dispatcher = $this->createMiddlewareDispatcher($kernel, $container);\n        $dispatcher->addDeferred(MockMiddlewareWithConstructor::class);\n        $dispatcher->handle($requestProphecy->reveal());\n\n        $this->assertSame($containerProphecy->reveal(), MockMiddlewareWithConstructor::$container);\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/CallableTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Slim\\Tests\\Providers\\PSR7ObjectProvider;\n\nclass CallableTest\n{\n    public static $CalledCount = 0;\n\n    public static $CalledContainer = null;\n\n    public function __construct($container = null)\n    {\n        static::$CalledContainer = $container;\n    }\n\n    public function toCall()\n    {\n        static::$CalledCount++;\n\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->createResponse();\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/InvocationStrategyTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\n\nclass InvocationStrategyTest implements InvocationStrategyInterface\n{\n    public static $LastCalledFor = null;\n\n    /**\n     * Invoke a route callable.\n     *\n     * @param callable               $callable       The callable to invoke using the strategy.\n     * @param ServerRequestInterface $request        The request object.\n     * @param ResponseInterface      $response       The response object.\n     * @param array                  $routeArguments The route's placeholder arguments\n     *\n     * @return ResponseInterface The response from the callable.\n     */\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface {\n        static::$LastCalledFor = $callable;\n        return $response;\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/InvokableTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nclass InvokableTest\n{\n    public static $CalledCount = 0;\n\n    public function __invoke()\n    {\n        return static::$CalledCount++;\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MiddlewareTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Slim\\Tests\\Providers\\PSR7ObjectProvider;\n\nclass MiddlewareTest implements MiddlewareInterface\n{\n    public static $CalledCount = 0;\n\n    /**\n     * {@inheritdoc}\n     */\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        static::$CalledCount++;\n\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        $responseFactory = $psr7ObjectProvider->getResponseFactory();\n\n        $response = $responseFactory\n            ->createResponse()\n            ->withHeader('Content-Type', 'text/plain');\n        $calledCount = static::$CalledCount;\n        $response->getBody()->write(\"{$calledCount}\");\n\n        return $response;\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockAction.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse InvalidArgumentException;\n\nuse function compact;\nuse function count;\nuse function json_encode;\n\nclass MockAction\n{\n    public function __call($name, array $arguments)\n    {\n        if (count($arguments) !== 3) {\n            throw new InvalidArgumentException(\"Not a Slim call\");\n        }\n\n        $response = $arguments[1];\n        $contents = json_encode(compact('name') + ['arguments' => $arguments[2]]);\n        $arguments[1]->getBody()->write($contents);\n\n        return $response;\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockCustomException.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Exception;\n\nclass MockCustomException extends Exception\n{\n}\n"
  },
  {
    "path": "tests/Mocks/MockCustomRequestHandlerInvocationStrategy.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\Interfaces\\RequestHandlerInvocationStrategyInterface;\n\nclass MockCustomRequestHandlerInvocationStrategy implements RequestHandlerInvocationStrategyInterface\n{\n    public static $CalledCount = 0;\n\n    public function __invoke(\n        callable $callable,\n        ServerRequestInterface $request,\n        ResponseInterface $response,\n        array $routeArguments\n    ): ResponseInterface {\n        self::$CalledCount += 1;\n        return $callable($request);\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockMiddlewareSlimCallable.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nclass MockMiddlewareSlimCallable\n{\n    /**\n     * @param ServerRequestInterface  $request\n     * @param RequestHandlerInterface $handler\n     * @return ResponseInterface\n     */\n    public function custom(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        return $handler->handle($request);\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockMiddlewareWithConstructor.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Prophecy\\Prophet;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nclass MockMiddlewareWithConstructor implements MiddlewareInterface\n{\n    /**\n     * @var ContainerInterface|null\n     */\n    public static $container;\n\n    /**\n     * @param ContainerInterface|null $container\n     */\n    public function __construct(?ContainerInterface $container = null)\n    {\n        self::$container = $container;\n    }\n\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $prophet = new Prophet();\n        $responseProphecy = $prophet->prophesize(ResponseInterface::class);\n        return $responseProphecy->reveal();\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockMiddlewareWithoutConstructor.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nclass MockMiddlewareWithoutConstructor implements MiddlewareInterface\n{\n    public static $CalledCount = 0;\n\n    /**\n     * @param ServerRequestInterface  $request\n     * @param RequestHandlerInterface $handler\n     * @return ResponseInterface\n     */\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $appendToOutput = $request->getAttribute('appendToOutput');\n        if ($appendToOutput !== null) {\n            $appendToOutput('Hello World');\n        }\n\n        static::$CalledCount++;\n\n        return $handler->handle($request);\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockMiddlewareWithoutInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nclass MockMiddlewareWithoutInterface\n{\n}\n"
  },
  {
    "path": "tests/Mocks/MockPsr17Factory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Slim\\Factory\\Psr17\\Psr17Factory;\n\nclass MockPsr17Factory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = '';\n    protected static string $streamFactoryClass = '';\n    protected static string $serverRequestCreatorClass = '';\n    protected static string $serverRequestCreatorMethod = '';\n}\n"
  },
  {
    "path": "tests/Mocks/MockPsr17FactoryWithoutStreamFactory.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Slim\\Factory\\Psr17\\Psr17Factory;\n\nclass MockPsr17FactoryWithoutStreamFactory extends Psr17Factory\n{\n    protected static string $responseFactoryClass = 'Slim\\Psr7\\Factory\\ResponseFactory';\n    protected static string $streamFactoryClass = '';\n    protected static string $serverRequestCreatorClass = '';\n    protected static string $serverRequestCreatorMethod = '';\n}\n"
  },
  {
    "path": "tests/Mocks/MockRequestHandler.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Slim\\Tests\\Providers\\PSR7ObjectProvider;\n\nclass MockRequestHandler implements RequestHandlerInterface\n{\n    /**\n     * @var int\n     */\n    private $calledCount = 0;\n\n    /**\n     * @param ServerRequestInterface $request\n     * @return ResponseInterface\n     */\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        $responseFactory = $psr7ObjectProvider->getResponseFactory();\n\n        $this->calledCount += 1;\n        return $responseFactory->createResponse();\n    }\n\n    /**\n     * @return int\n     */\n    public function getCalledCount(): int\n    {\n        return $this->calledCount;\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockSequenceMiddleware.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nclass MockSequenceMiddleware implements MiddlewareInterface\n{\n    /**\n     * @var string\n     */\n    public static $id = '0';\n\n    /**\n     * @var bool\n     */\n    public static $hasBeenInstantiated = false;\n\n    public function __construct()\n    {\n        static::$hasBeenInstantiated = true;\n    }\n\n    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface\n    {\n        $request = $request->withAddedHeader('X-SEQ-PRE-REQ-HANDLER', static::$id);\n        $response = $handler->handle($request);\n\n        return $response->withAddedHeader('X-SEQ-POST-REQ-HANDLER', static::$id);\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/MockStream.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Exception;\nuse InvalidArgumentException;\nuse Psr\\Http\\Message\\StreamInterface;\nuse RuntimeException;\n\nuse function clearstatcache;\nuse function fclose;\nuse function feof;\nuse function fopen;\nuse function fread;\nuse function fseek;\nuse function fstat;\nuse function ftell;\nuse function fwrite;\nuse function gettype;\nuse function is_resource;\nuse function is_string;\nuse function stream_get_contents;\nuse function stream_get_meta_data;\nuse function var_export;\n\nuse const SEEK_SET;\n\nclass MockStream implements StreamInterface\n{\n    /** @var resource A resource reference */\n    private $stream;\n\n    /** @var bool */\n    private $seekable;\n\n    /** @var bool */\n    private $readable;\n\n    /** @var bool */\n    private $writable;\n\n    /** @var array|mixed|null|void */\n    private $uri;\n\n    /** @var int|null */\n    private $size;\n\n    /** @var array Hash of readable and writable stream types */\n    private static $readWriteHash = [\n        'read' => [\n            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,\n            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,\n            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,\n            'x+t' => true, 'c+t' => true, 'a+' => true,\n        ],\n        'write' => [\n            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,\n            'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,\n            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,\n            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,\n        ],\n    ];\n\n    /**\n     * MockStream constructor.\n     * @param string|resource $body\n     */\n    public function __construct($body = '')\n    {\n        if (is_string($body)) {\n            $resource = fopen('php://temp', 'rw+');\n            fwrite($resource, $body);\n            $body = $resource;\n        }\n\n        if ('resource' === gettype($body)) {\n            $this->stream = $body;\n            $meta = stream_get_meta_data($this->stream);\n            $this->seekable = $meta['seekable'];\n            $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);\n            $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);\n            $this->uri = $this->getMetadata('uri');\n        } else {\n            throw new InvalidArgumentException(\n                'First argument to Stream::create() must be a string, resource or StreamInterface.'\n            );\n        }\n    }\n\n    /**\n     * Closes the stream when the destructed.\n     */\n    public function __destruct()\n    {\n        $this->close();\n    }\n\n    public function __toString(): string\n    {\n        try {\n            if ($this->isSeekable()) {\n                $this->seek(0);\n            }\n\n            return $this->getContents();\n        } catch (Exception $e) {\n            return '';\n        }\n    }\n\n    public function close(): void\n    {\n        if (isset($this->stream)) {\n            if (is_resource($this->stream)) {\n                fclose($this->stream);\n            }\n            $this->detach();\n        }\n    }\n\n    public function detach()\n    {\n        if (!isset($this->stream)) {\n            return null;\n        }\n\n        $result = $this->stream;\n        unset($this->stream);\n        $this->size = $this->uri = null;\n        $this->readable = $this->writable = $this->seekable = false;\n\n        return $result;\n    }\n\n    public function getSize(): ?int\n    {\n        if (null !== $this->size) {\n            return $this->size;\n        }\n\n        if (!isset($this->stream)) {\n            return null;\n        }\n\n        // Clear the stat cache if the stream has a URI\n        if ($this->uri) {\n            clearstatcache(true, $this->uri);\n        }\n\n        $stats = fstat($this->stream);\n        if (isset($stats['size'])) {\n            $this->size = $stats['size'];\n\n            return $this->size;\n        }\n\n        return null;\n    }\n\n    public function tell(): int\n    {\n        if (false === $result = ftell($this->stream)) {\n            throw new RuntimeException('Unable to determine stream position');\n        }\n\n        return $result;\n    }\n\n    public function eof(): bool\n    {\n        return !$this->stream || feof($this->stream);\n    }\n\n    public function isSeekable(): bool\n    {\n        return $this->seekable;\n    }\n\n    public function seek($offset, $whence = SEEK_SET): void\n    {\n        if (!$this->seekable) {\n            throw new RuntimeException('Stream is not seekable');\n        }\n\n        if (fseek($this->stream, $offset, $whence) === -1) {\n            throw new RuntimeException(\n                'Unable to seek to stream position '\n                . $offset . ' with whence ' . var_export($whence, true)\n            );\n        }\n    }\n\n    public function rewind(): void\n    {\n        $this->seek(0);\n    }\n\n    public function isWritable(): bool\n    {\n        return $this->writable;\n    }\n\n    public function write($string): int\n    {\n        if (!$this->writable) {\n            throw new RuntimeException('Cannot write to a non-writable stream');\n        }\n\n        // We can't know the size after writing anything\n        $this->size = null;\n\n        if (false === $result = fwrite($this->stream, $string)) {\n            throw new RuntimeException('Unable to write to stream');\n        }\n\n        return $result;\n    }\n\n    public function isReadable(): bool\n    {\n        return $this->readable;\n    }\n\n    public function read($length): string\n    {\n        if (!$this->readable) {\n            throw new RuntimeException('Cannot read from non-readable stream');\n        }\n\n        return fread($this->stream, $length);\n    }\n\n    public function getContents(): string\n    {\n        if (!isset($this->stream)) {\n            throw new RuntimeException('Unable to read stream contents');\n        }\n\n        if (false === $contents = stream_get_contents($this->stream)) {\n            throw new RuntimeException('Unable to read stream contents');\n        }\n\n        return $contents;\n    }\n\n    public function getMetadata($key = null)\n    {\n        if (!isset($this->stream)) {\n            return $key ? null : [];\n        }\n\n        if (null === $key) {\n            return stream_get_meta_data($this->stream);\n        }\n\n        $meta = stream_get_meta_data($this->stream);\n\n        return $meta[$key] ?? null;\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/RequestHandlerTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse Slim\\Tests\\Providers\\PSR7ObjectProvider;\n\nuse function debug_backtrace;\n\nclass RequestHandlerTest implements RequestHandlerInterface\n{\n    public static $CalledCount = 0;\n    public static $strategy = '';\n\n    public function handle(ServerRequestInterface $request): ResponseInterface\n    {\n        static::$CalledCount++;\n\n        // store the strategy that was used to call this handler - it's in the back trace\n        $trace = debug_backtrace();\n        if (isset($trace[1])) {\n            static::$strategy = $trace[1]['class'];\n        }\n\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        $responseFactory = $psr7ObjectProvider->getResponseFactory();\n\n        $response = $responseFactory\n            ->createResponse()\n            ->withHeader('Content-Type', 'text/plain');\n        $calledCount = static::$CalledCount;\n        $response->getBody()->write(\"{$calledCount}\");\n\n        return $response;\n    }\n\n    public function custom(ServerRequestInterface $request): ResponseInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        $responseFactory = $psr7ObjectProvider->getResponseFactory();\n\n        return $responseFactory->createResponse();\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/SlowPokeStream.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Exception;\nuse Psr\\Http\\Message\\StreamInterface;\n\nuse function min;\nuse function str_repeat;\nuse function usleep;\n\nuse const SEEK_SET;\n\nclass SlowPokeStream implements StreamInterface\n{\n    public const CHUNK_SIZE = 1;\n    public const SIZE = 500;\n\n    /**\n     * @var int\n     */\n    private $amountToRead;\n\n    public function __construct()\n    {\n        $this->amountToRead = self::SIZE;\n    }\n\n    public function __toString(): string\n    {\n        $content = '';\n        while (!$this->eof()) {\n            $content .= $this->read(self::CHUNK_SIZE);\n        }\n        return $content;\n    }\n\n    public function close(): void\n    {\n    }\n\n    public function detach()\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function eof(): bool\n    {\n        return $this->amountToRead === 0;\n    }\n\n    public function getContents(): string\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function getMetadata($key = null)\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function getSize(): ?int\n    {\n        return null;\n    }\n\n    public function isReadable(): bool\n    {\n        return true;\n    }\n\n    public function isSeekable(): bool\n    {\n        return false;\n    }\n\n    public function isWritable(): bool\n    {\n        return false;\n    }\n\n    public function read($length): string\n    {\n        usleep(1);\n        $size = min($this->amountToRead, self::CHUNK_SIZE, $length);\n        $this->amountToRead -= $size;\n        return str_repeat('.', $size);\n    }\n\n    public function rewind(): void\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function seek($offset, $whence = SEEK_SET): void\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function tell(): int\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function write($string): int\n    {\n        return strlen($string);\n    }\n}\n"
  },
  {
    "path": "tests/Mocks/SmallChunksStream.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Mocks;\n\nuse Exception;\nuse Psr\\Http\\Message\\StreamInterface;\n\nuse function min;\nuse function str_repeat;\n\nuse const SEEK_SET;\n\nclass SmallChunksStream implements StreamInterface\n{\n    public const CHUNK_SIZE = 10;\n    public const SIZE = 40;\n\n    /**\n     * @var int\n     */\n    private $amountToRead;\n\n    public function __construct()\n    {\n        $this->amountToRead = self::SIZE;\n    }\n\n    public function __toString(): string\n    {\n        return str_repeat('.', self::SIZE);\n    }\n\n    public function close(): void\n    {\n    }\n\n    public function detach()\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function eof(): bool\n    {\n        return $this->amountToRead === 0;\n    }\n\n    public function getContents(): string\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function getMetadata($key = null)\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function getSize(): ?int\n    {\n        return self::SIZE;\n    }\n\n    public function isReadable(): bool\n    {\n        return true;\n    }\n\n    public function isSeekable(): bool\n    {\n        return false;\n    }\n\n    public function isWritable(): bool\n    {\n        return false;\n    }\n\n    public function read($length): string\n    {\n        $size = min($this->amountToRead, self::CHUNK_SIZE, $length);\n        $this->amountToRead -= $size;\n\n        return str_repeat('.', min($length, $size));\n    }\n\n    public function rewind(): void\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function seek($offset, $whence = SEEK_SET): void\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function tell(): int\n    {\n        throw new Exception('not implemented');\n    }\n\n    public function write($string): int\n    {\n        return strlen($string);\n    }\n}\n"
  },
  {
    "path": "tests/Providers/PSR7ObjectProvider.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Providers;\n\nuse Nyholm\\Psr7\\Factory\\Psr17Factory;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestFactoryInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse Psr\\Http\\Message\\StreamInterface;\n\nuse function array_merge;\nuse function microtime;\nuse function time;\n\n/**\n * Class PSR7ObjectProvider\n *\n * @package Slim\\Tests\\Providers\n */\nclass PSR7ObjectProvider implements PSR7ObjectProviderInterface\n{\n    /**\n     * @param string $uri\n     * @param string $method\n     * @param array  $data\n     * @return ServerRequestInterface\n     */\n    public function createServerRequest(\n        string $uri,\n        string $method = 'GET',\n        array $data = []\n    ): ServerRequestInterface {\n        $headers = array_merge([\n            'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n            'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',\n            'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',\n            'HTTP_HOST' => 'localhost',\n            'HTTP_USER_AGENT' => 'Slim Framework',\n            'QUERY_STRING' => '',\n            'REMOTE_ADDR' => '127.0.0.1',\n            'REQUEST_METHOD' => $method,\n            'REQUEST_TIME' => time(),\n            'REQUEST_TIME_FLOAT' => microtime(true),\n            'REQUEST_URI' => '',\n            'SCRIPT_NAME' => '/index.php',\n            'SERVER_NAME' => 'localhost',\n            'SERVER_PORT' => 80,\n            'SERVER_PROTOCOL' => 'HTTP/1.1',\n        ], $data);\n\n        return $this\n            ->getServerRequestFactory()\n            ->createServerRequest($method, $uri, $headers);\n    }\n\n    /**\n     * @return ServerRequestFactoryInterface\n     */\n    public function getServerRequestFactory(): ServerRequestFactoryInterface\n    {\n        return new Psr17Factory();\n    }\n\n    /**\n     * @param int    $statusCode\n     * @param string $reasonPhrase\n     * @return ResponseInterface\n     */\n    public function createResponse(int $statusCode = 200, string $reasonPhrase = ''): ResponseInterface\n    {\n        return $this\n            ->getResponseFactory()\n            ->createResponse($statusCode, $reasonPhrase);\n    }\n\n    /**\n     * @return ResponseFactoryInterface\n     */\n    public function getResponseFactory(): ResponseFactoryInterface\n    {\n        return new Psr17Factory();\n    }\n\n    /**\n     * @param string $contents\n     * @return StreamInterface\n     */\n    public function createStream(string $contents = ''): StreamInterface\n    {\n        return $this\n            ->getStreamFactory()\n            ->createStream($contents);\n    }\n\n    /**\n     * @return StreamFactoryInterface\n     */\n    public function getStreamFactory(): StreamFactoryInterface\n    {\n        return new Psr17Factory();\n    }\n}\n"
  },
  {
    "path": "tests/Providers/PSR7ObjectProviderInterface.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Providers;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestFactoryInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse Psr\\Http\\Message\\StreamInterface;\n\n/**\n * Interface PSR7ObjectProviderInterface\n *\n * @package Slim\\Tests\\Providers\n */\ninterface PSR7ObjectProviderInterface\n{\n    /**\n     * @return ServerRequestFactoryInterface\n     */\n    public function getServerRequestFactory(): ServerRequestFactoryInterface;\n\n    /**\n     * @return ResponseFactoryInterface\n     */\n    public function getResponseFactory(): ResponseFactoryInterface;\n\n    /**\n     * @return StreamFactoryInterface\n     */\n    public function getStreamFactory(): StreamFactoryInterface;\n\n    /**\n     * @param string $uri\n     * @param string $method\n     * @param array  $data\n     * @return ServerRequestInterface\n     */\n    public function createServerRequest(string $uri, string $method = 'GET', array $data = []): ServerRequestInterface;\n\n    /**\n     * @param int    $statusCode\n     * @param string $reasonPhrase\n     * @return ResponseInterface\n     */\n    public function createResponse(int $statusCode = 200, string $reasonPhrase = ''): ResponseInterface;\n\n    /**\n     * @param string $contents\n     * @return StreamInterface\n     */\n    public function createStream(string $contents = ''): StreamInterface;\n}\n"
  },
  {
    "path": "tests/ResponseEmitterTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests;\n\nuse ReflectionClass;\nuse Slim\\ResponseEmitter;\nuse Slim\\Tests\\Assets\\HeaderStack;\nuse Slim\\Tests\\Mocks\\MockStream;\nuse Slim\\Tests\\Mocks\\SlowPokeStream;\nuse Slim\\Tests\\Mocks\\SmallChunksStream;\n\nuse function base64_decode;\nuse function fopen;\nuse function fwrite;\nuse function in_array;\nuse function popen;\nuse function rewind;\nuse function str_repeat;\nuse function stream_filter_append;\nuse function stream_filter_remove;\nuse function stream_get_filters;\nuse function strlen;\nuse function trim;\n\nuse const CONNECTION_ABORTED;\nuse const CONNECTION_TIMEOUT;\nuse const STREAM_FILTER_READ;\nuse const STREAM_FILTER_WRITE;\n\nclass ResponseEmitterTest extends TestCase\n{\n    public function setUp(): void\n    {\n        HeaderStack::reset();\n    }\n\n    public function tearDown(): void\n    {\n        HeaderStack::reset();\n    }\n\n    public function testRespond(): void\n    {\n        $response = $this->createResponse();\n        $response->getBody()->write('Hello');\n\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n\n        $this->expectOutputString('Hello');\n    }\n\n    public function testRespondWithPaddedStreamFilterOutput(): void\n    {\n        $availableFilter = stream_get_filters();\n\n        $filterName = 'string.rot13';\n        $unfilterName = 'string.rot13';\n        $specificFilterName = 'string.rot13';\n        $specificUnfilterName = 'string.rot13';\n\n        if (in_array($filterName, $availableFilter) && in_array($unfilterName, $availableFilter)) {\n            $key = base64_decode('xxxxxxxxxxxxxxxx');\n            $iv = base64_decode('Z6wNDk9LogWI4HYlRu0mng==');\n\n            $data = 'Hello';\n            $length = strlen($data);\n\n            $stream = fopen('php://temp', 'r+');\n            $filter = stream_filter_append($stream, $specificFilterName, STREAM_FILTER_WRITE, [\n                'key' => $key,\n                'iv' => $iv\n            ]);\n\n            fwrite($stream, $data);\n            rewind($stream);\n            stream_filter_remove($filter);\n            stream_filter_append($stream, $specificUnfilterName, STREAM_FILTER_READ, [\n                'key' => $key,\n                'iv' => $iv\n            ]);\n\n            $body = $this->getStreamFactory()->createStreamFromResource($stream);\n            $response = $this\n                ->createResponse()\n                ->withHeader('Content-Length', $length)\n                ->withBody($body);\n\n            $responseEmitter = new ResponseEmitter();\n            $responseEmitter->emit($response);\n\n            $this->expectOutputString('Hello');\n        } else {\n            $this->assertTrue(true);\n        }\n    }\n\n    public function testRespondIndeterminateLength(): void\n    {\n        $stream = fopen('php://temp', 'r+');\n        fwrite($stream, 'Hello');\n        rewind($stream);\n\n        $body = $this\n            ->getMockBuilder(MockStream::class)\n            ->setConstructorArgs([$stream])\n            ->onlyMethods(['getSize'])\n            ->getMock();\n        $body->method('getSize')->willReturn(null);\n\n        $response = $this->createResponse()->withBody($body);\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n\n        $this->expectOutputString('Hello');\n    }\n\n    public function testResponseWithStreamReadYieldingLessBytesThanAsked(): void\n    {\n        $body = new SmallChunksStream();\n        $response = $this->createResponse()->withBody($body);\n\n        $responseEmitter = new ResponseEmitter($body::CHUNK_SIZE * 2);\n        $responseEmitter->emit($response);\n\n        $this->expectOutputString(str_repeat('.', $body->getSize()));\n    }\n\n    public function testResponseReplacesPreviouslySetHeaders(): void\n    {\n        $response = $this\n            ->createResponse(200, 'OK')\n            ->withHeader('X-Foo', 'baz1')\n            ->withAddedHeader('X-Foo', 'baz2');\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n\n        $expectedStack = [\n            ['header' => 'X-Foo: baz1', 'replace' => true, 'status_code' => null],\n            ['header' => 'X-Foo: baz2', 'replace' => false, 'status_code' => null],\n            ['header' => 'HTTP/1.1 200 OK', 'replace' => true, 'status_code' => 200],\n        ];\n\n        $this->assertSame($expectedStack, HeaderStack::stack());\n    }\n\n    public function testResponseDoesNotReplacePreviouslySetSetCookieHeaders(): void\n    {\n        $response = $this\n            ->createResponse(200, 'OK')\n            ->withHeader('set-cOOkie', 'foo=bar')\n            ->withAddedHeader('Set-Cookie', 'bar=baz');\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n\n        $expectedStack = [\n            ['header' => 'set-cOOkie: foo=bar', 'replace' => false, 'status_code' => null],\n            ['header' => 'set-cOOkie: bar=baz', 'replace' => false, 'status_code' => null],\n            ['header' => 'HTTP/1.1 200 OK', 'replace' => true, 'status_code' => 200],\n        ];\n\n        $this->assertSame($expectedStack, HeaderStack::stack());\n    }\n\n    public function testIsResponseEmptyWithNonEmptyBodyAndTriggeringStatusCode(): void\n    {\n        $body = $this->createStream('Hello');\n        $response = $this\n            ->createResponse(204)\n            ->withBody($body);\n        $responseEmitter = new ResponseEmitter();\n\n        $this->assertTrue($responseEmitter->isResponseEmpty($response));\n    }\n\n    public function testIsResponseEmptyDoesNotReadAllDataFromNonEmptySeekableResponse(): void\n    {\n        $body = $this->createStream('Hello');\n        $response = $this\n            ->createResponse(200)\n            ->withBody($body);\n        $responseEmitter = new ResponseEmitter();\n\n        $responseEmitter->isResponseEmpty($response);\n\n        $this->assertTrue($body->isSeekable());\n        $this->assertFalse($body->eof());\n    }\n\n    public function testIsResponseEmptyDoesNotDrainNonSeekableResponseWithContent(): void\n    {\n        $resource = popen('echo 12', 'r');\n        $body = $this->getStreamFactory()->createStreamFromResource($resource);\n        $response = $this->createResponse(200)->withBody($body);\n        $responseEmitter = new ResponseEmitter();\n\n        $responseEmitter->isResponseEmpty($response);\n\n        $this->assertFalse($body->isSeekable());\n        $this->assertSame('12', trim((string) $body));\n    }\n\n    public function testAvoidReadFromSlowStreamAccordingToStatus(): void\n    {\n        $body = new SlowPokeStream();\n        $response = $this\n            ->createResponse(204, 'No content')\n            ->withBody($body);\n\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n\n        $this->assertFalse($body->eof());\n        $this->expectOutputString('');\n    }\n\n    public function testIsResponseEmptyWithEmptyBody(): void\n    {\n        $response = $this->createResponse(200);\n        $responseEmitter = new ResponseEmitter();\n\n        $this->assertTrue($responseEmitter->isResponseEmpty($response));\n    }\n\n    public function testIsResponseEmptyWithZeroAsBody(): void\n    {\n        $body = $this->createStream('0');\n        $response = $this\n            ->createResponse(200)\n            ->withBody($body);\n        $responseEmitter = new ResponseEmitter();\n\n        $this->assertFalse($responseEmitter->isResponseEmpty($response));\n    }\n\n    public function testWillHandleInvalidConnectionStatusWithADeterminateBody(): void\n    {\n        $body = $this->getStreamFactory()->createStreamFromResource(fopen('php://temp', 'r+'));\n        $body->write('Hello!' . \"\\n\");\n        $body->write('Hello!' . \"\\n\");\n\n        // Tell connection_status() to fail.\n        $GLOBALS['connection_status_return'] = CONNECTION_ABORTED;\n\n        $response = $this\n            ->createResponse()\n            ->withHeader('Content-Length', $body->getSize())\n            ->withBody($body);\n\n        $responseEmitter = new ResponseEmitter();\n        $responseEmitter->emit($response);\n\n        $this->expectOutputString(\"Hello!\\nHello!\\n\");\n\n        // Tell connection_status() to pass.\n        unset($GLOBALS['connection_status_return']);\n    }\n\n    public function testWillHandleInvalidConnectionStatusWithAnIndeterminateBody(): void\n    {\n        $body = $this->getStreamFactory()->createStreamFromResource(fopen('php://input', 'r+'));\n\n        // Tell connection_status() to fail.\n        $GLOBALS['connection_status_return'] = CONNECTION_TIMEOUT;\n\n        $response = $this\n            ->createResponse()\n            ->withBody($body);\n\n\n        $responseEmitter = new ResponseEmitter();\n\n        $mirror = new ReflectionClass(ResponseEmitter::class);\n        $emitBodyMethod = $mirror->getMethod('emitBody');\n        $this->setAccessible($emitBodyMethod);\n        $emitBodyMethod->invoke($responseEmitter, $response);\n\n        $this->expectOutputString(\"\");\n\n        // Tell connection_status() to pass.\n        unset($GLOBALS['connection_status_return']);\n    }\n}\n"
  },
  {
    "path": "tests/Routing/DispatcherTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse ReflectionMethod;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Routing\\Dispatcher;\nuse Slim\\Routing\\FastRouteDispatcher;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RoutingResults;\nuse Slim\\Tests\\TestCase;\n\nuse function dirname;\nuse function microtime;\nuse function uniqid;\nuse function unlink;\n\nclass DispatcherTest extends TestCase\n{\n    public function testCreateDispatcher()\n    {\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $dispatcher = new Dispatcher($routeCollector);\n\n        $method = new ReflectionMethod(Dispatcher::class, 'createDispatcher');\n        $this->setAccessible($method);\n\n        $this->assertInstanceOf(FastRouteDispatcher::class, $method->invoke($dispatcher));\n    }\n\n    /**\n     * Test cached routes file is created & that it holds our routes.\n     */\n    public function testRouteCacheFileCanBeDispatched()\n    {\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $dispatcher = new Dispatcher($routeCollector);\n\n        $route = $routeCollector->map(['GET'], '/', function () {\n        });\n        $route->setName('foo');\n\n        $cacheFile = __DIR__ . '/' . uniqid((string) microtime(true));\n        $routeCollector->setCacheFile($cacheFile);\n\n        $method = new ReflectionMethod(Dispatcher::class, 'createDispatcher');\n        $this->setAccessible($method);\n        $method->invoke($dispatcher);\n        $this->assertFileExists($cacheFile, 'cache file was not created');\n\n        $routeCollector2 = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector2->setCacheFile($cacheFile);\n        $dispatcher2 = new Dispatcher($routeCollector2);\n\n        $method = new ReflectionMethod(Dispatcher::class, 'createDispatcher');\n        $this->setAccessible($method);\n        $method->invoke($dispatcher2);\n\n        /** @var RoutingResults $result */\n        $result = $dispatcher2->dispatch('GET', '/');\n        $this->assertSame(FastRouteDispatcher::FOUND, $result->getRouteStatus());\n\n        unlink($cacheFile);\n    }\n\n    /**\n     * Calling createDispatcher as second time should give you back the same\n     * dispatcher as when you called it the first time.\n     */\n    public function testCreateDispatcherReturnsSameDispatcherASecondTime()\n    {\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $dispatcher = new Dispatcher($routeCollector);\n\n        $method = new ReflectionMethod(Dispatcher::class, 'createDispatcher');\n        $this->setAccessible($method);\n\n        $fastRouteDispatcher = $method->invoke($dispatcher);\n        $fastRouteDispatcher2 = $method->invoke($dispatcher);\n\n        $this->assertSame($fastRouteDispatcher, $fastRouteDispatcher2);\n    }\n\n    public function testGetAllowedMethods()\n    {\n        $methods = ['GET', 'POST', 'PUT'];\n        $uri = '/';\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->map($methods, $uri, function () {\n        });\n\n        $dispatcher = new Dispatcher($routeCollector);\n        $results = $dispatcher->getAllowedMethods('/');\n\n        $this->assertSame($methods, $results);\n    }\n\n    public function testDispatch()\n    {\n        $methods = ['GET', 'POST'];\n        $uri = '/hello/{name}';\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n\n        $callable = function () {\n        };\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $route = $routeCollector->map($methods, $uri, $callable);\n\n        $dispatcher = new Dispatcher($routeCollector);\n        $results = $dispatcher->dispatch('GET', '/hello/Foo%20Bar');\n\n        $this->assertSame(RoutingResults::FOUND, $results->getRouteStatus());\n        $this->assertSame('GET', $results->getMethod());\n        $this->assertSame('/hello/Foo%20Bar', $results->getUri());\n        $this->assertSame($route->getIdentifier(), $results->getRouteIdentifier());\n        $this->assertSame(['name' => 'Foo Bar'], $results->getRouteArguments());\n        $this->assertSame(['name' => 'Foo%20Bar'], $results->getRouteArguments(false));\n        $this->assertSame($methods, $results->getAllowedMethods());\n        $this->assertSame($dispatcher, $results->getDispatcher());\n    }\n}\n"
  },
  {
    "path": "tests/Routing/FastRouteDispatcherTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse FastRoute\\BadRouteException;\nuse FastRoute\\DataGenerator\\GroupCountBased;\nuse FastRoute\\RouteCollector;\nuse Slim\\Routing\\FastRouteDispatcher;\nuse Slim\\Tests\\TestCase;\n\nuse function FastRoute\\simpleDispatcher;\n\nclass FastRouteDispatcherTest extends TestCase\n{\n    /**\n     * @dataProvider provideFoundDispatchCases\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('provideFoundDispatchCases')]\n    public function testFoundDispatches($method, $uri, $callback, $handler, $argDict)\n    {\n        /** @var FastRouteDispatcher $dispatcher */\n        $dispatcher = simpleDispatcher($callback, $this->generateDispatcherOptions());\n\n        $results = $dispatcher->dispatch($method, $uri);\n\n        $this->assertSame($dispatcher::FOUND, $results[0]);\n        $this->assertSame($handler, $results[1]);\n        $this->assertSame($argDict, $results[2]);\n    }\n\n    /**\n     * Set appropriate options for the specific Dispatcher class we're testing\n     */\n    private function generateDispatcherOptions()\n    {\n        return [\n            'dataGenerator' => $this->getDataGeneratorClass(),\n            'dispatcher' => $this->getDispatcherClass()\n        ];\n    }\n\n    protected function getDataGeneratorClass()\n    {\n        return GroupCountBased::class;\n    }\n\n    protected function getDispatcherClass()\n    {\n        return FastRouteDispatcher::class;\n    }\n\n    /**\n     * @dataProvider provideNotFoundDispatchCases\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('provideNotFoundDispatchCases')]\n    public function testNotFoundDispatches($method, $uri, $callback)\n    {\n        /** @var FastRouteDispatcher $dispatcher */\n        $dispatcher = simpleDispatcher($callback, $this->generateDispatcherOptions());\n\n        $results = $dispatcher->dispatch($method, $uri);\n\n        $this->assertSame($dispatcher::NOT_FOUND, $results[0]);\n    }\n\n    /**\n     * @dataProvider provideMethodNotAllowedDispatchCases\n     * @param $method\n     * @param $uri\n     * @param $callback\n     * @param $allowedMethods\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('provideMethodNotAllowedDispatchCases')]\n    public function testMethodNotAllowedDispatches($method, $uri, $callback, $allowedMethods)\n    {\n        /** @var FastRouteDispatcher $dispatcher */\n        $dispatcher = simpleDispatcher($callback, $this->generateDispatcherOptions());\n\n        $results = $dispatcher->dispatch($method, $uri);\n\n        $this->assertSame($dispatcher::METHOD_NOT_ALLOWED, $results[0]);\n    }\n\n    /**\n     * @dataProvider provideMethodNotAllowedDispatchCases\n     * @param $method\n     * @param $uri\n     * @param $callback\n     * @param $allowedMethods\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('provideMethodNotAllowedDispatchCases')]\n    public function testGetAllowedMethods($method, $uri, $callback, $allowedMethods)\n    {\n        /** @var FastRouteDispatcher $dispatcher */\n        $dispatcher = simpleDispatcher($callback, $this->generateDispatcherOptions());\n\n        $results = $dispatcher->getAllowedMethods($uri);\n\n        $this->assertSame($results, $allowedMethods);\n    }\n\n    public function testDuplicateVariableNameError()\n    {\n        $this->expectException(BadRouteException::class);\n        $this->expectExceptionMessage('Cannot use the same placeholder \"test\" twice');\n\n        simpleDispatcher(function (RouteCollector $r) {\n            $r->addRoute('GET', '/foo/{test}/{test:\\d+}', 'handler0');\n        }, $this->generateDispatcherOptions());\n    }\n\n    public function testDuplicateVariableRoute()\n    {\n        $this->expectException(BadRouteException::class);\n        $this->expectExceptionMessage('Cannot register two routes matching \"/user/([^/]+)\" for method \"GET\"');\n\n        simpleDispatcher(function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{id}', 'handler0'); // oops, forgot \\d+ restriction ;)\n            $r->addRoute('GET', '/user/{name}', 'handler1');\n        }, $this->generateDispatcherOptions());\n    }\n\n    public function testDuplicateStaticRoute()\n    {\n        $this->expectException(BadRouteException::class);\n        $this->expectExceptionMessage('Cannot register two routes matching \"/user\" for method \"GET\"');\n\n        simpleDispatcher(function (RouteCollector $r) {\n            $r->addRoute('GET', '/user', 'handler0');\n            $r->addRoute('GET', '/user', 'handler1');\n        }, $this->generateDispatcherOptions());\n    }\n\n    /**\n     * @codingStandardsIgnoreStart\n     * @codingStandardsIgnoreEnd\n     */\n    public function testShadowedStaticRoute()\n    {\n        $this->expectException(BadRouteException::class);\n        $this->expectExceptionMessage('Static route \"/user/nikic\" is shadowed by previously defined variable route' .\n                                      ' \"/user/([^/]+)\" for method \"GET\"');\n\n        simpleDispatcher(function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}', 'handler0');\n            $r->addRoute('GET', '/user/nikic', 'handler1');\n        }, $this->generateDispatcherOptions());\n    }\n\n    public function testCapturing()\n    {\n        $this->expectException(BadRouteException::class);\n        $this->expectExceptionMessage('Regex \"(en|de)\" for parameter \"lang\" contains a capturing group');\n\n        simpleDispatcher(function (RouteCollector $r) {\n            $r->addRoute('GET', '/{lang:(en|de)}', 'handler0');\n        }, $this->generateDispatcherOptions());\n    }\n\n    public static function provideFoundDispatchCases()\n    {\n        $cases = [];\n\n        // 0 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/resource/123/456', 'handler0');\n        };\n\n        $method = 'GET';\n        $uri = '/resource/123/456';\n        $handler = 'handler0';\n        $argDict = [];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 1 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/handler0', 'handler0');\n            $r->addRoute('GET', '/handler1', 'handler1');\n            $r->addRoute('GET', '/handler2', 'handler2');\n        };\n\n        $method = 'GET';\n        $uri = '/handler2';\n        $handler = 'handler2';\n        $argDict = [];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 2 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');\n            $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');\n            $r->addRoute('GET', '/user/{name}', 'handler2');\n        };\n\n        $method = 'GET';\n        $uri = '/user/rdlowrey';\n        $handler = 'handler2';\n        $argDict = ['name' => 'rdlowrey'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 3 -------------------------------------------------------------------------------------->\n\n        // reuse $callback from #2\n\n        $method = 'GET';\n        $uri = '/user/12345';\n        $handler = 'handler1';\n        $argDict = ['id' => '12345'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 4 -------------------------------------------------------------------------------------->\n\n        // reuse $callback from #3\n\n        $method = 'GET';\n        $uri = '/user/NaN';\n        $handler = 'handler2';\n        $argDict = ['name' => 'NaN'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 5 -------------------------------------------------------------------------------------->\n\n        // reuse $callback from #4\n\n        $method = 'GET';\n        $uri = '/user/rdlowrey/12345';\n        $handler = 'handler0';\n        $argDict = ['name' => 'rdlowrey', 'id' => '12345'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 6 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler0');\n            $r->addRoute('GET', '/user/12345/extension', 'handler1');\n            $r->addRoute('GET', '/user/{id:[0-9]+}.{extension}', 'handler2');\n        };\n\n        $method = 'GET';\n        $uri = '/user/12345.svg';\n        $handler = 'handler2';\n        $argDict = ['id' => '12345', 'extension' => 'svg'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 7 ----- Test GET method fallback on HEAD route miss ------------------------------------>\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}', 'handler0');\n            $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler1');\n            $r->addRoute('GET', '/static0', 'handler2');\n            $r->addRoute('GET', '/static1', 'handler3');\n            $r->addRoute('HEAD', '/static1', 'handler4');\n        };\n\n        $method = 'HEAD';\n        $uri = '/user/rdlowrey';\n        $handler = 'handler0';\n        $argDict = ['name' => 'rdlowrey'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 8 ----- Test GET method fallback on HEAD route miss ------------------------------------>\n\n        // reuse $callback from #7\n\n        $method = 'HEAD';\n        $uri = '/user/rdlowrey/1234';\n        $handler = 'handler1';\n        $argDict = ['name' => 'rdlowrey', 'id' => '1234'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 9 ----- Test GET method fallback on HEAD route miss ------------------------------------>\n\n        // reuse $callback from #8\n\n        $method = 'HEAD';\n        $uri = '/static0';\n        $handler = 'handler2';\n        $argDict = [];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 10 ---- Test existing HEAD route used if available (no fallback) ----------------------->\n\n        // reuse $callback from #9\n\n        $method = 'HEAD';\n        $uri = '/static1';\n        $handler = 'handler4';\n        $argDict = [];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 11 ---- More specified routes are not shadowed by less specific of another method ------>\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}', 'handler0');\n            $r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1');\n        };\n\n        $method = 'POST';\n        $uri = '/user/rdlowrey';\n        $handler = 'handler1';\n        $argDict = ['name' => 'rdlowrey'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 12 ---- Handler of more specific routes is used, if it occurs first -------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}', 'handler0');\n            $r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1');\n            $r->addRoute('POST', '/user/{name}', 'handler2');\n        };\n\n        $method = 'POST';\n        $uri = '/user/rdlowrey';\n        $handler = 'handler1';\n        $argDict = ['name' => 'rdlowrey'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 13 ---- Route with constant suffix ----------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}', 'handler0');\n            $r->addRoute('GET', '/user/{name}/edit', 'handler1');\n        };\n\n        $method = 'GET';\n        $uri = '/user/rdlowrey/edit';\n        $handler = 'handler1';\n        $argDict = ['name' => 'rdlowrey'];\n\n        $cases[] = [$method, $uri, $callback, $handler, $argDict];\n\n        // 14 ---- Handle multiple methods with the same handler ---------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost');\n            $r->addRoute(['DELETE'], '/user', 'handlerDelete');\n            $r->addRoute([], '/user', 'handlerNone');\n        };\n\n        $argDict = [];\n        $cases[] = ['GET', '/user', $callback, 'handlerGetPost', $argDict];\n        $cases[] = ['POST', '/user', $callback, 'handlerGetPost', $argDict];\n        $cases[] = ['DELETE', '/user', $callback, 'handlerDelete', $argDict];\n\n        // 17 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('POST', '/user.json', 'handler0');\n            $r->addRoute('GET', '/{entity}.json', 'handler1');\n        };\n\n        $cases[] = ['GET', '/user.json', $callback, 'handler1', ['entity' => 'user']];\n\n        // 18 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '', 'handler0');\n        };\n\n        $cases[] = ['GET', '', $callback, 'handler0', []];\n\n        // 19 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('HEAD', '/a/{foo}', 'handler0');\n            $r->addRoute('GET', '/b/{foo}', 'handler1');\n        };\n\n        $cases[] = ['HEAD', '/b/bar', $callback, 'handler1', ['foo' => 'bar']];\n\n        // 20 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('HEAD', '/a', 'handler0');\n            $r->addRoute('GET', '/b', 'handler1');\n        };\n\n        $cases[] = ['HEAD', '/b', $callback, 'handler1', []];\n\n        // 21 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/foo', 'handler0');\n            $r->addRoute('HEAD', '/{bar}', 'handler1');\n        };\n\n        $cases[] = ['HEAD', '/foo', $callback, 'handler1', ['bar' => 'foo']];\n\n        // 22 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('*', '/user', 'handler0');\n            $r->addRoute('*', '/{user}', 'handler1');\n            $r->addRoute('GET', '/user', 'handler2');\n        };\n\n        $cases[] = ['GET', '/user', $callback, 'handler2', []];\n\n        // 23 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('*', '/user', 'handler0');\n            $r->addRoute('GET', '/user', 'handler1');\n        };\n\n        $cases[] = ['POST', '/user', $callback, 'handler0', []];\n\n        // 24 ----\n\n        $cases[] = ['HEAD', '/user', $callback, 'handler1', []];\n\n        // 25 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/{bar}', 'handler0');\n            $r->addRoute('*', '/foo', 'handler1');\n        };\n\n        $cases[] = ['GET', '/foo', $callback, 'handler0', ['bar' => 'foo']];\n\n        // 26 ----\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user', 'handler0');\n            $r->addRoute('*', '/{foo:.*}', 'handler1');\n        };\n\n        $cases[] = ['POST', '/bar', $callback, 'handler1', ['foo' => 'bar']];\n\n        // 27 --- International characters\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/новости/{name}', 'handler0');\n        };\n\n        $cases[] = ['GET', '/новости/rdlowrey', $callback, 'handler0', ['name' => 'rdlowrey']];\n\n        // x -------------------------------------------------------------------------------------->\n\n        return $cases;\n    }\n\n    public static function provideNotFoundDispatchCases()\n    {\n        $cases = [];\n\n        // 0 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/resource/123/456', 'handler0');\n        };\n\n        $method = 'GET';\n        $uri = '/not-found';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // 1 -------------------------------------------------------------------------------------->\n\n        // reuse callback from #0\n        $method = 'POST';\n        $uri = '/not-found';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // 2 -------------------------------------------------------------------------------------->\n\n        // reuse callback from #1\n        $method = 'PUT';\n        $uri = '/not-found';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // 3 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/handler0', 'handler0');\n            $r->addRoute('GET', '/handler1', 'handler1');\n            $r->addRoute('GET', '/handler2', 'handler2');\n        };\n\n        $method = 'GET';\n        $uri = '/not-found';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // 4 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');\n            $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');\n            $r->addRoute('GET', '/user/{name}', 'handler2');\n        };\n\n        $method = 'GET';\n        $uri = '/not-found';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // 5 -------------------------------------------------------------------------------------->\n\n        // reuse callback from #4\n        $method = 'GET';\n        $uri = '/user/rdlowrey/12345/not-found';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // 6 -------------------------------------------------------------------------------------->\n\n        // reuse callback from #5\n        $method = 'HEAD';\n\n        $cases[] = [$method, $uri, $callback];\n\n        // x -------------------------------------------------------------------------------------->\n\n        return $cases;\n    }\n\n    public static function provideMethodNotAllowedDispatchCases()\n    {\n        $cases = [];\n\n        // 0 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/resource/123/456', 'handler0');\n        };\n\n        $method = 'POST';\n        $uri = '/resource/123/456';\n        $allowedMethods = ['GET'];\n\n        $cases[] = [$method, $uri, $callback, $allowedMethods];\n\n        // 1 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/resource/123/456', 'handler0');\n            $r->addRoute('POST', '/resource/123/456', 'handler1');\n            $r->addRoute('PUT', '/resource/123/456', 'handler2');\n            $r->addRoute('*', '/', 'handler3');\n        };\n\n        $method = 'DELETE';\n        $uri = '/resource/123/456';\n        $allowedMethods = ['GET', 'POST', 'PUT'];\n\n        $cases[] = [$method, $uri, $callback, $allowedMethods];\n\n        // 2 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');\n            $r->addRoute('POST', '/user/{name}/{id:[0-9]+}', 'handler1');\n            $r->addRoute('PUT', '/user/{name}/{id:[0-9]+}', 'handler2');\n            $r->addRoute('PATCH', '/user/{name}/{id:[0-9]+}', 'handler3');\n        };\n\n        $method = 'DELETE';\n        $uri = '/user/rdlowrey/42';\n        $allowedMethods = ['GET', 'POST', 'PUT', 'PATCH'];\n\n        $cases[] = [$method, $uri, $callback, $allowedMethods];\n\n        // 3 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('POST', '/user/{name}', 'handler1');\n            $r->addRoute('PUT', '/user/{name:[a-z]+}', 'handler2');\n            $r->addRoute('PATCH', '/user/{name:[a-z]+}', 'handler3');\n        };\n\n        $method = 'GET';\n        $uri = '/user/rdlowrey';\n        $allowedMethods = ['POST', 'PUT', 'PATCH'];\n\n        $cases[] = [$method, $uri, $callback, $allowedMethods];\n\n        // 4 -------------------------------------------------------------------------------------->\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost');\n            $r->addRoute(['DELETE'], '/user', 'handlerDelete');\n            $r->addRoute([], '/user', 'handlerNone');\n        };\n\n        $cases[] = ['PUT', '/user', $callback, ['GET', 'POST', 'DELETE']];\n\n        // 5\n\n        $callback = function (RouteCollector $r) {\n            $r->addRoute('POST', '/user.json', 'handler0');\n            $r->addRoute('GET', '/{entity}.json', 'handler1');\n        };\n\n        $cases[] = ['PUT', '/user.json', $callback, ['POST', 'GET']];\n\n        // x -------------------------------------------------------------------------------------->\n\n        return $cases;\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteCollectorProxyTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse Prophecy\\Argument;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Slim\\CallableResolver;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteGroupInterface;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RouteCollectorProxy;\nuse Slim\\Tests\\TestCase;\n\nclass RouteCollectorProxyTest extends TestCase\n{\n    public function testGetResponseFactory()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n\n        $this->assertSame(\n            $responseFactoryProphecy->reveal(),\n            $routeCollectorProxy->getResponseFactory()\n        );\n    }\n\n    public function testGetCallableResolver()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n\n        $this->assertSame(\n            $callableResolverProphecy->reveal(),\n            $routeCollectorProxy->getCallableResolver()\n        );\n    }\n\n    public function testGetContainerReturnsInjectedInstance()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $containerProphecy->reveal()\n        );\n\n        $this->assertSame(\n            $containerProphecy->reveal(),\n            $routeCollectorProxy->getContainer()\n        );\n    }\n\n    public function testGetRouteCollectorReturnsInjectedInstance()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $containerProphecy->reveal(),\n            $routeCollectorProphecy->reveal()\n        );\n\n        $this->assertSame(\n            $routeCollectorProphecy->reveal(),\n            $routeCollectorProxy->getRouteCollector()\n        );\n    }\n\n    public function testGetSetBasePath()\n    {\n        $basePath = '/base/path';\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $containerProphecy->reveal()\n        );\n\n        $routeCollectorProxy->setBasePath($basePath);\n\n        $this->assertSame($basePath, $routeCollectorProxy->getBasePath());\n\n        $newBasePath = '/new/base/path';\n        $routeCollectorProxy->setBasePath('/new/base/path');\n\n        $this->assertSame($newBasePath, $routeCollectorProxy->getBasePath());\n    }\n\n    public function testGet()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['GET'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->get($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testPost()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['POST'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->post($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testPut()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['PUT'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->put($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testPatch()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['PATCH'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->patch($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testDelete()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['DELETE'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->delete($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testOptions()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['OPTIONS'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->options($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testAny()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->any($pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testMap()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/';\n        $methods = ['GET', 'POST'];\n        $callable = function () {\n        };\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn($pattern)\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->map($methods, $pattern, Argument::is($callable))\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $route = $routeCollectorProxy->map($methods, $pattern, $callable);\n\n        $this->assertSame($pattern, $route->getPattern());\n    }\n\n    public function testRedirect()\n    {\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n\n        $from = '/from';\n        $to = '/to';\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $responseProphecy\n            ->withHeader('Location', $to)\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->will(function () use ($responseProphecy) {\n                $this\n                    ->createResponse(302)\n                    ->willReturn($responseProphecy)\n                    ->shouldBeCalledOnce();\n                return $responseProphecy->reveal();\n            })\n            ->shouldBeCalledOnce();\n\n        $routeCollector = new RouteCollector(\n            $responseFactoryProphecy->reveal(),\n            $callableResolver\n        );\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal(),\n            $routeCollector\n        );\n\n        $route = $routeCollectorProxy->redirect($from, $to);\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertSame($responseProphecy->reveal(), $response);\n    }\n\n    public function testGroup()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $pattern = '/group';\n        $callable = function () {\n        };\n\n        $routeGroupProphecy = $this->prophesize(RouteGroupInterface::class);\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->group($pattern, Argument::is($callable))\n            ->willReturn($routeGroupProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxy = new RouteCollectorProxy(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $routeCollectorProphecy->reveal()\n        );\n\n        $routeCollectorProxy->group($pattern, $callable);\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteCollectorTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse Prophecy\\Argument;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RouteCollectorProxy;\nuse Slim\\Tests\\TestCase;\n\nuse function dirname;\nuse function file_exists;\nuse function file_put_contents;\nuse function sprintf;\nuse function unlink;\n\nclass RouteCollectorTest extends TestCase\n{\n    /**\n     * @var null|string\n     */\n    protected $cacheFile;\n\n    public function tearDown(): void\n    {\n        if ($this->cacheFile && file_exists($this->cacheFile)) {\n            unlink($this->cacheFile);\n        }\n    }\n\n    public function testGetSetBasePath()\n    {\n        $basePath = '/app';\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->setBasePath($basePath);\n\n        $this->assertSame($basePath, $routeCollector->getBasePath());\n    }\n\n    public function testMap()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $route = $routeCollector->map(['GET'], '/', function () {\n        });\n\n        $routes = $routeCollector->getRoutes();\n        $this->assertSame($route, $routes[$route->getIdentifier()]);\n    }\n\n    public function testMapPrependsGroupPattern()\n    {\n        $self = $this;\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n\n        $callable = function (RouteCollectorProxy $proxy) use ($self) {\n            $route = $proxy->get('/test', function () {\n            });\n\n            $self->assertSame('/prefix/test', $route->getPattern());\n        };\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $callableResolverProphecy\n            ->resolve(Argument::is($callable))\n            ->willReturn($callable)\n            ->shouldBeCalledOnce();\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->group('/prefix', $callable);\n    }\n\n    public function testGetRouteInvocationStrategy()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $invocationStrategyProphecy = $this->prophesize(InvocationStrategyInterface::class);\n\n        $routeCollector = new RouteCollector(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $containerProphecy->reveal(),\n            $invocationStrategyProphecy->reveal()\n        );\n\n        $this->assertSame($invocationStrategyProphecy->reveal(), $routeCollector->getDefaultInvocationStrategy());\n    }\n\n    public function testRemoveNamedRoute()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->setBasePath('/base/path');\n\n        $route = $routeCollector->map(['GET'], '/test', function () {\n        });\n        $route->setName('test');\n\n        $routes = $routeCollector->getRoutes();\n        $this->assertCount(1, $routes);\n\n        $routeCollector->removeNamedRoute('test');\n        $routes = $routeCollector->getRoutes();\n        $this->assertCount(0, $routes);\n    }\n\n    public function testRemoveNamedRouteWithARouteThatDoesNotExist()\n    {\n        $this->expectException(RuntimeException::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->removeNamedRoute('missing');\n    }\n\n    public function testLookupRouteThrowsExceptionIfRouteNotFound()\n    {\n        $this->expectException(RuntimeException::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->lookupRoute('missing');\n    }\n\n    /**\n     * Test cache file exists but is not writable\n     */\n    public function testCacheFileExistsAndIsNotReadable()\n    {\n        $this->cacheFile = __DIR__ . '/non-readable.cache';\n        file_put_contents($this->cacheFile, '<?php return []; ?>');\n\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage(sprintf('Route collector cache file `%s` is not readable', $this->cacheFile));\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->setCacheFile($this->cacheFile);\n    }\n\n    /**\n     * Test cache file does not exist and directory is not writable\n     */\n    public function testCacheFileDoesNotExistsAndDirectoryIsNotWritable()\n    {\n        $cacheFile = __DIR__ . '/non-writable-directory/router.cache';\n\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage(sprintf(\n            'Route collector cache file directory `%s` is not writable',\n            dirname($cacheFile)\n        ));\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->setCacheFile($cacheFile);\n    }\n\n    public function testSetCacheFileViaConstructor()\n    {\n        $cacheFile = __DIR__ . '/router.cache';\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector(\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            null,\n            null,\n            $cacheFile\n        );\n        $this->assertSame($cacheFile, $routeCollector->getCacheFile());\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteContextTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Interfaces\\RouteParserInterface;\nuse Slim\\Routing\\RouteContext;\nuse Slim\\Routing\\RoutingResults;\nuse Slim\\Tests\\TestCase;\n\nclass RouteContextTest extends TestCase\n{\n    public function testCanCreateInstanceFromServerRequest(): void\n    {\n        $route = $this->createMock(RouteInterface::class);\n        $routeParser = $this->createMock(RouteParserInterface::class);\n        $routingResults = $this->createMock(RoutingResults::class);\n\n        $serverRequest = $this->createServerRequest('/')\n                              ->withAttribute(RouteContext::BASE_PATH, '')\n                              ->withAttribute(RouteContext::ROUTE, $route)\n                              ->withAttribute(RouteContext::ROUTE_PARSER, $routeParser)\n                              ->withAttribute(RouteContext::ROUTING_RESULTS, $routingResults);\n\n        $routeContext = RouteContext::fromRequest($serverRequest);\n\n        $this->assertSame($route, $routeContext->getRoute());\n        $this->assertSame($routeParser, $routeContext->getRouteParser());\n        $this->assertSame($routingResults, $routeContext->getRoutingResults());\n        $this->assertSame('', $routeContext->getBasePath());\n    }\n\n    public function testCanCreateInstanceWithoutRoute(): void\n    {\n        $serverRequest = $this->createServerRequestWithRouteAttributes();\n\n        // Route attribute is not required\n        $serverRequest = $serverRequest->withoutAttribute(RouteContext::ROUTE);\n\n        $routeContext = RouteContext::fromRequest($serverRequest);\n        $this->assertNull($routeContext->getRoute());\n        $this->assertNotNull($routeContext->getRouteParser());\n        $this->assertNotNull($routeContext->getRoutingResults());\n        $this->assertNotNull($routeContext->getBasePath());\n    }\n\n    public function testCanCreateInstanceWithoutBasePathAndThrowExceptionIfGetBasePathIsCalled(): void\n    {\n        $serverRequest = $this->createServerRequestWithRouteAttributes();\n\n        // Route attribute is not required\n        $serverRequest = $serverRequest->withoutAttribute(RouteContext::BASE_PATH);\n\n        $routeContext = RouteContext::fromRequest($serverRequest);\n        $this->assertNotNull($routeContext->getRoute());\n        $this->assertNotNull($routeContext->getRouteParser());\n        $this->assertNotNull($routeContext->getRoutingResults());\n\n        $this->expectException(RuntimeException::class);\n        $routeContext->getBasePath();\n    }\n\n    public static function requiredRouteContextRequestAttributes(): array\n    {\n        return [\n            [RouteContext::ROUTE_PARSER],\n            [RouteContext::ROUTING_RESULTS],\n        ];\n    }\n\n    /**\n     * @dataProvider requiredRouteContextRequestAttributes\n     * @param string $attribute\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('requiredRouteContextRequestAttributes')]\n    public function testCannotCreateInstanceIfRequestIsMissingAttributes(string $attribute): void\n    {\n        $this->expectException(RuntimeException::class);\n\n        $serverRequest = $this->createServerRequestWithRouteAttributes()->withoutAttribute($attribute);\n\n        RouteContext::fromRequest($serverRequest);\n    }\n\n    private function createServerRequestWithRouteAttributes(): ServerRequestInterface\n    {\n        $route = $this->createMock(RouteInterface::class);\n        $routeParser = $this->createMock(RouteParserInterface::class);\n        $routingResults = $this->createMock(RoutingResults::class);\n\n        return $this->createServerRequest('/')\n                    ->withAttribute(RouteContext::BASE_PATH, '')\n                    ->withAttribute(RouteContext::ROUTE, $route)\n                    ->withAttribute(RouteContext::ROUTE_PARSER, $routeParser)\n                    ->withAttribute(RouteContext::ROUTING_RESULTS, $routingResults);\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteParserTest.php",
    "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse InvalidArgumentException;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\UriInterface;\nuse RuntimeException;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RouteParser;\nuse Slim\\Tests\\TestCase;\n\nclass RouteParserTest extends TestCase\n{\n    public static function urlForCases()\n    {\n        return [\n            'with base path' => [\n                true,\n                '/{first}/{second}',\n                ['first' => 'hello', 'second' => 'world'],\n                [],\n                '/app/hello/world',\n            ],\n            'without base path' => [\n                false,\n                '/{first}/{second}',\n                ['first' => 'hello', 'second' => 'world'],\n                [],\n                '/hello/world',\n            ],\n            'with query parameters' => [\n                false,\n                '/{first}/{second}',\n                ['first' => 'hello', 'second' => 'world'],\n                ['a' => 'b', 'c' => 'd'],\n                '/hello/world?a=b&c=d',\n            ],\n            'with query parameters containing array with string keys' => [\n                false,\n                '/{first}/{second}',\n                ['first' => 'hello', 'second' => 'world'],\n                ['a' => ['k' => '1', 'f' => 'x'], 'b', 'c' => 'd'],\n                '/hello/world?a%5Bk%5D=1&a%5Bf%5D=x&0=b&c=d',\n            ],\n            'with query parameters containing array with numeric keys' => [\n                false,\n                '/{first}/{second}',\n                ['first' => 'hello', 'second' => 'world'],\n                ['a' => ['b', 'x', 'y'], 'c' => 'd'],\n                '/hello/world?a%5B0%5D=b&a%5B1%5D=x&a%5B2%5D=y&c=d',\n            ],\n            'with argument without optional parameter' => [\n                false,\n                '/archive/{year}[/{month:[\\d:{2}]}[/d/{day}]]',\n                ['year' => '2015'],\n                [],\n                '/archive/2015',\n            ],\n            'with argument and optional parameter' => [\n                false,\n                '/archive/{year}[/{month:[\\d:{2}]}[/d/{day}]]',\n                ['year' => '2015', 'month' => '07'],\n                [],\n                '/archive/2015/07',\n            ],\n            'with argument and optional parameters' => [\n                false,\n                '/archive/{year}[/{month:[\\d:{2}]}[/d/{day}]]',\n                ['year' => '2015', 'month' => '07', 'day' => '19'],\n                [],\n                '/archive/2015/07/d/19',\n            ],\n        ];\n    }\n\n    public function testRelativePathForWithNoBasePath()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n\n        $route = $routeCollector->map(['GET'], '/{first}/{second}', function () {\n        });\n        $route->setName('test');\n\n        $routeParser = $routeCollector->getRouteParser();\n        $results = $routeParser->relativeUrlFor('test', ['first' => 'hello', 'second' => 'world']);\n\n        $this->assertSame('/hello/world', $results);\n    }\n\n    public function testBasePathIsIgnoreInRelativePathFor()\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeCollector->setBasePath('/app');\n\n        $route = $routeCollector->map(['GET'], '/{first}/{second}', function () {\n        });\n        $route->setName('test');\n\n        $routeParser = $routeCollector->getRouteParser();\n        $results = $routeParser->relativeUrlFor('test', ['first' => 'hello', 'second' => 'world']);\n\n        $this->assertSame('/hello/world', $results);\n    }\n\n    /**\n     * @dataProvider urlForCases\n     * @param $withBasePath\n     * @param $pattern\n     * @param $arguments\n     * @param $queryParams\n     * @param $expectedResult\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('urlForCases')]\n    public function testUrlForWithBasePath($withBasePath, $pattern, $arguments, $queryParams, $expectedResult)\n    {\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n\n        if ($withBasePath) {\n            $routeCollector->setBasePath('/app');\n        }\n\n        $route = $routeCollector->map(['GET'], $pattern, function () {\n        });\n        $route->setName('test');\n\n        $routeParser = $routeCollector->getRouteParser();\n        $results = $routeParser->urlFor('test', $arguments, $queryParams);\n\n        $this->assertSame($expectedResult, $results);\n    }\n\n    public function testUrlForWithMissingSegmentData()\n    {\n        $this->expectException(InvalidArgumentException::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $route = $routeCollector->map(['GET'], '/{first}/{last}', function () {\n        });\n        $route->setName('test');\n\n        $routeParser = $routeCollector->getRouteParser();\n        $routeParser->urlFor('test', ['last' => 'world']);\n    }\n\n    public function testUrlForRouteThatDoesNotExist()\n    {\n        $this->expectException(RuntimeException::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $routeCollector = new RouteCollector($responseFactoryProphecy->reveal(), $callableResolverProphecy->reveal());\n        $routeParser = $routeCollector->getRouteParser();\n\n        $routeParser->urlFor('test');\n    }\n\n    public function testFullUrlFor()\n    {\n        $uriProphecy = $this->prophesize(UriInterface::class);\n        $uriProphecy\n            ->getScheme()\n            ->willReturn('http')\n            ->shouldBeCalledOnce();\n\n        $uriProphecy\n            ->getAuthority()\n            ->willReturn('example.com:8080')\n            ->shouldBeCalledOnce();\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $routeProphecy\n            ->getPattern()\n            ->willReturn('/{token}')\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n\n        $routeCollectorProphecy\n            ->getBasePath()\n            ->willReturn('/app')\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProphecy\n            ->getNamedRoute('test')\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeParser = new RouteParser($routeCollectorProphecy->reveal());\n        $result = $routeParser->fullUrlFor($uriProphecy->reveal(), 'test', ['token' => '123']);\n\n        $expectedResult = 'http://example.com:8080/app/123';\n        $this->assertSame($expectedResult, $result);\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteResolverTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse Error;\nuse Prophecy\\Argument;\nuse Slim\\Interfaces\\DispatcherInterface;\nuse Slim\\Interfaces\\RouteCollectorInterface;\nuse Slim\\Interfaces\\RouteInterface;\nuse Slim\\Routing\\RouteResolver;\nuse Slim\\Routing\\RoutingResults;\nuse Slim\\Tests\\TestCase;\n\nuse function sprintf;\n\nclass RouteResolverTest extends TestCase\n{\n    public static function computeRoutingResultsDataProvider(): array\n    {\n        return [\n            ['GET', '', '/'],\n            ['GET', '/', '/'],\n            ['GET', '//foo', '//foo'],\n            ['GET', 'hello%20world', '/hello world'],\n        ];\n    }\n\n    /**\n     * @dataProvider computeRoutingResultsDataProvider\n     *\n     * @param string $method      The request method\n     * @param string $uri         The request uri\n     * @param string $expectedUri The expected uri after transformation in the computeRoutingResults()\n     */\n    #[\\PHPUnit\\Framework\\Attributes\\DataProvider('computeRoutingResultsDataProvider')]\n    public function testComputeRoutingResults(string $method, string $uri, string $expectedUri)\n    {\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routingResultsProphecy = $this->prophesize(RoutingResults::class);\n\n        $dispatcherProphecy = $this->prophesize(DispatcherInterface::class);\n        $dispatcherProphecy\n            ->dispatch(Argument::type('string'), Argument::type('string'))\n            ->will(function ($args) use ($routingResultsProphecy, $expectedUri) {\n                if ($args[1] !== $expectedUri) {\n                    throw new Error(sprintf(\n                        \"URI transformation failed.\\n  Received: '%s'\\n  Expected: '%s'\",\n                        $args[1],\n                        $expectedUri\n                    ));\n                }\n                return $routingResultsProphecy->reveal();\n            })\n            ->shouldBeCalledOnce();\n\n        $routeResolver = new RouteResolver(\n            $routeCollectorProphecy->reveal(),\n            $dispatcherProphecy->reveal()\n        );\n\n        $routeResolver->computeRoutingResults($uri, $method);\n    }\n\n    public function testResolveRoute()\n    {\n        $identifier = 'test';\n\n        $routeProphecy = $this->prophesize(RouteInterface::class);\n        $dispatcherProphecy = $this->prophesize(DispatcherInterface::class);\n\n        $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class);\n        $routeCollectorProphecy\n            ->lookupRoute($identifier)\n            ->willReturn($routeProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeResolver = new RouteResolver(\n            $routeCollectorProphecy->reveal(),\n            $dispatcherProphecy->reveal()\n        );\n\n        $routeResolver->resolveRoute($identifier);\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteRunnerTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests\\Routing;\n\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Slim\\MiddlewareDispatcher;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Routing\\RouteContext;\nuse Slim\\Routing\\RouteParser;\nuse Slim\\Routing\\RouteResolver;\nuse Slim\\Routing\\RouteRunner;\nuse Slim\\Routing\\RoutingResults;\nuse Slim\\Tests\\TestCase;\n\nclass RouteRunnerTest extends TestCase\n{\n    public function testRoutingIsPerformedIfRoutingResultsAreUnavailable()\n    {\n        $handler = (function (ServerRequestInterface $request, ResponseInterface $response) {\n            $routeParser = $request->getAttribute(RouteContext::ROUTE_PARSER);\n            $this->assertInstanceOf(RouteParser::class, $routeParser);\n\n            $routingResults = $request->getAttribute(RouteContext::ROUTING_RESULTS);\n            $this->assertInstanceOf(RoutingResults::class, $routingResults);\n\n            return $response;\n        })->bindTo($this);\n\n        $callableResolver = $this->getCallableResolver();\n        $responseFactory = $this->getResponseFactory();\n\n        $routeCollector = new RouteCollector($responseFactory, $callableResolver);\n        $routeCollector->map(['GET'], '/hello/{name}', $handler);\n\n        $routeParser = new RouteParser($routeCollector);\n        $routeResolver = new RouteResolver($routeCollector);\n\n        $request = $this->createServerRequest('https://example.com:443/hello/foo', 'GET');\n        $dispatcher = new RouteRunner($routeResolver, $routeParser);\n\n        $middlewareDispatcher = new MiddlewareDispatcher($dispatcher, $callableResolver);\n        $middlewareDispatcher->handle($request);\n    }\n}\n"
  },
  {
    "path": "tests/Routing/RouteTest.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests;\n\nuse Closure;\nuse Exception;\nuse Prophecy\\Argument;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\StreamInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse RuntimeException;\nuse Slim\\CallableResolver;\nuse Slim\\Handlers\\Strategies\\RequestHandler;\nuse Slim\\Handlers\\Strategies\\RequestResponse;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\Interfaces\\InvocationStrategyInterface;\nuse Slim\\Interfaces\\RouteCollectorProxyInterface;\nuse Slim\\Routing\\Route;\nuse Slim\\Routing\\RouteGroup;\nuse Slim\\Tests\\Mocks\\CallableTest;\nuse Slim\\Tests\\Mocks\\InvocationStrategyTest;\nuse Slim\\Tests\\Mocks\\MockCustomRequestHandlerInvocationStrategy;\nuse Slim\\Tests\\Mocks\\MockMiddlewareWithoutConstructor;\nuse Slim\\Tests\\Mocks\\MockMiddlewareWithoutInterface;\nuse Slim\\Tests\\Mocks\\RequestHandlerTest;\n\nuse function is_callable;\nuse function is_string;\nuse function ob_end_clean;\nuse function ob_start;\n\nclass RouteTest extends TestCase\n{\n    /**\n     * @param string|array        $methods\n     * @param string              $pattern\n     * @param Closure|string|null $callable\n     * @return Route\n     */\n    public function createRoute($methods = 'GET', string $pattern = '/', $callable = null): Route\n    {\n        $callable ??= function (ServerRequestInterface $request, ResponseInterface $response) {\n                return $response;\n        };\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $callableResolverProphecy\n            ->resolve(Argument::is($callable))\n            ->willReturn($callable);\n        $callableResolverProphecy\n            ->resolve(MockMiddlewareWithoutConstructor::class)\n            ->will(function ($args) {\n                return [new MockMiddlewareWithoutConstructor(), 'process'];\n            });\n\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n\n        $value = '';\n        $streamProphecy\n            ->write(Argument::type('string'))\n            ->will(function ($args) use ($value) {\n                $value .= $args[0];\n                $this->__toString()->willReturn($value);\n                return strlen($value);\n            });\n\n        $streamProphecy\n            ->__toString()\n            ->willReturn($value);\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseProphecy\n            ->getBody()\n            ->willReturn($streamProphecy->reveal());\n\n        $responseProphecy\n            ->withStatus(Argument::type('integer'))\n            ->will(function ($args) {\n                $this->getStatusCode()->willReturn($args[0]);\n                return $this->reveal();\n            });\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal());\n\n        $methods = is_string($methods) ? [$methods] : $methods;\n        return new Route(\n            $methods,\n            $pattern,\n            $callable,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n    }\n\n    public function testConstructor()\n    {\n        $methods = ['GET', 'POST'];\n        $pattern = '/hello/{name}';\n        $callable = function ($request, $response, $args) {\n            return $response;\n        };\n        $route = $this->createRoute($methods, $pattern, $callable);\n\n        $this->assertSame($methods, $route->getMethods());\n        $this->assertSame($pattern, $route->getPattern());\n        $this->assertSame($callable, $route->getCallable());\n    }\n\n    public function testGetMethodsReturnsArrayWhenConstructedWithString()\n    {\n        $route = $this->createRoute();\n\n        $this->assertSame(['GET'], $route->getMethods());\n    }\n\n    public function testGetMethods()\n    {\n        $methods = ['GET', 'POST'];\n        $route = $this->createRoute($methods);\n\n        $this->assertSame($methods, $route->getMethods());\n    }\n\n    public function testGetPattern()\n    {\n        $route = $this->createRoute();\n\n        $this->assertSame('/', $route->getPattern());\n    }\n\n    public function testGetCallable()\n    {\n        $route = $this->createRoute();\n\n        $this->assertTrue(is_callable($route->getCallable()));\n    }\n\n    public function testGetCallableResolver()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        };\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $route = new Route(\n            ['GET'],\n            '/',\n            $callable,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n\n        $this->assertSame($callableResolverProphecy->reveal(), $route->getCallableResolver());\n    }\n\n    public function testGetInvocationStrategy()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        };\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $strategyProphecy = $this->prophesize(InvocationStrategyInterface::class);\n\n        $route = new Route(\n            ['GET'],\n            '/',\n            $callable,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            $containerProphecy->reveal(),\n            $strategyProphecy->reveal()\n        );\n\n        $this->assertSame($strategyProphecy->reveal(), $route->getInvocationStrategy());\n    }\n\n    public function testSetInvocationStrategy()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        };\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $strategyProphecy = $this->prophesize(InvocationStrategyInterface::class);\n\n        $route = new Route(\n            ['GET'],\n            '/',\n            $callable,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n        $route->setInvocationStrategy($strategyProphecy->reveal());\n\n        $this->assertSame($strategyProphecy->reveal(), $route->getInvocationStrategy());\n    }\n\n    public function testGetGroups()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        };\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $strategyProphecy = $this->prophesize(InvocationStrategyInterface::class);\n        $routeCollectorProxyProphecy = $this->prophesize(RouteCollectorProxyInterface::class);\n\n        $routeGroup = new RouteGroup(\n            '/group',\n            $callable,\n            $callableResolverProphecy->reveal(),\n            $routeCollectorProxyProphecy->reveal()\n        );\n        $groups = [$routeGroup];\n\n        $route = new Route(\n            ['GET'],\n            '/',\n            $callable,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $strategyProphecy->reveal(),\n            $groups\n        );\n\n        $this->assertSame($groups, $route->getGroups());\n    }\n\n    public function testArgumentSetting()\n    {\n        $route = $this->createRoute();\n        $route->setArguments(['foo' => 'FOO', 'bar' => 'BAR']);\n        $this->assertSame($route->getArguments(), ['foo' => 'FOO', 'bar' => 'BAR']);\n\n        $route->setArgument('bar', 'bar');\n        $this->assertSame($route->getArguments(), ['foo' => 'FOO', 'bar' => 'bar']);\n\n        $route->setArgument('baz', 'BAZ');\n        $this->assertSame($route->getArguments(), ['foo' => 'FOO', 'bar' => 'bar', 'baz' => 'BAZ']);\n\n        $route->setArguments(['a' => 'b']);\n        $this->assertSame($route->getArguments(), ['a' => 'b']);\n        $this->assertSame($route->getArgument('a', 'default'), 'b');\n        $this->assertSame($route->getArgument('b', 'default'), 'default');\n    }\n\n    public function testAddMiddleware()\n    {\n        $route = $this->createRoute();\n        $called = 0;\n\n        $mw = function (ServerRequestInterface $request, RequestHandlerInterface $handler) use (&$called) {\n            $called++;\n            return $handler->handle($request);\n        };\n        $route->add($mw);\n\n        $request = $this->createServerRequest('/');\n        $route->run($request);\n\n        $this->assertSame($called, 1);\n    }\n\n    public function testAddMiddlewareOnGroup()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            return $response;\n        };\n\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n        $callableResolverProphecy\n            ->resolve(Argument::is($callable))\n            ->willReturn($callable)\n            ->shouldBeCalledOnce();\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $routeCollectorProxyProphecy = $this->prophesize(RouteCollectorProxyInterface::class);\n        $strategy = new RequestResponse();\n\n        $called = 0;\n        $mw = function (ServerRequestInterface $request, RequestHandlerInterface $handler) use (&$called) {\n            $called++;\n            return $handler->handle($request);\n        };\n\n        $routeGroup = new RouteGroup(\n            '/group',\n            $callable,\n            $callableResolverProphecy->reveal(),\n            $routeCollectorProxyProphecy->reveal()\n        );\n        $routeGroup->add($mw);\n        $groups = [$routeGroup];\n\n        $route = new Route(\n            ['GET'],\n            '/',\n            $callable,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal(),\n            null,\n            $strategy,\n            $groups\n        );\n\n        $request = $this->createServerRequest('/');\n        $route->run($request);\n\n        $this->assertSame($called, 1);\n    }\n\n    public function testAddClosureMiddleware()\n    {\n        $route = $this->createRoute();\n        $called = 0;\n\n        $route->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler) use (&$called) {\n            $called++;\n            return $handler->handle($request);\n        });\n\n        $request = $this->createServerRequest('/');\n        $route->run($request);\n\n        $this->assertSame($called, 1);\n    }\n\n    public function testAddMiddlewareUsingDeferredResolution()\n    {\n        $route = $this->createRoute();\n        $route->add(MockMiddlewareWithoutConstructor::class);\n\n        $output = '';\n        $appendToOutput = function (string $value) use (&$output) {\n            $output .= $value;\n        };\n        $request = $this->createServerRequest('/')->withAttribute('appendToOutput', $appendToOutput);\n        $route->run($request);\n\n        $this->assertSame('Hello World', $output);\n    }\n\n    public function testAddMiddlewareAsStringNotImplementingInterfaceThrowsException()\n    {\n        $this->expectException(RuntimeException::class);\n        $this->expectExceptionMessage(\n            'A middleware must be an object/class name referencing an implementation of ' .\n            'MiddlewareInterface or a callable with a matching signature.'\n        );\n\n        $route = $this->createRoute();\n        $route->add(new MockMiddlewareWithoutInterface());\n    }\n\n    public function testIdentifier()\n    {\n        $route = $this->createRoute();\n        $this->assertSame('route0', $route->getIdentifier());\n    }\n\n    public function testSetName()\n    {\n        $route = $this->createRoute();\n        $this->assertSame($route, $route->setName('foo'));\n        $this->assertSame('foo', $route->getName());\n    }\n\n    public function testControllerMethodAsStringResolvesWithoutContainer()\n    {\n        $callableResolver = new CallableResolver();\n        $responseFactory = $this->getResponseFactory();\n\n        $deferred = $callableResolver->resolve('\\Slim\\Tests\\Mocks\\CallableTest:toCall');\n        $route = new Route(['GET'], '/', $deferred, $responseFactory, $callableResolver);\n\n        CallableTest::$CalledCount = 0;\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame(1, CallableTest::$CalledCount);\n    }\n\n    public function testControllerMethodAsStringResolvesWithContainer()\n    {\n        $self = $this;\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n        $callableResolverProphecy = $this->prophesize(CallableResolverInterface::class);\n\n        $callable = 'callable';\n\n        $callableResolverProphecy\n            ->resolve(Argument::is($callable))\n            ->willReturn(function (\n                ServerRequestInterface $request,\n                ResponseInterface $response\n            ) use (\n                $self,\n                $responseProphecy\n            ) {\n                $self->assertSame($responseProphecy->reveal(), $response);\n                return $response;\n            })\n            ->shouldBeCalledOnce();\n\n        $deferred = $callableResolverProphecy->reveal()->resolve($callable);\n        $callableResolverProphecy\n            ->resolve(Argument::is($deferred))\n            ->willReturn($deferred)\n            ->shouldBeCalledOnce();\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal());\n\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolverProphecy->reveal()\n        );\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n    }\n\n    /**\n     * Ensure that the response returned by a route callable is the response\n     * object that is returned by __invoke().\n     */\n    public function testProcessWhenReturningAResponse()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('foo');\n            return $response;\n        };\n        $route = $this->createRoute(['GET'], '/', $callable);\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertSame('foo', (string) $response->getBody());\n    }\n\n    /**\n     * Ensure that anything echo'd in a route callable is, by default, NOT\n     * added to the response object body.\n     */\n    public function testRouteCallableDoesNotAppendEchoedOutput()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            echo \"foo\";\n            return $response->withStatus(201);\n        };\n        $route = $this->createRoute(['GET'], '/', $callable);\n\n        $request = $this->createServerRequest('/');\n\n        // We capture output buffer here only to clean test CLI output\n        ob_start();\n        $response = $route->run($request);\n        ob_end_clean();\n\n        // Output buffer is ignored without optional middleware\n        $this->assertSame('', (string) $response->getBody());\n        $this->assertSame(201, $response->getStatusCode());\n    }\n\n    /**\n     * Ensure that if a string is returned by a route callable, then it is\n     * added to the response object that is returned by __invoke().\n     */\n    public function testRouteCallableAppendsCorrectOutputToResponse()\n    {\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            $response->getBody()->write('foo');\n            return $response;\n        };\n        $route = $this->createRoute(['GET'], '/', $callable);\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertSame('foo', (string) $response->getBody());\n    }\n\n    public function testInvokeWithException()\n    {\n        $this->expectException(Exception::class);\n\n        $callable = function (ServerRequestInterface $request, ResponseInterface $response) {\n            throw new Exception();\n        };\n        $route = $this->createRoute(['GET'], '/', $callable);\n\n        $request = $this->createServerRequest('/');\n        $route->run($request);\n    }\n\n    /**\n     * Ensure that `foundHandler` is called on actual callable\n     */\n    public function testInvokeDeferredCallableWithNoContainer()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $callableResolver = new CallableResolver();\n        $invocationStrategy = new InvocationStrategyTest();\n\n        $deferred = '\\Slim\\Tests\\Mocks\\CallableTest:toCall';\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            null,\n            $invocationStrategy\n        );\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertEquals([new CallableTest(), 'toCall'], InvocationStrategyTest::$LastCalledFor);\n    }\n\n    /**\n     * Ensure that `foundHandler` is called on actual callable\n     */\n    public function testInvokeDeferredCallableWithContainer()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('\\Slim\\Tests\\Mocks\\CallableTest')->willReturn(true);\n        $containerProphecy->get('\\Slim\\Tests\\Mocks\\CallableTest')->willReturn(new CallableTest());\n\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n        $strategy = new InvocationStrategyTest();\n\n        $deferred = '\\Slim\\Tests\\Mocks\\CallableTest:toCall';\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal(),\n            $strategy\n        );\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertEquals([new CallableTest(), 'toCall'], InvocationStrategyTest::$LastCalledFor);\n    }\n\n    public function testInvokeUsesRequestHandlerStrategyForRequestHandlers()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has(RequestHandlerTest::class)->willReturn(true);\n        $containerProphecy->get(RequestHandlerTest::class)->willReturn(new RequestHandlerTest());\n\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n\n        $deferred = RequestHandlerTest::class;\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal()\n        );\n\n        $request = $this->createServerRequest('/', 'GET');\n        $route->run($request);\n\n        /** @var InvocationStrategyInterface $strategy */\n        $strategy = $containerProphecy\n            ->reveal()\n            ->get(RequestHandlerTest::class)::$strategy;\n\n        $this->assertSame(RequestHandler::class, $strategy);\n    }\n\n    public function testInvokeUsesUserSetStrategyForRequestHandlers()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has(RequestHandlerTest::class)->willReturn(true);\n        $containerProphecy->get(RequestHandlerTest::class)->willReturn(new RequestHandlerTest());\n\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n\n        $deferred = RequestHandlerTest::class;\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal()\n        );\n\n        $strategy = new MockCustomRequestHandlerInvocationStrategy();\n        $route->setInvocationStrategy($strategy);\n\n        $request = $this->createServerRequest('/', 'GET');\n        $route->run($request);\n\n        $this->assertSame(1, $strategy::$CalledCount);\n    }\n\n    public function testRequestHandlerStrategyAppendsRouteArgumentsAsAttributesToRequest()\n    {\n        $self = $this;\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has(RequestHandlerTest::class)->willReturn(true);\n        $containerProphecy->get(RequestHandlerTest::class)->willReturn(new RequestHandlerTest());\n\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n\n        $deferred = RequestHandlerTest::class;\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal()\n        );\n\n        $strategy = new RequestHandler(true);\n        $route->setInvocationStrategy($strategy);\n        $route->setArguments(['id' => 1]);\n\n        $requestProphecy = $this->prophesize(ServerRequestInterface::class);\n        $requestProphecy->withAttribute(Argument::type('string'), Argument::any())->will(function ($args) use ($self) {\n            $name = $args[0];\n            $value = $args[1];\n\n            $self->assertSame('id', $name);\n            $self->assertSame(1, $value);\n\n            return $this;\n        })->shouldBeCalledOnce();\n\n        $route->run($requestProphecy->reveal());\n    }\n\n    /**\n     * Ensure that the pattern can be dynamically changed\n     */\n    public function testPatternCanBeChanged()\n    {\n        $route = $this->createRoute();\n        $route->setPattern('/hola/{nombre}');\n\n        $this->assertSame('/hola/{nombre}', $route->getPattern());\n    }\n\n    /**\n     * Ensure that the callable can be changed\n     */\n    public function testChangingCallableWithNoContainer()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $callableResolver = new CallableResolver();\n        $strategy = new InvocationStrategyTest();\n\n        $deferred = 'NonExistent:toCall';\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            null,\n            $strategy\n        );\n        $route->setCallable('\\Slim\\Tests\\Mocks\\CallableTest:toCall'); //Then we fix it here.\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertEquals([new CallableTest(), 'toCall'], InvocationStrategyTest::$LastCalledFor);\n    }\n\n    /**\n     * Ensure that the callable can be changed\n     */\n    public function testChangingCallableWithContainer()\n    {\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('CallableTest2')->willReturn(true);\n        $containerProphecy->get('CallableTest2')->willReturn(new CallableTest());\n\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n        $strategy = new InvocationStrategyTest();\n\n        $deferred = 'NonExistent:toCall';\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal(),\n            $strategy\n        );\n        $route->setCallable('CallableTest2:toCall'); //Then we fix it here.\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertInstanceOf(ResponseInterface::class, $response);\n        $this->assertSame(\n            [$containerProphecy->reveal()->get('CallableTest2'), 'toCall'],\n            InvocationStrategyTest::$LastCalledFor\n        );\n    }\n\n    public function testRouteCallableIsResolvedUsingContainerWhenCallableResolverIsPresent()\n    {\n        $streamProphecy = $this->prophesize(StreamInterface::class);\n\n        $value = '';\n        $streamProphecy\n            ->write(Argument::type('string'))\n            ->will(function ($args) use ($value) {\n                $value .= $args[0];\n                $this->__toString()->willReturn($value);\n                return strlen($value);\n            });\n\n        $streamProphecy\n            ->__toString()\n            ->willReturn($value);\n\n        $responseProphecy = $this->prophesize(ResponseInterface::class);\n\n        $responseProphecy\n            ->getBody()\n            ->willReturn($streamProphecy->reveal())\n            ->shouldBeCalledTimes(2);\n\n        $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class);\n        $responseFactoryProphecy\n            ->createResponse()\n            ->willReturn($responseProphecy->reveal())\n            ->shouldBeCalledOnce();\n\n        $containerProphecy = $this->prophesize(ContainerInterface::class);\n        $containerProphecy->has('CallableTest3')->willReturn(true);\n        $containerProphecy->get('CallableTest3')->willReturn(new CallableTest());\n        $containerProphecy->has('ClosureMiddleware')->willReturn(true);\n        $containerProphecy->get('ClosureMiddleware')->willReturn(function () use ($responseFactoryProphecy) {\n            $response = $responseFactoryProphecy->reveal()->createResponse();\n            $response->getBody()->write('Hello');\n            return $response;\n        });\n\n        $callableResolver = new CallableResolver($containerProphecy->reveal());\n        $strategy = new InvocationStrategyTest();\n\n        $deferred = 'CallableTest3';\n        $route = new Route(\n            ['GET'],\n            '/',\n            $deferred,\n            $responseFactoryProphecy->reveal(),\n            $callableResolver,\n            $containerProphecy->reveal(),\n            $strategy\n        );\n        $route->add('ClosureMiddleware');\n\n        $request = $this->createServerRequest('/');\n        $response = $route->run($request);\n\n        $this->assertSame('Hello', (string) $response->getBody());\n    }\n}\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nnamespace Slim\\Tests;\n\nuse PHPUnit\\Framework\\TestCase as PHPUnitTestCase;\nuse Prophecy\\PhpUnit\\ProphecyTrait;\nuse Psr\\Container\\ContainerInterface;\nuse Psr\\Http\\Message\\ResponseFactoryInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestFactoryInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Message\\StreamFactoryInterface;\nuse Psr\\Http\\Message\\StreamInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\nuse ReflectionMethod;\nuse ReflectionProperty;\nuse Slim\\CallableResolver;\nuse Slim\\Interfaces\\CallableResolverInterface;\nuse Slim\\MiddlewareDispatcher;\nuse Slim\\Tests\\Providers\\PSR7ObjectProvider;\n\nabstract class TestCase extends PhpUnitTestCase\n{\n    use ProphecyTrait;\n\n    /**\n     * @return ServerRequestFactoryInterface\n     */\n    protected function getServerRequestFactory(): ServerRequestFactoryInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->getServerRequestFactory();\n    }\n\n    /**\n     * @return ResponseFactoryInterface\n     */\n    protected function getResponseFactory(): ResponseFactoryInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->getResponseFactory();\n    }\n\n    /**\n     * @return StreamFactoryInterface\n     */\n    protected function getStreamFactory(): StreamFactoryInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->getStreamFactory();\n    }\n\n    /**\n     * @param ContainerInterface|null $container\n     *\n     * @return CallableResolverInterface\n     */\n    protected function getCallableResolver(?ContainerInterface $container = null): CallableResolverInterface\n    {\n        return new CallableResolver($container);\n    }\n\n    /**\n     * @param RequestHandlerInterface $requestHandler\n     * @param ContainerInterface|null $container\n     * @param CallableResolverInterface|null $callableResolver\n     *\n     * @return MiddlewareDispatcher\n     */\n    protected function createMiddlewareDispatcher(\n        RequestHandlerInterface $requestHandler,\n        ?ContainerInterface $container = null,\n        ?CallableResolverInterface $callableResolver = null\n    ): MiddlewareDispatcher {\n        return new MiddlewareDispatcher(\n            $requestHandler,\n            $callableResolver ?? $this->getCallableResolver($container),\n            $container\n        );\n    }\n\n    /**\n     * @param string $uri\n     * @param string $method\n     * @param array  $data\n     * @return ServerRequestInterface\n     */\n    protected function createServerRequest(\n        string $uri,\n        string $method = 'GET',\n        array $data = []\n    ): ServerRequestInterface {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->createServerRequest($uri, $method, $data);\n    }\n\n    /**\n     * @param int    $statusCode\n     * @param string $reasonPhrase\n     * @return ResponseInterface\n     */\n    protected function createResponse(int $statusCode = 200, string $reasonPhrase = ''): ResponseInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->createResponse($statusCode, $reasonPhrase);\n    }\n\n    /**\n     * @param string $contents\n     * @return StreamInterface\n     */\n    protected function createStream(string $contents = ''): StreamInterface\n    {\n        $psr7ObjectProvider = new PSR7ObjectProvider();\n        return $psr7ObjectProvider->createStream($contents);\n    }\n\n    /**\n     * @param  ReflectionProperty|ReflectionMethod  $ref\n     * @return void\n     */\n    protected function setAccessible($ref, bool $accessible = true): void\n    {\n        if (PHP_VERSION_ID < 80100) {\n            $ref->setAccessible($accessible);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/bootstrap.php",
    "content": "<?php\n\n/**\n * Slim Framework (https://slimframework.com)\n *\n * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)\n */\n\ndeclare(strict_types=1);\n\nuse AdrianSuter\\Autoload\\Override\\Override;\nuse Slim\\ResponseEmitter;\nuse Slim\\Routing\\RouteCollector;\nuse Slim\\Tests\\Assets\\HeaderStack;\n\n$classLoader = require __DIR__ . '/../vendor/autoload.php';\n\nOverride::apply($classLoader, [\n    ResponseEmitter::class => [\n        'connection_status' => function (): int {\n            if (isset($GLOBALS['connection_status_return'])) {\n                return $GLOBALS['connection_status_return'];\n            }\n\n            return connection_status();\n        },\n        'header' => function (string $string, bool $replace = true, ?int $statusCode = null): void {\n            HeaderStack::push(\n                [\n                    'header' => $string,\n                    'replace' => $replace,\n                    'status_code' => $statusCode,\n                ]\n            );\n        },\n        'headers_sent' => function (): bool {\n            return false;\n        }\n    ],\n    RouteCollector::class => [\n        'is_readable' => function (string $file): bool {\n            return stripos($file, 'non-readable.cache') === false;\n        },\n        'is_writable' => function (string $path): bool {\n            return stripos($path, 'non-writable-directory') === false;\n        }\n    ]\n]);\n"
  }
]