mb_detect_encoding
Guesses which character encoding a string is in, from a list of candidate encodings.
mb_detect_encoding(string $string, array|string|null $encodings = null, bool $strict = false): string|false- Added in PHP 4.0.6
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string being inspected. |
$encodingsoptional | array|string|null= null | Candidate encodings, as an array of names or a single comma-separated string. If omitted or null, the current detect order from mb_detect_order() is used. |
$strictoptional | bool= false | When false, the closest matching candidate is returned even if the string is not valid in it. When true, false is returned unless the string is genuinely valid in one of the candidates. |
Return value
string|falseThe name of the detected encoding, or false when the string is valid in none of the candidates and strict is true.
Errors
Throws a ValueError if any candidate encoding name is not recognised by mbstring.
Changelog
- PHP 8.2.0The non-text encodings "Base64", "QPrint", "UUencode", "HTML entities", "7 bit" and "8 bit" are no longer returned.
- PHP 8.1.0Detection now uses heuristics to pick the most likely encoding and may not follow the order in which candidates were listed.
mb_detect_encoding() guesses. That word is doing all the work on this page: nothing in a byte stream records its own encoding, so the function tries each candidate and picks the one that looks most plausible. It is a last resort, not a design.
Two rules make it usable. First, always pass an explicit candidate list — two or three encodings you genuinely expect, never the whole of mb_list_encodings(). The more candidates you offer, the more ways there are to be confidently wrong. Second, pass strict: true. With strict left at false, the function returns the closest match even when the string is invalid in every candidate, so you get an encoding name for what is really garbage.
The single most reliable use is a yes/no check on one encoding: mb_detect_encoding($s, 'UTF-8', true) tells you whether the bytes are valid UTF-8. For that job mb_check_encoding() is clearer and returns a boolean directly.
Detection is weakest exactly where you need it most. Any pure-ASCII string is valid in nearly every encoding. Short strings carry too little evidence. And the single-byte encodings — ISO-8859-1, ISO-8859-15, Windows-1252 — overlap so heavily that distinguishing them is guesswork with no correct answer available. Prefer an HTTP Content-Type header, an XML declaration, or a documented file format whenever one exists.
Example
When to use it
- Decide per row whether an imported CSV line is UTF-8 or Windows-1252 before normalising it.
- Pick a conversion source for text pasted into a form by users on mixed platforms.
- Screen files in a legacy upload directory to find the ones that still need re-encoding.
- Confirm a third-party feed really is UTF-8 before trusting its Content-Type header.
Frequently asked questions
Is mb_detect_encoding reliable?
Not on its own — it guesses from byte patterns, and single-byte encodings such as ISO-8859-1 and Windows-1252 are frequently indistinguishable. Use a declared encoding from an HTTP header or file format when one exists, and always pass strict: true with a short candidate list when you must guess.
What is the difference between mb_detect_encoding and mb_check_encoding?
mb_detect_encoding() picks an encoding from candidates and returns its name; mb_check_encoding() verifies one encoding you name and returns a boolean. If you already know what the data should be, use mb_check_encoding().
Why does mb_detect_encoding return UTF-8 for broken bytes?
Because the third parameter, strict, defaults to false, which asks for the closest match rather than a valid one. Pass true as the third argument to get false when the string is not genuinely valid in any candidate.
More examples
Normalise mixed input to UTF-8
Each row is detected then converted from whatever it turned out to be, so both print "café". Keeping the candidate list down to the two encodings the data can actually be in is what makes this reliable; adding a third similar single-byte encoding would start producing wrong answers.
<?php
$inputs = [
"café",
mb_convert_encoding("café", 'ISO-8859-1', 'UTF-8'),
];
foreach ($inputs as $raw) {
$enc = mb_detect_encoding($raw, ['UTF-8', 'ISO-8859-1'], true);
echo $enc, " -> ", mb_convert_encoding($raw, 'UTF-8', $enc), "\n";
}Strict mode versus lenient mode
Strict mode correctly reports false. Lenient mode returns "UTF-8" for the same broken bytes, because it is only asked for the closest candidate. mb_check_encoding() agrees with strict mode and says so with a plain boolean — which is why it is the better tool when you already know the encoding you expect.
<?php
$bad = "caf\xE9 \xF0\x28"; // valid in no UTF-8 sense
var_dump(mb_detect_encoding($bad, ['UTF-8'], true));
var_dump(mb_detect_encoding($bad, ['UTF-8'], false));
var_dump(mb_check_encoding($bad, 'UTF-8'));ASCII matches almost everything
The same string is reported as valid in every one of these encodings, because ASCII bytes are legal in all of them. Detection can only ever narrow things down using bytes above 0x7F, so for ASCII-only content the answer it gives you is arbitrary rather than wrong.
<?php
$ascii = "plain text, no accents";
foreach ([['ASCII'], ['UTF-8'], ['ISO-8859-1'], ['Shift_JIS']] as $list) {
printf("%-12s %s\n", $list[0], var_export(
mb_detect_encoding($ascii, $list, true), true
));
}
Discussion