Multibyte strings7 min readExamples run on PHP 8.5.10

mb_convert_variables()

Learn how mb_convert_variables() converts strings, arrays and object properties between character encodings in place, and how its encoding detection and failure cases behave.

Signature

mb_convert_variables(
    string $to_encoding,
    array|string $from_encoding,
    mixed &$var,
    mixed &...$vars
): string|false
Available since PHP 4.0.67.48.08.18.28.38.48.5All versions →

Purpose

mb_convert_variables() converts the character encoding of one or more variables in place. You pass the target encoding, the encoding the data is currently in, and then the variables by reference. Strings are converted directly. Arrays and objects are walked, and the strings inside them are converted too. This makes it handy when a whole batch of input, such as decoded form fields or a parsed record, arrives in the same legacy encoding and must be normalised in one step.

Unlike mb_convert_encoding(), which returns a new value, this function modifies its arguments and returns something different: the name of the encoding the data was in before conversion.

Signature

Parameters

  • $to_encoding: the encoding to convert to, for example "UTF-8" or "ISO-8859-1". It has no default and must be a valid encoding name.
  • $from_encoding: the current encoding. You can give a single name, a comma-separated string, or an array of names. With several candidates, PHP detects which one fits. The special value "auto" is expanded according to the mbstring.language INI setting, so avoid it when you need environment-independent behaviour.
  • $var: the first variable to convert, passed by reference. It can be a string, an array or an object.
  • ...$vars: any number of further variables, also by reference.

All the variables are assumed to share one encoding. For arrays and objects, the strings inside are joined to make detection more reliable, because detection on short strings often fails. The consequence is that you cannot mix encodings inside a single array or object.

Return value and failure cases

On success the function returns the encoding the data had before conversion, as a string. If you passed one encoding, you get that name back. If you passed several, you learn which one was detected. This is useful for logging or for deciding what to do with later input.

It returns false when it cannot proceed, for example when detection among the candidates fails. An unknown encoding name for $to_encoding or $from_encoding throws a ValueError in PHP 8. Always compare the result with false using === before trusting it.

Example 1: converting a single string

The string below starts as UTF-8 and ends as ISO-8859-1. Looking at the bytes with bin2hex() shows the change: é was two bytes in UTF-8 and is now the single byte e9.

Example
<?php
$text = "café";
$from = mb_convert_variables("ISO-8859-1", "UTF-8", $text);
echo json_encode([
    "from" => $from,
    "hex" => bin2hex($text),
    "length" => strlen($text),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"from":"UTF-8","hex":"636166e9","length":4}

Example 2: several variables and detection

Here an array and a separate string are converted in one call. The candidate list ASCII,UTF-8 lets PHP pick the matching encoding from the data. Because the data contains non-ASCII characters in valid UTF-8, the detected source is UTF-8.

Example
<?php
$row = ["name" => "Zoë", "city" => "Zürich"];
$label = "Ærø";
$from = mb_convert_variables("ISO-8859-1", "ASCII,UTF-8", $row, $label);
echo json_encode([
    "from" => $from,
    "name" => bin2hex($row["name"]),
    "city" => bin2hex($row["city"]),
    "label" => bin2hex($label),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"from":"UTF-8","name":"5a6feb","city":"5afc72696368","label":"c672f8"}

Example 3: objects and invalid encodings

Object properties are converted in place as well. An invalid encoding name does not fail quietly: it throws a ValueError, which you can catch.

Example
<?php
$user = new stdClass();
$user->name = "Aé";
$result = mb_convert_variables("UTF-16BE", "UTF-8", $user);
echo json_encode([
    "from" => $result,
    "hex" => bin2hex($user->name),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";

try {
    $text = "abc";
    mb_convert_variables("NOT-AN-ENCODING", "UTF-8", $text);
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
{"from":"UTF-8","hex":"004100e9"}
ValueError

Edge cases

  • Detection needs enough data. Detection among several candidates can pick the wrong encoding for very short or ambiguous text. If you know the encoding, pass exactly one name and skip detection.
  • Mixed encodings are unsupported. Because the strings in an array or object are treated as one body of text, one element in UTF-8 and another in Shift_JIS cannot both be converted correctly.
  • Non-string values are left alone. Integers, booleans and null inside an array are not changed. Only string data is converted.
  • Invalid byte sequences. Data that is not valid in the declared source encoding may be replaced according to mbstring's substitution settings, so garbage in does not always raise an error.
  • Variables must be real variables. Because of the by-reference parameters, you cannot pass a literal or the result of a function call directly.

Common mistakes

  1. Treating the return value as the converted data. The return value is the previous encoding name, not the string. The converted data is in your variables.
  2. Forgetting the original is overwritten. If you need the source text later, copy it before the call.
  3. Skipping the === false check. A failed detection returns false, and using it as an encoding name later causes confusing bugs.
  4. Relying on "auto". Its expansion depends on mbstring.language, which can differ between servers.
  5. Converting already-converted data. Running the call twice with the same source encoding corrupts the text. Track the encoding you returned.
  6. Using it for one string when a return value is easier. For a single value, mb_convert_encoding() is often simpler because it returns the new string.
  • mb_convert_encoding() returns converted strings or arrays rather than editing variables.
  • mb_detect_encoding() identifies an encoding without converting.
  • mb_check_encoding() validates that data is well formed in an encoding.
  • mb_internal_encoding() reads or sets the default encoding.
  • mb_substitute_character() controls what replaces unconvertible characters.

Recap

mb_convert_variables() converts strings, arrays and object properties in place from one encoding, or a detected one, to another. It returns the original encoding name or false, throws ValueError for unknown encodings, and assumes all passed variables share one source encoding. Prefer a single explicit source encoding whenever you know it.

Tested examples

Convert a single string from UTF-8 to ISO-8859-1
<?php
$text = "café";
$from = mb_convert_variables("ISO-8859-1", "UTF-8", $text);
echo json_encode([
    "from" => $from,
    "hex" => bin2hex($text),
    "length" => strlen($text),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"from":"UTF-8","hex":"636166e9","length":4}
Convert an array and a string together with detection
<?php
$row = ["name" => "Zoë", "city" => "Zürich"];
$label = "Ærø";
$from = mb_convert_variables("ISO-8859-1", "ASCII,UTF-8", $row, $label);
echo json_encode([
    "from" => $from,
    "name" => bin2hex($row["name"]),
    "city" => bin2hex($row["city"]),
    "label" => bin2hex($label),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"from":"UTF-8","name":"5a6feb","city":"5afc72696368","label":"c672f8"}
Convert object properties and catch an invalid encoding
<?php
$user = new stdClass();
$user->name = "Aé";
$result = mb_convert_variables("UTF-16BE", "UTF-8", $user);
echo json_encode([
    "from" => $result,
    "hex" => bin2hex($user->name),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";

try {
    $text = "abc";
    mb_convert_variables("NOT-AN-ENCODING", "UTF-8", $text);
} catch (ValueError $e) {
    echo get_class($e), "\n";
}
Output
{"from":"UTF-8","hex":"004100e9"}
ValueError