Anchor Precedence In Alternation

ID

php.regex_anchor_precedence

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Php

Tags

regex, reliability

Description

Reports an alternation whose first branch starts with ^ (or \A) or whose last branch ends with $ (or \Z, \z), while at least one other branch of the same alternation carries no such anchor.

Rationale

An anchor binds tighter than |. So /^GET|POST|PUT$/ is not "one of these three, anchored at both ends" — PCRE reads it as /(?:^GET)|POST|(?:PUT$)/:

// Bad
preg_match('/^GET|POST|PUT$/', $method);

preg_match('/^GET|POST|PUT$/', 'DELETE-POST-NOW');   // 1 -- "POST" matched mid-string
  • The middle branch is anchored at neither end, so it matches anywhere in the subject.

  • For a validation pattern that means it accepts exactly the input it was written to reject, and the failure surfaces downstream as bad data rather than as a validation error.

  • The layout reads as if the anchors applied to the whole list, which is why the mistake survives review.

Remediation

Wrap the alternation in a group so the anchors apply to all of it.

// Good
preg_match('/^(GET|POST|PUT)$/', $method);

preg_match('/^(GET|POST|PUT)$/', 'DELETE-POST-NOW');   // 0

What is not reported

Two shapes look like the defect and are not:

  • A branch consisting of nothing but the anchor — /(?:,|$)/, /(|[\\])/. That is the deliberate "either this, or the boundary" idiom: the branch has no content to anchor.

  • The two-branch strip shape /^A|B$/, as in preg_replace('/^,|,$/', '', $s). Each branch is already anchored at the edge it is meant to strip.

Every alternation is judged at its own level — the specific group, or the top level, that directly contains it — so a nested alternation inside a properly wrapped group never inherits the anchors around it. A pattern carrying the x modifier is not analysed: under it whitespace and # comments stop being pattern content, so branch spans no longer measure what PCRE sees.

© 2026 Xygeni. All rights reserved