fix(dispatcher): surface preg_match() engine failures instead of treating them as no-match
All dispatcher strategies checked preg_match() results with `!== 1`, which conflates a genuine no-match (0) with a PCRE engine failure (false), e.g. PREG_BACKTRACK_LIMIT_ERROR from a catastrophically backtracking route pattern. Because dynamic routes for the same method are combined into shared regex chunks, one such route could make the preg_match() call for the whole chunk fail, silently turning an unrelated, cheap route in that chunk into a spurious NOT_FOUND. Add a shared matchRoute() helper on RegexBasedAbstract that calls preg_match() and throws a RuntimeException (including preg_last_error_msg()) when it returns false, while preserving the existing 0/1 no-match/match behavior. Update GroupCountBased, GroupPosBased, CharCountBased and MarkBased to use it. Fixes #167
This commit is contained in:
1 parent
c0905f9089
commit
7ac8857878
6 files changed
+62
-9
No files matched your search
@@ -8,6 +8,11 @@ use FastRoute\Dispatcher;
|
||||
use FastRoute\Dispatcher\Result\Matched;
|
||||
use FastRoute\Dispatcher\Result\MethodNotAllowed;
|
||||
use FastRoute\Dispatcher\Result\NotMatched;
|
||||
use RuntimeException;
|
||||
|
||||
use function preg_last_error_msg;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -35,6 +40,26 @@ abstract class RegexBasedAbstract implements Dispatcher
|
||||
/** @param DynamicRouteChunks $routeData */
|
||||
abstract protected function dispatchVariableRoute(array $routeData, string $uri): ?Matched;
|
||||
|
||||
/**
|
||||
* Matches a route regex, distinguishing a genuine "no match" (preg_match()
|
||||
* returns 0) from a PCRE engine failure such as PREG_BACKTRACK_LIMIT_ERROR
|
||||
* (returns false), which would otherwise fail unrelated routes sharing the
|
||||
* same combined regex chunk.
|
||||
*
|
||||
* @param array<int|string, mixed>|null $matches
|
||||
*/
|
||||
protected function matchRoute(string $regex, string $subject, ?array &$matches = null): int
|
||||
{
|
||||
$result = preg_match($regex, $subject, $matches);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException(
|
||||
sprintf('Regex matching failed for "%s": %s', $regex, preg_last_error_msg()),
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function dispatch(string $httpMethod, string $uri): Matched|NotMatched|MethodNotAllowed
|
||||
{
|
||||
if (isset($this->staticRouteMap[$httpMethod][$uri])) {
|
||||
|
||||
Reference in new issue
Block a user