Computers store text as bytes. In English, one letter is one byte. In many languages, and for emoji, one character takes several bytes. Those are called multibyte characters.
Standard PHP string functions (strlen, substr, strtoupper) work on bytes. For international text, they give wrong answers.
<?php
$hindi = "नमस्ते";
echo strlen($hindi) . "\n";
echo mb_strlen($hindi);
18
6
strlen() counted bytes (18). mb_strlen() counted characters (6).
The mb_ family
| Byte function | Multibyte version |
|---|---|
strlen() | mb_strlen() |
substr() | mb_substr() |
strtoupper() | mb_strtoupper() |
strtolower() | mb_strtolower() |
strpos() | mb_strpos() |
str_split() | mb_str_split() |
<?php
$text = "Crème brûlée";
echo mb_strtoupper($text) . "\n";
echo mb_substr($text, 0, 5) . "\n";
echo mb_strpos($text, "brûlée");
CRÈME BRÛLÉE
Crème
6
Cutting text safely
With substr(), you can cut a character in half and produce broken text. mb_substr() never does.
<?php
$word = "café";
echo mb_substr($word, 0, 4) . "\n";
echo mb_strimwidth("A long café menu", 0, 10, "...");
café
A long ...
Emoji
An emoji is often 4 bytes. Some look like one symbol but are made from several code points, which neither function counts as one.
<?php
echo strlen("😀") . "\n";
echo mb_strlen("😀");
4
1
Make sure everything is UTF-8
Multibyte functions assume UTF-8 by default. Keep the whole path UTF-8:
- Save your PHP files as UTF-8 (without BOM).
- Send the header
Content-Type: text/html; charset=utf-8, or write<meta charset="utf-8">in HTML. - Use a UTF-8 connection to your database (
utf8mb4in MySQL).
Needs the mbstring extension
The mb_ functions come from the mbstring extension. Most hosts enable it. Check with:
<?php
var_dump(extension_loaded("mbstring"));
Newer helpers
PHP 8.3 added mb_str_pad(), and PHP 8.4 added mb_trim(), mb_ltrim(), mb_rtrim(), mb_ucfirst() and mb_lcfirst(). On older versions, those names do not exist.
Default to mb_ for user text
mb_strlen() and mb_substr(). For identifiers you control, like file extensions, the plain functions are fine.