Skip to content

[Mbstring] Use titlecase mappings for MB_CASE_TITLE - #720

Merged
nicolas-grekas merged 1 commit into
symfony:1.xfrom
wleeaf:mbstring-title-case-mapping
Oct 28, 2026
Merged

nicolas-grekas merged 1 commit into
symfony:1.xfrom
wleeaf:mbstring-title-case-mapping

Conversation

@wleeaf

@wleeaf wleeaf commented Oct 24, 2026

Copy link
Copy Markdown
Contributor
Q A
Branch? 1.x
Bug fix? yes
New feature? no
Deprecations? no
Issues -
License MIT

mb_convert_case($s, MB_CASE_TITLE) uppercases the first letter of each word instead of titlecasing it. The two differ for digraphs, Georgian, Greek letters with ypogegrammeni, and the ligatures and ß of SpecialCasing.txt:

mb_convert_case('džemal ßa fix ᾳ ᾀ և საქართველო', MB_CASE_TITLE);
// ext/mbstring: 'Džemal Ssa Fix ᾼ ᾈ Եւ საქართველო'
// polyfill:     'DŽemal SSa FIx ΑΙ ἈΙ ԵՒ Საქართველო'

Georgian text is the most visible case. Mkhedruli letters titlecase to themselves, so the polyfill turns the first letter of every Georgian word into Mtavruli. mb_ucfirst() goes through the same code when mbstring is missing.

Compiler::unicodeMaps() now also writes a titleCase.php map. It holds the entries whose titlecase mapping differs from the uppercase one: field 14 of UnicodeData.txt and the title column of the unconditional SpecialCasing.txt rules. title_case() uses it before falling back to the uppercase map. I generated the map from the Unicode 13.0.0 files, the version the current upperCase.php and lowerCase.php come from (regenerating those two from 13.0.0 gives identical files). update-unidata.php moves the new file next to them.

The existing title-case assertion in testStrCase() sat behind if (\PCRE_VERSION >= '8.10'). That compares strings, so it is false for every PCRE2 version ("10.42 ..." < "8.10") and the assertion never ran on PHP >= 7.3. I removed the guard, since all supported PHP versions ship a PCRE that supports the regexp, and added an assertion for the cases above. The new assertion fails without the change.

On 20k random strings, the polyfill's MB_CASE_TITLE result differs from PHP 8.5's in 13947 cases before this change and 6616 after, and no string that matched before differs now. The remaining differences are final sigma and word boundaries after case-ignorable characters such as ·, which are separate issues. ./phpunit tests/Mbstring passes.

Copilot AI balanced review requested due to automatic review settings October 24, 2026 15:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@nicolas-grekas

Copy link
Copy Markdown
Member

Thank you @wleeaf.

@nicolas-grekas
nicolas-grekas merged commit 6726986 into symfony:1.x Oct 28, 2026
20 checks passed
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.

3 participants