grapheme_str_pad() View as Markdown

Under discussion
▴ 0 ▾ 0
community vote
Authors
Chris Abner Herrera Navarro, chrisabner@hotmail.com
Date
2026-11-03
Version
0.1
Target
PHP 8.7
Implementation
https://github.com/php/php-src/pull/25179
Source
wiki.php.net/rfc/grapheme_str_pad

Vote

Add grapheme_str_pad() as outlined in the RFC? no votes
Yes · No · Abstain

Discussions on the mailing list

[RFC] grapheme_str_pad()
by Chris Abner Herrera Navarro · 1 message

RFC text

Copied from wiki.php.net, last synchronized Nov 3, 2026 23:46 UTC

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 $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:

<?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_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()).

<?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

<?php
var_dump(grapheme_str_pad("5", 3, "0", STR_PAD_LEFT));          // string(3) "005", same as str_pad()
var_dump(grapheme_str_pad("ok", 5, "👨‍👩‍👧", STR_PAD_BOTH));   // "👨‍👩‍👧ok👨‍👩‍👧👨‍👩‍👧": 5 grapheme clusters
?>

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:

Vote: Add grapheme_str_pad() as outlined in the RFC? — Yes: 0 · No: 0 · Abstain: 0 (closed)

Patches and Tests

Implementation with tests: https://github.com/php/php-src/pull/25179

Implementation

After the RFC is implemented, this section will contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Rejected Features

None yet.

Changelog

  • 0.1 (2026-11-03): Initial draft.