Skip to content

Check preg_quote delimiters in array patterns - #6474

Merged
staabm merged 2 commits into
phpstan:2.3.xfrom
arpitjain099:fix/preg-quote-array-patterns
Sep 29, 2026
Merged

staabm merged 2 commits into
phpstan:2.3.xfrom
arpitjain099:fix/preg-quote-array-patterns

Conversation

@arpitjain099

@arpitjain099 arpitjain099 commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

RegularExpressionQuotingRule bails unless the first argument is a Concat, so it only ever sees a single-string pattern. But preg_replace, preg_replace_callback and preg_filter also take an array of patterns, and those calls reach the gate and get dropped.

RegularExpressionPatternRule::extractPatterns() already reads getConstantArrays() for exactly those three functions, so the two regexp rules disagreed about what a pattern argument can be.

I added four array cases to data/preg-quote.php, each the same delimiter mismatch the rule already reports for a bare string. The test passes unchanged on 2.3.x with all four present. A fifth case whose delimiter matches stays quiet, so the branch discriminates rather than firing on every array.

One thing to decide: this adds a level 5 error to code that is currently clean. If you would rather it land behind bleeding edge first, say so and I will move it.

Regexp tests 9/9, make lint clean, phpcs clean on the touched files.

Comment on lines +97 to +115
$errors = [];
foreach ($patternArg->items as $item) {
if (!$item->value instanceof Concat) {
continue;
}

$itemDelimiters = $this->regexExpressionHelper->getPatternDelimiters($item->value, $scope);
$errors = array_merge($errors, $this->validateQuoteDelimiters($item->value, $scope, $itemDelimiters));
}

return $errors;
}

if (!$patternArg instanceof Concat) {
return [];
}

$patternDelimiters = $this->regexExpressionHelper->getPatternDelimiters($normalizedArgs[0]->value, $scope);
return $this->validateQuoteDelimiters($normalizedArgs[0]->value, $scope, $patternDelimiters);
$patternDelimiters = $this->regexExpressionHelper->getPatternDelimiters($patternArg, $scope);
return $this->validateQuoteDelimiters($patternArg, $scope, $patternDelimiters);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be de-duplicated by collecting all concats in a array first and then do the processing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Both branches now just build a $patterns list, and the delimiter lookup plus validateQuoteDelimiters() happen once in a loop below. Rule test still passes and phpstan analyse of the file is clean.

RegularExpressionQuotingRule bails unless the first argument is a Concat, so it
only ever looks at a single-string pattern. preg_replace, preg_replace_callback
and preg_filter also accept an array of patterns, which is a documented calling
convention, and each element is a pattern in its own right.

RegularExpressionPatternRule::extractPatterns() already reads that form for the
same three functions, so the two regexp rules disagreed on what a pattern
argument can look like.

Walk the array items and validate each Concat element the same way a single
pattern is validated. Other functions in the list take a single pattern, so the
array branch is limited to the three that accept one.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
@staabm
staabm force-pushed the fix/preg-quote-array-patterns branch from e71632f to bdae48a Compare September 29, 2026 15:15
@staabm
staabm merged commit 7b75bbe into phpstan:2.3.x Sep 29, 2026
503 of 537 checks passed
@staabm

staabm commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants