Multibyte strings7 min readExamples run on PHP 8.5.10

mb_strlen()

Learn how mb_strlen() counts characters instead of bytes in PHP 8.4, how the encoding argument works, and where code points differ from visible symbols.

Signature

mb_strlen(string $string, ?string $encoding = null): int
Available since PHP 4.0.67.48.08.18.28.38.48.5All versions →

Purpose

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

  • $string is the text to measure. It is read as raw bytes and interpreted using the chosen encoding.
  • $encoding is the name of the character encoding, for example 'UTF-8' or 'ISO-8859-1'. Its default is null. When it is omitted or null, PHP uses the current internal encoding, which you can inspect with mb_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.

Example
<?php
$text = "h\u{E9}llo";
echo json_encode([
    'bytes' => strlen($text),
    'characters' => mb_strlen($text, 'UTF-8'),
]);
Output
{"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.

Example
<?php
$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'),
]);
Output
{"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.

Example
<?php
echo mb_strlen("caf\xE9", 'ISO-8859-1'), "\n";
try {
    mb_strlen('abc', 'NOPE');
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
4
ValueError

The first line prints 4 and the second prints ValueError.

Edge cases

  • Pure ASCII text gives the same number from strlen() and mb_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

  1. Using strlen() for user-facing limits. A 10-character limit measured in bytes rejects names that are perfectly fine.
  2. Relying on the default encoding. If another part of the program changes the internal encoding, results change too. Pass 'UTF-8' explicitly.
  3. Checking for false. Modern PHP throws instead, so a false check is dead code. Catch ValueError only if the encoding name comes from outside.
  4. Mixing byte and character functions. Measuring with mb_strlen() and cutting with substr() can split a character in half. Pair it with mb_substr().
  5. Assuming one character is one symbol. Emoji sequences and accents built from combining marks break that assumption.
  • strlen() returns the length in bytes.
  • mb_internal_encoding() gets or sets the default encoding used when $encoding is null.
  • 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() and mb_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

Bytes versus characters
<?php
$text = "h\u{E9}llo";
echo json_encode([
    'bytes' => strlen($text),
    'characters' => mb_strlen($text, 'UTF-8'),
]);
Output
{"bytes":6,"characters":5}
Code points are not always visible symbols
<?php
$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'),
]);
Output
{"combining":2,"thumbs":1,"family":5}
Other encodings and invalid names
<?php
echo mb_strlen("caf\xE9", 'ISO-8859-1'), "\n";
try {
    mb_strlen('abc', 'NOPE');
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
4
ValueError