Signature
mb_detect_encoding(string $string, array|string|null $encodings = null, bool $strict = false): string|falsePurpose
mb_detect_encoding() looks at a byte string and picks the most likely character encoding from a list of candidates you supply. It is most useful with multibyte encodings such as UTF-8 or Shift_JIS, where many byte sequences are simply invalid. If the bytes cannot be valid in a given encoding, that encoding is rejected.
The name promises more than the function delivers. It guesses. Whenever you can, rely on encoding information that travels with the data, such as a Content-Type header or a database column setting, and treat this function as a last resort.
Signature
Parameters and defaults
$string: the bytes to inspect.$encodings: the candidates to try. Pass an array such as['ASCII', 'UTF-8']or a single comma-separated string. If you omit it or passnull, PHP uses the current detect order, which comes from thembstring.detect_ordersetting ormb_detect_order(). The special nameautois expanded according tombstring.language.$strict: defaults tofalse. When it isfalseand the string is invalid in every candidate, PHP returns the closest match. When it istrue, PHP returnsfalseinstead. The default can also be changed with thembstring.strict_detectionsetting.
Return value and failure cases
The function returns the detected encoding name as a string, or false if the string is not valid in any listed encoding. In practice false only happens when $strict is true. Because the returned name is the canonical one, it may differ in case or spelling from what you passed in, for example SJIS-win. Unknown encoding names in the list are rejected with an error rather than guessed, so use names from mb_list_encodings().
Since PHP 8.1 the function uses heuristics among the valid candidates, so the result is not necessarily the first valid encoding in your list. Since PHP 8.2 it no longer returns the non-text pseudo-encodings Base64, QPrint, UUencode, HTML entities, 7 bit and 8 bit.
Example 1: ASCII versus UTF-8
With a short, strict candidate list the answer is clear. Plain ASCII bytes are valid ASCII. A string containing the UTF-8 bytes for é is not valid ASCII, so only UTF-8 remains.
$samples = [
'plain' => 'hello',
'accented' => "caf\xC3\xA9",
];
$result = [];
foreach ($samples as $name => $bytes) {
$result[$name] = mb_detect_encoding($bytes, ['ASCII', 'UTF-8'], true);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"plain":"ASCII","accented":"UTF-8"}
Example 2: the effect of strict
The bytes \xE1\xE9\xF3\xFA are áéóú in ISO-8859-1. They are not valid ASCII or UTF-8. Without strict mode PHP still names the closest candidate. With strict mode it admits that nothing fits. The pinned PHP 8.4 runtime guesses ASCII for this deliberately invalid sample in non-strict mode; that guess does not make the bytes valid ASCII. Heuristic guesses are not reliable declarations of an encoding.
$latin1 = "\xE1\xE9\xF3\xFA";
$result = [
mb_detect_encoding($latin1, ['ASCII', 'UTF-8'], false),
mb_detect_encoding($latin1, ['ASCII', 'UTF-8'], true),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["ASCII",false]
Example 3: detect, then convert
A common pattern is to accept either UTF-8 or a known legacy encoding, detect strictly, and convert the result to UTF-8. Here UTF-8 is rejected because the bytes are not a valid UTF-8 sequence, leaving ISO-8859-1.
$input = "\xE1\xE9\xF3\xFA";
$encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true);
$utf8 = $encoding === false ? null : mb_convert_encoding($input, 'UTF-8', $encoding);
echo json_encode(['encoding' => $encoding, 'utf8' => $utf8], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"encoding":"ISO-8859-1","utf8":"áéóú"}
Edge cases
- Ambiguous bytes. The sequence
\xC4\xA2is valid UTF-8 (the letterĢ), but it is also valid ISO-8859-1 and ISO-8859-5. With the list['UTF-8', 'ISO-8859-1', 'ISO-8859-5'], PHP 8.1 and later returnsISO-8859-1, notUTF-8. No algorithm can know which reading was intended. - Candidate order matters, but not absolutely. Order can influence ties, yet the heuristics may pick a later candidate.
- Single-byte encodings accept almost anything. If your list includes ISO-8859-1, nearly every byte string is valid in it, so it works as a catch-all and tells you little.
- Short strings. Very short inputs give the heuristics little evidence, so results are weaker.
Common mistakes
- Treating the result as a fact. It is a guess, and it can be wrong even for valid input.
- Leaving
$strictatfalseand then assuming the returned encoding is valid. Non-strict mode returns the closest match even for invalid bytes. - Using the function to validate UTF-8. Use
mb_check_encoding($string, 'UTF-8')for that. It answers a yes/no question directly. - Checking the result with a loose test such as
if ($encoding). Compare explicitly againstfalse. - Passing a huge candidate list. More candidates means more false positives. Offer only the encodings your data can realistically use.
- Forgetting that the detect order is global configuration. If you pass
null, results depend onmbstring.detect_order.
Related functions
mb_detect_order()gets or sets the default candidate order.mb_check_encoding()checks validity against one encoding.mb_convert_encoding()converts text between encodings.mb_list_encodings()lists the supported encoding names.
Recap
mb_detect_encoding() guesses among the encodings you offer. Give it a short, realistic list, use strict mode so invalid input yields false, and prefer declared encodings over detection whenever the data source provides them.
Tested examples
$samples = [
'plain' => 'hello',
'accented' => "caf\xC3\xA9",
];
$result = [];
foreach ($samples as $name => $bytes) {
$result[$name] = mb_detect_encoding($bytes, ['ASCII', 'UTF-8'], true);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"plain":"ASCII","accented":"UTF-8"}
$latin1 = "\xE1\xE9\xF3\xFA";
$result = [
mb_detect_encoding($latin1, ['ASCII', 'UTF-8'], false),
mb_detect_encoding($latin1, ['ASCII', 'UTF-8'], true),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["ASCII",false]
$input = "\xE1\xE9\xF3\xFA";
$encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true);
$utf8 = $encoding === false ? null : mb_convert_encoding($input, 'UTF-8', $encoding);
echo json_encode(['encoding' => $encoding, 'utf8' => $utf8], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"encoding":"ISO-8859-1","utf8":"áéóú"}