====== 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 [[rfc:mb_str_pad|mb_str_pad RFC]].
The same visible text should get the same padding:
===== Proposal =====
Add the following function to ext/intl:
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 ''$length'' is negative, or less than or equal to the length of ''$string'', ''$string'' is returned unchanged.
* ''$pad_type'' is one of the existing constants ''STR_PAD_LEFT'', ''STR_PAD_RIGHT'' (default) or ''STR_PAD_BOTH''. With ''STR_PAD_BOTH'' the extra grapheme cluster, if any, goes to the right, like in ''str_pad()''.
* ''$pad_string'' is 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 all ''grapheme_*'' 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:
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_string'' throws a ''ValueError'', unless no padding is needed.
* A ''$pad_type'' other than ''STR_PAD_LEFT'', ''STR_PAD_RIGHT'' or ''STR_PAD_BOTH'' throws a ''ValueError''.
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()'').
==== 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 other ''grapheme_*'' functions.
===== Voting Choices =====
Primary Vote requiring a 2/3 majority to accept the RFC:
* Yes
* No
* Abstain
===== 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 =====
* [[rfc:mb_str_pad|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."
* [[https://github.com/php/php-src/issues/10203|GH-10203]], where a grapheme cluster variant of mb_str_pad() was first suggested.
* [[rfc:grapheme_str_split|grapheme_str_split RFC]] and [[rfc:grapheme_strrev|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.