Signature
quoted_printable_decode(string $string): stringPurpose
quoted_printable_decode() converts text written in the quoted-printable transfer encoding back into an ordinary 8-bit string. Quoted-printable is a scheme from the MIME standard (RFC 2045, section 6.7). It lets mostly-ASCII text travel through channels that only handle 7-bit data. Any byte outside the safe range is written as an equals sign followed by two hexadecimal digits, so the byte 0xC3 becomes =C3.
You meet this format when you handle raw email bodies, MIME parts labelled Content-Transfer-Encoding: quoted-printable, or exports from mail systems. The function undoes the encoding. Its counterpart is quoted_printable_encode().
Signature
Parameters and defaults
$string: the quoted-printable input. It is required and has no default.
The function takes no options. It cannot be told which character set the result uses. That is up to you and, in email, to the charset parameter of the Content-Type header.
Return value and failure cases
The function always returns a string: the decoded bytes. It does not return false and does not signal malformed input with a warning. Instead it applies a few simple rules:
=followed by two hexadecimal digits becomes the single byte those digits describe. Upper and lower case hex digits are both accepted.=followed by a line break is a soft line break. The equals sign and the break are removed, so the two lines are joined. Spaces or tabs between the=and the break are removed too.- An
=that fits neither rule is copied through unchanged. - Every other character is copied as it is.
Because the parameter is typed string, passing something that cannot be converted, such as an array, raises a TypeError.
Example 1: decoding multibyte text
The encoded text below stores each UTF-8 byte of ö and Ä as an =XX pair. After decoding, the result is a normal UTF-8 string. Looking at the bytes with bin2hex() shows that é is really two bytes.
$decoded = quoted_printable_decode('M=C3=B6chten Sie ein paar =C3=84pfel?');
$bytes = bin2hex(quoted_printable_decode('caf=C3=A9'));
echo json_encode(['text' => $decoded, 'hex' => $bytes], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"text":"Möchten Sie ein paar Äpfel?","hex":"636166c3a9"}
Example 2: soft line breaks
Encoders cut long lines, because email lines should stay short. They mark each cut with a trailing =. The decoder removes the marker and the break, but real line breaks without a leading = are kept.
$result = [
'crlf' => quoted_printable_decode("Hello=\r\nWorld"),
'lf' => quoted_printable_decode("Line=\nbreak"),
'spaces' => quoted_printable_decode("Tail= \nend"),
'hard' => quoted_printable_decode("One\r\nTwo"),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"crlf":"HelloWorld","lf":"Linebreak","spaces":"Tailend","hard":"One\r\nTwo"}
Example 3: lenient handling of odd input
Lower-case hex digits are decoded. A sequence such as =ZZ is not valid hex, so it survives untouched. An underscore stays an underscore.
$result = [
'case' => quoted_printable_decode('a=3db=3Dc'),
'invalid' => quoted_printable_decode('x=ZZy'),
'underscore' => quoted_printable_decode('a_b'),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"case":"a=b=c","invalid":"x=ZZy","underscore":"a_b"}
Edge cases
- Literal equals signs. A real
=in the original text must be encoded as=3D. If it was not, the decoder may swallow or misread what follows it. - Non-UTF-8 results. A sequence such as
=FFproduces a byte that is not valid UTF-8 by itself. Functions likejson_encode()will refuse such a string, so inspect it withbin2hex()or convert it first. - Hard breaks are preserved. Line endings that are not preceded by
=are real line endings and stay in the output. - Empty input. An empty string decodes to an empty string.
Common mistakes
- Using it for encoded email headers. Header words such as
=?UTF-8?Q?...?=use the related Q encoding, where an underscore stands for a space. This function does not treat_specially and does not parse the=?charset?Q?wrapper. - Assuming the result is UTF-8. The function returns bytes. Whether they form UTF-8, ISO-8859-1 or something else depends on the original charset.
- Expecting validation. Malformed sequences are passed through, not reported. If you need strict checking, validate the input yourself before decoding.
- Decoding twice. Running the function on already decoded text can change content that happens to contain
=followed by two hex digits. - Mixing it up with URL or Base64 decoding. Percent encoding uses
%XXand is handled byrawurldecode(). Base64 is handled bybase64_decode().
Related functions
quoted_printable_encode()creates quoted-printable text from an 8-bit string.base64_decode()handles the other common MIME body encoding.rawurldecode()decodes percent-encoded text.bin2hex()helps you see the bytes a decode produced.mb_check_encoding()can test whether the decoded bytes are valid in a given encoding, if the mbstring extension is available.
Recap
quoted_printable_decode() takes one string and returns the decoded bytes. It turns =XX into a byte, removes soft line breaks, and leaves anything it does not understand alone. It reports no errors for bad input, it does not decode header Q-words, and it says nothing about character sets, so check the encoding of the result before using it as text.
Tested examples
$decoded = quoted_printable_decode('M=C3=B6chten Sie ein paar =C3=84pfel?');
$bytes = bin2hex(quoted_printable_decode('caf=C3=A9'));
echo json_encode(['text' => $decoded, 'hex' => $bytes], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"text":"Möchten Sie ein paar Äpfel?","hex":"636166c3a9"}
$result = [
'crlf' => quoted_printable_decode("Hello=\r\nWorld"),
'lf' => quoted_printable_decode("Line=\nbreak"),
'spaces' => quoted_printable_decode("Tail= \nend"),
'hard' => quoted_printable_decode("One\r\nTwo"),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"crlf":"HelloWorld","lf":"Linebreak","spaces":"Tailend","hard":"One\r\nTwo"}
$result = [
'case' => quoted_printable_decode('a=3db=3Dc'),
'invalid' => quoted_printable_decode('x=ZZy'),
'underscore' => quoted_printable_decode('a_b'),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"case":"a=b=c","invalid":"x=ZZy","underscore":"a_b"}