Signature
mb_strlen(string $string, ?string $encoding = null): intPurpose
mb_strlen() reports how many characters a string contains under a given character encoding. PHP strings are sequences of bytes, so the plain strlen() function returns a byte count. In UTF-8 many characters use two, three or four bytes, which makes the two answers differ as soon as the text goes beyond ASCII. Use mb_strlen() whenever the number you need is about characters, such as a form length limit, a truncation check or a padding calculation.
Signature
The function is part of the mbstring extension.
Parameters and defaults
$stringis the text to measure. It is read as raw bytes and interpreted using the chosen encoding.$encodingis the name of the character encoding, for example'UTF-8'or'ISO-8859-1'. Its default isnull. When it is omitted ornull, PHP uses the current internal encoding, which you can inspect withmb_internal_encoding(). Passing the encoding explicitly makes your code independent of that global setting, so it is a good habit.
Return value and failure cases
The function returns an int: the number of characters in the string, where a multibyte character counts as one. An empty string gives 0.
Since PHP 8.0, an unknown encoding name throws a ValueError. Older versions emitted a warning and returned false, but you no longer need to check for false in PHP 8.4. Also since 8.0 the $encoding parameter is nullable.
Example 1: bytes versus characters
The letter é (U+00E9) takes two bytes in UTF-8. The example builds it with an escape so the result does not depend on how the file was saved.
$text = "h\u{E9}llo";
echo json_encode([
'bytes' => strlen($text),
'characters' => mb_strlen($text, 'UTF-8'),
]);{"bytes":6,"characters":5}The string has five characters but six bytes, so the output is {"bytes":6,"characters":5}.
Example 2: code points are not always visible symbols
For UTF-8, mb_strlen() counts Unicode code points. A visible symbol can be made of several of them. A letter followed by a combining accent is two code points, and a family emoji joined with zero-width joiners is five.
$combining = "e\u{0301}";
$thumbs = "\u{1F44D}";
$family = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}";
echo json_encode([
'combining' => mb_strlen($combining, 'UTF-8'),
'thumbs' => mb_strlen($thumbs, 'UTF-8'),
'family' => mb_strlen($family, 'UTF-8'),
]);{"combining":2,"thumbs":1,"family":5}The result is {"combining":2,"thumbs":1,"family":5}. A single emoji outside the basic plane is one character even though it uses four bytes. If you need to count what a reader sees as one unit, look at grapheme_strlen() from the intl extension instead.
Example 3: other encodings and invalid names
The same bytes mean different things in different encodings, which is why the second argument matters. In ISO-8859-1 every character is exactly one byte. An unsupported encoding name raises a ValueError that you can catch.
echo mb_strlen("caf\xE9", 'ISO-8859-1'), "\n";
try {
mb_strlen('abc', 'NOPE');
} catch (ValueError $e) {
echo get_class($e), "\n";
}4 ValueError
The first line prints 4 and the second prints ValueError.
Edge cases
- Pure ASCII text gives the same number from
strlen()andmb_strlen(), so bugs often appear only when the first accented name or emoji arrives. - The count follows the encoding you declare, not what the bytes really are. Declaring the wrong encoding gives a number that is wrong but still an integer, without any error.
- Strings with bytes that are invalid for the chosen encoding are still processed, and the count for them should not be treated as meaningful. Validate input first with
mb_check_encoding()if it comes from an untrusted source. - Combining marks, zero-width joiners and variation selectors each add to the count, as shown in Example 2.
- The function counts characters, not display width. East Asian full-width characters count as one, although they usually occupy two columns in a terminal. Use
mb_strwidth()when you need columns.
Common mistakes
- Using
strlen()for user-facing limits. A 10-character limit measured in bytes rejects names that are perfectly fine. - Relying on the default encoding. If another part of the program changes the internal encoding, results change too. Pass
'UTF-8'explicitly. - Checking for
false. Modern PHP throws instead, so afalsecheck is dead code. CatchValueErroronly if the encoding name comes from outside. - Mixing byte and character functions. Measuring with
mb_strlen()and cutting withsubstr()can split a character in half. Pair it withmb_substr(). - Assuming one character is one symbol. Emoji sequences and accents built from combining marks break that assumption.
Related functions
strlen()returns the length in bytes.mb_internal_encoding()gets or sets the default encoding used when$encodingisnull.grapheme_strlen()counts grapheme units, closer to what a reader sees.iconv_strlen()is a similar character count from the iconv extension.mb_substr(),mb_strwidth()andmb_check_encoding()complete the typical multibyte toolkit.
Recap
mb_strlen() returns an integer character count for a string in a given encoding. Pass the encoding explicitly, remember that it counts code points rather than visible symbols, and expect a ValueError for an invalid encoding name. Use strlen() only when you really want bytes.
Tested examples
$text = "h\u{E9}llo";
echo json_encode([
'bytes' => strlen($text),
'characters' => mb_strlen($text, 'UTF-8'),
]);{"bytes":6,"characters":5}
$combining = "e\u{0301}";
$thumbs = "\u{1F44D}";
$family = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}";
echo json_encode([
'combining' => mb_strlen($combining, 'UTF-8'),
'thumbs' => mb_strlen($thumbs, 'UTF-8'),
'family' => mb_strlen($family, 'UTF-8'),
]);{"combining":2,"thumbs":1,"family":5}
echo mb_strlen("caf\xE9", 'ISO-8859-1'), "\n";
try {
mb_strlen('abc', 'NOPE');
} catch (ValueError $e) {
echo get_class($e), "\n";
}4 ValueError