PHP RFC: grapheme_str_pad
- Version: 0.1
- Date: 2026-11-03
- Author: Chris Abner Herrera Navarro, chrisabner@hotmail.com
- Status: Under Discussion
- Target Version: PHP 8.7
- Implementation: https://github.com/php/php-src/pull/25179
- Discussion thread: https://news-web.php.net/php.internals/132995
- Voting thread: TBD
Introduction
PHP has a padding function for bytes, str_pad(), and one for code points, mb_str_pad() (PHP 8.3). It has no padding function for grapheme clusters, the unit the grapheme_* functions of ext/intl use for “a character as a user sees it”. This RFC proposes grapheme_str_pad(), as already suggested in the Future Scope of the mb_str_pad RFC.
The same visible text should get the same padding:
<?php $nfc = "Caf\u{00E9}"; // "Café", é as one code point (U+00E9) $nfd = "Cafe\u{0301}"; // "Café", e followed by U+0301 COMBINING ACUTE ACCENT var_dump(mb_str_pad($nfc, 6, '.')); // string(7) "Café.." var_dump(mb_str_pad($nfd, 6, '.')); // string(7) "Café." one dot less var_dump(grapheme_str_pad($nfc, 6, '.')); // string(7) "Café.." var_dump(grapheme_str_pad($nfd, 6, '.')); // string(8) "Café.." ?>
Proposal
Add the following function to ext/intl:
<?php function grapheme_str_pad(string $string, int $length, string $pad_string = " ", int $pad_type = STR_PAD_RIGHT): string|false {} ?>
It pads $string on the left, the right or both sides with $pad_string until the result is $length grapheme clusters long. $length and the length of the input are counted in grapheme clusters, as grapheme_strlen() counts them.
The function mirrors mb_str_pad() and str_pad():
- If
$lengthis negative, or less than or equal to the length of$string,$stringis returned unchanged. $pad_typeis one of the existing constantsSTR_PAD_LEFT,STR_PAD_RIGHT(default) orSTR_PAD_BOTH. WithSTR_PAD_BOTHthe extra grapheme cluster, if any, goes to the right, like instr_pad().$pad_stringis repeated as often as needed. If the last copy does not fit entirely, it is cut on a grapheme cluster boundary. The padding string is never split inside a grapheme cluster.- For ASCII input the result is identical to
str_pad(). The two-byte sequence“\r\n”is a single grapheme cluster, as in allgrapheme_*functions.
Why not mb_str_pad()
mb_str_pad() counts code points. Many characters a user perceives as one are made of several code points: letters with combining marks (text in Unicode normalization form NFD, which some platforms and input methods produce), Japanese kana with a combining dakuten (が = か + U+3099), most Indic scripts, emoji with skin tone modifiers and emoji ZWJ sequences. For such text mb_str_pad() adds too little padding, and it can split the padding string inside a grapheme cluster, which changes the padding text itself:
<?php // Padding string "üö" in NFD: u + U+0308, o + U+0308 (4 code points, 2 grapheme clusters) var_dump(mb_str_pad("a", 4, "u\u{0308}o\u{0308}")); // string(5) "aüo" ö lost its diaeresis var_dump(grapheme_str_pad("a", 4, "u\u{0308}o\u{0308}")); // string(10) "aüöü" $ga = "\u{304B}\u{3099}\u{304E}"; // "がぎ": が is か + U+3099, ぎ is precomposed var_dump(mb_str_pad($ga, 4, '*')); // string(10) "がぎ*" var_dump(grapheme_str_pad($ga, 4, '*')); // string(11) "がぎ**" var_dump(mb_str_pad("👍🏽", 3, '-', STR_PAD_LEFT)); // string(9) "-👍🏽" var_dump(grapheme_str_pad("👍🏽", 3, '-', STR_PAD_LEFT)); // string(10) "--👍🏽" ?>
The grapheme_* functions exist precisely for this unit. Adding the padding function completes the set of byte, code point and grapheme cluster variants, as grapheme_str_split() (PHP 8.4) and grapheme_strrev() (PHP 8.6) did for str_split() and strrev().
Doing it in userland
A correct userland version needs grapheme_strlen() for the input, grapheme_strlen() and grapheme_substr() for the padding string, and the same left/right arithmetic as str_pad(). Symfony's String component does exactly this in UnicodeString::padStart(), padEnd() and padBoth(), whose length is grapheme_strlen(). Everybody else has to reimplement it, or use mb_str_pad() and get the results shown above.
Error conditions
Same as mb_str_pad():
- An empty
$pad_stringthrows aValueError, unless no padding is needed. - A
$pad_typeother thanSTR_PAD_LEFT,STR_PAD_RIGHTorSTR_PAD_BOTHthrows aValueError.
Like grapheme_strlen(), invalid UTF-8 in $string or $pad_string makes the function return false and sets the intl error (intl_get_error_code() / intl_get_error_message()).
<?php grapheme_str_pad("abc", 5, ""); // ValueError: grapheme_str_pad(): Argument #3 ($pad_string) must not be empty grapheme_str_pad("abc", 5, "-", 42); // ValueError: grapheme_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH var_dump(grapheme_str_pad("abc\xFF", 6, ".")); // bool(false) echo intl_get_error_message(); // grapheme_str_pad(): Invalid UTF-8 string: U_INVALID_CHAR_FOUND ?>
Examples
Backward Incompatible Changes
A userland function named grapheme_str_pad() in the global namespace would conflict. No definitions were found on 2026-11-03 with GitHub code search (“function grapheme_str_pad(”, PHP) or Sourcegraph (grapheme_str_pad, PHP). No other changes.
Proposed PHP Version(s)
Next PHP 8.x (PHP 8.7).
RFC Impact
To the Ecosystem
None beyond knowing the new function.
To Existing Extensions
Adds grapheme_str_pad() to ext/intl. No change to existing functions.
To SAPIs
None.
Open Issues
None.
Future Scope
- Padding to a display width (where East Asian wide characters and many emoji take two terminal columns, see
mb_strwidth()) is a different problem and is not addressed by this RFC.grapheme_str_pad()counts grapheme clusters, consistently with the othergrapheme_*functions.
Voting Choices
Primary Vote requiring a 2/3 majority to accept the RFC:
Patches and Tests
Implementation with tests: https://github.com/php/php-src/pull/25179
Implementation
After the RFC is implemented, this section will contain:
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
References
- mb_str_pad RFC, Future Scope: “In the future we could add a string padding function that works on grapheme clusters instead of code points: grapheme_str_pad(). This should be added to ext/intl. This will of course require another RFC.”
- GH-10203, where a grapheme cluster variant of mb_str_pad() was first suggested.
- grapheme_str_split RFC and grapheme_strrev RFC, previous grapheme counterparts of string functions.
- Discussion thread: https://news-web.php.net/php.internals/132995
Rejected Features
None yet.
Changelog
- 0.1 (2026-11-03): Initial draft.