Multibyte strings7 min readExamples run on PHP 8.5.10

mb_decode_numericentity()

Learn how mb_decode_numericentity() turns HTML numeric character references such as é back into real characters, and how its conversion map controls which ones are decoded.

Signature

mb_decode_numericentity(string $string, array $map, ?string $encoding = null): string
Available since PHP 4.0.67.48.08.18.28.38.48.5All versions →

Purpose

mb_decode_numericentity() converts HTML numeric character references, such as é, into the characters they stand for. It is the counterpart of mb_encode_numericentity(). You need it when text arrives with accented letters, currency symbols or other non-ASCII characters written as numeric references, and you want a normal string again.

The function does not decode every reference blindly. You pass a conversion map that describes which code ranges are eligible. References outside those ranges stay as written.

Signature

Parameters and defaults

  • $string: the text that may contain numeric references.
  • $map: a flat array of integers. It is read in groups of four values per rule: start_code, end_code, offset and mask. The start and end values define the code area to convert. The offset and mask let you shift and mask the number, which is mainly useful for custom schemes.
  • $encoding: the character encoding of the string and the result. The default is null, which means the current internal encoding is used. Passing 'UTF-8' explicitly makes the code independent of configuration.

Return value and failures

The function returns the converted string. If nothing matches, the text comes back unchanged.

It throws a ValueError if $encoding is not a valid encoding name. Since PHP 8.4.0 it also throws a ValueError if $map contains a value that is not an int, float, bool, null or numeric string, that is, a value that cannot be implicitly converted to an integer. Before PHP 8.0.0 an invalid encoding produced a warning instead; with PHP 8.4 you can simply catch the exception.

Example 1: decode references in a UTF-8 string

The common map [0x80, 0x10FFFF, 0, 0x1FFFFF] covers everything above ASCII, with no offset and a mask wide enough for any Unicode code point.

Example
<?php
$map = [0x80, 0x10FFFF, 0, 0x1FFFFF];
echo mb_decode_numericentity('Caf&#233; &#8364;5', $map, 'UTF-8'), "\n";
Output
Café €5

Example 2: the map limits what is decoded

Here the only rule covers 0x41 to 0x5A, the uppercase ASCII letters. The reference for A (65) is inside the range and is decoded. The reference for lowercase a (97) is outside it and is left alone.

Example
<?php
$map = [0x41, 0x5A, 0, 0xFF];
echo mb_decode_numericentity('&#65;&#97;', $map, 'UTF-8'), "\n";
Output
A&#97;

Example 3: round trip with the encoder

Using the same map for both functions gives a reliable round trip. The encoder produces references for non-ASCII characters and the decoder restores the original text.

Example
<?php
$map = [0x80, 0x10FFFF, 0, 0x1FFFFF];
$original = 'Привет';
$encoded = mb_encode_numericentity($original, $map, 'UTF-8');
$decoded = mb_decode_numericentity($encoded, $map, 'UTF-8');
echo $encoded, "\n";
echo $decoded, "\n";
var_dump($decoded === $original);
Output
&#1055;&#1088;&#1080;&#1074;&#1077;&#1090;
Привет
bool(true)

Edge cases

  • A string with no references is returned as it is.
  • A reference whose number lies outside every rule in the map is preserved literally, as Example 2 shows.
  • Several rules can be placed one after another in the same flat array, four values each, to decode more than one range.
  • The encoding matters. The result is built in the encoding you give, so the string you pass in and the encoding name should agree.

Common mistakes

  1. Treating the map as optional or as a list of characters. It is a flat array of numeric rules, four integers per rule, not a list of characters to decode.
  2. Choosing a range that is too narrow. If a reference stays undecoded, check whether its number falls between the start and end values of some rule.
  3. Putting non-numeric values in the map. In PHP 8.4 a value that cannot be converted to an integer throws a ValueError, so do not assume it is silently ignored.
  4. Relying on the default encoding. If the internal encoding differs from the data, the result can be wrong. Pass 'UTF-8' when you work with UTF-8 text.
  5. Expecting it to decode named entities. References like &eacute; or &amp; are not numeric references. Use html_entity_decode() for those.
  6. Assuming decoded text is safe HTML. Decoding &#60;script&#62; can produce markup characters. Escape output properly after decoding when it is shown in a page.
  • mb_encode_numericentity(): the opposite conversion, from characters to numeric references.
  • html_entity_decode(): decodes named and numeric HTML entities using HTML rules.
  • mb_convert_encoding(): converts text between character encodings.
  • mb_internal_encoding(): reads or sets the default encoding that null refers to.

Recap

Use mb_decode_numericentity() to turn numeric references into real characters inside the code ranges you describe in $map. Give the encoding explicitly, remember that out-of-range references are left unchanged, and expect a ValueError for an invalid encoding or, in PHP 8.4, for map values that are not numeric.

Tested examples

Decode references in a UTF-8 string
<?php
$map = [0x80, 0x10FFFF, 0, 0x1FFFFF];
echo mb_decode_numericentity('Caf&#233; &#8364;5', $map, 'UTF-8'), "\n";
Output
Café €5
The map limits what is decoded
<?php
$map = [0x41, 0x5A, 0, 0xFF];
echo mb_decode_numericentity('&#65;&#97;', $map, 'UTF-8'), "\n";
Output
A&#97;
Round trip with the encoder
<?php
$map = [0x80, 0x10FFFF, 0, 0x1FFFFF];
$original = 'Привет';
$encoded = mb_encode_numericentity($original, $map, 'UTF-8');
$decoded = mb_decode_numericentity($encoded, $map, 'UTF-8');
echo $encoded, "\n";
echo $decoded, "\n";
var_dump($decoded === $original);
Output
&#1055;&#1088;&#1080;&#1074;&#1077;&#1090;
Привет
bool(true)