Possessive Quantifier Conflict

ID

php.regex_possessive_quantifier_conflict

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Php

Tags

regex, reliability

Description

Reports a possessive repetition (X*+, X++, X{n,}+) over a single character or simple class, immediately followed by a mandatory character or class whose members the repetition has already taken.

Rationale

A possessive quantifier consumes as much as it can and never backtracks. Whatever it swallowed is gone, so a mandatory atom right after it cannot be fed from that run:

// Bad
preg_match('/^\d++\d$/', '12345');   // 0 -- \d++ took all five digits and gives none back
preg_match('/a++a/', 'aaaa');        // 0
  • The pattern can never match any input at all, so the branch it guards is dead.

  • It normally arrives from tightening a greedy quantifier for performance: \d+\d matches and \d++\d does not, and the change looks like a pure optimisation.

Remediation

Either drop the possessive marker where backtracking is what makes the pattern work, or fold the mandatory atom into the repetition’s own bounds.

// Good
preg_match('/^\d+\d$/', '12345');    // 1 -- greedy, so it gives a digit back
preg_match('/^\d{2,}+$/', '12345');  // 1 -- says "two or more" directly

What is not reported

The check is deliberately narrow, because a wrong verdict here would push someone to rewrite a working pattern:

  • only a single character or a simple class — including \d, \w, \s and their negations — takes part in the subset test on either side;

  • only the atom immediately adjacent to the quantifier is examined, so /aa++bc/ is fine: the possessive body is the second a, and b was never a candidate;

  • a lookbehind or a group after the quantifier consumes nothing and never conflicts;

  • an atom that is itself quantified with a zero minimum — /a++a*/ — is not mandatory and is left alone.

© 2026 Xygeni. All rights reserved