mb_ord
Returns the Unicode code point of the first character of a string.
mb_ord(string $string, ?string $encoding = null): int|false- Added in PHP 7.2.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string whose first character is inspected. Any characters after the first are ignored. |
$encodingoptional | ?string= null | The encoding the string is in. If omitted or null, the internal encoding is used. |
Return value
int|falseThe Unicode code point of the first character, or false on failure.
Errors
Throws a ValueError if encoding is not a name mbstring recognises. An empty or malformed first character is reported by returning false.
Changelog
- PHP 8.0.0encoding became nullable.
mb_ord() is the inverse of mb_chr() and the multibyte replacement for ord(). ord() returns the value of the first byte, so on any non-ASCII character it gives you a lead byte such as 195 rather than the code point you wanted. mb_ord() decodes the whole character and returns its Unicode code point.
Note that it looks only at the first character; everything after it is ignored. To walk a whole string, split it with mb_str_split() and call mb_ord() on each piece.
The result is useful whenever you need to classify characters numerically rather than by pattern: detecting whether a string contains astral-plane characters above U+FFFF (most emoji, which older MySQL utf8 columns cannot store), checking whether input falls inside a specific Unicode block, or generating a stable numeric key. It is also the natural way to produce U+XXXX notation for debugging, via printf('U+%04X', mb_ord($c)).
The return type is int|false. An empty string yields false, and so does a first character that is malformed in the given encoding. Because false compares loosely equal to 0, and 0 is a legitimate code point (NUL), always test with ===.
Example
When to use it
- Detect emoji or other characters above U+FFFF before writing to a database column that cannot hold them.
- Print U+XXXX notation when debugging an encoding problem.
- Check whether every character of a name falls inside an allowed Unicode range.
- Convert a character to a numeric key for a lookup table or checksum.
Frequently asked questions
What is the difference between ord and mb_ord?
ord() returns the numeric value of the first byte, so for a multi-byte character it gives a meaningless lead-byte value such as 195. mb_ord() decodes the full character and returns its Unicode code point.
How do I get the Unicode code point of an emoji in PHP?
Call mb_ord($emoji, 'UTF-8') — for example mb_ord('🐘', 'UTF-8') returns 128024, which is U+1F418. Format it for display with printf('U+%04X', $cp).
Does mb_ord work on a whole string?
No, it only reads the first character and ignores the rest. Split the string with mb_str_split() and call mb_ord() on each character to get every code point.
More examples
Round-trip with mb_chr and walk a string
mb_ord() and mb_chr() are exact inverses for any valid character, which the identity check confirms. Splitting with mb_str_split() first is the standard way to get code points for a whole string, since mb_ord() itself only ever reports on the first character.
<?php
$char = "ñ";
$cp = mb_ord($char, 'UTF-8');
printf("%s = U+%04X\n", $char, $cp);
var_dump(mb_chr($cp, 'UTF-8') === $char);
foreach (mb_str_split("héllo", 1, 'UTF-8') as $c) {
printf("%s\tU+%04X\n", $c, mb_ord($c, 'UTF-8'));
}Find characters that will not fit in a three-byte column
Anything above U+FFFF needs four bytes in UTF-8, which is exactly what MySQL's old three-byte utf8 charset cannot store. Testing the code point catches emoji, rare CJK characters and mathematical symbols alike, where a hand-written emoji regex would miss most of them.
<?php
function hasAstral(string $s): bool {
foreach (mb_str_split($s, 1, 'UTF-8') as $c) {
if (mb_ord($c, 'UTF-8') > 0xFFFF) {
return true;
}
}
return false;
}
var_dump(hasAstral("plain café"));
var_dump(hasAstral("party 🎉"));The false return and why === matters
"\0" is a real character with code point 0, while the invalid byte 0xFF returns false. Both report true for a loose == 0 comparison, which is why === 0 is the only safe test for the NUL character. An empty string is different again: since PHP 8 it throws a ValueError rather than returning false.
<?php
$cases = ["A", "\0", "\xFF"];
foreach ($cases as $s) {
$cp = mb_ord($s, 'UTF-8');
printf("%-8s %-8s loose-zero:%s\n",
bin2hex($s),
var_export($cp, true),
var_export($cp == 0, true)
);
}
// An empty string is a ValueError, not a false return.
try {
mb_ord('', 'UTF-8');
} catch (ValueError $e) {
echo 'ValueError: ', $e->getMessage(), "\n";
}
Discussion