diff --git a/README.md b/README.md index 65ea6de..37ec4ae 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ $r->addRoute('POST', '/test', 'handler'); $r->addRoute(['GET', 'POST'], '/test', 'handler'); ``` -By default the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo` +By default, the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo` and matching the regex `[^/]+`. To adjust the pattern the placeholder matches, you can specify a custom pattern by writing `{bar:[0-9]+}`. Some examples: @@ -102,7 +102,7 @@ Custom patterns for route placeholders cannot use capturing groups. For example is not a valid placeholder, because `()` is a capturing group. Instead you can use either `{lang:en|de}` or `{lang:(?:en|de)}`. -Furthermore parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]` +Furthermore, parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]` will match both `/foo` and `/foobar`. Optional parts are only supported in a trailing position, not in the middle of a route. @@ -142,7 +142,7 @@ $r->addRoute('POST', '/post-route', 'post_handler'); #### Route Groups -Additionally, you can specify routes inside of a group. All routes defined inside a group will have a common prefix. +Additionally, you can specify routes inside a group. All routes defined inside a group will have a common prefix. For example, defining your routes as: @@ -178,13 +178,15 @@ $dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) { $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); $r->addRoute('GET', '/user/{name}', 'handler2'); }, [ - 'cacheFile' => __DIR__ . '/route.cache', /* required */ + 'cacheKey' => __DIR__ . '/route.cache', /* required */ + // 'cacheFile' => __DIR__ . '/route.cache', /* will still work for v1 compatibility */ 'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default */ + 'cacheDriver' => FastRoute\Cache\FileCache::class, /* optional, class name or instance of the cache driver - defaults to file cache */ ]); ``` The second parameter to the function is an options array, which can be used to specify the cache -file location, among other things. +key (e.g. file location when using files for caching), caching driver, among other things. ### Dispatching a URI @@ -237,7 +239,7 @@ interface Dispatcher { ``` The route parser takes a route pattern string and converts it into an array of route infos, where -each route info is again an array of it's parts. The structure is best understood using an example: +each route info is again an array of its parts. The structure is best understood using an example: /* The route /user/{id:\d+}[/{name}] converts to the following array: */ [ diff --git a/composer.json b/composer.json index 81ee506..e7beb37 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ } ], "require": { - "php": ">=8.1.0" + "php": ">=8.1.0", + "psr/simple-cache": "^2.0 || ^3.0" }, "require-dev": { "lcobucci/coding-standard": "^11.0", @@ -25,9 +26,6 @@ "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "^10.3" }, - "suggest": { - "ext-apcu": "To be able to use APCu cache driver" - }, "autoload": { "psr-4": { "FastRoute\\": "src/" diff --git a/composer.lock b/composer.lock index 1edb455..fcb4329 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,60 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "15caf1db569069edcb6962bf2579bf95", - "packages": [], + "content-hash": "33ff31143ee093ff78a64d215f60180e", + "packages": [ + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + } + ], "packages-dev": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -404,25 +456,27 @@ }, { "name": "nikic/php-parser", - "version": "v4.17.1", + "version": "v5.0.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc", + "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -430,7 +484,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -454,9 +508,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0" }, - "time": "2023-08-13T19:53:39+00:00" + "time": "2024-01-07T17:17:35+00:00" }, { "name": "phar-io/manifest", @@ -571,21 +625,21 @@ }, { "name": "phpbench/container", - "version": "2.2.1", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", - "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392" + "reference": "a59b929e00b87b532ca6d0edd8eca0967655af33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/container/zipball/6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", - "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", + "url": "https://api.github.com/repos/phpbench/container/zipball/a59b929e00b87b532ca6d0edd8eca0967655af33", + "reference": "a59b929e00b87b532ca6d0edd8eca0967655af33", "shasum": "" }, "require": { "psr/container": "^1.0|^2.0", - "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0" + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.16", @@ -616,9 +670,9 @@ "description": "Simple, configurable, service container.", "support": { "issues": "https://github.com/phpbench/container/issues", - "source": "https://github.com/phpbench/container/tree/2.2.1" + "source": "https://github.com/phpbench/container/tree/2.2.2" }, - "time": "2022-01-25T10:17:35+00:00" + "time": "2023-10-30T13:38:26+00:00" }, { "name": "phpbench/dom", @@ -673,49 +727,50 @@ }, { "name": "phpbench/phpbench", - "version": "1.2.14", + "version": "1.2.15", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "edbd1b7ecf704eb01f7a2bcd1b8aa8c189f9fa4e" + "reference": "f7000319695cfad04a57fc64bf7ef7abdf4c437c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/edbd1b7ecf704eb01f7a2bcd1b8aa8c189f9fa4e", - "reference": "edbd1b7ecf704eb01f7a2bcd1b8aa8c189f9fa4e", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/f7000319695cfad04a57fc64bf7ef7abdf4c437c", + "reference": "f7000319695cfad04a57fc64bf7ef7abdf4c437c", "shasum": "" }, "require": { - "doctrine/annotations": "^1.13 || ^2.0", + "doctrine/annotations": "^2.0", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", "ext-tokenizer": "*", - "php": "^7.4 || ^8.0", + "php": "^8.1", "phpbench/container": "^2.1", "phpbench/dom": "~0.3.3", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", - "symfony/console": "^4.2 || ^5.0 || ^6.0", - "symfony/filesystem": "^4.2 || ^5.0 || ^6.0", - "symfony/finder": "^4.2 || ^5.0 || ^6.0", - "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0", - "symfony/process": "^4.2 || ^5.0 || ^6.0", + "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/finder": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/process": "^4.2 || ^5.0 || ^6.0 || ^7.0", "webmozart/glob": "^4.6" }, "require-dev": { "dantleech/invoke": "^2.0", "friendsofphp/php-cs-fixer": "^3.0", "jangregor/phpstan-prophecy": "^1.0", - "phpspec/prophecy": "^1.12", + "phpspec/prophecy": "dev-master", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.0", - "symfony/error-handler": "^5.2 || ^6.0", - "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0" + "phpunit/phpunit": "^10.0", + "rector/rector": "^0.18.10", + "symfony/error-handler": "^5.2 || ^6.0 || ^7.0", + "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "suggest": { "ext-xdebug": "For Xdebug profiling extension." @@ -749,9 +804,16 @@ } ], "description": "PHP Benchmarking Framework", + "keywords": [ + "benchmarking", + "optimization", + "performance", + "profiling", + "testing" + ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.2.14" + "source": "https://github.com/phpbench/phpbench/tree/1.2.15" }, "funding": [ { @@ -759,7 +821,7 @@ "type": "github" } ], - "time": "2023-07-09T09:16:08+00:00" + "time": "2023-11-29T12:21:11+00:00" }, { "name": "phpstan/extension-installer", @@ -807,16 +869,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.24.1", + "version": "1.25.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9f854d275c2dbf84915a5c0ec9a2d17d2cd86b01" + "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9f854d275c2dbf84915a5c0ec9a2d17d2cd86b01", - "reference": "9f854d275c2dbf84915a5c0ec9a2d17d2cd86b01", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240", + "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240", "shasum": "" }, "require": { @@ -848,22 +910,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0" }, - "time": "2023-09-18T12:18:02+00:00" + "time": "2024-01-04T17:06:16+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.35", + "version": "1.10.57", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3" + "reference": "1627b1d03446904aaa77593f370c5201d2ecc34e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e730e5facb75ffe09dfb229795e8c01a459f26c3", - "reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1627b1d03446904aaa77593f370c5201d2ecc34e", + "reference": "1627b1d03446904aaa77593f370c5201d2ecc34e", "shasum": "" }, "require": { @@ -912,7 +974,7 @@ "type": "tidelift" } ], - "time": "2023-09-19T15:27:56+00:00" + "time": "2024-01-24T11:51:34+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -964,16 +1026,16 @@ }, { "name": "phpstan/phpstan-phpunit", - "version": "1.3.14", + "version": "1.3.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "614acc10c522e319639bf38b0698a4a566665f04" + "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/614acc10c522e319639bf38b0698a4a566665f04", - "reference": "614acc10c522e319639bf38b0698a4a566665f04", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", + "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", "shasum": "" }, "require": { @@ -1010,27 +1072,27 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.14" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15" }, - "time": "2023-08-25T09:46:39+00:00" + "time": "2023-10-09T18:58:39+00:00" }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.5.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6" + "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6", - "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542", + "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10.34" }, "require-dev": { "nikic/php-parser": "^4.13.0", @@ -1059,29 +1121,29 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2" }, - "time": "2023-03-29T14:47:40+00:00" + "time": "2023-10-30T14:35:06+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.6", + "version": "10.1.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "56f33548fe522c8d82da7ff3824b42829d324364" + "reference": "78c3b7625965c2513ee96569a4dbb62601784145" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364", - "reference": "56f33548fe522c8d82da7ff3824b42829d324364", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145", + "reference": "78c3b7625965c2513ee96569a4dbb62601784145", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1", "phpunit/php-file-iterator": "^4.0", "phpunit/php-text-template": "^3.0", @@ -1131,7 +1193,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11" }, "funding": [ { @@ -1139,7 +1201,7 @@ "type": "github" } ], - "time": "2023-09-19T04:59:03+00:00" + "time": "2023-12-21T15:38:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1386,16 +1448,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.3.5", + "version": "10.5.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503" + "reference": "0bd663704f0165c9e76fe4f06ffa6a1ca727fdbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503", - "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0bd663704f0165c9e76fe4f06ffa6a1ca727fdbe", + "reference": "0bd663704f0165c9e76fe4f06ffa6a1ca727fdbe", "shasum": "" }, "require": { @@ -1435,7 +1497,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.3-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -1467,7 +1529,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.9" }, "funding": [ { @@ -1483,7 +1545,7 @@ "type": "tidelift" } ], - "time": "2023-09-19T05:42:37+00:00" + "time": "2024-01-22T14:35:40+00:00" }, { "name": "psr/cache", @@ -1883,20 +1945,20 @@ }, { "name": "sebastian/complexity", - "version": "3.0.1", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -1905,7 +1967,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -1929,7 +1991,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -1937,20 +1999,20 @@ "type": "github" } ], - "time": "2023-08-31T09:55:53+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", - "version": "5.0.3", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f", "shasum": "" }, "require": { @@ -1963,7 +2025,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -1996,7 +2058,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0" }, "funding": [ { @@ -2004,7 +2066,7 @@ "type": "github" } ], - "time": "2023-05-01T07:48:21+00:00" + "time": "2023-12-22T10:55:06+00:00" }, { "name": "sebastian/environment", @@ -2072,16 +2134,16 @@ }, { "name": "sebastian/exporter", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344" + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c3fa8483f9539b190f7cd4bfc4a07631dd1df344", - "reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", "shasum": "" }, "require": { @@ -2095,7 +2157,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -2138,7 +2200,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" }, "funding": [ { @@ -2146,7 +2208,7 @@ "type": "github" } ], - "time": "2023-09-18T07:15:37+00:00" + "time": "2023-09-24T13:22:09+00:00" }, { "name": "sebastian/global-state", @@ -2212,20 +2274,20 @@ }, { "name": "sebastian/lines-of-code", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -2258,7 +2320,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -2266,7 +2328,7 @@ "type": "github" } ], - "time": "2023-08-31T09:25:50+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", @@ -2554,16 +2616,16 @@ }, { "name": "seld/jsonlint", - "version": "1.10.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" + "reference": "76d449a358ece77d6f1d6331c68453e657172202" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", - "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/76d449a358ece77d6f1d6331c68453e657172202", + "reference": "76d449a358ece77d6f1d6331c68453e657172202", "shasum": "" }, "require": { @@ -2590,7 +2652,7 @@ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "homepage": "https://seld.be" } ], "description": "JSON Linter", @@ -2602,7 +2664,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.1" }, "funding": [ { @@ -2614,36 +2676,36 @@ "type": "tidelift" } ], - "time": "2023-05-11T13:16:46+00:00" + "time": "2023-12-18T13:03:25+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.13.4", + "version": "8.14.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "4b2af2fb17773656d02fbfb5d18024ebd19fe322" + "reference": "fea1fd6f137cc84f9cba0ae30d549615dbc6a926" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/4b2af2fb17773656d02fbfb5d18024ebd19fe322", - "reference": "4b2af2fb17773656d02fbfb5d18024ebd19fe322", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/fea1fd6f137cc84f9cba0ae30d549615dbc6a926", + "reference": "fea1fd6f137cc84f9cba0ae30d549615dbc6a926", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": "^1.23.0", + "phpstan/phpdoc-parser": "^1.23.1", "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.10.26", - "phpstan/phpstan-deprecation-rules": "1.1.3", - "phpstan/phpstan-phpunit": "1.3.13", + "phpstan/phpstan": "1.10.37", + "phpstan/phpstan-deprecation-rules": "1.1.4", + "phpstan/phpstan-phpunit": "1.3.14", "phpstan/phpstan-strict-rules": "1.5.1", - "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.2.6" + "phpunit/phpunit": "8.5.21|9.6.8|10.3.5" }, "type": "phpcodesniffer-standard", "extra": { @@ -2667,7 +2729,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.13.4" + "source": "https://github.com/slevomat/coding-standard/tree/8.14.1" }, "funding": [ { @@ -2679,20 +2741,20 @@ "type": "tidelift" } ], - "time": "2023-07-25T10:28:55+00:00" + "time": "2023-10-08T07:28:08+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.2", + "version": "3.8.1", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "14f5fff1e64118595db5408e946f3a22c75807f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/14f5fff1e64118595db5408e946f3a22c75807f7", + "reference": "14f5fff1e64118595db5408e946f3a22c75807f7", "shasum": "" }, "require": { @@ -2702,11 +2764,11 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", "extra": { @@ -2721,35 +2783,58 @@ "authors": [ { "name": "Greg Sherwood", - "role": "lead" + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", "standards", "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, - "time": "2023-02-22T23:07:41+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-01-11T20:47:48+00:00" }, { "name": "symfony/console", - "version": "v6.3.4", + "version": "v6.4.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" + "reference": "0254811a143e6bc6c8deea08b589a7e68a37f625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", + "url": "https://api.github.com/repos/symfony/console/zipball/0254811a143e6bc6c8deea08b589a7e68a37f625", + "reference": "0254811a143e6bc6c8deea08b589a7e68a37f625", "shasum": "" }, "require": { @@ -2757,7 +2842,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -2771,12 +2856,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -2810,7 +2899,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.4" + "source": "https://github.com/symfony/console/tree/v6.4.2" }, "funding": [ { @@ -2826,11 +2915,11 @@ "type": "tidelift" } ], - "time": "2023-08-16T10:10:12+00:00" + "time": "2023-12-10T16:15:48+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -2877,7 +2966,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -2897,16 +2986,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.3.1", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59", + "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59", "shasum": "" }, "require": { @@ -2940,7 +3029,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + "source": "https://github.com/symfony/filesystem/tree/v6.4.0" }, "funding": [ { @@ -2956,27 +3045,27 @@ "type": "tidelift" } ], - "time": "2023-06-01T08:30:39+00:00" + "time": "2023-07-26T17:27:13+00:00" }, { "name": "symfony/finder", - "version": "v6.3.3", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e" + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9915db259f67d21eefee768c1abcf1cc61b1fc9e", - "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e", + "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -3004,7 +3093,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.3" + "source": "https://github.com/symfony/finder/tree/v6.4.0" }, "funding": [ { @@ -3020,20 +3109,20 @@ "type": "tidelift" } ], - "time": "2023-07-31T08:31:44+00:00" + "time": "2023-10-31T17:30:12+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" + "reference": "22301f0e7fdeaacc14318928612dee79be99860e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/22301f0e7fdeaacc14318928612dee79be99860e", + "reference": "22301f0e7fdeaacc14318928612dee79be99860e", "shasum": "" }, "require": { @@ -3071,7 +3160,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.0" }, "funding": [ { @@ -3087,7 +3176,7 @@ "type": "tidelift" } ], - "time": "2023-05-12T14:21:09+00:00" + "time": "2023-08-08T10:16:24+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3421,16 +3510,16 @@ }, { "name": "symfony/process", - "version": "v6.3.4", + "version": "v6.4.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" + "reference": "c4b1ef0bc80533d87a2e969806172f1c2a980241" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", + "url": "https://api.github.com/repos/symfony/process/zipball/c4b1ef0bc80533d87a2e969806172f1c2a980241", + "reference": "c4b1ef0bc80533d87a2e969806172f1c2a980241", "shasum": "" }, "require": { @@ -3462,7 +3551,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.4" + "source": "https://github.com/symfony/process/tree/v6.4.2" }, "funding": [ { @@ -3478,25 +3567,25 @@ "type": "tidelift" } ], - "time": "2023-08-07T10:39:22+00:00" + "time": "2023-12-22T16:42:54+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.3.0", + "version": "v3.4.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -3544,7 +3633,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" }, "funding": [ { @@ -3560,20 +3649,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-12-26T14:02:43+00:00" }, { "name": "symfony/string", - "version": "v6.3.2", + "version": "v6.4.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "53d1a83225002635bca3482fcbf963001313fb68" + "reference": "7cb80bc10bfcdf6b5492741c0b9357dac66940bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", - "reference": "53d1a83225002635bca3482fcbf963001313fb68", + "url": "https://api.github.com/repos/symfony/string/zipball/7cb80bc10bfcdf6b5492741c0b9357dac66940bc", + "reference": "7cb80bc10bfcdf6b5492741c0b9357dac66940bc", "shasum": "" }, "require": { @@ -3587,11 +3676,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -3630,7 +3719,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.2" + "source": "https://github.com/symfony/string/tree/v6.4.2" }, "funding": [ { @@ -3646,20 +3735,20 @@ "type": "tidelift" } ], - "time": "2023-07-05T08:41:27+00:00" + "time": "2023-12-10T16:15:48+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -3688,7 +3777,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -3696,7 +3785,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" }, { "name": "webmozart/glob", @@ -3757,5 +3846,5 @@ "php": ">=8.1.0" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 96f56b1..8450d0a 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -23,6 +23,7 @@ src/Dispatcher/Result/* + test/FastRouteTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 93fc433..4778302 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,3 +4,7 @@ parameters: - benchmark - src - test + + ignoreErrors: + # We're marking this as deprecated, there's no point in alerting ourselves here... + - '#Call to deprecated function FastRoute\\(cached|simple)Dispatcher.*#' diff --git a/src/Cache/ApcuCache.php b/src/Cache/Psr16Cache.php similarity index 50% rename from src/Cache/ApcuCache.php rename to src/Cache/Psr16Cache.php index 3bdc36f..5e2cad7 100644 --- a/src/Cache/ApcuCache.php +++ b/src/Cache/Psr16Cache.php @@ -4,25 +4,30 @@ namespace FastRoute\Cache; use FastRoute\Cache; +use FastRoute\DataGenerator; +use Psr\SimpleCache\CacheInterface; -use function apcu_add; -use function apcu_fetch; use function is_array; -final class ApcuCache implements Cache +/** @phpstan-import-type RouteData from DataGenerator */ +final class Psr16Cache implements Cache { - /** @inheritdoc */ + public function __construct(private readonly CacheInterface $cache) + { + } + + /** @inheritDoc */ public function get(string $key, callable $loader): array { - $result = apcu_fetch($key, $itemFetched); + $result = $this->cache->get($key); - if ($itemFetched && is_array($result)) { + if (is_array($result)) { // @phpstan-ignore-next-line because we won´t be able to validate the array shape in a performant way return $result; } $data = $loader(); - apcu_add($key, $data); + $this->cache->set($key, $data); return $data; } diff --git a/src/DataGenerator/RegexBasedAbstract.php b/src/DataGenerator/RegexBasedAbstract.php index dbf0c3a..8e0c21b 100644 --- a/src/DataGenerator/RegexBasedAbstract.php +++ b/src/DataGenerator/RegexBasedAbstract.php @@ -112,7 +112,7 @@ private function addStaticRoute(string $httpMethod, array $routeData, mixed $han /** @param array $routeData */ private function addVariableRoute(string $httpMethod, array $routeData, mixed $handler): void { - $route = Route::fromParsedRoute($httpMethod, $routeData, $handler); + $route = new Route($httpMethod, $routeData, $handler); $regex = $route->regex; if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { diff --git a/src/Dispatcher.php b/src/Dispatcher.php index 0d90122..6a62c3a 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -16,7 +16,7 @@ interface Dispatcher /** * Dispatches against the provided HTTP method verb and URI. * - * Returns array with one of the following formats: + * Returns an object that also has an array shape with one of the following formats: * * [self::NOT_FOUND] * [self::METHOD_NOT_ALLOWED, ['GET', 'OTHER_ALLOWED_METHODS']] diff --git a/src/FastRoute.php b/src/FastRoute.php new file mode 100644 index 0000000..6048e17 --- /dev/null +++ b/src/FastRoute.php @@ -0,0 +1,128 @@ + $routeParser + * @param class-string $dataGenerator + * @param class-string $dispatcher + * @param class-string $routeCollector + * @param Cache|class-string|null $cacheDriver + */ + private function __construct( + private readonly Closure $routeDefinitionCallback, + private readonly string $routeParser, + private readonly string $dataGenerator, + private readonly string $dispatcher, + private readonly string $routeCollector, + private readonly Cache|string|null $cacheDriver, + ) { + } + + /** @param Closure(RouteCollector):void $routeDefinitionCallback */ + public static function recommendedSettings(Closure $routeDefinitionCallback): self + { + return new self( + $routeDefinitionCallback, + RouteParser\Std::class, + DataGenerator\MarkBased::class, + Dispatcher\MarkBased::class, + RouteCollector::class, + FileCache::class, + ); + } + + public function disableCache(): self + { + return new self( + $this->routeDefinitionCallback, + $this->routeParser, + $this->dataGenerator, + $this->dispatcher, + $this->routeCollector, + null, + ); + } + + /** @param Cache|class-string $driver */ + public function withCache(Cache|string $driver): self + { + return new self( + $this->routeDefinitionCallback, + $this->routeParser, + $this->dataGenerator, + $this->dispatcher, + $this->routeCollector, + $driver, + ); + } + + public function useCharCountDispatcher(): self + { + return $this->useCustomDispatcher(DataGenerator\CharCountBased::class, Dispatcher\CharCountBased::class); + } + + public function useGroupCountDispatcher(): self + { + return $this->useCustomDispatcher(DataGenerator\GroupCountBased::class, Dispatcher\GroupCountBased::class); + } + + public function useGroupPosDispatcher(): self + { + return $this->useCustomDispatcher(DataGenerator\GroupPosBased::class, Dispatcher\GroupPosBased::class); + } + + public function useMarkDispatcher(): self + { + return $this->useCustomDispatcher(DataGenerator\MarkBased::class, Dispatcher\MarkBased::class); + } + + /** + * @param class-string $dataGenerator + * @param class-string $dispatcher + */ + public function useCustomDispatcher(string $dataGenerator, string $dispatcher): self + { + return new self( + $this->routeDefinitionCallback, + $this->routeParser, + $dataGenerator, + $dispatcher, + $this->routeCollector, + $this->cacheDriver, + ); + } + + public function dispatcher(string $cacheKey): Dispatcher + { + $loader = function (): array { + $collector = new $this->routeCollector( + new $this->routeParser(), + new $this->dataGenerator(), + ); + + ($this->routeDefinitionCallback)($collector); + + return $collector->getData(); + }; + + if ($this->cacheDriver === null) { + return new $this->dispatcher($loader()); + } + + $cache = is_string($this->cacheDriver) + ? new $this->cacheDriver() + : $this->cacheDriver; + + return new $this->dispatcher($cache->get($cacheKey, $loader)); + } +} diff --git a/src/Route.php b/src/Route.php index 8e72198..19a54cb 100644 --- a/src/Route.php +++ b/src/Route.php @@ -9,26 +9,18 @@ class Route { - /** @param array $variables */ - public function __construct( - public string $httpMethod, - public mixed $handler, - public string $regex, - public array $variables, - ) { - } + public readonly string $regex; - /** @param array $routeData */ - public static function fromParsedRoute(string $httpMethod, array $routeData, mixed $handler): self - { - [$regex, $variables] = self::extractRegex($routeData); + /** @var array $variables */ + public readonly array $variables; - return new self( - $httpMethod, - $handler, - $regex, - $variables, - ); + /** @param array $routeData */ + public function __construct( + public readonly string $httpMethod, + array $routeData, + public readonly mixed $handler, + ) { + [$this->regex, $this->variables] = self::extractRegex($routeData); } /** diff --git a/src/RouteCollector.php b/src/RouteCollector.php index 2b80bc2..0a1ebd9 100644 --- a/src/RouteCollector.php +++ b/src/RouteCollector.php @@ -8,8 +8,10 @@ class RouteCollector { protected string $currentGroupPrefix = ''; - public function __construct(protected RouteParser $routeParser, protected DataGenerator $dataGenerator) - { + public function __construct( + protected readonly RouteParser $routeParser, + protected readonly DataGenerator $dataGenerator, + ) { } /** diff --git a/src/functions.php b/src/functions.php index ba0890b..a619ed5 100644 --- a/src/functions.php +++ b/src/functions.php @@ -6,12 +6,18 @@ use FastRoute\Cache\FileCache; use LogicException; -use function array_key_exists; use function function_exists; use function is_string; if (! function_exists('FastRoute\simpleDispatcher')) { - /** @param array{routeParser?: class-string, dataGenerator?: class-string, dispatcher?: class-string, routeCollector?: class-string, cacheDisabled?: bool, cacheKey?: string, cacheDriver?: class-string|Cache} $options */ + /** + * @deprecated since v2.0 and will be removed in v3.0 + * + * @see FastRoute::recommendedSettings() + * @see FastRoute::disableCache() + * + * @param array{routeParser?: class-string, dataGenerator?: class-string, dispatcher?: class-string, routeCollector?: class-string, cacheDisabled?: bool, cacheKey?: string, cacheFile?: string, cacheDriver?: class-string|Cache} $options + */ function simpleDispatcher(callable $routeDefinitionCallback, array $options = []): Dispatcher { return \FastRoute\cachedDispatcher( @@ -20,7 +26,13 @@ function simpleDispatcher(callable $routeDefinitionCallback, array $options = [] ); } - /** @param array{routeParser?: class-string, dataGenerator?: class-string, dispatcher?: class-string, routeCollector?: class-string, cacheDisabled?: bool, cacheKey?: string, cacheDriver?: class-string|Cache} $options */ + /** + * @deprecated since v2.0 and will be removed in v3.0 + * + * @see FastRoute::recommendedSettings() + * + * @param array{routeParser?: class-string, dataGenerator?: class-string, dispatcher?: class-string, routeCollector?: class-string, cacheDisabled?: bool, cacheKey?: string, cacheFile?: string, cacheDriver?: class-string|Cache} $options + */ function cachedDispatcher(callable $routeDefinitionCallback, array $options = []): Dispatcher { $options += [ @@ -47,7 +59,9 @@ function cachedDispatcher(callable $routeDefinitionCallback, array $options = [] return new $options['dispatcher']($loader()); } - if (! array_key_exists('cacheKey', $options)) { + $cacheKey = $options['cacheKey'] ?? $options['cacheFile'] ?? null; + + if ($cacheKey === null) { throw new LogicException('Must specify "cacheKey" option'); } @@ -57,6 +71,6 @@ function cachedDispatcher(callable $routeDefinitionCallback, array $options = [] $cache = new $cache(); } - return new $options['dispatcher']($cache->get($options['cacheKey'], $loader)); + return new $options['dispatcher']($cache->get($cacheKey, $loader)); } } diff --git a/test/Cache/Psr16CacheTest.php b/test/Cache/Psr16CacheTest.php new file mode 100644 index 0000000..ed9ff81 --- /dev/null +++ b/test/Cache/Psr16CacheTest.php @@ -0,0 +1,55 @@ + ['/' => 'test']], []]; + + $adapter = new Psr16Cache($this->createDummyCache($data)); + $result = $adapter->get('test', static fn () => $generatedData); + + self::assertSame($generatedData, $result); + self::assertSame($generatedData, $data['test']); + + // Try again, now with a different callback + $result = $adapter->get('test', static fn () => [['POST' => ['/' => 'test']], []]); + + self::assertSame($generatedData, $result); + } + + /** @param array $data */ + private function createDummyCache(array &$data): CacheInterface + { + $cache = $this->createMock(CacheInterface::class); + + $cache->method('get') + ->willReturnCallback( + static function (string $key, mixed $default) use (&$data): mixed { + return $data[$key] ?? $default; + }, + ); + + $cache->method('set') + ->willReturnCallback( + static function (string $key, mixed $value) use (&$data): bool { + $data[$key] = $value; + + return true; + }, + ); + + return $cache; + } +} diff --git a/test/Dispatcher/CachingTest.php b/test/Dispatcher/CachingTest.php index 6c233ed..e91b7da 100644 --- a/test/Dispatcher/CachingTest.php +++ b/test/Dispatcher/CachingTest.php @@ -27,21 +27,23 @@ public function warmUpCache(): void $this->createDispatcher(); } - public function createDispatcher(): Dispatcher + public function createDispatcher(string $optionName = 'cacheKey'): Dispatcher { return cachedDispatcher( static function (RouteCollector $collector): void { $collector->get('/testing', ['test']); $collector->get('/admin/{page}', ['admin-page']); }, - ['cacheKey' => self::CACHE_FILE], + // @phpstan-ignore-next-line we're doing dynamic configuration... + [$optionName => self::CACHE_FILE], ); } #[PHPUnit\Test] - public function dynamicRouteShouldMatch(): void + #[PHPUnit\DataProvider('possiblePropertyNames')] + public function dynamicRouteShouldMatch(string $propertyName): void { - $dispatcher = $this->createDispatcher(); + $dispatcher = $this->createDispatcher($propertyName); $result = $dispatcher->dispatch('GET', '/admin/1234'); self::assertSame(Dispatcher::FOUND, $result[0]); @@ -50,9 +52,10 @@ public function dynamicRouteShouldMatch(): void } #[PHPUnit\Test] - public function staticRouteShouldMatch(): void + #[PHPUnit\DataProvider('possiblePropertyNames')] + public function staticRouteShouldMatch(string $propertyName): void { - $dispatcher = $this->createDispatcher(); + $dispatcher = $this->createDispatcher($propertyName); $result = $dispatcher->dispatch('GET', '/testing'); self::assertSame(Dispatcher::FOUND, $result[0]); @@ -60,11 +63,19 @@ public function staticRouteShouldMatch(): void } #[PHPUnit\Test] - public function missingRoutShouldNotBeFound(): void + #[PHPUnit\DataProvider('possiblePropertyNames')] + public function missingRoutShouldNotBeFound(string $propertyName): void { - $dispatcher = $this->createDispatcher(); + $dispatcher = $this->createDispatcher($propertyName); $result = $dispatcher->dispatch('GET', '/testing2'); self::assertSame(Dispatcher::NOT_FOUND, $result[0]); } + + /** @return iterable */ + public static function possiblePropertyNames(): iterable + { + yield 'v1' => ['cacheFile']; + yield 'v2' => ['cacheKey']; + } } diff --git a/test/FastRouteTest.php b/test/FastRouteTest.php new file mode 100644 index 0000000..e103d3b --- /dev/null +++ b/test/FastRouteTest.php @@ -0,0 +1,100 @@ +disableCache() + ->dispatcher('test'); + + self::assertInstanceOf(Dispatcher\MarkBased::class, $dispatcher); + } + + #[PHPUnit\Test] + public function canBeConfiguredToUseCharCountDispatcher(): void + { + $dispatcher = FastRoute::recommendedSettings(self::routes(...)) + ->disableCache() + ->useCharCountDispatcher() + ->dispatcher('test'); + + self::assertInstanceOf(Dispatcher\CharCountBased::class, $dispatcher); + } + + #[PHPUnit\Test] + public function canBeConfiguredToUseGroupPosDispatcher(): void + { + $dispatcher = FastRoute::recommendedSettings(self::routes(...)) + ->disableCache() + ->useGroupPosDispatcher() + ->dispatcher('test'); + + self::assertInstanceOf(Dispatcher\GroupPosBased::class, $dispatcher); + } + + #[PHPUnit\Test] + public function canBeConfiguredToUseGroupCountDispatcher(): void + { + $dispatcher = FastRoute::recommendedSettings(self::routes(...)) + ->disableCache() + ->useGroupCountDispatcher() + ->dispatcher('test'); + + self::assertInstanceOf(Dispatcher\GroupCountBased::class, $dispatcher); + } + + #[PHPUnit\Test] + public function canBeConfiguredToUseMarkDispatcher(): void + { + $dispatcher = FastRoute::recommendedSettings(self::routes(...)) + ->disableCache() + ->useCharCountDispatcher() + ->useMarkDispatcher() + ->dispatcher('test'); + + self::assertInstanceOf(Dispatcher\MarkBased::class, $dispatcher); + } + + #[PHPUnit\Test] + public function canBeConfiguredToUseCustomCache(): void + { + $cache = new class () implements Cache { + /** @inheritDoc */ + public function get(string $key, callable $loader): array + { + if ($key === 'test') { + return [['GET' => ['/' => 'test2']], []]; + } + + throw new RuntimeException('This dummy implementation is not meant for other cases'); + } + }; + + $dispatcher = FastRoute::recommendedSettings(self::routes(...)) + ->withCache($cache) + ->dispatcher('test'); + + $result = $dispatcher->dispatch('GET', '/'); + + self::assertInstanceOf(Dispatcher\Result\Matched::class, $result); + self::assertSame('test2', $result->handler); // should use data from cache, not from loader + } + + private static function routes(RouteCollector $collector): void + { + $collector->get('/', 'test'); + } +}