utf8_encode
Converts an ISO-8859-1 string to UTF-8; deprecated since PHP 8.2.
utf8_encode(string $string): string- Added in PHP 4.0
- Deprecated since PHP 8.2.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | A string assumed to be encoded as ISO-8859-1. |
Return value
stringThe UTF-8 translation of the input string.
Errors
Emits an E_DEPRECATED diagnostic on every call as of PHP 8.2.0.
Changelog
- PHP 8.2.0Deprecated. Use mb_convert_encoding(), iconv() or UConverter::transcode() instead.
- PHP 7.2.0Moved from the XML extension into PHP core; previously it was only available when ext/xml was installed.
utf8_encode() maps every byte of the input to the Unicode code point with the same numeric value and re-encodes it as UTF-8. That is the correct conversion for exactly one input encoding, ISO-8859-1 (Latin-1), and for nothing else. It has been deprecated since PHP 8.2 and every call now emits an E_DEPRECATED diagnostic.
What to use instead
Replace it with mb_convert_encoding($s, 'UTF-8', 'ISO-8859-1'), iconv('ISO-8859-1', 'UTF-8', $s), or UConverter::transcode(). All three let you state the source encoding explicitly, which is the point: nine times out of ten the legacy data was actually Windows-1252, not ISO-8859-1, and only the explicit call can express that.
What trips people up
The function name reads like "make this UTF-8", but it does no detection. Run it on a string that is already UTF-8 and every byte is re-encoded a second time, producing the familiar mojibake where café becomes café. Run it on Windows-1252 and the bytes 0x80–0x9F — the euro sign, curly quotes, em dashes — become invisible C1 control characters rather than the intended symbols.
Check what you actually have with mb_check_encoding() before converting anything.
Example
When to use it
- Migrating a legacy Latin-1 database column to UTF-8 (replace the call with mb_convert_encoding first).
- Reading an old fixed-format export file whose bytes are genuinely ISO-8859-1.
- Auditing a codebase for double-encoding bugs caused by an earlier utf8_encode call.
Frequently asked questions
Is utf8_encode removed in PHP 8.5?
No, it still exists but has been deprecated since PHP 8.2 and emits an E_DEPRECATED diagnostic on every call. It is expected to be removed in a future major version, so migrate to mb_convert_encoding() or iconv() now.
What should I use instead of utf8_encode?
mb_convert_encoding($string, 'UTF-8', 'ISO-8859-1') is the direct replacement, or iconv('ISO-8859-1', 'UTF-8', $string). Check the real source encoding first — legacy Windows data is almost always Windows-1252, not ISO-8859-1.
Why did utf8_encode turn my accented characters into gibberish?
Because the input was already UTF-8. utf8_encode() does no detection and re-encodes each byte, so é (bytes C3 A9) becomes é. Test with mb_check_encoding($s, 'UTF-8') before converting.
More examples
The double-encoding trap
The input is already valid UTF-8, but utf8_encode() cannot tell — it treats the two bytes C3 A9 as two separate Latin-1 characters and encodes each again, producing four bytes and the visible mojibake café. The bin2hex output makes the doubling explicit. Note that the check for ISO-8859-1 returns true, which is why encoding detection cannot save you here: every byte sequence is valid Latin-1.
<?php
$utf8 = 'café';
$broken = @utf8_encode($utf8);
echo $broken, "\n";
var_dump(bin2hex($utf8), bin2hex($broken));
var_dump(mb_check_encoding($utf8, 'ISO-8859-1'));Windows-1252 is not ISO-8859-1
Byte 0x80 is the euro sign in Windows-1252 but an unassigned control position in ISO-8859-1. utf8_encode() produces C2 80, an invisible C1 control character, while mb_convert_encoding() with the correct source encoding produces E2 82 AC — a real euro sign. Most "Latin-1" legacy data from Windows systems is really Windows-1252, so this is the usual cause of missing euro signs and curly quotes.
<?php
$cp1252 = "Price: \x80" . '9,99';
echo 'utf8_encode : ', @utf8_encode($cp1252), "\n";
echo 'mb_convert : ', mb_convert_encoding($cp1252, 'UTF-8', 'Windows-1252'), "\n";
var_dump(bin2hex(@utf8_encode("\x80")), bin2hex(mb_convert_encoding("\x80", 'UTF-8', 'Windows-1252')));Migrating a legacy column safely
Real legacy tables are usually mixed: some rows were written as Latin-1 bytes and some were already UTF-8. Checking with mb_check_encoding() before converting leaves the valid UTF-8 rows alone and only converts the rest, avoiding the double-encoding a blanket utf8_encode() pass would cause. The hex column shows which rows started as single high bytes.
<?php
$legacy = ["Jos\xE9", 'Ana', "M\xFCller", 'café'];
foreach ($legacy as $row) {
$fixed = mb_check_encoding($row, 'UTF-8')
? $row
: mb_convert_encoding($row, 'UTF-8', 'Windows-1252');
printf("%-12s -> %s\n", bin2hex($row), $fixed);
}
Discussion