mb_chr
Returns the character for a given Unicode code point, encoded in the requested character encoding.
mb_chr(int $codepoint, ?string $encoding = null): string|false- Added in PHP 7.2.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$codepoint | int | A Unicode code point, for example 128024 for U+1F418 ELEPHANT. |
$encodingoptional | ?string= null | The encoding the returned character should be in. If omitted or null, the internal encoding is used. |
Return value
string|falseA string containing the character for the code point, or false if the code point cannot be represented in the requested encoding.
Errors
Throws a ValueError if encoding is not a name mbstring recognises. An unrepresentable code point is reported by returning false, not by throwing.
Changelog
- PHP 8.0.0encoding became nullable.
mb_chr() is the multibyte counterpart of chr(). Where chr() takes a byte value from 0 to 255 and hands back one byte, mb_chr() takes a Unicode code point and hands back the complete multi-byte character — one to four bytes for UTF-8.
You reach for it whenever you have a number and need the character it names: decoding numeric HTML entities such as é, turning a U+1F600 style token from a data feed into a real emoji, generating a character range, or reversing something that mb_ord() produced. It is the natural building block for any code that reasons about characters as numbers.
The return type is string|false and the false case matters. If the code point has no representation in the target encoding — asking for the euro sign in ISO-8859-1, or any emoji in a single-byte encoding — you get false, not a substitution character. Test with === against false before concatenating, because false silently becomes an empty string inside a string context and the character just vanishes from your output.
Passing a negative code point, or one above U+10FFFF, also yields false. Surrogate code points in the U+D800–U+DFFF range are not valid characters and are rejected too.
Example
When to use it
- Decode numeric HTML entities like 😀 into the actual characters.
- Turn a "U+1F600" style code point column from a data export into real emoji.
- Build a test string covering a specific Unicode block, one code point at a time.
- Round-trip characters through their numeric values alongside mb_ord().
Frequently asked questions
What is the difference between chr and mb_chr?
chr() takes a byte value 0–255 and returns exactly one byte, so it cannot produce any character outside the single-byte range. mb_chr() takes a Unicode code point and returns the full multi-byte encoding of that character.
Why does mb_chr return false?
Because the code point cannot be represented in the requested encoding, or because it is out of range — negative, above U+10FFFF, or a surrogate in U+D800–U+DFFF. Compare with === false before using the result.
How do I convert a Unicode code point to an emoji in PHP?
Call mb_chr($codepoint, 'UTF-8') — for example mb_chr(0x1F600, 'UTF-8') returns 😀. The equivalent literal form is "\u{1F600}" in a double-quoted string, which is preferable when the code point is a constant.
More examples
Build a string from a list of code points
Four code points become four characters but nine bytes: two ASCII letters at one byte each, a heart at three and a globe at four. The gap between mb_strlen() and strlen() is exactly why you use mb_chr() instead of chr() as soon as your data leaves ASCII.
<?php
$points = [72, 101, 0x2764, 0x1F30D];
$out = '';
foreach ($points as $p) {
$out .= mb_chr($p, 'UTF-8');
}
echo $out, "\n";
echo mb_strlen($out, 'UTF-8'), " characters, ", strlen($out), " bytes\n";Decode numeric HTML entities
Each &#N; token is replaced by the character for code point N, producing "Café 😀 €". This works for any decimal entity, including astral-plane characters above U+FFFF that older entity tables often miss.
<?php
$html = "Café 😀 €";
$decoded = preg_replace_callback(
'/&#(\d+);/',
fn(array $m): string => mb_chr((int) $m[1], 'UTF-8'),
$html
);
echo $decoded, "\n";Guard against unrepresentable characters
A and é exist in Latin-1 and come back as characters; the euro sign and the elephant do not, so mb_chr() returns false and the fallback prints a readable placeholder instead of dropping the character. Without the === false test those two lines would print nothing at all.
<?php
function toLatin1(int $cp): string {
$c = mb_chr($cp, 'ISO-8859-1');
return $c === false ? '[U+' . strtoupper(dechex($cp)) . ']' : $c;
}
foreach ([0x41, 0xE9, 0x20AC, 0x1F418] as $cp) {
echo mb_convert_encoding(toLatin1($cp), 'UTF-8', 'ISO-8859-1'), "\n";
}
Discussion