htmlspecialchars_decode
Converts the entities produced by htmlspecialchars back into their plain characters.
htmlspecialchars_decode(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string- Added in PHP 5.1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string to decode. |
$flagsoptional | int= ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 | Bitmask specifying which quote entities are decoded and which document type's table is used. |
Return value
stringThe decoded string, with &, <, > and the enabled quote entities turned back into characters.
Changelog
- PHP 8.1.0The default flags changed from ENT_COMPAT to ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, so single-quote entities are now decoded by default.
htmlspecialchars_decode is the exact inverse of htmlspecialchars. It handles only the entities that function produces — &, <, > and, depending on the flags, " and '. Everything else, including ©, and numeric references, is left untouched.
When to reach for it
Use it when you know the text was escaped with htmlspecialchars and you need the original back for a non-HTML destination: a plain-text email, a PDF, a CSV export, a search index, or a length check. That narrow scope is a feature — it will not accidentally turn into an invisible non-breaking space or resolve entities that were meant to stay literal.
What trips people up
The flags must match the ones used when encoding. Decode with ENT_NOQUOTES what you encoded with ENT_QUOTES and the quote entities survive into the output. PHP 8.1 changed the default from ENT_COMPAT to ENT_QUOTES, so a round trip that used to leave ' behind now decodes cleanly.
And as with any decoder: the result is unsafe to print into HTML. Decode for processing, then re-escape before output.
Example
When to use it
- Recover the original text of an escaped comment before sending it in a plain-text email.
- Undo escaping on database rows an old application stored HTML-encoded, before re-indexing them.
- Measure the real length of a value that was stored escaped, since & counts as five characters.
Frequently asked questions
Why is not being decoded?
Because htmlspecialchars_decode only handles the entities htmlspecialchars creates: &, <, >, " and '. Use html_entity_decode to resolve the full named-entity table.
Which flags should I pass?
The same ones you used when encoding. Since PHP 8.1 both functions default to ENT_QUOTES, so leaving the argument off on both sides gives a clean round trip.
Is it safe to print the decoded string in a page?
No. Decoding restores working markup, so re-escape with htmlspecialchars before sending anything to a browser.
More examples
A clean round trip
Encoding and decoding with the same ENT_QUOTES flag returns the string byte-for-byte. Any mismatch in flags between the two calls would leave quote entities in the decoded result and the comparison would be false.
<?php
$original = "5 o'clock & \"sharp\" <b>";
$encoded = htmlspecialchars($original, ENT_QUOTES, "UTF-8");
echo $encoded, "\n";
$decoded = htmlspecialchars_decode($encoded, ENT_QUOTES);
var_dump($decoded === $original);Mismatched flags leave entities behind
ENT_QUOTES decodes both quote styles. ENT_COMPAT decodes only the double quotes, leaving ' in place of the apostrophe. ENT_NOQUOTES leaves both entities visible — the decoder must be told the same thing the encoder was.
<?php
$encoded = htmlspecialchars("it's a \"test\"", ENT_QUOTES, "UTF-8");
echo htmlspecialchars_decode($encoded, ENT_QUOTES), "\n";
echo htmlspecialchars_decode($encoded, ENT_COMPAT), "\n";
echo htmlspecialchars_decode($encoded, ENT_NOQUOTES), "\n";Narrow scope compared to html_entity_decode
The first line decodes only & and <, leaving the named and numeric entities intact. html_entity_decode resolves all of them. The final check shows surviving as six literal characters, which is exactly the behaviour you want when those entities are meant to stay.
<?php
$s = "& < © é |—";
echo htmlspecialchars_decode($s, ENT_QUOTES), "\n";
echo html_entity_decode($s, ENT_QUOTES, "UTF-8"), "\n";
var_dump(strlen(htmlspecialchars_decode(" ")));
Discussion