Grapheme Cluster In Character Class

ID

php.regex_grapheme_cluster_in_class

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Php

Tags

regex, reliability

Description

Reports a character class that combines a base character with a following Unicode combining mark. What reads as one accented letter is two code points, and a character class enumerates its members rather than sequencing them.

Rationale

An n-tilde can be written two ways: as the single precomposed code point U+00F1, or as n followed by the combining tilde U+0303. Inside a character class the second spelling comes apart:

// Bad -- the class was typed with a decomposed letter
preg_match('/[ñ]/u', 'n');            // 1 -- the bare base letter matched
preg_match('/[ñ]/u', "\u{0303}");     // 1 -- the bare combining mark matched
  • The class holds two members, n and the mark, so it matches either one alone and never the pair.

  • Nothing in the source shows this: the two spellings look identical in every editor, so the class appears to name one letter.

  • The u modifier does not help. With it the class has two code-point members; without it PCRE works a byte at a time and the mark contributes its individual UTF-8 bytes, so the class matches lone continuation bytes instead. The class never matches the grapheme either way.

Remediation

Match the grapheme as a sequence rather than as a class member — with an alternation when there are several — or use the precomposed code point.

// Good
preg_match('/(?:ñ|ö)/u', $s);          // each alternative is a sequence
preg_match('/[\x{00F1}\x{00F6}]/u', $s);   // precomposed code points, one member each

What is not reported

A mark written as an explicit escape — /[n\x{0303}]/u — is accepted: the author clearly meant that individual code point, not an accidental grapheme. A decomposed letter outside any character class is also fine, because there the two code points really are a sequence.

© 2026 Xygeni. All rights reserved