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,
nand 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
umodifier 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