Multibyte strings7 min readExamples run on PHP 8.5.10

mb_ord()

Learn how mb_ord() returns the Unicode code point of the first character in a string, how encodings change the result, and where it differs from ord().

Signature

mb_ord(string $string, ?string $encoding = null): int|false
Available since PHP 7.2.07.48.08.18.28.38.48.5All versions →

Purpose

mb_ord() answers the question "which Unicode code point is the first character of this string?" It reads the string in a given character encoding, decodes the first character and returns its numeric value. The letter A gives 65, the euro sign gives 8364, and the elephant emoji gives 128024.

This is different from ord(), which only looks at the first byte. In UTF-8 many characters take two, three or four bytes, so a byte value says little about the character itself. mb_ord() is the multibyte-aware counterpart and it complements mb_chr(), which goes in the opposite direction.

Signature

Parameters and defaults

  • $string: the text to inspect. Only its first character matters; the rest is ignored.
  • $encoding: the character encoding of $string, such as "UTF-8", "ISO-8859-1" or "Windows-1252". If you omit it or pass null, PHP uses the current internal encoding, which you can read with mb_internal_encoding().

Passing the encoding explicitly keeps your code independent of configuration, so it is a good habit.

Return value and failure cases

On success you get an int: the Unicode code point of the first character. On failure the function returns false, so a strict comparison with false is the correct way to detect a problem.

Since PHP 8.0.0, an invalid $encoding name throws a ValueError. Before 8.0.0 it emitted a warning and returned false. Also since 8.0.0, the $encoding parameter is nullable.

Example 1: Basic code points

The following example decodes four characters of increasing byte length. Unicode escapes keep the source file unambiguous.

Example
<?php
$values = [
    mb_ord("A", "UTF-8"),
    mb_ord("\u{E9}", "UTF-8"),
    mb_ord("\u{20AC}", "UTF-8"),
    mb_ord("\u{1F418}", "UTF-8"),
];
echo json_encode($values), "\n";
Output
[65,233,8364,128024]

The values are 65, 233, 8364 and 128024. They are the code points U+0041, U+00E9, U+20AC and U+1F418.

Example 2: Only the first character counts

The next example compares mb_ord() with ord() on a string that starts with a three-byte character.

Example
<?php
$text = "\u{20AC}uro";
echo json_encode([
    'mb_ord' => mb_ord($text, 'UTF-8'),
    'ord' => ord($text[0]),
    'bytes' => strlen("\u{20AC}"),
]), "\n";
Output
{"mb_ord":8364,"ord":226,"bytes":3}

mb_ord() returns the code point 8364 for the whole euro sign. ord() returns 226, which is merely the first of its three UTF-8 bytes. The example passes $text[0], the first byte, because calling ord() with a longer string is deprecated as of PHP 8.5. The trailing letters uro are ignored by both functions.

Example 3: Encoding changes the meaning

The same raw byte can be a different character in a different encoding. Byte 0x80 is a control character in ISO-8859-1 but the euro sign in Windows-1252. An unknown encoding name raises a ValueError.

Example
<?php
echo json_encode([
    mb_ord("\x80", "ISO-8859-1"),
    mb_ord("\x80", "Windows-1252"),
]), "\n";

try {
    mb_ord("A", "NOPE");
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
[128,8364]
ValueError

Edge cases

  • Emoji and characters outside the Basic Multilingual Plane return a single large integer, not a surrogate pair, so values above 65535 are normal.
  • Combining sequences: a letter followed by a combining accent is two code points. mb_ord() reports only the first one, so it does not describe a whole user-perceived character (grapheme).
  • Empty strings do not have a first character, so check the input before calling the function rather than relying on a particular result.
  • Malformed input may lead to a false return. Always compare strictly with false, because a valid code point is never false but a loose check against 0 could still confuse you.

Common mistakes

  1. Using ord() for non-ASCII text and expecting a code point. It returns a byte value from 0 to 255.
  2. Forgetting the encoding and depending on a default that differs between environments.
  3. Assuming the function inspects the whole string. It only decodes the first character; to process every character, split the string first, for example with mb_str_split().
  4. Writing if (!mb_ord($s)) instead of === false.
  5. Passing a user-supplied encoding name without validating it, which can trigger a ValueError.
  • mb_chr() converts a code point back to a character.
  • mb_internal_encoding() gets or sets the default encoding used when $encoding is null.
  • IntlChar::ord() returns a code point when the intl extension is available.
  • ord() returns the first byte of a string.

Recap

mb_ord() decodes the first character of a string in the given encoding and returns its Unicode code point as an int, or false on failure. Name the encoding explicitly, remember that only the first character is read, and use ord() only when you really want a byte.

Tested examples

Basic code points for characters of different byte lengths
<?php
$values = [
    mb_ord("A", "UTF-8"),
    mb_ord("\u{E9}", "UTF-8"),
    mb_ord("\u{20AC}", "UTF-8"),
    mb_ord("\u{1F418}", "UTF-8"),
];
echo json_encode($values), "\n";
Output
[65,233,8364,128024]
mb_ord() versus ord() on a multibyte first character
<?php
$text = "\u{20AC}uro";
echo json_encode([
    'mb_ord' => mb_ord($text, 'UTF-8'),
    'ord' => ord($text[0]),
    'bytes' => strlen("\u{20AC}"),
]), "\n";
Output
{"mb_ord":8364,"ord":226,"bytes":3}
Different encodings and an invalid encoding name
<?php
echo json_encode([
    mb_ord("\x80", "ISO-8859-1"),
    mb_ord("\x80", "Windows-1252"),
]), "\n";

try {
    mb_ord("A", "NOPE");
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
[128,8364]
ValueError