utf8_encode

Converts an ISO-8859-1 string to UTF-8; deprecated since PHP 8.2.

Signature
utf8_encode(string $string): string
  • Added in PHP 4.0
  • Deprecated since PHP 8.2.0

Parameters

ParameterTypeDescription
$stringstringA 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

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · php
<?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.

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.