init
This commit is contained in:
Vendored
+351
@@ -0,0 +1,351 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Removed
|
||||
|
||||
## 4.15.1 - 2025-11-21
|
||||
|
||||
## Fixed
|
||||
|
||||
- Allow PHPUnit 10, 11 and 12 when testing Slim itself (#3411)
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for PHP 8.5 (#3415)
|
||||
|
||||
**Full Changelog**: https://github.com/slimphp/Slim/compare/4.15.0...4.15.1
|
||||
|
||||
## 4.15.0 - 2025-08-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix DocBlocks for callable route handlers (#3389)
|
||||
- Change class keyword to lowercase (#3346)
|
||||
- Fix tests for PHP 8.3
|
||||
- Fixes the build status badge in Readme (#3331)
|
||||
- Fix text and eol attributes for * selector in .gitattributes (#3391)
|
||||
- Deprecate setArgument/s (#3383)
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for PHP 8.4
|
||||
- Add phpstan v2
|
||||
|
||||
### Changed
|
||||
|
||||
- Update http urls in composer.json (#3399)
|
||||
|
||||
**Full Changelog**: https://github.com/slimphp/Slim/compare/4.14.0...4.15.0
|
||||
|
||||
## 4.14.0 - 2024-06-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not HTML entity encode in PlainTextErrorRenderer by @akrabat in https://github.com/slimphp/Slim/pull/3319
|
||||
- Only render tip to error log if plain text renderer is used by @akrabat in https://github.com/slimphp/Slim/pull/3321
|
||||
- Add template generics for PSR-11 implementations in PHPStan and Psalm by @limarkxx in https://github.com/slimphp/Slim/pull/3322
|
||||
- Update squizlabs/php_codesniffer requirement from ^3.9 to ^3.10 by @dependabot in https://github.com/slimphp/Slim/pull/3324
|
||||
- Update phpstan/phpstan requirement from ^1.10 to ^1.11 by @dependabot in https://github.com/slimphp/Slim/pull/3325
|
||||
- Update psr/http-factory requirement from ^1.0 to ^1.1 by @dependabot in https://github.com/slimphp/Slim/pull/3326
|
||||
|
||||
#### Type hinting with template generics
|
||||
|
||||
With 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:
|
||||
|
||||
* `/** @var \Slim\App<null> $app */` if you are not using a DI container, or
|
||||
* `/** @var \Slim\App<\Psr\Container\ContainerInterface> $app */` if you are
|
||||
|
||||
You 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 */`.
|
||||
|
||||
### New Contributors
|
||||
|
||||
* @limarkxx made their first contribution in https://github.com/slimphp/Slim/pull/3322
|
||||
|
||||
**Full Changelog**: https://github.com/slimphp/Slim/compare/4.13.0...4.14.0
|
||||
|
||||
# 4.13.0 - 2024-03-03
|
||||
|
||||
- [3277: Create HttpTooManyRequestsException.php](https://github.com/slimphp/Slim/pull/3277) thanks to @flavioheleno
|
||||
- [3278: Remove HttpGoneException executable flag](https://github.com/slimphp/Slim/pull/3278) thanks to @flavioheleno
|
||||
- [3285: Update guzzlehttp/psr7 requirement from ^2.5 to ^2.6](https://github.com/slimphp/Slim/pull/3285) thanks to @dependabot[bot]
|
||||
- [3290: Bump actions/checkout from 3 to 4](https://github.com/slimphp/Slim/pull/3290) thanks to @dependabot[bot]
|
||||
- [3291: Fix line length](https://github.com/slimphp/Slim/pull/3291) thanks to @l0gicgate
|
||||
- [3296: PSR 7 http-message version requirement](https://github.com/slimphp/Slim/issues/3296) thanks to @rotexdegba
|
||||
- [3297: Allow Diactoros 3](https://github.com/slimphp/Slim/pull/3297) thanks to @derrabus
|
||||
- [3299: Update tests and add PHP 8.3 to the CI matrix](https://github.com/slimphp/Slim/pull/3299) thanks to @akrabat
|
||||
- [3301: Update nyholm/psr7-server requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3301) thanks to @dependabot[bot]
|
||||
- [3302: Add support for psr/http-message ^2.0](https://github.com/slimphp/Slim/pull/3302) thanks to @rotexdegba
|
||||
- [3305: Update phpspec/prophecy-phpunit requirement from ^2.0 to ^2.1](https://github.com/slimphp/Slim/pull/3305) thanks to @dependabot[bot]
|
||||
- [3306: Update phpspec/prophecy requirement from ^1.17 to ^1.18](https://github.com/slimphp/Slim/pull/3306) thanks to @dependabot[bot]
|
||||
- [3308: Update squizlabs/php_codesniffer requirement from ^3.7 to ^3.8](https://github.com/slimphp/Slim/pull/3308) thanks to @dependabot[bot]
|
||||
- [3313: Bump ramsey/composer-install from 2 to 3](https://github.com/slimphp/Slim/pull/3313) thanks to @dependabot[bot]
|
||||
- [3314: Update phpspec/prophecy requirement from ^1.18 to ^1.19](https://github.com/slimphp/Slim/pull/3314) thanks to @dependabot[bot]
|
||||
- [3315: Update squizlabs/php_codesniffer requirement from ^3.8 to ^3.9](https://github.com/slimphp/Slim/pull/3315) thanks to @dependabot[bot]
|
||||
|
||||
# 4.12.0 - 2023-07-23
|
||||
|
||||
- [3220: Refactor](https://github.com/slimphp/Slim/pull/3220) thanks to @amirkhodabande
|
||||
- [3237: Update phpstan/phpstan requirement from ^1.8 to ^1.9](https://github.com/slimphp/Slim/pull/3237) thanks to @dependabot[bot]
|
||||
- [3238: Update slim/http requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/3238) thanks to @dependabot[bot]
|
||||
- [3239: Update slim/psr7 requirement from ^1.5 to ^1.6](https://github.com/slimphp/Slim/pull/3239) thanks to @dependabot[bot]
|
||||
- [3240: Update phpspec/prophecy requirement from ^1.15 to ^1.16](https://github.com/slimphp/Slim/pull/3240) thanks to @dependabot[bot]
|
||||
- [3241: Update adriansuter/php-autoload-override requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3241) thanks to @dependabot[bot]
|
||||
- [3245: New ability to override RouteGroupInterface in the Route class](https://github.com/slimphp/Slim/pull/3245) thanks to @githubjeka
|
||||
- [3253: Fix HttpBadRequestException description](https://github.com/slimphp/Slim/pull/3253) thanks to @jsanahuja
|
||||
- [3254: Update phpunit/phpunit requirement from ^9.5 to ^9.6](https://github.com/slimphp/Slim/pull/3254) thanks to @dependabot[bot]
|
||||
- [3255: Update phpstan/phpstan requirement from ^1.9 to ^1.10](https://github.com/slimphp/Slim/pull/3255) thanks to @dependabot[bot]
|
||||
- [3256: Update phpspec/prophecy requirement from ^1.16 to ^1.17](https://github.com/slimphp/Slim/pull/3256) thanks to @dependabot[bot]
|
||||
- [3264: Update psr/http-message requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3264) thanks to @dependabot[bot]
|
||||
- [3265: Update nyholm/psr7 requirement from ^1.5 to ^1.7](https://github.com/slimphp/Slim/pull/3265) thanks to @dependabot[bot]
|
||||
- [3266: Update guzzlehttp/psr7 requirement from ^2.4 to ^2.5](https://github.com/slimphp/Slim/pull/3266) thanks to @dependabot[bot]
|
||||
- [3267: Update nyholm/psr7 requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3267) thanks to @dependabot[bot]
|
||||
- [3269: Update httpsoft/http-server-request requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3269) thanks to @dependabot[bot]
|
||||
- [3270: Update httpsoft/http-message requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3270) thanks to @dependabot[bot]
|
||||
- [3271: prevent multiple entries of same methode in FastRouteDispatcher](https://github.com/slimphp/Slim/pull/3271) thanks to @papparazzo
|
||||
|
||||
## 4.11.0 - 2022-11-06
|
||||
|
||||
- [3180: Declare types](https://github.com/slimphp/Slim/pull/3180) thanks to @nbayramberdiyev
|
||||
- [3181: Update laminas/laminas-diactoros requirement from ^2.8 to ^2.9](https://github.com/slimphp/Slim/pull/3181) thanks to @dependabot[bot]
|
||||
- [3182: Update guzzlehttp/psr7 requirement from ^2.1 to ^2.2](https://github.com/slimphp/Slim/pull/3182) thanks to @dependabot[bot]
|
||||
- [3183: Update phpstan/phpstan requirement from ^1.4 to ^1.5](https://github.com/slimphp/Slim/pull/3183) thanks to @dependabot[bot]
|
||||
- [3184: Update adriansuter/php-autoload-override requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/3184) thanks to @dependabot[bot]
|
||||
- [3189: Update phpstan/phpstan requirement from ^1.5 to ^1.6](https://github.com/slimphp/Slim/pull/3189) thanks to @dependabot[bot]
|
||||
- [3191: Adding property types to Middleware classes](https://github.com/slimphp/Slim/pull/3191) thanks to @ashleycoles
|
||||
- [3193: Handlers types](https://github.com/slimphp/Slim/pull/3193) thanks to @ashleycoles
|
||||
- [3194: Adding types to AbstractErrorRenderer](https://github.com/slimphp/Slim/pull/3194) thanks to @ashleycoles
|
||||
- [3195: Adding prop types for Exception classes](https://github.com/slimphp/Slim/pull/3195) thanks to @ashleycoles
|
||||
- [3196: Adding property type declarations for Factory classes](https://github.com/slimphp/Slim/pull/3196) thanks to @ashleycoles
|
||||
- [3197: Remove redundant docblock types](https://github.com/slimphp/Slim/pull/3197) thanks to @theodorejb
|
||||
- [3199: Update laminas/laminas-diactoros requirement from ^2.9 to ^2.11](https://github.com/slimphp/Slim/pull/3199) thanks to @dependabot[bot]
|
||||
- [3200: Update phpstan/phpstan requirement from ^1.6 to ^1.7](https://github.com/slimphp/Slim/pull/3200) thanks to @dependabot[bot]
|
||||
- [3205: Update guzzlehttp/psr7 requirement from ^2.2 to ^2.4](https://github.com/slimphp/Slim/pull/3205) thanks to @dependabot[bot]
|
||||
- [3206: Update squizlabs/php_codesniffer requirement from ^3.6 to ^3.7](https://github.com/slimphp/Slim/pull/3206) thanks to @dependabot[bot]
|
||||
- [3207: Update phpstan/phpstan requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3207) thanks to @dependabot[bot]
|
||||
- [3211: Assign null coalescing to coalesce equal](https://github.com/slimphp/Slim/pull/3211) thanks to @MathiasReker
|
||||
- [3213: Void return](https://github.com/slimphp/Slim/pull/3213) thanks to @MathiasReker
|
||||
- [3214: Is null](https://github.com/slimphp/Slim/pull/3214) thanks to @MathiasReker
|
||||
- [3216: Refactor](https://github.com/slimphp/Slim/pull/3216) thanks to @mehdihasanpour
|
||||
- [3218: Refactor some code](https://github.com/slimphp/Slim/pull/3218) thanks to @mehdihasanpour
|
||||
- [3221: Cleanup](https://github.com/slimphp/Slim/pull/3221) thanks to @mehdihasanpour
|
||||
- [3225: Update laminas/laminas-diactoros requirement from ^2.11 to ^2.14](https://github.com/slimphp/Slim/pull/3225) thanks to @dependabot[bot]
|
||||
- [3228: Using assertSame to let assert equal be restricted](https://github.com/slimphp/Slim/pull/3228) thanks to @peter279k
|
||||
- [3229: Update laminas/laminas-diactoros requirement from ^2.14 to ^2.17](https://github.com/slimphp/Slim/pull/3229) thanks to @dependabot[bot]
|
||||
- [3235: Persist routes indexed by name in RouteCollector for improved performance.](https://github.com/slimphp/Slim/pull/3235) thanks to @BusterNeece
|
||||
|
||||
## 4.10.0 - 2022-03-14
|
||||
|
||||
- [3120: Add a new PSR-17 factory to Psr17FactoryProvider](https://github.com/slimphp/Slim/pull/3120) thanks to @solventt
|
||||
- [3123: Replace deprecated setMethods() in tests](https://github.com/slimphp/Slim/pull/3123) thanks to @solventt
|
||||
- [3126: Update guzzlehttp/psr7 requirement from ^2.0 to ^2.1](https://github.com/slimphp/Slim/pull/3126) thanks to @dependabot[bot]
|
||||
- [3127: PHPStan v1.0](https://github.com/slimphp/Slim/pull/3127) thanks to @t0mmy742
|
||||
- [3128: Update phpstan/phpstan requirement from ^1.0 to ^1.2](https://github.com/slimphp/Slim/pull/3128) thanks to @dependabot[bot]
|
||||
- [3129: Deprecate PHP 7.3](https://github.com/slimphp/Slim/pull/3129) thanks to @l0gicgate
|
||||
- [3130: Removed double defined PHP 7.4](https://github.com/slimphp/Slim/pull/3130) thanks to @flangofas
|
||||
- [3132: Add new `RequestResponseNamedArgs` route strategy](https://github.com/slimphp/Slim/pull/3132) thanks to @adoy
|
||||
- [3133: Improve typehinting for `RouteParserInterface`](https://github.com/slimphp/Slim/pull/3133) thanks to @jerowork
|
||||
- [3135: Update phpstan/phpstan requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/3135) thanks to @dependabot[bot]
|
||||
- [3137: Update phpspec/prophecy requirement from ^1.14 to ^1.15](https://github.com/slimphp/Slim/pull/3137) thanks to @dependabot[bot]
|
||||
- [3138: Update license year](https://github.com/slimphp/Slim/pull/3138) thanks to @Awilum
|
||||
- [3139: Fixed #1730 (reintroduced in 4.x)](https://github.com/slimphp/Slim/pull/3139) thanks to @adoy
|
||||
- [3145: Update phpstan/phpstan requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3145) thanks to @dependabot[bot]
|
||||
- [3146: Inherit HttpException from RuntimeException](https://github.com/slimphp/Slim/pull/3146) thanks to @nbayramberdiyev
|
||||
- [3148: Upgrade to HTML5](https://github.com/slimphp/Slim/pull/3148) thanks to @nbayramberdiyev
|
||||
- [3172: Update nyholm/psr7 requirement from ^1.4 to ^1.5](https://github.com/slimphp/Slim/pull/3172) thanks to @dependabot[bot]
|
||||
|
||||
## 4.9.0 - 2021-10-05
|
||||
|
||||
- [3058: Implement exception class for Gone Http error](https://github.com/slimphp/Slim/pull/3058) thanks to @TheKernelPanic
|
||||
- [3086: Update slim/psr7 requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3086) thanks to @dependabot[bot]
|
||||
- [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]
|
||||
- [3093: Update phpstan/phpstan requirement from ^0.12.85 to ^0.12.90](https://github.com/slimphp/Slim/pull/3093) thanks to @dependabot[bot]
|
||||
- [3099: Allow updated psr log](https://github.com/slimphp/Slim/pull/3099) thanks to @t0mmy742
|
||||
- [3104: Drop php7.2](https://github.com/slimphp/Slim/pull/3104) thanks to @t0mmy742
|
||||
- [3106: Use PSR-17 factory from Guzzle/psr7 2.0](https://github.com/slimphp/Slim/pull/3106) thanks to @t0mmy742
|
||||
- [3108: Update README file](https://github.com/slimphp/Slim/pull/3108) thanks to @t0mmy742
|
||||
- [3112: Update laminas/laminas-diactoros requirement from ^2.6 to ^2.8](https://github.com/slimphp/Slim/pull/3112) thanks to @dependabot[bot]
|
||||
- [3114: Update slim/psr7 requirement from ^1.4 to ^1.5](https://github.com/slimphp/Slim/pull/3114) thanks to @dependabot[bot]
|
||||
- [3115: Update phpstan/phpstan requirement from ^0.12.96 to ^0.12.99](https://github.com/slimphp/Slim/pull/3115) thanks to @dependabot[bot]
|
||||
- [3116: Remove Zend Diactoros references](https://github.com/slimphp/Slim/pull/3116) thanks to @l0gicgate
|
||||
|
||||
## 4.8.0 - 2021-05-19
|
||||
|
||||
- [3034: Fix phpunit dependency version](https://github.com/slimphp/Slim/pull/3034) thanks to @l0gicgate
|
||||
- [3037: Replace Travis by GitHub Actions](https://github.com/slimphp/Slim/pull/3037) thanks to @t0mmy742
|
||||
- [3043: Cover App creation from AppFactory with empty Container](https://github.com/slimphp/Slim/pull/3043) thanks to @t0mmy742
|
||||
- [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]
|
||||
- [3047: documentation: min php 7.2 required](https://github.com/slimphp/Slim/pull/3047) thanks to @Rotzbua
|
||||
- [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]
|
||||
- [3056: Fix docblock in ErrorMiddleware](https://github.com/slimphp/Slim/pull/3056) thanks to @piotr-cz
|
||||
- [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]
|
||||
- [3061: Update nyholm/psr7 requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3061) thanks to @dependabot-preview[bot]
|
||||
- [3063: Allow ^1.0 || ^2.0 in psr/container](https://github.com/slimphp/Slim/pull/3063) thanks to @Ayesh
|
||||
- [3069: Classname/Method Callable Arrays](https://github.com/slimphp/Slim/pull/3069) thanks to @ddrv
|
||||
- [3078: Update squizlabs/php_codesniffer requirement from ^3.5 to ^3.6](https://github.com/slimphp/Slim/pull/3078) thanks to @dependabot[bot]
|
||||
- [3079: Update phpspec/prophecy requirement from ^1.12 to ^1.13](https://github.com/slimphp/Slim/pull/3079) thanks to @dependabot[bot]
|
||||
- [3080: Update guzzlehttp/psr7 requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3080) thanks to @dependabot[bot]
|
||||
- [3082: Update phpstan/phpstan requirement from ^0.12.80 to ^0.12.85](https://github.com/slimphp/Slim/pull/3082) thanks to @dependabot[bot]
|
||||
|
||||
## 4.7.0 - 2020-11-30
|
||||
|
||||
### Fixed
|
||||
- [3027: Fix: FastRoute dispatcher and data generator should match](https://github.com/slimphp/Slim/pull/3027) thanks to @edudobay
|
||||
|
||||
### Added
|
||||
- [3015: PHP 8 support](https://github.com/slimphp/Slim/pull/3015) thanks to @edudobay
|
||||
|
||||
### Optimizations
|
||||
- [3024: Randomize tests](https://github.com/slimphp/Slim/pull/3024) thanks to @pawel-slowik
|
||||
|
||||
## 4.6.0 - 2020-11-15
|
||||
|
||||
### Fixed
|
||||
- [2942: Fix PHPdoc for error handlers in ErrorMiddleware ](https://github.com/slimphp/Slim/pull/2942) thanks to @TiMESPLiNTER
|
||||
- [2944: Remove unused function in ErrorHandler](https://github.com/slimphp/Slim/pull/2944) thanks to @l0gicgate
|
||||
- [2960: Fix phpstan 0.12 errors](https://github.com/slimphp/Slim/pull/2960) thanks to @adriansuter
|
||||
- [2982: Removing cloning statements in tests](https://github.com/slimphp/Slim/pull/2982) thanks to @l0gicgate
|
||||
- [3017: Fix request creator factory test](https://github.com/slimphp/Slim/pull/3017) thanks to @pawel-slowik
|
||||
- [3022: Ensure RouteParser Always Present After Routing](https://github.com/slimphp/Slim/pull/3022) thanks to @l0gicgate
|
||||
|
||||
### Added
|
||||
- [2949: Add the support in composer.json](https://github.com/slimphp/Slim/pull/2949) thanks to @ddrv
|
||||
- [2958: Strict empty string content type checking in BodyParsingMiddleware::getMediaType](https://github.com/slimphp/Slim/pull/2958) thanks to @Ayesh
|
||||
- [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
|
||||
- [3001: Add missing `$strategy` parameter in a Route test](https://github.com/slimphp/Slim/pull/3001) thanks to @pawel-slowik
|
||||
|
||||
### Optimizations
|
||||
- [2951: Minor optimizations in if() blocks](https://github.com/slimphp/Slim/pull/2951) thanks to @Ayesh
|
||||
- [2959: Micro optimization: Declare closures in BodyParsingMiddleware as static](https://github.com/slimphp/Slim/pull/2959) thanks to @Ayesh
|
||||
- [2978: Split the routing results to its own function.](https://github.com/slimphp/Slim/pull/2978) thanks to @dlundgren
|
||||
|
||||
### Dependencies Updated
|
||||
- [2953: Update nyholm/psr7-server requirement from ^0.4.1](https://github.com/slimphp/Slim/pull/2953) thanks to @dependabot-preview[bot]
|
||||
- [2954: Update laminas/laminas-diactoros requirement from ^2.1 to ^2.3](https://github.com/slimphp/Slim/pull/2954) thanks to @dependabot-preview[bot]
|
||||
- [2955: Update guzzlehttp/psr7 requirement from ^1.5 to ^1.6](https://github.com/slimphp/Slim/pull/2955) thanks to @dependabot-preview[bot]
|
||||
- [2956: Update slim/psr7 requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/2956) thanks to @dependabot-preview[bot]
|
||||
- [2957: Update nyholm/psr7 requirement from ^1.1 to ^1.2](https://github.com/slimphp/Slim/pull/2957) thanks to @dependabot-preview[bot]
|
||||
- [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]
|
||||
- [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]
|
||||
- [2967: Update nyholm/psr7 requirement from ^1.2 to ^1.3](https://github.com/slimphp/Slim/pull/2967) thanks to @dependabot-preview[bot]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [2981: Update phpspec/prophecy requirement from ^1.10 to ^1.11](https://github.com/slimphp/Slim/pull/2981) thanks to @dependabot-preview[bot]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [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]
|
||||
- [3010: Update guzzlehttp/psr7 requirement from ^1.6 to ^1.7](https://github.com/slimphp/Slim/pull/3010) thanks to @dependabot-preview[bot]
|
||||
- [3011: Update phpspec/prophecy requirement from ^1.11 to ^1.12](https://github.com/slimphp/Slim/pull/3011) thanks to @dependabot-preview[bot]
|
||||
- [3012: Update slim/http requirement from ^1.0 to ^1.1](https://github.com/slimphp/Slim/pull/3012) thanks to @dependabot-preview[bot]
|
||||
- [3013: Update slim/psr7 requirement from ^1.1 to ^1.2](https://github.com/slimphp/Slim/pull/3013) thanks to @dependabot-preview[bot]
|
||||
- [3014: Update laminas/laminas-diactoros requirement from ^2.3 to ^2.4](https://github.com/slimphp/Slim/pull/3014) thanks to @dependabot-preview[bot]
|
||||
- [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]
|
||||
|
||||
## 4.5.0 - 2020-04-14
|
||||
|
||||
### Added
|
||||
- [2928](https://github.com/slimphp/Slim/pull/2928) Test against PHP 7.4
|
||||
- [2937](https://github.com/slimphp/Slim/pull/2937) Add support for PSR-3
|
||||
|
||||
### Fixed
|
||||
- [2916](https://github.com/slimphp/Slim/pull/2916) Rename phpcs.xml to phpcs.xml.dist
|
||||
- [2917](https://github.com/slimphp/Slim/pull/2917) Update .editorconfig
|
||||
- [2925](https://github.com/slimphp/Slim/pull/2925) ResponseEmitter: Don't remove Content-Type and Content-Length when body is empt
|
||||
- [2932](https://github.com/slimphp/Slim/pull/2932) Update the Tidelift enterprise language
|
||||
- [2938](https://github.com/slimphp/Slim/pull/2938) Modify usage of deprecated expectExceptionMessageRegExp() method
|
||||
|
||||
## 4.4.0 - 2020-01-04
|
||||
|
||||
### Added
|
||||
- [2862](https://github.com/slimphp/Slim/pull/2862) Optionally handle subclasses of exceptions in custom error handler
|
||||
- [2869](https://github.com/slimphp/Slim/pull/2869) php-di/php-di added in composer suggestion
|
||||
- [2874](https://github.com/slimphp/Slim/pull/2874) Add `null` to param type-hints
|
||||
- [2889](https://github.com/slimphp/Slim/pull/2889) Make `RouteContext` attributes customizable and change default to use private names
|
||||
- [2907](https://github.com/slimphp/Slim/pull/2907) Migrate to PSR-12 convention
|
||||
- [2910](https://github.com/slimphp/Slim/pull/2910) Migrate Zend to Laminas
|
||||
- [2912](https://github.com/slimphp/Slim/pull/2912) Add Laminas PSR17 Factory
|
||||
- [2913](https://github.com/slimphp/Slim/pull/2913) Update php-autoload-override version
|
||||
- [2914](https://github.com/slimphp/Slim/pull/2914) Added ability to add handled exceptions as an array
|
||||
|
||||
### Fixed
|
||||
- [2864](https://github.com/slimphp/Slim/pull/2864) Optimize error message in error handling if displayErrorDetails was not set
|
||||
- [2876](https://github.com/slimphp/Slim/pull/2876) Update links from http to https
|
||||
- [2877](https://github.com/slimphp/Slim/pull/2877) Fix docblock for `Slim\Routing\RouteCollector::cacheFile`
|
||||
- [2878](https://github.com/slimphp/Slim/pull/2878) check body is writable only on ouput buffering append
|
||||
- [2896](https://github.com/slimphp/Slim/pull/2896) Render errors uniformly
|
||||
- [2902](https://github.com/slimphp/Slim/pull/2902) Fix prophecies
|
||||
- [2908](https://github.com/slimphp/Slim/pull/2908) Use autoload-dev for `Slim\Tests` namespace
|
||||
|
||||
### Removed
|
||||
- [2871](https://github.com/slimphp/Slim/pull/2871) Remove explicit type-hint
|
||||
- [2872](https://github.com/slimphp/Slim/pull/2872) Remove type-hint
|
||||
|
||||
## 4.3.0 - 2019-10-05
|
||||
|
||||
### Added
|
||||
- [2819](https://github.com/slimphp/Slim/pull/2819) Added description to addRoutingMiddleware()
|
||||
- [2820](https://github.com/slimphp/Slim/pull/2820) Update link to homepage in composer.json
|
||||
- [2828](https://github.com/slimphp/Slim/pull/2828) Allow URIs with leading multi-slashes
|
||||
- [2832](https://github.com/slimphp/Slim/pull/2832) Refactor `FastRouteDispatcher`
|
||||
- [2835](https://github.com/slimphp/Slim/pull/2835) Rename `pathFor` to `urlFor` in docblock
|
||||
- [2846](https://github.com/slimphp/Slim/pull/2846) Correcting the branch name as per issue-2843
|
||||
- [2849](https://github.com/slimphp/Slim/pull/2849) Create class alias for FastRoute\RouteCollector
|
||||
- [2855](https://github.com/slimphp/Slim/pull/2855) Add list of allowed methods to HttpMethodNotAllowedException
|
||||
- [2860](https://github.com/slimphp/Slim/pull/2860) Add base path to `$request` and use `RouteContext` to read
|
||||
|
||||
### Fixed
|
||||
- [2839](https://github.com/slimphp/Slim/pull/2839) Fix description for handler signature in phpdocs
|
||||
- [2844](https://github.com/slimphp/Slim/pull/2844) Handle base path by routeCollector instead of RouteCollectorProxy
|
||||
- [2845](https://github.com/slimphp/Slim/pull/2845) Fix composer scripts
|
||||
- [2851](https://github.com/slimphp/Slim/pull/2851) Fix example of 'Hello World' app
|
||||
- [2854](https://github.com/slimphp/Slim/pull/2854) Fix undefined property in tests
|
||||
|
||||
### Removed
|
||||
- [2853](https://github.com/slimphp/Slim/pull/2853) Remove unused classes
|
||||
|
||||
## 4.2.0 - 2019-08-20
|
||||
|
||||
### Added
|
||||
- [2787](https://github.com/slimphp/Slim/pull/2787) Add an advanced callable resolver
|
||||
- [2791](https://github.com/slimphp/Slim/pull/2791) Add `inferPrivatePropertyTypeFromConstructor` to phpstan
|
||||
- [2793](https://github.com/slimphp/Slim/pull/2793) Add ability to configure application via a container in `AppFactory`
|
||||
- [2798](https://github.com/slimphp/Slim/pull/2798) Add PSR-7 Agnostic Body Parsing Middleware
|
||||
- [2801](https://github.com/slimphp/Slim/pull/2801) Add `setLogErrorRenderer()` method to `ErrorHandler`
|
||||
- [2807](https://github.com/slimphp/Slim/pull/2807) Add check for Slim callable notation if no resolver given
|
||||
- [2803](https://github.com/slimphp/Slim/pull/2803) Add ability to emit non seekable streams in `ResponseEmitter`
|
||||
- [2817](https://github.com/slimphp/Slim/pull/2817) Add the ability to pass in a custom `MiddlewareDispatcherInterface` to the `App`
|
||||
|
||||
### Fixed
|
||||
- [2789](https://github.com/slimphp/Slim/pull/2789) Fix Cookie header detection in `ResponseEmitter`
|
||||
- [2796](https://github.com/slimphp/Slim/pull/2796) Fix http message format
|
||||
- [2800](https://github.com/slimphp/Slim/pull/2800) Fix null comparisons more clear in `ErrorHandler`
|
||||
- [2802](https://github.com/slimphp/Slim/pull/2802) Fix incorrect search of a header in stack
|
||||
- [2806](https://github.com/slimphp/Slim/pull/2806) Simplify `Route::prepare()` method argument preparation
|
||||
- [2809](https://github.com/slimphp/Slim/pull/2809) Eliminate a duplicate code via HOF in `MiddlewareDispatcher`
|
||||
- [2816](https://github.com/slimphp/Slim/pull/2816) Fix RouteCollectorProxy::redirect() bug
|
||||
|
||||
### Removed
|
||||
- [2811](https://github.com/slimphp/Slim/pull/2811) Remove `DeferredCallable`
|
||||
|
||||
## 4.1.0 - 2019-08-06
|
||||
|
||||
### Added
|
||||
- [#2779](https://github.com/slimphp/Slim/pull/2774) Add support for Slim callables `Class:method` resolution & Container Closure auto-binding in `MiddlewareDispatcher`
|
||||
- [#2774](https://github.com/slimphp/Slim/pull/2774) Add possibility for custom `RequestHandler` invocation strategies
|
||||
|
||||
### Fixed
|
||||
- [#2776](https://github.com/slimphp/Slim/pull/2774) Fix group middleware on multiple nested groups
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011-2022 Josh Lockhart
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
# Security Policy
|
||||
|
||||
### Supported Versions
|
||||
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 3.x.x | :white_check_mark: |
|
||||
| 4.x.x | :white_check_mark: |
|
||||
|
||||
|
||||
### Reporting a Vulnerability
|
||||
|
||||
To report a vulnerability please send an email to security@slimframework.com
|
||||
Vendored
+226
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Slim\Factory\ServerRequestCreatorFactory;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\MiddlewareDispatcherInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
use Slim\Interfaces\RouteResolverInterface;
|
||||
use Slim\Middleware\BodyParsingMiddleware;
|
||||
use Slim\Middleware\ErrorMiddleware;
|
||||
use Slim\Middleware\RoutingMiddleware;
|
||||
use Slim\Routing\RouteCollectorProxy;
|
||||
use Slim\Routing\RouteResolver;
|
||||
use Slim\Routing\RouteRunner;
|
||||
|
||||
use function strtoupper;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
* @template-extends RouteCollectorProxy<TContainerInterface>
|
||||
*/
|
||||
class App extends RouteCollectorProxy implements RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Current version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const VERSION = '4.15.1';
|
||||
|
||||
protected RouteResolverInterface $routeResolver;
|
||||
|
||||
protected MiddlewareDispatcherInterface $middlewareDispatcher;
|
||||
|
||||
/**
|
||||
* @param TContainerInterface $container
|
||||
*/
|
||||
public function __construct(
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
?ContainerInterface $container = null,
|
||||
?CallableResolverInterface $callableResolver = null,
|
||||
?RouteCollectorInterface $routeCollector = null,
|
||||
?RouteResolverInterface $routeResolver = null,
|
||||
?MiddlewareDispatcherInterface $middlewareDispatcher = null
|
||||
) {
|
||||
parent::__construct(
|
||||
$responseFactory,
|
||||
$callableResolver ?? new CallableResolver($container),
|
||||
$container,
|
||||
$routeCollector
|
||||
);
|
||||
|
||||
$this->routeResolver = $routeResolver ?? new RouteResolver($this->routeCollector);
|
||||
$routeRunner = new RouteRunner($this->routeResolver, $this->routeCollector->getRouteParser(), $this);
|
||||
|
||||
if (!$middlewareDispatcher) {
|
||||
$middlewareDispatcher = new MiddlewareDispatcher($routeRunner, $this->callableResolver, $container);
|
||||
} else {
|
||||
$middlewareDispatcher->seedMiddlewareStack($routeRunner);
|
||||
}
|
||||
|
||||
$this->middlewareDispatcher = $middlewareDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RouteResolverInterface
|
||||
*/
|
||||
public function getRouteResolver(): RouteResolverInterface
|
||||
{
|
||||
return $this->routeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MiddlewareDispatcherInterface
|
||||
*/
|
||||
public function getMiddlewareDispatcher(): MiddlewareDispatcherInterface
|
||||
{
|
||||
return $this->middlewareDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MiddlewareInterface|string|callable $middleware
|
||||
* @return App<TContainerInterface>
|
||||
*/
|
||||
public function add($middleware): self
|
||||
{
|
||||
$this->middlewareDispatcher->add($middleware);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MiddlewareInterface $middleware
|
||||
* @return App<TContainerInterface>
|
||||
*/
|
||||
public function addMiddleware(MiddlewareInterface $middleware): self
|
||||
{
|
||||
$this->middlewareDispatcher->addMiddleware($middleware);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Slim built-in routing middleware to the app middleware stack
|
||||
*
|
||||
* This method can be used to control middleware order and is not required for default routing operation.
|
||||
*
|
||||
* @return RoutingMiddleware
|
||||
*/
|
||||
public function addRoutingMiddleware(): RoutingMiddleware
|
||||
{
|
||||
$routingMiddleware = new RoutingMiddleware(
|
||||
$this->getRouteResolver(),
|
||||
$this->getRouteCollector()->getRouteParser()
|
||||
);
|
||||
$this->add($routingMiddleware);
|
||||
return $routingMiddleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Slim built-in error middleware to the app middleware stack
|
||||
*
|
||||
* @param bool $displayErrorDetails
|
||||
* @param bool $logErrors
|
||||
* @param bool $logErrorDetails
|
||||
* @param LoggerInterface|null $logger
|
||||
*
|
||||
* @return ErrorMiddleware
|
||||
*/
|
||||
public function addErrorMiddleware(
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails,
|
||||
?LoggerInterface $logger = null
|
||||
): ErrorMiddleware {
|
||||
$errorMiddleware = new ErrorMiddleware(
|
||||
$this->getCallableResolver(),
|
||||
$this->getResponseFactory(),
|
||||
$displayErrorDetails,
|
||||
$logErrors,
|
||||
$logErrorDetails,
|
||||
$logger
|
||||
);
|
||||
$this->add($errorMiddleware);
|
||||
return $errorMiddleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Slim body parsing middleware to the app middleware stack
|
||||
*
|
||||
* @param callable[] $bodyParsers
|
||||
*
|
||||
* @return BodyParsingMiddleware
|
||||
*/
|
||||
public function addBodyParsingMiddleware(array $bodyParsers = []): BodyParsingMiddleware
|
||||
{
|
||||
$bodyParsingMiddleware = new BodyParsingMiddleware($bodyParsers);
|
||||
$this->add($bodyParsingMiddleware);
|
||||
return $bodyParsingMiddleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run application
|
||||
*
|
||||
* This method traverses the application middleware stack and then sends the
|
||||
* resultant Response object to the HTTP client.
|
||||
*
|
||||
* @param ServerRequestInterface|null $request
|
||||
* @return void
|
||||
*/
|
||||
public function run(?ServerRequestInterface $request = null): void
|
||||
{
|
||||
if (!$request) {
|
||||
$serverRequestCreator = ServerRequestCreatorFactory::create();
|
||||
$request = $serverRequestCreator->createServerRequestFromGlobals();
|
||||
}
|
||||
|
||||
$response = $this->handle($request);
|
||||
$responseEmitter = new ResponseEmitter();
|
||||
$responseEmitter->emit($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request
|
||||
*
|
||||
* This method traverses the application middleware stack and then returns the
|
||||
* resultant Response object.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$response = $this->middlewareDispatcher->handle($request);
|
||||
|
||||
/**
|
||||
* This is to be in compliance with RFC 2616, Section 9.
|
||||
* If the incoming request method is HEAD, we need to ensure that the response body
|
||||
* is empty as the request may fall back on a GET route handler due to FastRoute's
|
||||
* routing logic which could potentially append content to the response body
|
||||
* https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
|
||||
*/
|
||||
$method = strtoupper($request->getMethod());
|
||||
if ($method === 'HEAD') {
|
||||
$emptyBody = $this->responseFactory->createResponse()->getBody();
|
||||
return $response->withBody($emptyBody);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim;
|
||||
|
||||
use Closure;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Interfaces\AdvancedCallableResolverInterface;
|
||||
|
||||
use function class_exists;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
*/
|
||||
final class CallableResolver implements AdvancedCallableResolverInterface
|
||||
{
|
||||
public static string $callablePattern = '!^([^\:]+)\:([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!';
|
||||
|
||||
/** @var TContainerInterface $container */
|
||||
private ?ContainerInterface $container;
|
||||
|
||||
/**
|
||||
* @param TContainerInterface $container
|
||||
*/
|
||||
public function __construct(?ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolve($toResolve): callable
|
||||
{
|
||||
$toResolve = $this->prepareToResolve($toResolve);
|
||||
if (is_callable($toResolve)) {
|
||||
return $this->bindToContainer($toResolve);
|
||||
}
|
||||
$resolved = $toResolve;
|
||||
if (is_string($toResolve)) {
|
||||
$resolved = $this->resolveSlimNotation($toResolve);
|
||||
$resolved[1] ??= '__invoke';
|
||||
}
|
||||
$callable = $this->assertCallable($resolved, $toResolve);
|
||||
return $this->bindToContainer($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolveRoute($toResolve): callable
|
||||
{
|
||||
return $this->resolveByPredicate($toResolve, [$this, 'isRoute'], 'handle');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolveMiddleware($toResolve): callable
|
||||
{
|
||||
return $this->resolveByPredicate($toResolve, [$this, 'isMiddleware'], 'process');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|array{class-string, string}|string $toResolve
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function resolveByPredicate($toResolve, callable $predicate, string $defaultMethod): callable
|
||||
{
|
||||
$toResolve = $this->prepareToResolve($toResolve);
|
||||
if (is_callable($toResolve)) {
|
||||
return $this->bindToContainer($toResolve);
|
||||
}
|
||||
$resolved = $toResolve;
|
||||
if ($predicate($toResolve)) {
|
||||
$resolved = [$toResolve, $defaultMethod];
|
||||
}
|
||||
if (is_string($toResolve)) {
|
||||
[$instance, $method] = $this->resolveSlimNotation($toResolve);
|
||||
if ($method === null && $predicate($instance)) {
|
||||
$method = $defaultMethod;
|
||||
}
|
||||
$resolved = [$instance, $method ?? '__invoke'];
|
||||
}
|
||||
$callable = $this->assertCallable($resolved, $toResolve);
|
||||
return $this->bindToContainer($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $toResolve
|
||||
*/
|
||||
private function isRoute($toResolve): bool
|
||||
{
|
||||
return $toResolve instanceof RequestHandlerInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $toResolve
|
||||
*/
|
||||
private function isMiddleware($toResolve): bool
|
||||
{
|
||||
return $toResolve instanceof MiddlewareInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @return array{object, string|null} [Instance, Method Name]
|
||||
*/
|
||||
private function resolveSlimNotation(string $toResolve): array
|
||||
{
|
||||
/** @psalm-suppress ArgumentTypeCoercion */
|
||||
preg_match(CallableResolver::$callablePattern, $toResolve, $matches);
|
||||
[$class, $method] = $matches ? [$matches[1], $matches[2]] : [$toResolve, null];
|
||||
|
||||
if ($this->container && $this->container->has($class)) {
|
||||
$instance = $this->container->get($class);
|
||||
if (!is_object($instance)) {
|
||||
throw new RuntimeException(sprintf('%s container entry is not an object', $class));
|
||||
}
|
||||
} else {
|
||||
if (!class_exists($class)) {
|
||||
if ($method) {
|
||||
$class .= '::' . $method . '()';
|
||||
}
|
||||
throw new RuntimeException(sprintf('Callable %s does not exist', $class));
|
||||
}
|
||||
$instance = new $class($this->container);
|
||||
}
|
||||
return [$instance, $method];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $resolved
|
||||
* @param mixed $toResolve
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function assertCallable($resolved, $toResolve): callable
|
||||
{
|
||||
if (!is_callable($resolved)) {
|
||||
if (is_callable($toResolve) || is_object($toResolve) || is_array($toResolve)) {
|
||||
$formatedToResolve = ($toResolveJson = json_encode($toResolve)) !== false ? $toResolveJson : '';
|
||||
} else {
|
||||
$formatedToResolve = is_string($toResolve) ? $toResolve : '';
|
||||
}
|
||||
throw new RuntimeException(sprintf('%s is not resolvable', $formatedToResolve));
|
||||
}
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
private function bindToContainer(callable $callable): callable
|
||||
{
|
||||
if (is_array($callable) && $callable[0] instanceof Closure) {
|
||||
$callable = $callable[0];
|
||||
}
|
||||
if ($this->container && $callable instanceof Closure) {
|
||||
/** @var Closure $callable */
|
||||
$callable = $callable->bindTo($this->container);
|
||||
}
|
||||
return $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|string|array{class-string, string}|mixed $toResolve
|
||||
*
|
||||
* @return callable|string|array{class-string, string}|mixed
|
||||
*/
|
||||
private function prepareToResolve($toResolve)
|
||||
{
|
||||
if (!is_array($toResolve)) {
|
||||
return $toResolve;
|
||||
}
|
||||
$candidate = $toResolve;
|
||||
$class = array_shift($candidate);
|
||||
$method = array_shift($candidate);
|
||||
if (is_string($class) && is_string($method)) {
|
||||
return $class . ':' . $method;
|
||||
}
|
||||
return $toResolve;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Error;
|
||||
|
||||
use Slim\Exception\HttpException;
|
||||
use Slim\Interfaces\ErrorRendererInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Abstract Slim application error renderer
|
||||
*
|
||||
* It outputs the error message and diagnostic information in one of the following formats:
|
||||
* JSON, XML, Plain Text or HTML
|
||||
*/
|
||||
abstract class AbstractErrorRenderer implements ErrorRendererInterface
|
||||
{
|
||||
protected string $defaultErrorTitle = 'Slim Application Error';
|
||||
|
||||
protected string $defaultErrorDescription = 'A website error has occurred. Sorry for the temporary inconvenience.';
|
||||
|
||||
protected function getErrorTitle(Throwable $exception): string
|
||||
{
|
||||
if ($exception instanceof HttpException) {
|
||||
return $exception->getTitle();
|
||||
}
|
||||
|
||||
return $this->defaultErrorTitle;
|
||||
}
|
||||
|
||||
protected function getErrorDescription(Throwable $exception): string
|
||||
{
|
||||
if ($exception instanceof HttpException) {
|
||||
return $exception->getDescription();
|
||||
}
|
||||
|
||||
return $this->defaultErrorDescription;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Error\Renderers;
|
||||
|
||||
use Slim\Error\AbstractErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
use function get_class;
|
||||
use function htmlentities;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Default Slim application HTML Error Renderer
|
||||
*/
|
||||
class HtmlErrorRenderer extends AbstractErrorRenderer
|
||||
{
|
||||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string
|
||||
{
|
||||
if ($displayErrorDetails) {
|
||||
$html = '<p>The application could not run because of the following error:</p>';
|
||||
$html .= '<h2>Details</h2>';
|
||||
$html .= $this->renderExceptionFragment($exception);
|
||||
} else {
|
||||
$html = "<p>{$this->getErrorDescription($exception)}</p>";
|
||||
}
|
||||
|
||||
return $this->renderHtmlBody($this->getErrorTitle($exception), $html);
|
||||
}
|
||||
|
||||
private function renderExceptionFragment(Throwable $exception): string
|
||||
{
|
||||
$html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception));
|
||||
|
||||
$code = $exception->getCode();
|
||||
$html .= sprintf('<div><strong>Code:</strong> %s</div>', $code);
|
||||
|
||||
$html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($exception->getMessage()));
|
||||
|
||||
$html .= sprintf('<div><strong>File:</strong> %s</div>', $exception->getFile());
|
||||
|
||||
$html .= sprintf('<div><strong>Line:</strong> %s</div>', $exception->getLine());
|
||||
|
||||
$html .= '<h2>Trace</h2>';
|
||||
$html .= sprintf('<pre>%s</pre>', htmlentities($exception->getTraceAsString()));
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function renderHtmlBody(string $title = '', string $html = ''): string
|
||||
{
|
||||
return sprintf(
|
||||
'<!doctype html>' .
|
||||
'<html lang="en">' .
|
||||
' <head>' .
|
||||
' <meta charset="utf-8">' .
|
||||
' <meta name="viewport" content="width=device-width, initial-scale=1">' .
|
||||
' <title>%s</title>' .
|
||||
' <style>' .
|
||||
' body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif}' .
|
||||
' h1{margin:0;font-size:48px;font-weight:normal;line-height:48px}' .
|
||||
' strong{display:inline-block;width:65px}' .
|
||||
' </style>' .
|
||||
' </head>' .
|
||||
' <body>' .
|
||||
' <h1>%s</h1>' .
|
||||
' <div>%s</div>' .
|
||||
' <a href="#" onclick="window.history.go(-1)">Go Back</a>' .
|
||||
' </body>' .
|
||||
'</html>',
|
||||
$title,
|
||||
$title,
|
||||
$html
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Error\Renderers;
|
||||
|
||||
use Slim\Error\AbstractErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
use function get_class;
|
||||
use function json_encode;
|
||||
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
|
||||
/**
|
||||
* Default Slim application JSON Error Renderer
|
||||
*/
|
||||
class JsonErrorRenderer extends AbstractErrorRenderer
|
||||
{
|
||||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string
|
||||
{
|
||||
$error = ['message' => $this->getErrorTitle($exception)];
|
||||
|
||||
if ($displayErrorDetails) {
|
||||
$error['exception'] = [];
|
||||
do {
|
||||
$error['exception'][] = $this->formatExceptionFragment($exception);
|
||||
} while ($exception = $exception->getPrevious());
|
||||
}
|
||||
|
||||
return (string) json_encode($error, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string|int>
|
||||
*/
|
||||
private function formatExceptionFragment(Throwable $exception): array
|
||||
{
|
||||
$code = $exception->getCode();
|
||||
return [
|
||||
'type' => get_class($exception),
|
||||
'code' => $code,
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Error\Renderers;
|
||||
|
||||
use Slim\Error\AbstractErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
use function get_class;
|
||||
use function htmlentities;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Default Slim application Plain Text Error Renderer
|
||||
*/
|
||||
class PlainTextErrorRenderer extends AbstractErrorRenderer
|
||||
{
|
||||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string
|
||||
{
|
||||
$text = "{$this->getErrorTitle($exception)}\n";
|
||||
|
||||
if ($displayErrorDetails) {
|
||||
$text .= $this->formatExceptionFragment($exception);
|
||||
|
||||
while ($exception = $exception->getPrevious()) {
|
||||
$text .= "\nPrevious Error:\n";
|
||||
$text .= $this->formatExceptionFragment($exception);
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function formatExceptionFragment(Throwable $exception): string
|
||||
{
|
||||
$text = sprintf("Type: %s\n", get_class($exception));
|
||||
|
||||
$code = $exception->getCode();
|
||||
|
||||
$text .= sprintf("Code: %s\n", $code);
|
||||
|
||||
$text .= sprintf("Message: %s\n", $exception->getMessage());
|
||||
|
||||
$text .= sprintf("File: %s\n", $exception->getFile());
|
||||
|
||||
$text .= sprintf("Line: %s\n", $exception->getLine());
|
||||
|
||||
$text .= sprintf('Trace: %s', $exception->getTraceAsString());
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Error\Renderers;
|
||||
|
||||
use Slim\Error\AbstractErrorRenderer;
|
||||
use Throwable;
|
||||
|
||||
use function get_class;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* Default Slim application XML Error Renderer
|
||||
*/
|
||||
class XmlErrorRenderer extends AbstractErrorRenderer
|
||||
{
|
||||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string
|
||||
{
|
||||
$xml = '<' . '?xml version="1.0" encoding="UTF-8" standalone="yes"?' . ">\n";
|
||||
$xml .= "<error>\n <message>" . $this->createCdataSection($this->getErrorTitle($exception)) . "</message>\n";
|
||||
|
||||
if ($displayErrorDetails) {
|
||||
do {
|
||||
$xml .= " <exception>\n";
|
||||
$xml .= ' <type>' . get_class($exception) . "</type>\n";
|
||||
$xml .= ' <code>' . $exception->getCode() . "</code>\n";
|
||||
$xml .= ' <message>' . $this->createCdataSection($exception->getMessage()) . "</message>\n";
|
||||
$xml .= ' <file>' . $exception->getFile() . "</file>\n";
|
||||
$xml .= ' <line>' . $exception->getLine() . "</line>\n";
|
||||
$xml .= " </exception>\n";
|
||||
} while ($exception = $exception->getPrevious());
|
||||
}
|
||||
|
||||
$xml .= '</error>';
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CDATA section with the given content.
|
||||
*/
|
||||
private function createCdataSection(string $content): string
|
||||
{
|
||||
return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpBadRequestException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 400;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Bad request.';
|
||||
|
||||
protected string $title = '400 Bad Request';
|
||||
protected string $description = 'The server cannot or will not process ' .
|
||||
'the request due to an apparent client error.';
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @method int getCode()
|
||||
*/
|
||||
class HttpException extends RuntimeException
|
||||
{
|
||||
protected ServerRequestInterface $request;
|
||||
|
||||
protected string $title = '';
|
||||
|
||||
protected string $description = '';
|
||||
|
||||
public function __construct(
|
||||
ServerRequestInterface $request,
|
||||
string $message = '',
|
||||
int $code = 0,
|
||||
?Throwable $previous = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function getRequest(): ServerRequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): self
|
||||
{
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): self
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpForbiddenException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 403;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Forbidden.';
|
||||
|
||||
protected string $title = '403 Forbidden';
|
||||
protected string $description = 'You are not permitted to perform the requested operation.';
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpGoneException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 410;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Gone.';
|
||||
|
||||
protected string $title = '410 Gone';
|
||||
protected string $description = 'The target resource is no longer available at the origin server.';
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpInternalServerErrorException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 500;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Internal server error.';
|
||||
|
||||
protected string $title = '500 Internal Server Error';
|
||||
protected string $description = 'Unexpected condition encountered preventing server from fulfilling request.';
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
use function implode;
|
||||
|
||||
class HttpMethodNotAllowedException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $allowedMethods = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 405;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Method not allowed.';
|
||||
|
||||
protected string $title = '405 Method Not Allowed';
|
||||
protected string $description = 'The request method is not supported for the requested resource.';
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedMethods(): array
|
||||
{
|
||||
return $this->allowedMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
*/
|
||||
public function setAllowedMethods(array $methods): self
|
||||
{
|
||||
$this->allowedMethods = $methods;
|
||||
$this->message = 'Method not allowed. Must be one of: ' . implode(', ', $methods);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
class HttpNotFoundException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 404;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Not found.';
|
||||
|
||||
protected string $title = '404 Not Found';
|
||||
protected string $description = 'The requested resource could not be found. Please verify the URI and try again.';
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpNotImplementedException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 501;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Not implemented.';
|
||||
|
||||
protected string $title = '501 Not Implemented';
|
||||
protected string $description = 'The server does not support the functionality required to fulfill the request.';
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Throwable;
|
||||
|
||||
abstract class HttpSpecializedException extends HttpException
|
||||
{
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string|null $message
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct(ServerRequestInterface $request, ?string $message = null, ?Throwable $previous = null)
|
||||
{
|
||||
if ($message !== null) {
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
parent::__construct($request, $this->message, $this->code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpTooManyRequestsException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 429;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Too many requests.';
|
||||
|
||||
protected string $title = '429 Too Many Requests';
|
||||
protected string $description = 'The client application has surpassed its rate limit, ' .
|
||||
'or number of requests they can send in a given period of time.';
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Exception;
|
||||
|
||||
/** @api */
|
||||
class HttpUnauthorizedException extends HttpSpecializedException
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $code = 401;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Unauthorized.';
|
||||
|
||||
protected string $title = '401 Unauthorized';
|
||||
protected string $description = 'The request requires valid user authentication.';
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use RuntimeException;
|
||||
use Slim\App;
|
||||
use Slim\Factory\Psr17\Psr17Factory;
|
||||
use Slim\Factory\Psr17\Psr17FactoryProvider;
|
||||
use Slim\Factory\Psr17\SlimHttpPsr17Factory;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\MiddlewareDispatcherInterface;
|
||||
use Slim\Interfaces\Psr17FactoryProviderInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
use Slim\Interfaces\RouteResolverInterface;
|
||||
|
||||
/** @api */
|
||||
class AppFactory
|
||||
{
|
||||
protected static ?Psr17FactoryProviderInterface $psr17FactoryProvider = null;
|
||||
|
||||
protected static ?ResponseFactoryInterface $responseFactory = null;
|
||||
|
||||
protected static ?StreamFactoryInterface $streamFactory = null;
|
||||
|
||||
protected static ?ContainerInterface $container = null;
|
||||
|
||||
protected static ?CallableResolverInterface $callableResolver = null;
|
||||
|
||||
protected static ?RouteCollectorInterface $routeCollector = null;
|
||||
|
||||
protected static ?RouteResolverInterface $routeResolver = null;
|
||||
|
||||
protected static ?MiddlewareDispatcherInterface $middlewareDispatcher = null;
|
||||
|
||||
protected static bool $slimHttpDecoratorsAutomaticDetectionEnabled = true;
|
||||
|
||||
/**
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
* @param TContainerInterface $container
|
||||
* @return (TContainerInterface is ContainerInterface ? App<TContainerInterface> : App<ContainerInterface|null>)
|
||||
*/
|
||||
public static function create(
|
||||
?ResponseFactoryInterface $responseFactory = null,
|
||||
?ContainerInterface $container = null,
|
||||
?CallableResolverInterface $callableResolver = null,
|
||||
?RouteCollectorInterface $routeCollector = null,
|
||||
?RouteResolverInterface $routeResolver = null,
|
||||
?MiddlewareDispatcherInterface $middlewareDispatcher = null
|
||||
): App {
|
||||
static::$responseFactory = $responseFactory ?? static::$responseFactory;
|
||||
return new App(
|
||||
self::determineResponseFactory(),
|
||||
$container ?? static::$container,
|
||||
$callableResolver ?? static::$callableResolver,
|
||||
$routeCollector ?? static::$routeCollector,
|
||||
$routeResolver ?? static::$routeResolver,
|
||||
$middlewareDispatcher ?? static::$middlewareDispatcher
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TContainerInterface of (ContainerInterface)
|
||||
* @param TContainerInterface $container
|
||||
* @return App<TContainerInterface>
|
||||
*/
|
||||
public static function createFromContainer(ContainerInterface $container): App
|
||||
{
|
||||
$responseFactory = $container->has(ResponseFactoryInterface::class)
|
||||
&& (
|
||||
$responseFactoryFromContainer = $container->get(ResponseFactoryInterface::class)
|
||||
) instanceof ResponseFactoryInterface
|
||||
? $responseFactoryFromContainer
|
||||
: self::determineResponseFactory();
|
||||
|
||||
$callableResolver = $container->has(CallableResolverInterface::class)
|
||||
&& (
|
||||
$callableResolverFromContainer = $container->get(CallableResolverInterface::class)
|
||||
) instanceof CallableResolverInterface
|
||||
? $callableResolverFromContainer
|
||||
: null;
|
||||
|
||||
$routeCollector = $container->has(RouteCollectorInterface::class)
|
||||
&& (
|
||||
$routeCollectorFromContainer = $container->get(RouteCollectorInterface::class)
|
||||
) instanceof RouteCollectorInterface
|
||||
? $routeCollectorFromContainer
|
||||
: null;
|
||||
|
||||
$routeResolver = $container->has(RouteResolverInterface::class)
|
||||
&& (
|
||||
$routeResolverFromContainer = $container->get(RouteResolverInterface::class)
|
||||
) instanceof RouteResolverInterface
|
||||
? $routeResolverFromContainer
|
||||
: null;
|
||||
|
||||
$middlewareDispatcher = $container->has(MiddlewareDispatcherInterface::class)
|
||||
&& (
|
||||
$middlewareDispatcherFromContainer = $container->get(MiddlewareDispatcherInterface::class)
|
||||
) instanceof MiddlewareDispatcherInterface
|
||||
? $middlewareDispatcherFromContainer
|
||||
: null;
|
||||
|
||||
return new App(
|
||||
$responseFactory,
|
||||
$container,
|
||||
$callableResolver,
|
||||
$routeCollector,
|
||||
$routeResolver,
|
||||
$middlewareDispatcher
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function determineResponseFactory(): ResponseFactoryInterface
|
||||
{
|
||||
if (static::$responseFactory) {
|
||||
if (static::$streamFactory) {
|
||||
return static::attemptResponseFactoryDecoration(static::$responseFactory, static::$streamFactory);
|
||||
}
|
||||
return static::$responseFactory;
|
||||
}
|
||||
|
||||
$psr17FactoryProvider = static::$psr17FactoryProvider ?? new Psr17FactoryProvider();
|
||||
|
||||
/** @var Psr17Factory $psr17factory */
|
||||
foreach ($psr17FactoryProvider->getFactories() as $psr17factory) {
|
||||
if ($psr17factory::isResponseFactoryAvailable()) {
|
||||
$responseFactory = $psr17factory::getResponseFactory();
|
||||
|
||||
if (static::$streamFactory || $psr17factory::isStreamFactoryAvailable()) {
|
||||
$streamFactory = static::$streamFactory ?? $psr17factory::getStreamFactory();
|
||||
return static::attemptResponseFactoryDecoration($responseFactory, $streamFactory);
|
||||
}
|
||||
|
||||
return $responseFactory;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"Could not detect any PSR-17 ResponseFactory implementations. " .
|
||||
"Please install a supported implementation in order to use `AppFactory::create()`. " .
|
||||
"See https://github.com/slimphp/Slim/blob/4.x/README.md for a list of supported implementations."
|
||||
);
|
||||
}
|
||||
|
||||
protected static function attemptResponseFactoryDecoration(
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
StreamFactoryInterface $streamFactory
|
||||
): ResponseFactoryInterface {
|
||||
if (
|
||||
static::$slimHttpDecoratorsAutomaticDetectionEnabled
|
||||
&& SlimHttpPsr17Factory::isResponseFactoryAvailable()
|
||||
) {
|
||||
return SlimHttpPsr17Factory::createDecoratedResponseFactory($responseFactory, $streamFactory);
|
||||
}
|
||||
|
||||
return $responseFactory;
|
||||
}
|
||||
|
||||
public static function setPsr17FactoryProvider(Psr17FactoryProviderInterface $psr17FactoryProvider): void
|
||||
{
|
||||
static::$psr17FactoryProvider = $psr17FactoryProvider;
|
||||
}
|
||||
|
||||
public static function setResponseFactory(ResponseFactoryInterface $responseFactory): void
|
||||
{
|
||||
static::$responseFactory = $responseFactory;
|
||||
}
|
||||
|
||||
public static function setStreamFactory(StreamFactoryInterface $streamFactory): void
|
||||
{
|
||||
static::$streamFactory = $streamFactory;
|
||||
}
|
||||
|
||||
public static function setContainer(ContainerInterface $container): void
|
||||
{
|
||||
static::$container = $container;
|
||||
}
|
||||
|
||||
public static function setCallableResolver(CallableResolverInterface $callableResolver): void
|
||||
{
|
||||
static::$callableResolver = $callableResolver;
|
||||
}
|
||||
|
||||
public static function setRouteCollector(RouteCollectorInterface $routeCollector): void
|
||||
{
|
||||
static::$routeCollector = $routeCollector;
|
||||
}
|
||||
|
||||
public static function setRouteResolver(RouteResolverInterface $routeResolver): void
|
||||
{
|
||||
static::$routeResolver = $routeResolver;
|
||||
}
|
||||
|
||||
public static function setMiddlewareDispatcher(MiddlewareDispatcherInterface $middlewareDispatcher): void
|
||||
{
|
||||
static::$middlewareDispatcher = $middlewareDispatcher;
|
||||
}
|
||||
|
||||
public static function setSlimHttpDecoratorsAutomaticDetection(bool $enabled): void
|
||||
{
|
||||
static::$slimHttpDecoratorsAutomaticDetectionEnabled = $enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
class GuzzlePsr17Factory extends Psr17Factory
|
||||
{
|
||||
protected static string $responseFactoryClass = 'GuzzleHttp\Psr7\HttpFactory';
|
||||
protected static string $streamFactoryClass = 'GuzzleHttp\Psr7\HttpFactory';
|
||||
protected static string $serverRequestCreatorClass = 'GuzzleHttp\Psr7\ServerRequest';
|
||||
protected static string $serverRequestCreatorMethod = 'fromGlobals';
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
class HttpSoftPsr17Factory extends Psr17Factory
|
||||
{
|
||||
protected static string $responseFactoryClass = 'HttpSoft\Message\ResponseFactory';
|
||||
protected static string $streamFactoryClass = 'HttpSoft\Message\StreamFactory';
|
||||
protected static string $serverRequestCreatorClass = 'HttpSoft\ServerRequest\ServerRequestCreator';
|
||||
protected static string $serverRequestCreatorMethod = 'createFromGlobals';
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
class LaminasDiactorosPsr17Factory extends Psr17Factory
|
||||
{
|
||||
protected static string $responseFactoryClass = 'Laminas\Diactoros\ResponseFactory';
|
||||
protected static string $streamFactoryClass = 'Laminas\Diactoros\StreamFactory';
|
||||
protected static string $serverRequestCreatorClass = 'Laminas\Diactoros\ServerRequestFactory';
|
||||
protected static string $serverRequestCreatorMethod = 'fromGlobals';
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
use Slim\Interfaces\ServerRequestCreatorInterface;
|
||||
|
||||
class NyholmPsr17Factory extends Psr17Factory
|
||||
{
|
||||
protected static string $responseFactoryClass = 'Nyholm\Psr7\Factory\Psr17Factory';
|
||||
protected static string $streamFactoryClass = 'Nyholm\Psr7\Factory\Psr17Factory';
|
||||
protected static string $serverRequestCreatorClass = 'Nyholm\Psr7Server\ServerRequestCreator';
|
||||
protected static string $serverRequestCreatorMethod = 'fromGlobals';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getServerRequestCreator(): ServerRequestCreatorInterface
|
||||
{
|
||||
/*
|
||||
* Nyholm Psr17Factory implements all factories in one unified
|
||||
* factory which implements all of the PSR-17 factory interfaces
|
||||
*/
|
||||
$psr17Factory = new static::$responseFactoryClass();
|
||||
|
||||
$serverRequestCreator = new static::$serverRequestCreatorClass(
|
||||
$psr17Factory,
|
||||
$psr17Factory,
|
||||
$psr17Factory,
|
||||
$psr17Factory
|
||||
);
|
||||
|
||||
return new ServerRequestCreator($serverRequestCreator, static::$serverRequestCreatorMethod);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Interfaces\Psr17FactoryInterface;
|
||||
use Slim\Interfaces\ServerRequestCreatorInterface;
|
||||
|
||||
use function class_exists;
|
||||
use function get_called_class;
|
||||
|
||||
abstract class Psr17Factory implements Psr17FactoryInterface
|
||||
{
|
||||
protected static string $responseFactoryClass;
|
||||
|
||||
protected static string $streamFactoryClass;
|
||||
|
||||
protected static string $serverRequestCreatorClass;
|
||||
|
||||
protected static string $serverRequestCreatorMethod;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getResponseFactory(): ResponseFactoryInterface
|
||||
{
|
||||
if (
|
||||
!static::isResponseFactoryAvailable()
|
||||
|| !(($responseFactory = new static::$responseFactoryClass()) instanceof ResponseFactoryInterface)
|
||||
) {
|
||||
throw new RuntimeException(get_called_class() . ' could not instantiate a response factory.');
|
||||
}
|
||||
|
||||
return $responseFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getStreamFactory(): StreamFactoryInterface
|
||||
{
|
||||
if (
|
||||
!static::isStreamFactoryAvailable()
|
||||
|| !(($streamFactory = new static::$streamFactoryClass()) instanceof StreamFactoryInterface)
|
||||
) {
|
||||
throw new RuntimeException(get_called_class() . ' could not instantiate a stream factory.');
|
||||
}
|
||||
|
||||
return $streamFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getServerRequestCreator(): ServerRequestCreatorInterface
|
||||
{
|
||||
if (!static::isServerRequestCreatorAvailable()) {
|
||||
throw new RuntimeException(get_called_class() . ' could not instantiate a server request creator.');
|
||||
}
|
||||
|
||||
return new ServerRequestCreator(static::$serverRequestCreatorClass, static::$serverRequestCreatorMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isResponseFactoryAvailable(): bool
|
||||
{
|
||||
return static::$responseFactoryClass && class_exists(static::$responseFactoryClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isStreamFactoryAvailable(): bool
|
||||
{
|
||||
return static::$streamFactoryClass && class_exists(static::$streamFactoryClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isServerRequestCreatorAvailable(): bool
|
||||
{
|
||||
return (
|
||||
static::$serverRequestCreatorClass
|
||||
&& static::$serverRequestCreatorMethod
|
||||
&& class_exists(static::$serverRequestCreatorClass)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
use Slim\Interfaces\Psr17FactoryProviderInterface;
|
||||
|
||||
use function array_unshift;
|
||||
|
||||
class Psr17FactoryProvider implements Psr17FactoryProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected static array $factories = [
|
||||
SlimPsr17Factory::class,
|
||||
HttpSoftPsr17Factory::class,
|
||||
NyholmPsr17Factory::class,
|
||||
LaminasDiactorosPsr17Factory::class,
|
||||
GuzzlePsr17Factory::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getFactories(): array
|
||||
{
|
||||
return static::$factories;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function setFactories(array $factories): void
|
||||
{
|
||||
static::$factories = $factories;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function addFactory(string $factory): void
|
||||
{
|
||||
array_unshift(static::$factories, $factory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
use Closure;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Interfaces\ServerRequestCreatorInterface;
|
||||
|
||||
class ServerRequestCreator implements ServerRequestCreatorInterface
|
||||
{
|
||||
/**
|
||||
* @var object|string
|
||||
*/
|
||||
protected $serverRequestCreator;
|
||||
|
||||
protected string $serverRequestCreatorMethod;
|
||||
|
||||
/**
|
||||
* @param object|string $serverRequestCreator
|
||||
*/
|
||||
public function __construct($serverRequestCreator, string $serverRequestCreatorMethod)
|
||||
{
|
||||
$this->serverRequestCreator = $serverRequestCreator;
|
||||
$this->serverRequestCreatorMethod = $serverRequestCreatorMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createServerRequestFromGlobals(): ServerRequestInterface
|
||||
{
|
||||
/** @var callable $callable */
|
||||
$callable = [$this->serverRequestCreator, $this->serverRequestCreatorMethod];
|
||||
|
||||
/** @var ServerRequestInterface */
|
||||
return (Closure::fromCallable($callable))();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class SlimHttpPsr17Factory extends Psr17Factory
|
||||
{
|
||||
protected static string $responseFactoryClass = 'Slim\Http\Factory\DecoratedResponseFactory';
|
||||
|
||||
/**
|
||||
* @throws RuntimeException when the factory could not be instantiated
|
||||
*/
|
||||
public static function createDecoratedResponseFactory(
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
StreamFactoryInterface $streamFactory
|
||||
): ResponseFactoryInterface {
|
||||
if (
|
||||
!((
|
||||
$decoratedResponseFactory = new static::$responseFactoryClass($responseFactory, $streamFactory)
|
||||
) instanceof ResponseFactoryInterface
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(get_called_class() . ' could not instantiate a decorated response factory.');
|
||||
}
|
||||
|
||||
return $decoratedResponseFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Interfaces\ServerRequestCreatorInterface;
|
||||
|
||||
use function class_exists;
|
||||
|
||||
class SlimHttpServerRequestCreator implements ServerRequestCreatorInterface
|
||||
{
|
||||
protected ServerRequestCreatorInterface $serverRequestCreator;
|
||||
|
||||
protected static string $serverRequestDecoratorClass = 'Slim\Http\ServerRequest';
|
||||
|
||||
public function __construct(ServerRequestCreatorInterface $serverRequestCreator)
|
||||
{
|
||||
$this->serverRequestCreator = $serverRequestCreator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createServerRequestFromGlobals(): ServerRequestInterface
|
||||
{
|
||||
if (!static::isServerRequestDecoratorAvailable()) {
|
||||
throw new RuntimeException('The Slim-Http ServerRequest decorator is not available.');
|
||||
}
|
||||
|
||||
$request = $this->serverRequestCreator->createServerRequestFromGlobals();
|
||||
|
||||
if (
|
||||
!((
|
||||
$decoratedServerRequest = new static::$serverRequestDecoratorClass($request)
|
||||
) instanceof ServerRequestInterface)
|
||||
) {
|
||||
throw new RuntimeException(get_called_class() . ' could not instantiate a decorated server request.');
|
||||
}
|
||||
|
||||
return $decoratedServerRequest;
|
||||
}
|
||||
|
||||
public static function isServerRequestDecoratorAvailable(): bool
|
||||
{
|
||||
return class_exists(static::$serverRequestDecoratorClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory\Psr17;
|
||||
|
||||
class SlimPsr17Factory extends Psr17Factory
|
||||
{
|
||||
protected static string $responseFactoryClass = 'Slim\Psr7\Factory\ResponseFactory';
|
||||
protected static string $streamFactoryClass = 'Slim\Psr7\Factory\StreamFactory';
|
||||
protected static string $serverRequestCreatorClass = 'Slim\Psr7\Factory\ServerRequestFactory';
|
||||
protected static string $serverRequestCreatorMethod = 'createFromGlobals';
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Factory;
|
||||
|
||||
use RuntimeException;
|
||||
use Slim\Factory\Psr17\Psr17Factory;
|
||||
use Slim\Factory\Psr17\Psr17FactoryProvider;
|
||||
use Slim\Factory\Psr17\SlimHttpServerRequestCreator;
|
||||
use Slim\Interfaces\Psr17FactoryProviderInterface;
|
||||
use Slim\Interfaces\ServerRequestCreatorInterface;
|
||||
|
||||
/** @api */
|
||||
class ServerRequestCreatorFactory
|
||||
{
|
||||
protected static ?Psr17FactoryProviderInterface $psr17FactoryProvider = null;
|
||||
|
||||
protected static ?ServerRequestCreatorInterface $serverRequestCreator = null;
|
||||
|
||||
protected static bool $slimHttpDecoratorsAutomaticDetectionEnabled = true;
|
||||
|
||||
public static function create(): ServerRequestCreatorInterface
|
||||
{
|
||||
return static::determineServerRequestCreator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function determineServerRequestCreator(): ServerRequestCreatorInterface
|
||||
{
|
||||
if (static::$serverRequestCreator) {
|
||||
return static::attemptServerRequestCreatorDecoration(static::$serverRequestCreator);
|
||||
}
|
||||
|
||||
$psr17FactoryProvider = static::$psr17FactoryProvider ?? new Psr17FactoryProvider();
|
||||
|
||||
/** @var Psr17Factory $psr17Factory */
|
||||
foreach ($psr17FactoryProvider->getFactories() as $psr17Factory) {
|
||||
if ($psr17Factory::isServerRequestCreatorAvailable()) {
|
||||
$serverRequestCreator = $psr17Factory::getServerRequestCreator();
|
||||
return static::attemptServerRequestCreatorDecoration($serverRequestCreator);
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
"Could not detect any ServerRequest creator implementations. " .
|
||||
"Please install a supported implementation in order to use `App::run()` " .
|
||||
"without having to pass in a `ServerRequest` object. " .
|
||||
"See https://github.com/slimphp/Slim/blob/4.x/README.md for a list of supported implementations."
|
||||
);
|
||||
}
|
||||
|
||||
protected static function attemptServerRequestCreatorDecoration(
|
||||
ServerRequestCreatorInterface $serverRequestCreator
|
||||
): ServerRequestCreatorInterface {
|
||||
if (
|
||||
static::$slimHttpDecoratorsAutomaticDetectionEnabled
|
||||
&& SlimHttpServerRequestCreator::isServerRequestDecoratorAvailable()
|
||||
) {
|
||||
return new SlimHttpServerRequestCreator($serverRequestCreator);
|
||||
}
|
||||
|
||||
return $serverRequestCreator;
|
||||
}
|
||||
|
||||
public static function setPsr17FactoryProvider(Psr17FactoryProviderInterface $psr17FactoryProvider): void
|
||||
{
|
||||
static::$psr17FactoryProvider = $psr17FactoryProvider;
|
||||
}
|
||||
|
||||
public static function setServerRequestCreator(ServerRequestCreatorInterface $serverRequestCreator): void
|
||||
{
|
||||
self::$serverRequestCreator = $serverRequestCreator;
|
||||
}
|
||||
|
||||
public static function setSlimHttpDecoratorsAutomaticDetection(bool $enabled): void
|
||||
{
|
||||
static::$slimHttpDecoratorsAutomaticDetectionEnabled = $enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Handlers;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Error\Renderers\HtmlErrorRenderer;
|
||||
use Slim\Error\Renderers\JsonErrorRenderer;
|
||||
use Slim\Error\Renderers\PlainTextErrorRenderer;
|
||||
use Slim\Error\Renderers\XmlErrorRenderer;
|
||||
use Slim\Exception\HttpException;
|
||||
use Slim\Exception\HttpMethodNotAllowedException;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\ErrorHandlerInterface;
|
||||
use Slim\Interfaces\ErrorRendererInterface;
|
||||
use Slim\Logger;
|
||||
use Throwable;
|
||||
|
||||
use function array_intersect;
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function call_user_func;
|
||||
use function count;
|
||||
use function current;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function next;
|
||||
use function preg_match;
|
||||
|
||||
/**
|
||||
* Default Slim application error handler
|
||||
*
|
||||
* It outputs the error message and diagnostic information in one of the following formats:
|
||||
* JSON, XML, Plain Text or HTML based on the Accept header.
|
||||
* @api
|
||||
*/
|
||||
class ErrorHandler implements ErrorHandlerInterface
|
||||
{
|
||||
protected string $defaultErrorRendererContentType = 'text/html';
|
||||
|
||||
/**
|
||||
* @var ErrorRendererInterface|string|callable
|
||||
*/
|
||||
protected $defaultErrorRenderer = HtmlErrorRenderer::class;
|
||||
|
||||
/**
|
||||
* @var ErrorRendererInterface|string|callable
|
||||
*/
|
||||
protected $logErrorRenderer = PlainTextErrorRenderer::class;
|
||||
|
||||
/**
|
||||
* @var array<string|callable>
|
||||
*/
|
||||
protected array $errorRenderers = [
|
||||
'application/json' => JsonErrorRenderer::class,
|
||||
'application/xml' => XmlErrorRenderer::class,
|
||||
'text/xml' => XmlErrorRenderer::class,
|
||||
'text/html' => HtmlErrorRenderer::class,
|
||||
'text/plain' => PlainTextErrorRenderer::class,
|
||||
];
|
||||
|
||||
protected bool $displayErrorDetails = false;
|
||||
|
||||
protected bool $logErrors;
|
||||
|
||||
protected bool $logErrorDetails = false;
|
||||
|
||||
protected ?string $contentType = null;
|
||||
|
||||
protected ?string $method = null;
|
||||
|
||||
protected ServerRequestInterface $request;
|
||||
|
||||
protected Throwable $exception;
|
||||
|
||||
protected int $statusCode;
|
||||
|
||||
protected CallableResolverInterface $callableResolver;
|
||||
|
||||
protected ResponseFactoryInterface $responseFactory;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
CallableResolverInterface $callableResolver,
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->responseFactory = $responseFactory;
|
||||
$this->logger = $logger ?: $this->getDefaultLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke error handler
|
||||
*
|
||||
* @param ServerRequestInterface $request The most recent Request object
|
||||
* @param Throwable $exception The caught Exception object
|
||||
* @param bool $displayErrorDetails Whether or not to display the error details
|
||||
* @param bool $logErrors Whether or not to log errors
|
||||
* @param bool $logErrorDetails Whether or not to log error details
|
||||
*/
|
||||
public function __invoke(
|
||||
ServerRequestInterface $request,
|
||||
Throwable $exception,
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails
|
||||
): ResponseInterface {
|
||||
$this->displayErrorDetails = $displayErrorDetails;
|
||||
$this->logErrors = $logErrors;
|
||||
$this->logErrorDetails = $logErrorDetails;
|
||||
$this->request = $request;
|
||||
$this->exception = $exception;
|
||||
$this->method = $request->getMethod();
|
||||
$this->statusCode = $this->determineStatusCode();
|
||||
if ($this->contentType === null) {
|
||||
$this->contentType = $this->determineContentType($request);
|
||||
}
|
||||
|
||||
if ($logErrors) {
|
||||
$this->writeToErrorLog();
|
||||
}
|
||||
|
||||
return $this->respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the content type for all error handler responses.
|
||||
*
|
||||
* @param string|null $contentType The content type
|
||||
*/
|
||||
public function forceContentType(?string $contentType): void
|
||||
{
|
||||
$this->contentType = $contentType;
|
||||
}
|
||||
|
||||
protected function determineStatusCode(): int
|
||||
{
|
||||
if ($this->method === 'OPTIONS') {
|
||||
return 200;
|
||||
}
|
||||
|
||||
if ($this->exception instanceof HttpException) {
|
||||
return $this->exception->getCode();
|
||||
}
|
||||
|
||||
return 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which content type we know about is wanted using Accept header
|
||||
*
|
||||
* Note: This method is a bare-bones implementation designed specifically for
|
||||
* Slim's error handling requirements. Consider a fully-feature solution such
|
||||
* as willdurand/negotiation for any other situation.
|
||||
*/
|
||||
protected function determineContentType(ServerRequestInterface $request): ?string
|
||||
{
|
||||
$acceptHeader = $request->getHeaderLine('Accept');
|
||||
$selectedContentTypes = array_intersect(
|
||||
explode(',', $acceptHeader),
|
||||
array_keys($this->errorRenderers)
|
||||
);
|
||||
$count = count($selectedContentTypes);
|
||||
|
||||
if ($count) {
|
||||
$current = current($selectedContentTypes);
|
||||
|
||||
/**
|
||||
* Ensure other supported content types take precedence over text/plain
|
||||
* when multiple content types are provided via Accept header.
|
||||
*/
|
||||
if ($current === 'text/plain' && $count > 1) {
|
||||
$next = next($selectedContentTypes);
|
||||
if (is_string($next)) {
|
||||
return $next;
|
||||
}
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
if (is_string($current)) {
|
||||
return $current;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/\+(json|xml)/', $acceptHeader, $matches)) {
|
||||
$mediaType = 'application/' . $matches[1];
|
||||
if (array_key_exists($mediaType, $this->errorRenderers)) {
|
||||
return $mediaType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which renderer to use based on content type
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function determineRenderer(): callable
|
||||
{
|
||||
if ($this->contentType !== null && array_key_exists($this->contentType, $this->errorRenderers)) {
|
||||
$renderer = $this->errorRenderers[$this->contentType];
|
||||
} else {
|
||||
$renderer = $this->defaultErrorRenderer;
|
||||
}
|
||||
|
||||
return $this->callableResolver->resolve($renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an error renderer for a specific content-type
|
||||
*
|
||||
* @param string $contentType The content-type this renderer should be registered to
|
||||
* @param ErrorRendererInterface|string|callable $errorRenderer The error renderer
|
||||
*/
|
||||
public function registerErrorRenderer(string $contentType, $errorRenderer): void
|
||||
{
|
||||
$this->errorRenderers[$contentType] = $errorRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default error renderer
|
||||
*
|
||||
* @param string $contentType The content type of the default error renderer
|
||||
* @param ErrorRendererInterface|string|callable $errorRenderer The default error renderer
|
||||
*/
|
||||
public function setDefaultErrorRenderer(string $contentType, $errorRenderer): void
|
||||
{
|
||||
$this->defaultErrorRendererContentType = $contentType;
|
||||
$this->defaultErrorRenderer = $errorRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the renderer for the error logger
|
||||
*
|
||||
* @param ErrorRendererInterface|string|callable $logErrorRenderer
|
||||
*/
|
||||
public function setLogErrorRenderer($logErrorRenderer): void
|
||||
{
|
||||
$this->logErrorRenderer = $logErrorRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the error log if $logErrors has been set to true
|
||||
*/
|
||||
protected function writeToErrorLog(): void
|
||||
{
|
||||
$renderer = $this->callableResolver->resolve($this->logErrorRenderer);
|
||||
|
||||
/** @var string $error */
|
||||
$error = $renderer($this->exception, $this->logErrorDetails);
|
||||
|
||||
if ($this->logErrorRenderer === PlainTextErrorRenderer::class && !$this->displayErrorDetails) {
|
||||
$error .= "\nTips: To display error details in HTTP response ";
|
||||
$error .= 'set "displayErrorDetails" to true in the ErrorHandler constructor.';
|
||||
}
|
||||
|
||||
$this->logError($error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the error_log function so that this can be easily tested
|
||||
*/
|
||||
protected function logError(string $error): void
|
||||
{
|
||||
$this->logger->error($error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a default logger implementation.
|
||||
*/
|
||||
protected function getDefaultLogger(): LoggerInterface
|
||||
{
|
||||
return new Logger();
|
||||
}
|
||||
|
||||
protected function respond(): ResponseInterface
|
||||
{
|
||||
$response = $this->responseFactory->createResponse($this->statusCode);
|
||||
if ($this->contentType !== null && array_key_exists($this->contentType, $this->errorRenderers)) {
|
||||
$response = $response->withHeader('Content-type', $this->contentType);
|
||||
} else {
|
||||
$response = $response->withHeader('Content-type', $this->defaultErrorRendererContentType);
|
||||
}
|
||||
|
||||
if ($this->exception instanceof HttpMethodNotAllowedException) {
|
||||
$allowedMethods = implode(', ', $this->exception->getAllowedMethods());
|
||||
$response = $response->withHeader('Allow', $allowedMethods);
|
||||
}
|
||||
|
||||
$renderer = $this->determineRenderer();
|
||||
$body = call_user_func($renderer, $this->exception, $this->displayErrorDetails);
|
||||
if ($body !== false) {
|
||||
/** @var string $body */
|
||||
$response->getBody()->write($body);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Handlers\Strategies;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Interfaces\RequestHandlerInvocationStrategyInterface;
|
||||
|
||||
/**
|
||||
* PSR-15 RequestHandler invocation strategy
|
||||
*/
|
||||
class RequestHandler implements RequestHandlerInvocationStrategyInterface
|
||||
{
|
||||
protected bool $appendRouteArgumentsToRequestAttributes;
|
||||
|
||||
public function __construct(bool $appendRouteArgumentsToRequestAttributes = false)
|
||||
{
|
||||
$this->appendRouteArgumentsToRequestAttributes = $appendRouteArgumentsToRequestAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a route callable that implements RequestHandlerInterface
|
||||
*
|
||||
* @param array<string, string> $routeArguments
|
||||
*/
|
||||
public function __invoke(
|
||||
callable $callable,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array $routeArguments
|
||||
): ResponseInterface {
|
||||
if ($this->appendRouteArgumentsToRequestAttributes) {
|
||||
foreach ($routeArguments as $k => $v) {
|
||||
$request = $request->withAttribute($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
/** @var ResponseInterface */
|
||||
return $callable($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Handlers\Strategies;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Interfaces\InvocationStrategyInterface;
|
||||
|
||||
/**
|
||||
* Default route callback strategy with route parameters as an array of arguments.
|
||||
*/
|
||||
class RequestResponse implements InvocationStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Invoke a route callable with request, response, and all route parameters
|
||||
* as an array of arguments.
|
||||
*
|
||||
* @param array<string, string> $routeArguments
|
||||
*/
|
||||
public function __invoke(
|
||||
callable $callable,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array $routeArguments
|
||||
): ResponseInterface {
|
||||
foreach ($routeArguments as $k => $v) {
|
||||
$request = $request->withAttribute($k, $v);
|
||||
}
|
||||
|
||||
/** @var ResponseInterface */
|
||||
return $callable($request, $response, $routeArguments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Handlers\Strategies;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Interfaces\InvocationStrategyInterface;
|
||||
|
||||
use function array_values;
|
||||
|
||||
/**
|
||||
* Route callback strategy with route parameters as individual arguments.
|
||||
* @api
|
||||
*/
|
||||
class RequestResponseArgs implements InvocationStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Invoke a route callable with request, response and all route parameters
|
||||
* as individual arguments.
|
||||
*
|
||||
* @param array<string, string> $routeArguments
|
||||
*/
|
||||
public function __invoke(
|
||||
callable $callable,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array $routeArguments
|
||||
): ResponseInterface {
|
||||
/** @var ResponseInterface */
|
||||
return $callable($request, $response, ...array_values($routeArguments));
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Handlers\Strategies;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Interfaces\InvocationStrategyInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Route callback strategy with route parameters as individual arguments.
|
||||
* @api
|
||||
*/
|
||||
class RequestResponseNamedArgs implements InvocationStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Invoke a route callable with request, response and all route parameters
|
||||
* as individual arguments.
|
||||
*
|
||||
* @param array<string, string> $routeArguments
|
||||
*/
|
||||
public function __invoke(
|
||||
callable $callable,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array $routeArguments
|
||||
): ResponseInterface {
|
||||
/** @var ResponseInterface */
|
||||
return $callable($request, $response, ...$routeArguments);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
interface AdvancedCallableResolverInterface extends CallableResolverInterface
|
||||
{
|
||||
/**
|
||||
* Resolve $toResolve into a callable
|
||||
*
|
||||
* @param callable|array{class-string, string}|string $toResolve
|
||||
*/
|
||||
public function resolveRoute($toResolve): callable;
|
||||
|
||||
/**
|
||||
* Resolve $toResolve into a callable
|
||||
*
|
||||
* @param callable|array{class-string, string}|string $toResolve
|
||||
*/
|
||||
public function resolveMiddleware($toResolve): callable;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
interface CallableResolverInterface
|
||||
{
|
||||
/**
|
||||
* Resolve $toResolve into a callable
|
||||
*
|
||||
* @param callable|array{class-string, string}|string $toResolve
|
||||
*/
|
||||
public function resolve($toResolve): callable;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Slim\Routing\RoutingResults;
|
||||
|
||||
interface DispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Get routing results for a given request method and uri
|
||||
*/
|
||||
public function dispatch(string $method, string $uri): RoutingResults;
|
||||
|
||||
/**
|
||||
* Get allowed methods for a given uri
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedMethods(string $uri): array;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Throwable;
|
||||
|
||||
interface ErrorHandlerInterface
|
||||
{
|
||||
public function __invoke(
|
||||
ServerRequestInterface $request,
|
||||
Throwable $exception,
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails
|
||||
): ResponseInterface;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Throwable;
|
||||
|
||||
interface ErrorRendererInterface
|
||||
{
|
||||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Defines a contract for invoking a route callable.
|
||||
*/
|
||||
interface InvocationStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Invoke a route callable.
|
||||
*
|
||||
* @param callable $callable The callable to invoke using the strategy.
|
||||
* @param ServerRequestInterface $request The request object.
|
||||
* @param ResponseInterface $response The response object.
|
||||
* @param array<string, string> $routeArguments The route's placeholder arguments
|
||||
*
|
||||
* @return ResponseInterface The response from the callable.
|
||||
*/
|
||||
public function __invoke(
|
||||
callable $callable,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array $routeArguments
|
||||
): ResponseInterface;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/** @api */
|
||||
interface MiddlewareDispatcherInterface extends RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Add a new middleware to the stack
|
||||
*
|
||||
* Middleware are organized as a stack. That means middleware
|
||||
* that have been added before will be executed after the newly
|
||||
* added one (last in, first out).
|
||||
*
|
||||
* @param MiddlewareInterface|string|callable $middleware
|
||||
*/
|
||||
public function add($middleware): self;
|
||||
|
||||
/**
|
||||
* Add a new middleware to the stack
|
||||
*
|
||||
* Middleware are organized as a stack. That means middleware
|
||||
* that have been added before will be executed after the newly
|
||||
* added one (last in, first out).
|
||||
*/
|
||||
public function addMiddleware(MiddlewareInterface $middleware): self;
|
||||
|
||||
/**
|
||||
* Seed the middleware stack with the inner request handler
|
||||
*/
|
||||
public function seedMiddlewareStack(RequestHandlerInterface $kernel): void;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use RuntimeException;
|
||||
|
||||
interface Psr17FactoryInterface
|
||||
{
|
||||
/**
|
||||
* @throws RuntimeException when the factory could not be instantiated
|
||||
*/
|
||||
public static function getResponseFactory(): ResponseFactoryInterface;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException when the factory could not be instantiated
|
||||
*/
|
||||
public static function getStreamFactory(): StreamFactoryInterface;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException when the factory could not be instantiated
|
||||
*/
|
||||
public static function getServerRequestCreator(): ServerRequestCreatorInterface;
|
||||
|
||||
/**
|
||||
* Is the PSR-17 ResponseFactory available
|
||||
*/
|
||||
public static function isResponseFactoryAvailable(): bool;
|
||||
|
||||
/**
|
||||
* Is the PSR-17 StreamFactory available
|
||||
*/
|
||||
public static function isStreamFactoryAvailable(): bool;
|
||||
|
||||
/**
|
||||
* Is the ServerRequest creator available
|
||||
*/
|
||||
public static function isServerRequestCreatorAvailable(): bool;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
/** @api */
|
||||
interface Psr17FactoryProviderInterface
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getFactories(): array;
|
||||
|
||||
/**
|
||||
* @param string[] $factories
|
||||
*/
|
||||
public static function setFactories(array $factories): void;
|
||||
|
||||
public static function addFactory(string $factory): void;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
interface RequestHandlerInvocationStrategyInterface extends InvocationStrategyInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/** @api */
|
||||
interface RouteCollectorInterface
|
||||
{
|
||||
/**
|
||||
* Get the route parser
|
||||
*/
|
||||
public function getRouteParser(): RouteParserInterface;
|
||||
|
||||
/**
|
||||
* Get default route invocation strategy
|
||||
*/
|
||||
public function getDefaultInvocationStrategy(): InvocationStrategyInterface;
|
||||
|
||||
/**
|
||||
* Set default route invocation strategy
|
||||
*/
|
||||
public function setDefaultInvocationStrategy(InvocationStrategyInterface $strategy): RouteCollectorInterface;
|
||||
|
||||
/**
|
||||
* Get path to FastRoute cache file
|
||||
*/
|
||||
public function getCacheFile(): ?string;
|
||||
|
||||
/**
|
||||
* Set path to FastRoute cache file
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function setCacheFile(string $cacheFile): RouteCollectorInterface;
|
||||
|
||||
/**
|
||||
* Get the base path used in pathFor()
|
||||
*/
|
||||
public function getBasePath(): string;
|
||||
|
||||
/**
|
||||
* Set the base path used in pathFor()
|
||||
*/
|
||||
public function setBasePath(string $basePath): RouteCollectorInterface;
|
||||
|
||||
/**
|
||||
* Get route objects
|
||||
*
|
||||
* @return RouteInterface[]
|
||||
*/
|
||||
public function getRoutes(): array;
|
||||
|
||||
/**
|
||||
* Get named route object
|
||||
*
|
||||
* @param string $name Route name
|
||||
*
|
||||
* @throws RuntimeException If named route does not exist
|
||||
*/
|
||||
public function getNamedRoute(string $name): RouteInterface;
|
||||
|
||||
/**
|
||||
* Remove named route
|
||||
*
|
||||
* @param string $name Route name
|
||||
*
|
||||
* @throws RuntimeException If named route does not exist
|
||||
*/
|
||||
public function removeNamedRoute(string $name): RouteCollectorInterface;
|
||||
|
||||
/**
|
||||
* Lookup a route via the route's unique identifier
|
||||
*
|
||||
* @throws RuntimeException If route of identifier does not exist
|
||||
*/
|
||||
public function lookupRoute(string $identifier): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add route group
|
||||
* @param string|callable $callable
|
||||
*/
|
||||
public function group(string $pattern, $callable): RouteGroupInterface;
|
||||
|
||||
/**
|
||||
* Add route
|
||||
*
|
||||
* @param string[] $methods Array of HTTP methods
|
||||
* @param string $pattern The route pattern
|
||||
* @param callable|array{class-string, string}|string $handler The route callable
|
||||
*/
|
||||
public function map(array $methods, string $pattern, $handler): RouteInterface;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
*/
|
||||
interface RouteCollectorProxyInterface
|
||||
{
|
||||
public function getResponseFactory(): ResponseFactoryInterface;
|
||||
|
||||
public function getCallableResolver(): CallableResolverInterface;
|
||||
|
||||
/**
|
||||
* @return TContainerInterface
|
||||
*/
|
||||
public function getContainer(): ?ContainerInterface;
|
||||
|
||||
public function getRouteCollector(): RouteCollectorInterface;
|
||||
|
||||
/**
|
||||
* Get the RouteCollectorProxy's base path
|
||||
*/
|
||||
public function getBasePath(): string;
|
||||
|
||||
/**
|
||||
* Set the RouteCollectorProxy's base path
|
||||
* @return RouteCollectorProxyInterface<TContainerInterface>
|
||||
*/
|
||||
public function setBasePath(string $basePath): RouteCollectorProxyInterface;
|
||||
|
||||
/**
|
||||
* Add GET route
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function get(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add POST route
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function post(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add PUT route
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function put(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add PATCH route
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function patch(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add DELETE route
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function delete(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add OPTIONS route
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function options(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add route for any HTTP method
|
||||
*
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function any(string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Add route with multiple methods
|
||||
*
|
||||
* @param string[] $methods Numeric array of HTTP method names
|
||||
* @param string $pattern The route URI pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callback routine
|
||||
*/
|
||||
public function map(array $methods, string $pattern, $callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Route Groups
|
||||
*
|
||||
* This method accepts a route pattern and a callback. All route
|
||||
* declarations in the callback will be prepended by the group(s)
|
||||
* that it is in.
|
||||
* @param string|callable $callable
|
||||
*/
|
||||
public function group(string $pattern, $callable): RouteGroupInterface;
|
||||
|
||||
/**
|
||||
* Add a route that sends an HTTP redirect
|
||||
*
|
||||
* @param string|UriInterface $to
|
||||
*/
|
||||
public function redirect(string $from, $to, int $status = 302): RouteInterface;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Slim\MiddlewareDispatcher;
|
||||
|
||||
/** @api */
|
||||
interface RouteGroupInterface
|
||||
{
|
||||
public function collectRoutes(): RouteGroupInterface;
|
||||
|
||||
/**
|
||||
* Add middleware to the route group
|
||||
*
|
||||
* @param MiddlewareInterface|string|callable $middleware
|
||||
*/
|
||||
public function add($middleware): RouteGroupInterface;
|
||||
|
||||
/**
|
||||
* Add middleware to the route group
|
||||
*/
|
||||
public function addMiddleware(MiddlewareInterface $middleware): RouteGroupInterface;
|
||||
|
||||
/**
|
||||
* Append the group's middleware to the MiddlewareDispatcher
|
||||
* @param MiddlewareDispatcher<\Psr\Container\ContainerInterface|null> $dispatcher
|
||||
*/
|
||||
public function appendMiddlewareToDispatcher(MiddlewareDispatcher $dispatcher): RouteGroupInterface;
|
||||
|
||||
/**
|
||||
* Get the RouteGroup's pattern
|
||||
*/
|
||||
public function getPattern(): string;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
|
||||
/** @api */
|
||||
interface RouteInterface
|
||||
{
|
||||
/**
|
||||
* Get route invocation strategy
|
||||
*/
|
||||
public function getInvocationStrategy(): InvocationStrategyInterface;
|
||||
|
||||
/**
|
||||
* Set route invocation strategy
|
||||
*/
|
||||
public function setInvocationStrategy(InvocationStrategyInterface $invocationStrategy): RouteInterface;
|
||||
|
||||
/**
|
||||
* Get route methods
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMethods(): array;
|
||||
|
||||
/**
|
||||
* Get route pattern
|
||||
*/
|
||||
public function getPattern(): string;
|
||||
|
||||
/**
|
||||
* Set route pattern
|
||||
*/
|
||||
public function setPattern(string $pattern): RouteInterface;
|
||||
|
||||
/**
|
||||
* Get route callable
|
||||
*
|
||||
* @return callable|array{class-string, string}|string
|
||||
*/
|
||||
public function getCallable();
|
||||
|
||||
/**
|
||||
* Set route callable
|
||||
*
|
||||
* @param callable|array{class-string, string}|string $callable
|
||||
*/
|
||||
public function setCallable($callable): RouteInterface;
|
||||
|
||||
/**
|
||||
* Get route name
|
||||
*/
|
||||
public function getName(): ?string;
|
||||
|
||||
/**
|
||||
* Set route name
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setName(string $name): RouteInterface;
|
||||
|
||||
/**
|
||||
* Get the route's unique identifier
|
||||
*/
|
||||
public function getIdentifier(): string;
|
||||
|
||||
/**
|
||||
* Retrieve a specific route argument
|
||||
*/
|
||||
public function getArgument(string $name, ?string $default = null): ?string;
|
||||
|
||||
/**
|
||||
* Get route arguments
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getArguments(): array;
|
||||
|
||||
/**
|
||||
* Set a route argument
|
||||
*
|
||||
* @deprecated 4.14.1 Use a middleware for custom route arguments now.
|
||||
*/
|
||||
public function setArgument(string $name, string $value): RouteInterface;
|
||||
|
||||
/**
|
||||
* Replace route arguments
|
||||
*
|
||||
* @param array<string, string> $arguments
|
||||
*
|
||||
* @deprecated 4.14.1 Use a middleware for custom route arguments now.
|
||||
*/
|
||||
public function setArguments(array $arguments): self;
|
||||
|
||||
/**
|
||||
* @param MiddlewareInterface|string|callable $middleware
|
||||
*/
|
||||
public function add($middleware): self;
|
||||
|
||||
public function addMiddleware(MiddlewareInterface $middleware): self;
|
||||
|
||||
/**
|
||||
* Prepare the route for use
|
||||
*
|
||||
* @param array<string, string> $arguments
|
||||
*/
|
||||
public function prepare(array $arguments): self;
|
||||
|
||||
/**
|
||||
* Run route
|
||||
*
|
||||
* This method traverses the middleware stack, including the route's callable
|
||||
* and captures the resultant HTTP response object. It then sends the response
|
||||
* back to the Application.
|
||||
*/
|
||||
public function run(ServerRequestInterface $request): ResponseInterface;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/** @api */
|
||||
interface RouteParserInterface
|
||||
{
|
||||
/**
|
||||
* Build the path for a named route excluding the base path
|
||||
*
|
||||
* @param string $routeName Route name
|
||||
* @param array<string, string> $data Named argument replacement data
|
||||
* @param array<string, string | array<array-key, string>> $queryParams Optional query string parameters
|
||||
*
|
||||
* @throws RuntimeException If named route does not exist
|
||||
* @throws InvalidArgumentException If required data not provided
|
||||
*/
|
||||
public function relativeUrlFor(string $routeName, array $data = [], array $queryParams = []): string;
|
||||
|
||||
/**
|
||||
* Build the path for a named route including the base path
|
||||
*
|
||||
* @param string $routeName Route name
|
||||
* @param array<string, string> $data Named argument replacement data
|
||||
* @param array<string, string | array<array-key, string>> $queryParams Optional query string parameters
|
||||
*
|
||||
* @throws RuntimeException If named route does not exist
|
||||
* @throws InvalidArgumentException If required data not provided
|
||||
*/
|
||||
public function urlFor(string $routeName, array $data = [], array $queryParams = []): string;
|
||||
|
||||
/**
|
||||
* Get fully qualified URL for named route
|
||||
*
|
||||
* @param UriInterface $uri
|
||||
* @param string $routeName Route name
|
||||
* @param array<string, string> $data Named argument replacement data
|
||||
* @param array<string, string | array<array-key, string>> $queryParams Optional query string parameters
|
||||
*/
|
||||
public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Slim\Routing\RoutingResults;
|
||||
|
||||
interface RouteResolverInterface
|
||||
{
|
||||
/**
|
||||
* @param string $uri Should be ServerRequestInterface::getUri()->getPath()
|
||||
*/
|
||||
public function computeRoutingResults(string $uri, string $method): RoutingResults;
|
||||
|
||||
public function resolveRoute(string $identifier): RouteInterface;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Interfaces;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
interface ServerRequestCreatorInterface
|
||||
{
|
||||
public function createServerRequestFromGlobals(): ServerRequestInterface;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Psr\Log\InvalidArgumentException;
|
||||
use Stringable;
|
||||
|
||||
use function error_log;
|
||||
|
||||
class Logger extends AbstractLogger
|
||||
{
|
||||
/**
|
||||
* @param mixed $level
|
||||
* @param string|Stringable $message
|
||||
* @param array<mixed> $context
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
error_log((string) $message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
use function count;
|
||||
use function explode;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function libxml_clear_errors;
|
||||
use function libxml_disable_entity_loader;
|
||||
use function libxml_use_internal_errors;
|
||||
use function parse_str;
|
||||
use function simplexml_load_string;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
use const LIBXML_VERSION;
|
||||
|
||||
/** @api */
|
||||
class BodyParsingMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
protected array $bodyParsers;
|
||||
|
||||
/**
|
||||
* @param callable[] $bodyParsers list of body parsers as an associative array of mediaType => callable
|
||||
*/
|
||||
public function __construct(array $bodyParsers = [])
|
||||
{
|
||||
$this->registerDefaultBodyParsers();
|
||||
|
||||
foreach ($bodyParsers as $mediaType => $parser) {
|
||||
$this->registerBodyParser($mediaType, $parser);
|
||||
}
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
|
||||
if (empty($parsedBody)) {
|
||||
$parsedBody = $this->parseBody($request);
|
||||
$request = $request->withParsedBody($parsedBody);
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mediaType A HTTP media type (excluding content-type params).
|
||||
* @param callable $callable A callable that returns parsed contents for media type.
|
||||
*/
|
||||
public function registerBodyParser(string $mediaType, callable $callable): self
|
||||
{
|
||||
$this->bodyParsers[$mediaType] = $callable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mediaType A HTTP media type (excluding content-type params).
|
||||
*/
|
||||
public function hasBodyParser(string $mediaType): bool
|
||||
{
|
||||
return isset($this->bodyParsers[$mediaType]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mediaType A HTTP media type (excluding content-type params).
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getBodyParser(string $mediaType): callable
|
||||
{
|
||||
if (!isset($this->bodyParsers[$mediaType])) {
|
||||
throw new RuntimeException('No parser for type ' . $mediaType);
|
||||
}
|
||||
return $this->bodyParsers[$mediaType];
|
||||
}
|
||||
|
||||
protected function registerDefaultBodyParsers(): void
|
||||
{
|
||||
$this->registerBodyParser('application/json', static function ($input) {
|
||||
/** @var string $input */
|
||||
$result = json_decode($input, true);
|
||||
|
||||
if (!is_array($result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
|
||||
$this->registerBodyParser('application/x-www-form-urlencoded', static function ($input) {
|
||||
/** @var string $input */
|
||||
parse_str($input, $data);
|
||||
|
||||
return $data;
|
||||
});
|
||||
|
||||
$xmlCallable = static function ($input) {
|
||||
/** @var string $input */
|
||||
|
||||
$backup = self::disableXmlEntityLoader(true);
|
||||
$backup_errors = libxml_use_internal_errors(true);
|
||||
$result = simplexml_load_string($input);
|
||||
|
||||
self::disableXmlEntityLoader($backup);
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($backup_errors);
|
||||
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
};
|
||||
|
||||
$this->registerBodyParser('application/xml', $xmlCallable);
|
||||
$this->registerBodyParser('text/xml', $xmlCallable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|array<mixed>|object
|
||||
*/
|
||||
protected function parseBody(ServerRequestInterface $request)
|
||||
{
|
||||
$mediaType = $this->getMediaType($request);
|
||||
if ($mediaType === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if this specific media type has a parser registered first
|
||||
if (!isset($this->bodyParsers[$mediaType])) {
|
||||
// If not, look for a media type with a structured syntax suffix (RFC 6839)
|
||||
$parts = explode('+', $mediaType);
|
||||
if (count($parts) >= 2) {
|
||||
$mediaType = 'application/' . $parts[count($parts) - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->bodyParsers[$mediaType])) {
|
||||
$body = (string)$request->getBody();
|
||||
$parsed = $this->bodyParsers[$mediaType]($body);
|
||||
|
||||
if ($parsed !== null && !is_object($parsed) && !is_array($parsed)) {
|
||||
throw new RuntimeException(
|
||||
'Request body media type parser return value must be an array, an object, or null'
|
||||
);
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null The serverRequest media type, minus content-type params
|
||||
*/
|
||||
protected function getMediaType(ServerRequestInterface $request): ?string
|
||||
{
|
||||
$contentType = $request->getHeader('Content-Type')[0] ?? null;
|
||||
|
||||
if (is_string($contentType) && trim($contentType) !== '') {
|
||||
$contentTypeParts = explode(';', $contentType);
|
||||
return strtolower(trim($contentTypeParts[0]));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static function disableXmlEntityLoader(bool $disable): bool
|
||||
{
|
||||
if (LIBXML_VERSION >= 20900) {
|
||||
// libxml >= 2.9.0 disables entity loading by default, so it is
|
||||
// safe to skip the real call (deprecated in PHP 8).
|
||||
return true;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
return libxml_disable_entity_loader($disable);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/** @api */
|
||||
class ContentLengthMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$response = $handler->handle($request);
|
||||
|
||||
// Add Content-Length header if not already added
|
||||
$size = $response->getBody()->getSize();
|
||||
if ($size !== null && !$response->hasHeader('Content-Length')) {
|
||||
$response = $response->withHeader('Content-Length', (string) $size);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Slim\Exception\HttpException;
|
||||
use Slim\Handlers\ErrorHandler;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\ErrorHandlerInterface;
|
||||
use Throwable;
|
||||
|
||||
use function get_class;
|
||||
use function is_subclass_of;
|
||||
|
||||
/** @api */
|
||||
class ErrorMiddleware implements MiddlewareInterface
|
||||
{
|
||||
protected CallableResolverInterface $callableResolver;
|
||||
|
||||
protected ResponseFactoryInterface $responseFactory;
|
||||
|
||||
protected bool $displayErrorDetails;
|
||||
|
||||
protected bool $logErrors;
|
||||
|
||||
protected bool $logErrorDetails;
|
||||
|
||||
protected ?LoggerInterface $logger = null;
|
||||
|
||||
/**
|
||||
* @var ErrorHandlerInterface[]|callable[]|string[]
|
||||
*/
|
||||
protected array $handlers = [];
|
||||
|
||||
/**
|
||||
* @var ErrorHandlerInterface[]|callable[]|string[]
|
||||
*/
|
||||
protected array $subClassHandlers = [];
|
||||
|
||||
/**
|
||||
* @var ErrorHandlerInterface|callable|string|null
|
||||
*/
|
||||
protected $defaultErrorHandler;
|
||||
|
||||
public function __construct(
|
||||
CallableResolverInterface $callableResolver,
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
bool $displayErrorDetails,
|
||||
bool $logErrors,
|
||||
bool $logErrorDetails,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->responseFactory = $responseFactory;
|
||||
$this->displayErrorDetails = $displayErrorDetails;
|
||||
$this->logErrors = $logErrors;
|
||||
$this->logErrorDetails = $logErrorDetails;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Throwable $e) {
|
||||
return $this->handleException($request, $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleException(ServerRequestInterface $request, Throwable $exception): ResponseInterface
|
||||
{
|
||||
if ($exception instanceof HttpException) {
|
||||
$request = $exception->getRequest();
|
||||
}
|
||||
|
||||
$exceptionType = get_class($exception);
|
||||
$handler = $this->getErrorHandler($exceptionType);
|
||||
|
||||
/** @var ResponseInterface */
|
||||
return $handler($request, $exception, $this->displayErrorDetails, $this->logErrors, $this->logErrorDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get callable to handle scenarios where an error
|
||||
* occurs when processing the current request.
|
||||
*
|
||||
* @param string $type Exception/Throwable name. ie: RuntimeException::class
|
||||
* @return callable|ErrorHandler
|
||||
*/
|
||||
public function getErrorHandler(string $type)
|
||||
{
|
||||
if (isset($this->handlers[$type])) {
|
||||
return $this->callableResolver->resolve($this->handlers[$type]);
|
||||
}
|
||||
|
||||
if (isset($this->subClassHandlers[$type])) {
|
||||
return $this->callableResolver->resolve($this->subClassHandlers[$type]);
|
||||
}
|
||||
|
||||
foreach ($this->subClassHandlers as $class => $handler) {
|
||||
if (is_subclass_of($type, $class)) {
|
||||
return $this->callableResolver->resolve($handler);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getDefaultErrorHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default error handler
|
||||
*
|
||||
* @return ErrorHandler|callable
|
||||
*/
|
||||
public function getDefaultErrorHandler()
|
||||
{
|
||||
if ($this->defaultErrorHandler === null) {
|
||||
$this->defaultErrorHandler = new ErrorHandler(
|
||||
$this->callableResolver,
|
||||
$this->responseFactory,
|
||||
$this->logger
|
||||
);
|
||||
}
|
||||
|
||||
return $this->callableResolver->resolve($this->defaultErrorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set callable as the default Slim application error handler.
|
||||
*
|
||||
* The callable signature MUST match the ErrorHandlerInterface
|
||||
*
|
||||
* @param string|callable|ErrorHandler $handler
|
||||
* @see ErrorHandlerInterface
|
||||
*
|
||||
* 1. Instance of \Psr\Http\Message\ServerRequestInterface
|
||||
* 2. Instance of \Throwable
|
||||
* 3. Boolean $displayErrorDetails
|
||||
* 4. Boolean $logErrors
|
||||
* 5. Boolean $logErrorDetails
|
||||
*
|
||||
* The callable MUST return an instance of
|
||||
* \Psr\Http\Message\ResponseInterface.
|
||||
*
|
||||
*/
|
||||
public function setDefaultErrorHandler($handler): self
|
||||
{
|
||||
$this->defaultErrorHandler = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set callable to handle scenarios where an error
|
||||
* occurs when processing the current request.
|
||||
*
|
||||
* The callable signature MUST match the ErrorHandlerInterface
|
||||
*
|
||||
* Pass true to $handleSubclasses to make the handler handle all subclasses of
|
||||
* the type as well. Pass an array of classes to make the same function handle multiple exceptions.
|
||||
*
|
||||
* @param string|string[] $typeOrTypes Exception/Throwable name.
|
||||
* ie: RuntimeException::class or an array of classes
|
||||
* ie: [HttpNotFoundException::class, HttpMethodNotAllowedException::class]
|
||||
* @param string|callable|ErrorHandlerInterface $handler
|
||||
*
|
||||
* @see ErrorHandlerInterface
|
||||
*
|
||||
* 1. Instance of \Psr\Http\Message\ServerRequestInterface
|
||||
* 2. Instance of \Throwable
|
||||
* 3. Boolean $displayErrorDetails
|
||||
* 4. Boolean $logErrors
|
||||
* 5. Boolean $logErrorDetails
|
||||
*
|
||||
* The callable MUST return an instance of
|
||||
* \Psr\Http\Message\ResponseInterface.
|
||||
*
|
||||
*/
|
||||
public function setErrorHandler($typeOrTypes, $handler, bool $handleSubclasses = false): self
|
||||
{
|
||||
if (is_array($typeOrTypes)) {
|
||||
foreach ($typeOrTypes as $type) {
|
||||
$this->addErrorHandler($type, $handler, $handleSubclasses);
|
||||
}
|
||||
} else {
|
||||
$this->addErrorHandler($typeOrTypes, $handler, $handleSubclasses);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally to avoid code repetition when passing multiple exceptions to setErrorHandler().
|
||||
* @param string|callable|ErrorHandlerInterface $handler
|
||||
*/
|
||||
private function addErrorHandler(string $type, $handler, bool $handleSubclasses): void
|
||||
{
|
||||
if ($handleSubclasses) {
|
||||
$this->subClassHandlers[$type] = $handler;
|
||||
} else {
|
||||
$this->handlers[$type] = $handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
use function is_array;
|
||||
use function strtoupper;
|
||||
|
||||
/** @api */
|
||||
class MethodOverrideMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$methodHeader = $request->getHeaderLine('X-Http-Method-Override');
|
||||
|
||||
if ($methodHeader) {
|
||||
$request = $request->withMethod($methodHeader);
|
||||
} elseif (strtoupper($request->getMethod()) === 'POST') {
|
||||
$body = $request->getParsedBody();
|
||||
|
||||
if (is_array($body) && !empty($body['_METHOD']) && is_string($body['_METHOD'])) {
|
||||
$request = $request->withMethod($body['_METHOD']);
|
||||
}
|
||||
|
||||
if ($request->getBody()->eof()) {
|
||||
$request->getBody()->rewind();
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Middleware;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Throwable;
|
||||
|
||||
use function in_array;
|
||||
use function ob_end_clean;
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
|
||||
/** @api */
|
||||
class OutputBufferingMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public const APPEND = 'append';
|
||||
public const PREPEND = 'prepend';
|
||||
|
||||
protected StreamFactoryInterface $streamFactory;
|
||||
|
||||
protected string $style;
|
||||
|
||||
/**
|
||||
* @param string $style Either "append" or "prepend"
|
||||
*/
|
||||
public function __construct(StreamFactoryInterface $streamFactory, string $style = 'append')
|
||||
{
|
||||
$this->streamFactory = $streamFactory;
|
||||
$this->style = $style;
|
||||
|
||||
if (!in_array($style, [static::APPEND, static::PREPEND], true)) {
|
||||
throw new InvalidArgumentException("Invalid style `{$style}`. Must be `append` or `prepend`");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
try {
|
||||
ob_start();
|
||||
$response = $handler->handle($request);
|
||||
$output = ob_get_clean();
|
||||
} catch (Throwable $e) {
|
||||
ob_end_clean();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!empty($output)) {
|
||||
if ($this->style === static::PREPEND) {
|
||||
$body = $this->streamFactory->createStream();
|
||||
$body->write($output . $response->getBody());
|
||||
$response = $response->withBody($body);
|
||||
} elseif ($this->style === static::APPEND && $response->getBody()->isWritable()) {
|
||||
$response->getBody()->write($output);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Exception\HttpMethodNotAllowedException;
|
||||
use Slim\Exception\HttpNotFoundException;
|
||||
use Slim\Interfaces\RouteParserInterface;
|
||||
use Slim\Interfaces\RouteResolverInterface;
|
||||
use Slim\Routing\RouteContext;
|
||||
use Slim\Routing\RoutingResults;
|
||||
|
||||
class RoutingMiddleware implements MiddlewareInterface
|
||||
{
|
||||
protected RouteResolverInterface $routeResolver;
|
||||
|
||||
protected RouteParserInterface $routeParser;
|
||||
|
||||
public function __construct(RouteResolverInterface $routeResolver, RouteParserInterface $routeParser)
|
||||
{
|
||||
$this->routeResolver = $routeResolver;
|
||||
$this->routeParser = $routeParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpNotFoundException
|
||||
* @throws HttpMethodNotAllowedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$request = $this->performRouting($request);
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform routing
|
||||
*
|
||||
* @param ServerRequestInterface $request PSR7 Server Request
|
||||
*
|
||||
* @throws HttpNotFoundException
|
||||
* @throws HttpMethodNotAllowedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function performRouting(ServerRequestInterface $request): ServerRequestInterface
|
||||
{
|
||||
$request = $request->withAttribute(RouteContext::ROUTE_PARSER, $this->routeParser);
|
||||
|
||||
$routingResults = $this->resolveRoutingResultsFromRequest($request);
|
||||
$routeStatus = $routingResults->getRouteStatus();
|
||||
|
||||
$request = $request->withAttribute(RouteContext::ROUTING_RESULTS, $routingResults);
|
||||
|
||||
switch ($routeStatus) {
|
||||
case RoutingResults::FOUND:
|
||||
$routeArguments = $routingResults->getRouteArguments();
|
||||
$routeIdentifier = $routingResults->getRouteIdentifier() ?? '';
|
||||
$route = $this->routeResolver
|
||||
->resolveRoute($routeIdentifier)
|
||||
->prepare($routeArguments);
|
||||
return $request->withAttribute(RouteContext::ROUTE, $route);
|
||||
|
||||
case RoutingResults::NOT_FOUND:
|
||||
throw new HttpNotFoundException($request);
|
||||
|
||||
case RoutingResults::METHOD_NOT_ALLOWED:
|
||||
$exception = new HttpMethodNotAllowedException($request);
|
||||
$exception->setAllowedMethods($routingResults->getAllowedMethods());
|
||||
throw $exception;
|
||||
|
||||
default:
|
||||
throw new RuntimeException('An unexpected error occurred while performing routing.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the route from the given request
|
||||
*/
|
||||
protected function resolveRoutingResultsFromRequest(ServerRequestInterface $request): RoutingResults
|
||||
{
|
||||
return $this->routeResolver->computeRoutingResults(
|
||||
$request->getUri()->getPath(),
|
||||
$request->getMethod()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim;
|
||||
|
||||
use Closure;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Interfaces\AdvancedCallableResolverInterface;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\MiddlewareDispatcherInterface;
|
||||
|
||||
use function class_exists;
|
||||
use function function_exists;
|
||||
use function is_callable;
|
||||
use function is_string;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
*/
|
||||
class MiddlewareDispatcher implements MiddlewareDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Tip of the middleware call stack
|
||||
*/
|
||||
protected RequestHandlerInterface $tip;
|
||||
|
||||
protected ?CallableResolverInterface $callableResolver;
|
||||
|
||||
/** @var TContainerInterface $container */
|
||||
protected ?ContainerInterface $container;
|
||||
|
||||
/**
|
||||
* @param TContainerInterface $container
|
||||
*/
|
||||
public function __construct(
|
||||
RequestHandlerInterface $kernel,
|
||||
?CallableResolverInterface $callableResolver = null,
|
||||
?ContainerInterface $container = null
|
||||
) {
|
||||
$this->seedMiddlewareStack($kernel);
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function seedMiddlewareStack(RequestHandlerInterface $kernel): void
|
||||
{
|
||||
$this->tip = $kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the middleware stack
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->tip->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new middleware to the stack
|
||||
*
|
||||
* Middleware are organized as a stack. That means middleware
|
||||
* that have been added before will be executed after the newly
|
||||
* added one (last in, first out).
|
||||
*
|
||||
* @param MiddlewareInterface|string|callable $middleware
|
||||
*/
|
||||
public function add($middleware): MiddlewareDispatcherInterface
|
||||
{
|
||||
if ($middleware instanceof MiddlewareInterface) {
|
||||
return $this->addMiddleware($middleware);
|
||||
}
|
||||
|
||||
if (is_string($middleware)) {
|
||||
return $this->addDeferred($middleware);
|
||||
}
|
||||
|
||||
if (is_callable($middleware)) {
|
||||
return $this->addCallable($middleware);
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
throw new RuntimeException(
|
||||
'A middleware must be an object/class name referencing an implementation of ' .
|
||||
'MiddlewareInterface or a callable with a matching signature.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new middleware to the stack
|
||||
*
|
||||
* Middleware are organized as a stack. That means middleware
|
||||
* that have been added before will be executed after the newly
|
||||
* added one (last in, first out).
|
||||
*/
|
||||
public function addMiddleware(MiddlewareInterface $middleware): MiddlewareDispatcherInterface
|
||||
{
|
||||
$next = $this->tip;
|
||||
$this->tip = new class ($middleware, $next) implements RequestHandlerInterface {
|
||||
private MiddlewareInterface $middleware;
|
||||
|
||||
private RequestHandlerInterface $next;
|
||||
|
||||
public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $next)
|
||||
{
|
||||
$this->middleware = $middleware;
|
||||
$this->next = $next;
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->middleware->process($request, $this->next);
|
||||
}
|
||||
};
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new middleware by class name
|
||||
*
|
||||
* Middleware are organized as a stack. That means middleware
|
||||
* that have been added before will be executed after the newly
|
||||
* added one (last in, first out).
|
||||
* @return MiddlewareDispatcher<TContainerInterface>
|
||||
*/
|
||||
public function addDeferred(string $middleware): self
|
||||
{
|
||||
$next = $this->tip;
|
||||
$this->tip = new class (
|
||||
$middleware,
|
||||
$next,
|
||||
$this->container,
|
||||
$this->callableResolver
|
||||
) implements RequestHandlerInterface {
|
||||
private string $middleware;
|
||||
|
||||
private RequestHandlerInterface $next;
|
||||
|
||||
private ?ContainerInterface $container;
|
||||
|
||||
private ?CallableResolverInterface $callableResolver;
|
||||
|
||||
public function __construct(
|
||||
string $middleware,
|
||||
RequestHandlerInterface $next,
|
||||
?ContainerInterface $container = null,
|
||||
?CallableResolverInterface $callableResolver = null
|
||||
) {
|
||||
$this->middleware = $middleware;
|
||||
$this->next = $next;
|
||||
$this->container = $container;
|
||||
$this->callableResolver = $callableResolver;
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {
|
||||
$callable = $this->callableResolver->resolveMiddleware($this->middleware);
|
||||
/** @var ResponseInterface */
|
||||
return $callable($request, $this->next);
|
||||
}
|
||||
|
||||
$callable = null;
|
||||
|
||||
if ($this->callableResolver instanceof CallableResolverInterface) {
|
||||
try {
|
||||
$callable = $this->callableResolver->resolve($this->middleware);
|
||||
} catch (RuntimeException $e) {
|
||||
// Do Nothing
|
||||
}
|
||||
}
|
||||
|
||||
if (!$callable) {
|
||||
$resolved = $this->middleware;
|
||||
$instance = null;
|
||||
$method = null;
|
||||
|
||||
/** @psalm-suppress ArgumentTypeCoercion */
|
||||
// Check for Slim callable as `class:method`
|
||||
if (preg_match(CallableResolver::$callablePattern, $resolved, $matches)) {
|
||||
$resolved = $matches[1];
|
||||
$method = $matches[2];
|
||||
}
|
||||
|
||||
if ($this->container && $this->container->has($resolved)) {
|
||||
$instance = $this->container->get($resolved);
|
||||
if ($instance instanceof MiddlewareInterface) {
|
||||
return $instance->process($request, $this->next);
|
||||
}
|
||||
} elseif (!function_exists($resolved)) {
|
||||
if (!class_exists($resolved)) {
|
||||
throw new RuntimeException(sprintf('Middleware %s does not exist', $resolved));
|
||||
}
|
||||
$instance = new $resolved($this->container);
|
||||
}
|
||||
|
||||
if ($instance && $instance instanceof MiddlewareInterface) {
|
||||
return $instance->process($request, $this->next);
|
||||
}
|
||||
|
||||
$callable = $instance ?? $resolved;
|
||||
if ($instance && $method) {
|
||||
$callable = [$instance, $method];
|
||||
}
|
||||
|
||||
if ($this->container && $callable instanceof Closure) {
|
||||
$callable = $callable->bindTo($this->container);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_callable($callable)) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
'Middleware %s is not resolvable',
|
||||
$this->middleware
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @var ResponseInterface */
|
||||
return $callable($request, $this->next);
|
||||
}
|
||||
};
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a (non-standard) callable middleware to the stack
|
||||
*
|
||||
* Middleware are organized as a stack. That means middleware
|
||||
* that have been added before will be executed after the newly
|
||||
* added one (last in, first out).
|
||||
* @return MiddlewareDispatcher<TContainerInterface>
|
||||
*/
|
||||
public function addCallable(callable $middleware): self
|
||||
{
|
||||
$next = $this->tip;
|
||||
|
||||
if ($this->container && $middleware instanceof Closure) {
|
||||
/** @var Closure $middleware */
|
||||
$middleware = $middleware->bindTo($this->container);
|
||||
}
|
||||
|
||||
$this->tip = new class ($middleware, $next) implements RequestHandlerInterface {
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $middleware;
|
||||
|
||||
/**
|
||||
* @var RequestHandlerInterface
|
||||
*/
|
||||
private $next;
|
||||
|
||||
public function __construct(callable $middleware, RequestHandlerInterface $next)
|
||||
{
|
||||
$this->middleware = $middleware;
|
||||
$this->next = $next;
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
/** @var ResponseInterface */
|
||||
return ($this->middleware)($request, $this->next);
|
||||
}
|
||||
};
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
use function connection_status;
|
||||
use function header;
|
||||
use function headers_sent;
|
||||
use function in_array;
|
||||
use function min;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function strtolower;
|
||||
|
||||
use const CONNECTION_NORMAL;
|
||||
|
||||
class ResponseEmitter
|
||||
{
|
||||
private int $responseChunkSize;
|
||||
|
||||
public function __construct(int $responseChunkSize = 4096)
|
||||
{
|
||||
$this->responseChunkSize = $responseChunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the response the client
|
||||
*/
|
||||
public function emit(ResponseInterface $response): void
|
||||
{
|
||||
$isEmpty = $this->isResponseEmpty($response);
|
||||
if (headers_sent() === false) {
|
||||
$this->emitHeaders($response);
|
||||
|
||||
// Set the status _after_ the headers, because of PHP's "helpful" behavior with location headers.
|
||||
// See https://github.com/slimphp/Slim/issues/1730
|
||||
|
||||
$this->emitStatusLine($response);
|
||||
}
|
||||
|
||||
if (!$isEmpty) {
|
||||
$this->emitBody($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit Response Headers
|
||||
*/
|
||||
private function emitHeaders(ResponseInterface $response): void
|
||||
{
|
||||
foreach ($response->getHeaders() as $name => $values) {
|
||||
$first = strtolower($name) !== 'set-cookie';
|
||||
foreach ($values as $value) {
|
||||
$header = sprintf('%s: %s', $name, $value);
|
||||
header($header, $first);
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit Status Line
|
||||
*/
|
||||
private function emitStatusLine(ResponseInterface $response): void
|
||||
{
|
||||
$statusLine = sprintf(
|
||||
'HTTP/%s %s %s',
|
||||
$response->getProtocolVersion(),
|
||||
$response->getStatusCode(),
|
||||
$response->getReasonPhrase()
|
||||
);
|
||||
header($statusLine, true, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit Body
|
||||
*/
|
||||
private function emitBody(ResponseInterface $response): void
|
||||
{
|
||||
$body = $response->getBody();
|
||||
if ($body->isSeekable()) {
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
$amountToRead = (int) $response->getHeaderLine('Content-Length');
|
||||
if (!$amountToRead) {
|
||||
$amountToRead = $body->getSize();
|
||||
}
|
||||
|
||||
if ($amountToRead) {
|
||||
while ($amountToRead > 0 && !$body->eof()) {
|
||||
$length = min($this->responseChunkSize, $amountToRead);
|
||||
$data = $body->read($length);
|
||||
echo $data;
|
||||
|
||||
$amountToRead -= strlen($data);
|
||||
|
||||
if (connection_status() !== CONNECTION_NORMAL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (!$body->eof()) {
|
||||
echo $body->read($this->responseChunkSize);
|
||||
if (connection_status() !== CONNECTION_NORMAL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts response body is empty or status code is 204, 205 or 304
|
||||
*/
|
||||
public function isResponseEmpty(ResponseInterface $response): bool
|
||||
{
|
||||
if (in_array($response->getStatusCode(), [204, 205, 304], true)) {
|
||||
return true;
|
||||
}
|
||||
$stream = $response->getBody();
|
||||
$seekable = $stream->isSeekable();
|
||||
if ($seekable) {
|
||||
$stream->rewind();
|
||||
}
|
||||
return $seekable ? $stream->read(1) === '' : $stream->eof();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use FastRoute\DataGenerator\GroupCountBased;
|
||||
use FastRoute\RouteCollector as FastRouteCollector;
|
||||
use FastRoute\RouteParser\Std;
|
||||
use Slim\Interfaces\DispatcherInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
|
||||
class Dispatcher implements DispatcherInterface
|
||||
{
|
||||
private RouteCollectorInterface $routeCollector;
|
||||
|
||||
private ?FastRouteDispatcher $dispatcher = null;
|
||||
|
||||
public function __construct(RouteCollectorInterface $routeCollector)
|
||||
{
|
||||
$this->routeCollector = $routeCollector;
|
||||
}
|
||||
|
||||
protected function createDispatcher(): FastRouteDispatcher
|
||||
{
|
||||
if ($this->dispatcher) {
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
$routeDefinitionCallback = function (FastRouteCollector $r): void {
|
||||
$basePath = $this->routeCollector->getBasePath();
|
||||
|
||||
foreach ($this->routeCollector->getRoutes() as $route) {
|
||||
$r->addRoute($route->getMethods(), $basePath . $route->getPattern(), $route->getIdentifier());
|
||||
}
|
||||
};
|
||||
|
||||
$cacheFile = $this->routeCollector->getCacheFile();
|
||||
if ($cacheFile) {
|
||||
/** @var FastRouteDispatcher $dispatcher */
|
||||
$dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [
|
||||
'dataGenerator' => GroupCountBased::class,
|
||||
'dispatcher' => FastRouteDispatcher::class,
|
||||
'routeParser' => new Std(),
|
||||
'cacheFile' => $cacheFile,
|
||||
]);
|
||||
} else {
|
||||
/** @var FastRouteDispatcher $dispatcher */
|
||||
$dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback, [
|
||||
'dataGenerator' => GroupCountBased::class,
|
||||
'dispatcher' => FastRouteDispatcher::class,
|
||||
'routeParser' => new Std(),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->dispatcher = $dispatcher;
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dispatch(string $method, string $uri): RoutingResults
|
||||
{
|
||||
$dispatcher = $this->createDispatcher();
|
||||
$results = $dispatcher->dispatch($method, $uri);
|
||||
return new RoutingResults($this, $method, $uri, $results[0], $results[1], $results[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAllowedMethods(string $uri): array
|
||||
{
|
||||
$dispatcher = $this->createDispatcher();
|
||||
return $dispatcher->getAllowedMethods($uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use FastRoute\Dispatcher\GroupCountBased;
|
||||
|
||||
class FastRouteDispatcher extends GroupCountBased
|
||||
{
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
private array $allowedMethods = [];
|
||||
|
||||
/**
|
||||
* @param string $httpMethod
|
||||
* @param string $uri
|
||||
*
|
||||
* @return array{int, string|null, array<string, string>}
|
||||
*/
|
||||
public function dispatch($httpMethod, $uri): array
|
||||
{
|
||||
$routingResults = $this->routingResults($httpMethod, $uri);
|
||||
if ($routingResults[0] === self::FOUND) {
|
||||
return $routingResults;
|
||||
}
|
||||
|
||||
// For HEAD requests, attempt fallback to GET
|
||||
if ($httpMethod === 'HEAD') {
|
||||
$routingResults = $this->routingResults('GET', $uri);
|
||||
if ($routingResults[0] === self::FOUND) {
|
||||
return $routingResults;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing else matches, try fallback routes
|
||||
$routingResults = $this->routingResults('*', $uri);
|
||||
if ($routingResults[0] === self::FOUND) {
|
||||
return $routingResults;
|
||||
}
|
||||
|
||||
if (!empty($this->getAllowedMethods($uri))) {
|
||||
return [self::METHOD_NOT_ALLOWED, null, []];
|
||||
}
|
||||
|
||||
return [self::NOT_FOUND, null, []];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $httpMethod
|
||||
* @param string $uri
|
||||
*
|
||||
* @return array{int, string|null, array<string, string>}
|
||||
*/
|
||||
private function routingResults(string $httpMethod, string $uri): array
|
||||
{
|
||||
if (isset($this->staticRouteMap[$httpMethod][$uri])) {
|
||||
/** @var string $routeIdentifier */
|
||||
$routeIdentifier = $this->staticRouteMap[$httpMethod][$uri];
|
||||
return [self::FOUND, $routeIdentifier, []];
|
||||
}
|
||||
|
||||
if (isset($this->variableRouteData[$httpMethod])) {
|
||||
/** @var array{0: int, 1?: string, 2?: array<string, string>} $result */
|
||||
$result = $this->dispatchVariableRoute($this->variableRouteData[$httpMethod], $uri);
|
||||
if ($result[0] === self::FOUND) {
|
||||
/** @var array{int, string, array<string, string>} $result */
|
||||
return [self::FOUND, $result[1], $result[2]];
|
||||
}
|
||||
}
|
||||
|
||||
return [self::NOT_FOUND, null, []];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedMethods(string $uri): array
|
||||
{
|
||||
if (isset($this->allowedMethods[$uri])) {
|
||||
return $this->allowedMethods[$uri];
|
||||
}
|
||||
|
||||
$allowedMethods = [];
|
||||
foreach ($this->staticRouteMap as $method => $uriMap) {
|
||||
if (isset($uriMap[$uri])) {
|
||||
$allowedMethods[$method] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->variableRouteData as $method => $routeData) {
|
||||
$result = $this->dispatchVariableRoute($routeData, $uri);
|
||||
if ($result[0] === self::FOUND) {
|
||||
$allowedMethods[$method] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->allowedMethods[$uri] = array_keys($allowedMethods);
|
||||
}
|
||||
}
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Slim\Handlers\Strategies\RequestHandler;
|
||||
use Slim\Handlers\Strategies\RequestResponse;
|
||||
use Slim\Interfaces\AdvancedCallableResolverInterface;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\InvocationStrategyInterface;
|
||||
use Slim\Interfaces\RequestHandlerInvocationStrategyInterface;
|
||||
use Slim\Interfaces\RouteGroupInterface;
|
||||
use Slim\Interfaces\RouteInterface;
|
||||
use Slim\MiddlewareDispatcher;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_replace;
|
||||
use function array_reverse;
|
||||
use function class_implements;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
*/
|
||||
class Route implements RouteInterface, RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* HTTP methods supported by this route
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $methods = [];
|
||||
|
||||
/**
|
||||
* Route identifier
|
||||
*/
|
||||
protected string $identifier;
|
||||
|
||||
/**
|
||||
* Route name
|
||||
*/
|
||||
protected ?string $name = null;
|
||||
|
||||
/**
|
||||
* Parent route groups
|
||||
*
|
||||
* @var RouteGroupInterface[]
|
||||
*/
|
||||
protected array $groups;
|
||||
|
||||
protected InvocationStrategyInterface $invocationStrategy;
|
||||
|
||||
/**
|
||||
* Route parameters
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected array $arguments = [];
|
||||
|
||||
/**
|
||||
* Route arguments parameters
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected array $savedArguments = [];
|
||||
|
||||
/**
|
||||
* Container
|
||||
* @var TContainerInterface $container
|
||||
*/
|
||||
protected ?ContainerInterface $container = null;
|
||||
|
||||
/** @var MiddlewareDispatcher<TContainerInterface> $middlewareDispatcher */
|
||||
protected MiddlewareDispatcher $middlewareDispatcher;
|
||||
|
||||
/**
|
||||
* Route callable
|
||||
*
|
||||
* @var callable|array{class-string, string}|string
|
||||
*/
|
||||
protected $callable;
|
||||
|
||||
protected CallableResolverInterface $callableResolver;
|
||||
|
||||
protected ResponseFactoryInterface $responseFactory;
|
||||
|
||||
/**
|
||||
* Route pattern
|
||||
*/
|
||||
protected string $pattern;
|
||||
|
||||
protected bool $groupMiddlewareAppended = false;
|
||||
|
||||
/**
|
||||
* @param string[] $methods The route HTTP methods
|
||||
* @param string $pattern The route pattern
|
||||
* @param callable|array{class-string, string}|string $callable The route callable
|
||||
* @param ResponseFactoryInterface $responseFactory
|
||||
* @param CallableResolverInterface $callableResolver
|
||||
* @param TContainerInterface $container
|
||||
* @param InvocationStrategyInterface|null $invocationStrategy
|
||||
* @param RouteGroupInterface[] $groups The parent route groups
|
||||
* @param int $identifier The route identifier
|
||||
*/
|
||||
public function __construct(
|
||||
array $methods,
|
||||
string $pattern,
|
||||
$callable,
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
CallableResolverInterface $callableResolver,
|
||||
?ContainerInterface $container = null,
|
||||
?InvocationStrategyInterface $invocationStrategy = null,
|
||||
array $groups = [],
|
||||
int $identifier = 0
|
||||
) {
|
||||
$this->methods = $methods;
|
||||
$this->pattern = $pattern;
|
||||
$this->callable = $callable;
|
||||
$this->responseFactory = $responseFactory;
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->container = $container;
|
||||
$this->invocationStrategy = $invocationStrategy ?? new RequestResponse();
|
||||
$this->groups = $groups;
|
||||
$this->identifier = 'route' . $identifier;
|
||||
$this->middlewareDispatcher = new MiddlewareDispatcher($this, $callableResolver, $container);
|
||||
}
|
||||
|
||||
public function getCallableResolver(): CallableResolverInterface
|
||||
{
|
||||
return $this->callableResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInvocationStrategy(): InvocationStrategyInterface
|
||||
{
|
||||
return $this->invocationStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setInvocationStrategy(InvocationStrategyInterface $invocationStrategy): RouteInterface
|
||||
{
|
||||
$this->invocationStrategy = $invocationStrategy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMethods(): array
|
||||
{
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPattern(): string
|
||||
{
|
||||
return $this->pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPattern(string $pattern): RouteInterface
|
||||
{
|
||||
$this->pattern = $pattern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCallable($callable): RouteInterface
|
||||
{
|
||||
$this->callable = $callable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName(string $name): RouteInterface
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArgument(string $name, ?string $default = null): ?string
|
||||
{
|
||||
if (array_key_exists($name, $this->arguments)) {
|
||||
return $this->arguments[$name];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments, bool $includeInSavedArguments = true): RouteInterface
|
||||
{
|
||||
if ($includeInSavedArguments) {
|
||||
$this->savedArguments = $arguments;
|
||||
}
|
||||
|
||||
$this->arguments = $arguments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RouteGroupInterface[]
|
||||
*/
|
||||
public function getGroups(): array
|
||||
{
|
||||
return $this->groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($middleware): RouteInterface
|
||||
{
|
||||
$this->middlewareDispatcher->add($middleware);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMiddleware(MiddlewareInterface $middleware): RouteInterface
|
||||
{
|
||||
$this->middlewareDispatcher->addMiddleware($middleware);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare(array $arguments): RouteInterface
|
||||
{
|
||||
$this->arguments = array_replace($this->savedArguments, $arguments);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArgument(string $name, string $value, bool $includeInSavedArguments = true): RouteInterface
|
||||
{
|
||||
if ($includeInSavedArguments) {
|
||||
$this->savedArguments[$name] = $value;
|
||||
}
|
||||
|
||||
$this->arguments[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
if (!$this->groupMiddlewareAppended) {
|
||||
$this->appendGroupMiddlewareToRoute();
|
||||
}
|
||||
|
||||
return $this->middlewareDispatcher->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function appendGroupMiddlewareToRoute(): void
|
||||
{
|
||||
$inner = $this->middlewareDispatcher;
|
||||
$this->middlewareDispatcher = new MiddlewareDispatcher($inner, $this->callableResolver, $this->container);
|
||||
|
||||
foreach (array_reverse($this->groups) as $group) {
|
||||
$group->appendMiddlewareToDispatcher($this->middlewareDispatcher);
|
||||
}
|
||||
|
||||
$this->groupMiddlewareAppended = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {
|
||||
$callable = $this->callableResolver->resolveRoute($this->callable);
|
||||
} else {
|
||||
$callable = $this->callableResolver->resolve($this->callable);
|
||||
}
|
||||
$strategy = $this->invocationStrategy;
|
||||
|
||||
$strategyImplements = class_implements($strategy);
|
||||
|
||||
if (
|
||||
is_array($callable)
|
||||
&& $callable[0] instanceof RequestHandlerInterface
|
||||
&& !in_array(RequestHandlerInvocationStrategyInterface::class, $strategyImplements)
|
||||
) {
|
||||
$strategy = new RequestHandler();
|
||||
}
|
||||
|
||||
$response = $this->responseFactory->createResponse();
|
||||
return $strategy($callable, $request, $response, $this->arguments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Handlers\Strategies\RequestResponse;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\InvocationStrategyInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
use Slim\Interfaces\RouteCollectorProxyInterface;
|
||||
use Slim\Interfaces\RouteGroupInterface;
|
||||
use Slim\Interfaces\RouteInterface;
|
||||
use Slim\Interfaces\RouteParserInterface;
|
||||
|
||||
use function array_pop;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function is_readable;
|
||||
use function is_writable;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* RouteCollector is used to collect routes and route groups
|
||||
* as well as generate paths and URLs relative to its environment
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
*/
|
||||
class RouteCollector implements RouteCollectorInterface
|
||||
{
|
||||
protected RouteParserInterface $routeParser;
|
||||
|
||||
protected CallableResolverInterface $callableResolver;
|
||||
|
||||
protected ?ContainerInterface $container = null;
|
||||
|
||||
protected InvocationStrategyInterface $defaultInvocationStrategy;
|
||||
|
||||
/**
|
||||
* Base path used in pathFor()
|
||||
*/
|
||||
protected string $basePath = '';
|
||||
|
||||
/**
|
||||
* Path to fast route cache file. Set to null to disable route caching
|
||||
*/
|
||||
protected ?string $cacheFile = null;
|
||||
|
||||
/**
|
||||
* Routes
|
||||
*
|
||||
* @var RouteInterface[]
|
||||
*/
|
||||
protected array $routes = [];
|
||||
|
||||
/**
|
||||
* Routes indexed by name
|
||||
*
|
||||
* @var RouteInterface[]
|
||||
*/
|
||||
protected array $routesByName = [];
|
||||
|
||||
/**
|
||||
* Route groups
|
||||
*
|
||||
* @var RouteGroupInterface[]
|
||||
*/
|
||||
protected array $routeGroups = [];
|
||||
|
||||
/**
|
||||
* Route counter incrementer
|
||||
*/
|
||||
protected int $routeCounter = 0;
|
||||
|
||||
protected ResponseFactoryInterface $responseFactory;
|
||||
|
||||
/**
|
||||
* @param TContainerInterface $container
|
||||
*/
|
||||
public function __construct(
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
CallableResolverInterface $callableResolver,
|
||||
?ContainerInterface $container = null,
|
||||
?InvocationStrategyInterface $defaultInvocationStrategy = null,
|
||||
?RouteParserInterface $routeParser = null,
|
||||
?string $cacheFile = null
|
||||
) {
|
||||
$this->responseFactory = $responseFactory;
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->container = $container;
|
||||
$this->defaultInvocationStrategy = $defaultInvocationStrategy ?? new RequestResponse();
|
||||
$this->routeParser = $routeParser ?? new RouteParser($this);
|
||||
|
||||
if ($cacheFile) {
|
||||
$this->setCacheFile($cacheFile);
|
||||
}
|
||||
}
|
||||
|
||||
public function getRouteParser(): RouteParserInterface
|
||||
{
|
||||
return $this->routeParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default route invocation strategy
|
||||
*/
|
||||
public function getDefaultInvocationStrategy(): InvocationStrategyInterface
|
||||
{
|
||||
return $this->defaultInvocationStrategy;
|
||||
}
|
||||
|
||||
public function setDefaultInvocationStrategy(InvocationStrategyInterface $strategy): RouteCollectorInterface
|
||||
{
|
||||
$this->defaultInvocationStrategy = $strategy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheFile(): ?string
|
||||
{
|
||||
return $this->cacheFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCacheFile(string $cacheFile): RouteCollectorInterface
|
||||
{
|
||||
if (file_exists($cacheFile) && !is_readable($cacheFile)) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Route collector cache file `%s` is not readable', $cacheFile)
|
||||
);
|
||||
}
|
||||
|
||||
if (!file_exists($cacheFile) && !is_writable(dirname($cacheFile))) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Route collector cache file directory `%s` is not writable', dirname($cacheFile))
|
||||
);
|
||||
}
|
||||
|
||||
$this->cacheFile = $cacheFile;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBasePath(): string
|
||||
{
|
||||
return $this->basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base path used in urlFor()
|
||||
*/
|
||||
public function setBasePath(string $basePath): RouteCollectorInterface
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes(): array
|
||||
{
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeNamedRoute(string $name): RouteCollectorInterface
|
||||
{
|
||||
$route = $this->getNamedRoute($name);
|
||||
|
||||
/** @psalm-suppress PossiblyNullArrayOffset */
|
||||
unset($this->routesByName[$route->getName()], $this->routes[$route->getIdentifier()]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNamedRoute(string $name): RouteInterface
|
||||
{
|
||||
if (isset($this->routesByName[$name])) {
|
||||
$route = $this->routesByName[$name];
|
||||
if ($route->getName() === $name) {
|
||||
return $route;
|
||||
}
|
||||
|
||||
unset($this->routesByName[$name]);
|
||||
}
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
if ($name === $route->getName()) {
|
||||
$this->routesByName[$name] = $route;
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException('Named route does not exist for name: ' . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lookupRoute(string $identifier): RouteInterface
|
||||
{
|
||||
if (!isset($this->routes[$identifier])) {
|
||||
throw new RuntimeException('Route not found, looks like your route cache is stale.');
|
||||
}
|
||||
return $this->routes[$identifier];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function group(string $pattern, $callable): RouteGroupInterface
|
||||
{
|
||||
$routeGroup = $this->createGroup($pattern, $callable);
|
||||
$this->routeGroups[] = $routeGroup;
|
||||
|
||||
$routeGroup->collectRoutes();
|
||||
array_pop($this->routeGroups);
|
||||
|
||||
return $routeGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|callable $callable
|
||||
*/
|
||||
protected function createGroup(string $pattern, $callable): RouteGroupInterface
|
||||
{
|
||||
$routeCollectorProxy = $this->createProxy($pattern);
|
||||
return new RouteGroup($pattern, $callable, $this->callableResolver, $routeCollectorProxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RouteCollectorProxyInterface<TContainerInterface>
|
||||
*/
|
||||
protected function createProxy(string $pattern): RouteCollectorProxyInterface
|
||||
{
|
||||
/** @var RouteCollectorProxy<TContainerInterface> */
|
||||
return new RouteCollectorProxy(
|
||||
$this->responseFactory,
|
||||
$this->callableResolver,
|
||||
$this->container,
|
||||
$this,
|
||||
$pattern
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function map(array $methods, string $pattern, $handler): RouteInterface
|
||||
{
|
||||
$route = $this->createRoute($methods, $pattern, $handler);
|
||||
$this->routes[$route->getIdentifier()] = $route;
|
||||
|
||||
$routeName = $route->getName();
|
||||
if ($routeName !== null && !isset($this->routesByName[$routeName])) {
|
||||
$this->routesByName[$routeName] = $route;
|
||||
}
|
||||
|
||||
$this->routeCounter++;
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @param callable|array{class-string, string}|string $callable
|
||||
*/
|
||||
protected function createRoute(array $methods, string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return new Route(
|
||||
$methods,
|
||||
$pattern,
|
||||
$callable,
|
||||
$this->responseFactory,
|
||||
$this->callableResolver,
|
||||
$this->container,
|
||||
$this->defaultInvocationStrategy,
|
||||
$this->routeGroups,
|
||||
$this->routeCounter
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
use Slim\Interfaces\RouteCollectorProxyInterface;
|
||||
use Slim\Interfaces\RouteGroupInterface;
|
||||
use Slim\Interfaces\RouteInterface;
|
||||
|
||||
/**
|
||||
* @template TContainerInterface of (ContainerInterface|null)
|
||||
* @template-implements RouteCollectorProxyInterface<TContainerInterface>
|
||||
*/
|
||||
class RouteCollectorProxy implements RouteCollectorProxyInterface
|
||||
{
|
||||
protected ResponseFactoryInterface $responseFactory;
|
||||
|
||||
protected CallableResolverInterface $callableResolver;
|
||||
|
||||
/** @var TContainerInterface */
|
||||
protected ?ContainerInterface $container = null;
|
||||
|
||||
protected RouteCollectorInterface $routeCollector;
|
||||
|
||||
protected string $groupPattern;
|
||||
|
||||
/**
|
||||
* @param TContainerInterface $container
|
||||
*/
|
||||
public function __construct(
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
CallableResolverInterface $callableResolver,
|
||||
?ContainerInterface $container = null,
|
||||
?RouteCollectorInterface $routeCollector = null,
|
||||
string $groupPattern = ''
|
||||
) {
|
||||
$this->responseFactory = $responseFactory;
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->container = $container;
|
||||
$this->routeCollector = $routeCollector ?? new RouteCollector($responseFactory, $callableResolver, $container);
|
||||
$this->groupPattern = $groupPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResponseFactory(): ResponseFactoryInterface
|
||||
{
|
||||
return $this->responseFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCallableResolver(): CallableResolverInterface
|
||||
{
|
||||
return $this->callableResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return TContainerInterface
|
||||
*/
|
||||
public function getContainer(): ?ContainerInterface
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRouteCollector(): RouteCollectorInterface
|
||||
{
|
||||
return $this->routeCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBasePath(): string
|
||||
{
|
||||
return $this->routeCollector->getBasePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setBasePath(string $basePath): RouteCollectorProxyInterface
|
||||
{
|
||||
$this->routeCollector->setBasePath($basePath);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['GET'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function post(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['POST'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function put(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['PUT'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function patch(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['PATCH'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['DELETE'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function options(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['OPTIONS'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function any(string $pattern, $callable): RouteInterface
|
||||
{
|
||||
return $this->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function map(array $methods, string $pattern, $callable): RouteInterface
|
||||
{
|
||||
$pattern = $this->groupPattern . $pattern;
|
||||
|
||||
return $this->routeCollector->map($methods, $pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function group(string $pattern, $callable): RouteGroupInterface
|
||||
{
|
||||
$pattern = $this->groupPattern . $pattern;
|
||||
|
||||
return $this->routeCollector->group($pattern, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function redirect(string $from, $to, int $status = 302): RouteInterface
|
||||
{
|
||||
$responseFactory = $this->responseFactory;
|
||||
|
||||
$handler = function () use ($to, $status, $responseFactory) {
|
||||
$response = $responseFactory->createResponse($status);
|
||||
return $response->withHeader('Location', (string) $to);
|
||||
};
|
||||
|
||||
return $this->get($from, $handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RuntimeException;
|
||||
use Slim\Interfaces\RouteInterface;
|
||||
use Slim\Interfaces\RouteParserInterface;
|
||||
|
||||
/** @api */
|
||||
final class RouteContext
|
||||
{
|
||||
public const ROUTE = '__route__';
|
||||
|
||||
public const ROUTE_PARSER = '__routeParser__';
|
||||
|
||||
public const ROUTING_RESULTS = '__routingResults__';
|
||||
|
||||
public const BASE_PATH = '__basePath__';
|
||||
|
||||
public static function fromRequest(ServerRequestInterface $serverRequest): self
|
||||
{
|
||||
$route = $serverRequest->getAttribute(self::ROUTE);
|
||||
$routeParser = $serverRequest->getAttribute(self::ROUTE_PARSER);
|
||||
$routingResults = $serverRequest->getAttribute(self::ROUTING_RESULTS);
|
||||
$basePath = $serverRequest->getAttribute(self::BASE_PATH);
|
||||
|
||||
if ($routeParser === null || $routingResults === null) {
|
||||
throw new RuntimeException('Cannot create RouteContext before routing has been completed');
|
||||
}
|
||||
|
||||
/** @var RouteInterface|null $route */
|
||||
/** @var RouteParserInterface $routeParser */
|
||||
/** @var RoutingResults $routingResults */
|
||||
/** @var string|null $basePath */
|
||||
return new self($route, $routeParser, $routingResults, $basePath);
|
||||
}
|
||||
|
||||
private ?RouteInterface $route;
|
||||
|
||||
private RouteParserInterface $routeParser;
|
||||
|
||||
private RoutingResults $routingResults;
|
||||
|
||||
private ?string $basePath;
|
||||
|
||||
private function __construct(
|
||||
?RouteInterface $route,
|
||||
RouteParserInterface $routeParser,
|
||||
RoutingResults $routingResults,
|
||||
?string $basePath = null
|
||||
) {
|
||||
$this->route = $route;
|
||||
$this->routeParser = $routeParser;
|
||||
$this->routingResults = $routingResults;
|
||||
$this->basePath = $basePath;
|
||||
}
|
||||
|
||||
public function getRoute(): ?RouteInterface
|
||||
{
|
||||
return $this->route;
|
||||
}
|
||||
|
||||
public function getRouteParser(): RouteParserInterface
|
||||
{
|
||||
return $this->routeParser;
|
||||
}
|
||||
|
||||
public function getRoutingResults(): RoutingResults
|
||||
{
|
||||
return $this->routingResults;
|
||||
}
|
||||
|
||||
public function getBasePath(): string
|
||||
{
|
||||
if ($this->basePath === null) {
|
||||
throw new RuntimeException('No base path defined.');
|
||||
}
|
||||
return $this->basePath;
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Slim\Interfaces\AdvancedCallableResolverInterface;
|
||||
use Slim\Interfaces\CallableResolverInterface;
|
||||
use Slim\Interfaces\RouteCollectorProxyInterface;
|
||||
use Slim\Interfaces\RouteGroupInterface;
|
||||
use Slim\MiddlewareDispatcher;
|
||||
|
||||
class RouteGroup implements RouteGroupInterface
|
||||
{
|
||||
/**
|
||||
* @var callable|string
|
||||
*/
|
||||
protected $callable;
|
||||
|
||||
protected CallableResolverInterface $callableResolver;
|
||||
|
||||
/**
|
||||
* @var RouteCollectorProxyInterface<\Psr\Container\ContainerInterface|null>
|
||||
*/
|
||||
protected RouteCollectorProxyInterface $routeCollectorProxy;
|
||||
|
||||
/**
|
||||
* @var MiddlewareInterface[]|string[]|callable[]
|
||||
*/
|
||||
protected array $middleware = [];
|
||||
|
||||
protected string $pattern;
|
||||
|
||||
/**
|
||||
* @param callable|string $callable
|
||||
* @param RouteCollectorProxyInterface<\Psr\Container\ContainerInterface|null> $routeCollectorProxy
|
||||
*/
|
||||
public function __construct(
|
||||
string $pattern,
|
||||
$callable,
|
||||
CallableResolverInterface $callableResolver,
|
||||
RouteCollectorProxyInterface $routeCollectorProxy
|
||||
) {
|
||||
$this->pattern = $pattern;
|
||||
$this->callable = $callable;
|
||||
$this->callableResolver = $callableResolver;
|
||||
$this->routeCollectorProxy = $routeCollectorProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectRoutes(): RouteGroupInterface
|
||||
{
|
||||
if ($this->callableResolver instanceof AdvancedCallableResolverInterface) {
|
||||
$callable = $this->callableResolver->resolveRoute($this->callable);
|
||||
} else {
|
||||
$callable = $this->callableResolver->resolve($this->callable);
|
||||
}
|
||||
$callable($this->routeCollectorProxy);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($middleware): RouteGroupInterface
|
||||
{
|
||||
$this->middleware[] = $middleware;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMiddleware(MiddlewareInterface $middleware): RouteGroupInterface
|
||||
{
|
||||
$this->middleware[] = $middleware;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param MiddlewareDispatcher<\Psr\Container\ContainerInterface|null> $dispatcher
|
||||
*/
|
||||
public function appendMiddlewareToDispatcher(MiddlewareDispatcher $dispatcher): RouteGroupInterface
|
||||
{
|
||||
foreach ($this->middleware as $middleware) {
|
||||
$dispatcher->add($middleware);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPattern(): string
|
||||
{
|
||||
return $this->pattern;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use FastRoute\RouteParser\Std;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
use Slim\Interfaces\RouteParserInterface;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_reverse;
|
||||
use function http_build_query;
|
||||
use function implode;
|
||||
use function is_string;
|
||||
|
||||
class RouteParser implements RouteParserInterface
|
||||
{
|
||||
private RouteCollectorInterface $routeCollector;
|
||||
|
||||
private Std $routeParser;
|
||||
|
||||
public function __construct(RouteCollectorInterface $routeCollector)
|
||||
{
|
||||
$this->routeCollector = $routeCollector;
|
||||
$this->routeParser = new Std();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function relativeUrlFor(string $routeName, array $data = [], array $queryParams = []): string
|
||||
{
|
||||
$route = $this->routeCollector->getNamedRoute($routeName);
|
||||
$pattern = $route->getPattern();
|
||||
|
||||
$segments = [];
|
||||
$segmentName = '';
|
||||
|
||||
/*
|
||||
* $routes is an associative array of expressions representing a route as multiple segments
|
||||
* There is an expression for each optional parameter plus one without the optional parameters
|
||||
* The most specific is last, hence why we reverse the array before iterating over it
|
||||
*/
|
||||
$expressions = array_reverse($this->routeParser->parse($pattern));
|
||||
foreach ($expressions as $expression) {
|
||||
foreach ($expression as $segment) {
|
||||
/*
|
||||
* Each $segment is either a string or an array of strings
|
||||
* containing optional parameters of an expression
|
||||
*/
|
||||
if (is_string($segment)) {
|
||||
$segments[] = $segment;
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string[] $segment */
|
||||
/*
|
||||
* If we don't have a data element for this segment in the provided $data
|
||||
* we cancel testing to move onto the next expression with a less specific item
|
||||
*/
|
||||
if (!array_key_exists($segment[0], $data)) {
|
||||
$segments = [];
|
||||
$segmentName = $segment[0];
|
||||
break;
|
||||
}
|
||||
|
||||
$segments[] = $data[$segment[0]];
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get to this logic block we have found all the parameters
|
||||
* for the provided $data which means we don't need to continue testing
|
||||
* less specific expressions
|
||||
*/
|
||||
if (!empty($segments)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($segments)) {
|
||||
throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
|
||||
}
|
||||
|
||||
$url = implode('', $segments);
|
||||
if ($queryParams) {
|
||||
$url .= '?' . http_build_query($queryParams);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function urlFor(string $routeName, array $data = [], array $queryParams = []): string
|
||||
{
|
||||
$basePath = $this->routeCollector->getBasePath();
|
||||
$url = $this->relativeUrlFor($routeName, $data, $queryParams);
|
||||
|
||||
if ($basePath) {
|
||||
$url = $basePath . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string
|
||||
{
|
||||
$path = $this->urlFor($routeName, $data, $queryParams);
|
||||
$scheme = $uri->getScheme();
|
||||
$authority = $uri->getAuthority();
|
||||
$protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : '');
|
||||
return $protocol . $path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use RuntimeException;
|
||||
use Slim\Interfaces\DispatcherInterface;
|
||||
use Slim\Interfaces\RouteCollectorInterface;
|
||||
use Slim\Interfaces\RouteInterface;
|
||||
use Slim\Interfaces\RouteResolverInterface;
|
||||
|
||||
use function rawurldecode;
|
||||
|
||||
/**
|
||||
* RouteResolver instantiates the FastRoute dispatcher
|
||||
* and computes the routing results of a given URI and request method
|
||||
*/
|
||||
class RouteResolver implements RouteResolverInterface
|
||||
{
|
||||
protected RouteCollectorInterface $routeCollector;
|
||||
|
||||
private DispatcherInterface $dispatcher;
|
||||
|
||||
public function __construct(RouteCollectorInterface $routeCollector, ?DispatcherInterface $dispatcher = null)
|
||||
{
|
||||
$this->routeCollector = $routeCollector;
|
||||
$this->dispatcher = $dispatcher ?? new Dispatcher($routeCollector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri Should be $request->getUri()->getPath()
|
||||
*/
|
||||
public function computeRoutingResults(string $uri, string $method): RoutingResults
|
||||
{
|
||||
$uri = rawurldecode($uri);
|
||||
if ($uri === '' || $uri[0] !== '/') {
|
||||
$uri = '/' . $uri;
|
||||
}
|
||||
return $this->dispatcher->dispatch($method, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function resolveRoute(string $identifier): RouteInterface
|
||||
{
|
||||
return $this->routeCollector->lookupRoute($identifier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Slim\Exception\HttpMethodNotAllowedException;
|
||||
use Slim\Exception\HttpNotFoundException;
|
||||
use Slim\Interfaces\RouteCollectorProxyInterface;
|
||||
use Slim\Interfaces\RouteParserInterface;
|
||||
use Slim\Interfaces\RouteResolverInterface;
|
||||
use Slim\Middleware\RoutingMiddleware;
|
||||
|
||||
class RouteRunner implements RequestHandlerInterface
|
||||
{
|
||||
private RouteResolverInterface $routeResolver;
|
||||
|
||||
private RouteParserInterface $routeParser;
|
||||
|
||||
/**
|
||||
* @var RouteCollectorProxyInterface<\Psr\Container\ContainerInterface|null>
|
||||
*/
|
||||
private ?RouteCollectorProxyInterface $routeCollectorProxy;
|
||||
|
||||
/**
|
||||
* @param RouteCollectorProxyInterface<\Psr\Container\ContainerInterface|null> $routeCollectorProxy
|
||||
*/
|
||||
public function __construct(
|
||||
RouteResolverInterface $routeResolver,
|
||||
RouteParserInterface $routeParser,
|
||||
?RouteCollectorProxyInterface $routeCollectorProxy = null
|
||||
) {
|
||||
$this->routeResolver = $routeResolver;
|
||||
$this->routeParser = $routeParser;
|
||||
$this->routeCollectorProxy = $routeCollectorProxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* This request handler is instantiated automatically in App::__construct()
|
||||
* It is at the very tip of the middleware queue meaning it will be executed
|
||||
* last and it detects whether or not routing has been performed in the user
|
||||
* defined middleware stack. In the event that the user did not perform routing
|
||||
* it is done here
|
||||
*
|
||||
* @throws HttpNotFoundException
|
||||
* @throws HttpMethodNotAllowedException
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
// If routing hasn't been done, then do it now so we can dispatch
|
||||
if ($request->getAttribute(RouteContext::ROUTING_RESULTS) === null) {
|
||||
$routingMiddleware = new RoutingMiddleware($this->routeResolver, $this->routeParser);
|
||||
$request = $routingMiddleware->performRouting($request);
|
||||
}
|
||||
|
||||
if ($this->routeCollectorProxy !== null) {
|
||||
$request = $request->withAttribute(
|
||||
RouteContext::BASE_PATH,
|
||||
$this->routeCollectorProxy->getBasePath()
|
||||
);
|
||||
}
|
||||
|
||||
/** @var Route<\Psr\Container\ContainerInterface|null> $route */
|
||||
$route = $request->getAttribute(RouteContext::ROUTE);
|
||||
return $route->run($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Slim Framework (https://slimframework.com)
|
||||
*
|
||||
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Slim\Routing;
|
||||
|
||||
use Slim\Interfaces\DispatcherInterface;
|
||||
|
||||
use function rawurldecode;
|
||||
|
||||
/** @api */
|
||||
class RoutingResults
|
||||
{
|
||||
public const NOT_FOUND = 0;
|
||||
public const FOUND = 1;
|
||||
public const METHOD_NOT_ALLOWED = 2;
|
||||
|
||||
protected DispatcherInterface $dispatcher;
|
||||
|
||||
protected string $method;
|
||||
|
||||
protected string $uri;
|
||||
|
||||
/**
|
||||
* The status is one of the constants shown above
|
||||
* NOT_FOUND = 0
|
||||
* FOUND = 1
|
||||
* METHOD_NOT_ALLOWED = 2
|
||||
*/
|
||||
protected int $routeStatus;
|
||||
|
||||
protected ?string $routeIdentifier = null;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected array $routeArguments;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $routeArguments
|
||||
*/
|
||||
public function __construct(
|
||||
DispatcherInterface $dispatcher,
|
||||
string $method,
|
||||
string $uri,
|
||||
int $routeStatus,
|
||||
?string $routeIdentifier = null,
|
||||
array $routeArguments = []
|
||||
) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->method = $method;
|
||||
$this->uri = $uri;
|
||||
$this->routeStatus = $routeStatus;
|
||||
$this->routeIdentifier = $routeIdentifier;
|
||||
$this->routeArguments = $routeArguments;
|
||||
}
|
||||
|
||||
public function getDispatcher(): DispatcherInterface
|
||||
{
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function getUri(): string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function getRouteStatus(): int
|
||||
{
|
||||
return $this->routeStatus;
|
||||
}
|
||||
|
||||
public function getRouteIdentifier(): ?string
|
||||
{
|
||||
return $this->routeIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getRouteArguments(bool $urlDecode = true): array
|
||||
{
|
||||
if (!$urlDecode) {
|
||||
return $this->routeArguments;
|
||||
}
|
||||
|
||||
$routeArguments = [];
|
||||
foreach ($this->routeArguments as $key => $value) {
|
||||
$routeArguments[$key] = rawurldecode($value);
|
||||
}
|
||||
|
||||
return $routeArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedMethods(): array
|
||||
{
|
||||
return $this->dispatcher->getAllowedMethods($this->uri);
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"type": "library",
|
||||
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
|
||||
"keywords": ["framework","micro","api","router"],
|
||||
"homepage": "https://www.slimframework.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Josh Lockhart",
|
||||
"email": "hello@joshlockhart.com",
|
||||
"homepage": "https://joshlockhart.com"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Smith",
|
||||
"email": "a.smith@silentworks.co.uk",
|
||||
"homepage": "https://silentworks.co.uk"
|
||||
},
|
||||
{
|
||||
"name": "Rob Allen",
|
||||
"email": "rob@akrabat.com",
|
||||
"homepage": "https://akrabat.com"
|
||||
},
|
||||
{
|
||||
"name": "Pierre Berube",
|
||||
"email": "pierre@lgse.com",
|
||||
"homepage": "https://www.lgse.com"
|
||||
},
|
||||
{
|
||||
"name": "Gabriel Manricks",
|
||||
"email": "gmanricks@me.com",
|
||||
"homepage": "http://gabrielmanricks.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://www.slimframework.com/docs/v4/",
|
||||
"forum": "https://discourse.slimframework.com/",
|
||||
"irc": "irc://irc.freenode.net:6667/slimphp",
|
||||
"issues": "https://github.com/slimphp/Slim/issues",
|
||||
"rss": "https://www.slimframework.com/blog/feed.rss",
|
||||
"slack": "https://slimphp.slack.com/",
|
||||
"source": "https://github.com/slimphp/Slim",
|
||||
"wiki": "https://github.com/slimphp/Slim/wiki"
|
||||
},
|
||||
"require": {
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"ext-json": "*",
|
||||
"nikic/fast-route": "^1.3",
|
||||
"psr/container": "^1.0 || ^2.0",
|
||||
"psr/http-factory": "^1.1",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"psr/http-server-handler": "^1.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-simplexml": "*",
|
||||
"adriansuter/php-autoload-override": "^1.4 || ^2",
|
||||
"guzzlehttp/psr7": "^2.6",
|
||||
"httpsoft/http-message": "^1.1",
|
||||
"httpsoft/http-server-request": "^1.1",
|
||||
"laminas/laminas-diactoros": "^2.17 || ^3",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"nyholm/psr7-server": "^1.1",
|
||||
"phpspec/prophecy": "^1.19",
|
||||
"phpspec/prophecy-phpunit": "^2.1",
|
||||
"phpstan/phpstan": "^1 || ^2",
|
||||
"phpunit/phpunit": "^9.6 || ^10 || ^11 || ^12",
|
||||
"slim/http": "^1.3",
|
||||
"slim/psr7": "^1.6",
|
||||
"squizlabs/php_codesniffer": "^3.10",
|
||||
"vimeo/psalm": "^5 || ^6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Slim\\": "Slim"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Slim\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"@phpunit",
|
||||
"@phpcs",
|
||||
"@phpstan",
|
||||
"@psalm"
|
||||
],
|
||||
"phpunit": "phpunit",
|
||||
"phpcs": "phpcs",
|
||||
"phpstan": "phpstan --memory-limit=-1",
|
||||
"psalm": "psalm --no-cache"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-simplexml": "Needed to support XML format in BodyParsingMiddleware",
|
||||
"ext-xml": "Needed to support XML format in BodyParsingMiddleware",
|
||||
"slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information.",
|
||||
"php-di/php-di": "PHP-DI is the recommended container library to be used with Slim"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user