mb_strlen
Counts the number of characters in a string for a given encoding, not the number of bytes.
mb_strlen(string $string, ?string $encoding = null): int- Added in PHP 4.0.6
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string being measured. |
$encodingoptional | ?string= null | Character encoding. Null uses the internal encoding. |
Return value
intThe number of characters in the string; each multibyte character counts as 1.
Errors
Throws a ValueError if encoding is not a valid encoding.
Changelog
- PHP 8.0.0encoding is nullable now.
- PHP 8.0.0Now throws a ValueError if encoding is invalid. Previously an E_WARNING was emitted and false was returned.
mb_strlen() answers "how many characters", where strlen() answers "how many bytes". For pure ASCII they agree; for anything else they diverge immediately. strlen('café') is 5 because é occupies two bytes in UTF-8, while mb_strlen('café') is 4.
When to reach for it
Any length you show a user or validate against a human-facing rule should come from mb_strlen(): a 280-character post limit, a minimum password length, a username rule, a character counter in a form. Any length that describes storage or transport — a byte-limited column, a Content-Length, a buffer size — should come from strlen(). Choosing the wrong one is how a German or Japanese user gets rejected by a validator that lets English through.
What trips people up
It counts codepoints, and codepoints are not glyphs. A skin-toned emoji like 👍🏽 is 2 codepoints, a family emoji can be 7, and e followed by a combining acute is 2 even though it looks identical to a precomposed é. Normalising with Normalizer::normalize() fixes the second case; grapheme_strlen() fixes the first.
The encoding argument matters too: pass the wrong one and the count is wrong but no error is raised, because the bytes are still decodable as something.
Example
When to use it
- Enforce a 280-character post limit fairly for users writing in any language
- Validate a minimum password length by characters rather than bytes
- Check whether a value fits a character-counted database column before insert
- Compare against strlen() to detect whether a string contains any multibyte characters
Frequently asked questions
What is the difference between strlen and mb_strlen?
strlen counts bytes and mb_strlen counts characters in the given encoding. They return the same number only for pure ASCII, so use mb_strlen for anything a user typed and strlen for storage or transport limits.
Does mb_strlen count emoji as one character?
Only simple single-codepoint emoji. Emoji with skin-tone modifiers, flags or zero-width joiners span several codepoints, so use grapheme_strlen() from the intl extension when you need the count a user would give.
Why does mb_strlen return the wrong count for my string?
Almost always an encoding mismatch — the string is not in the encoding mb_strlen assumed. Set mb_internal_encoding('UTF-8') or pass the encoding explicitly as the second argument.
More examples
Validating a character limit
The German line is 17 characters but 20 bytes and the Japanese line is 10 characters but 30 bytes. A limit checked with strlen() would reject the Japanese post despite it being the shortest of the three by any human measure.
<?php
mb_internal_encoding('UTF-8');
$posts = ['Hello there', 'Grüße aus München', '日本語のテキストです'];
foreach ($posts as $p) {
$ok = mb_strlen($p) <= 15 ? 'ok' : 'too long';
printf("%-2d chars / %-2d bytes %s\n", mb_strlen($p), strlen($p), $ok);
}Codepoints versus graphemes
Both print as é but one counts as 1 character and the other as 2, and they are not equal as strings. This is why user input should be Unicode-normalised before you measure or compare it.
<?php
mb_internal_encoding('UTF-8');
$precomposed = "\u{00E9}"; // é as one codepoint
$combining = "e\u{0301}"; // e + combining acute
echo $precomposed, ' = ', mb_strlen($precomposed), "\n";
echo $combining, ' = ', mb_strlen($combining), "\n";
var_dump($precomposed === $combining);
Discussion