Signature
htmlspecialchars(
string $string,
int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401,
?string $encoding = null,
bool $double_encode = true
): stringPurpose
htmlspecialchars() turns characters that have a special meaning in HTML into entities, so the browser shows them as text instead of parsing them as markup. It converts only a small set: the ampersand, double quote, single quote (depending on flags), less-than and greater-than. When you print user-supplied text into an HTML page, this is the usual first line of defence against broken markup and cross-site scripting.
Signature (PHP 8.4)
Parameters and defaults
$string: the text to convert.$flags: a bitmask that controls three things: quote handling (ENT_COMPAT,ENT_QUOTES,ENT_NOQUOTES), handling of invalid byte sequences (ENT_SUBSTITUTE,ENT_IGNORE) and the document type (ENT_HTML401,ENT_HTML5,ENT_XHTML,ENT_XML1). Since PHP 8.1.0 the default isENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401.$encoding: the character set of the input. When it isnull, PHP uses thedefault_charsetsetting. For UTF-8 pages, passing'UTF-8'explicitly is a good habit.$double_encode: whentrue(the default), everything is converted, including the&of an existing entity such as&. Whenfalse, existing entities are left alone.
Return value and failure cases
The function returns the converted string. If the input contains an invalid code unit sequence for the chosen encoding, it returns an empty string, unless ENT_SUBSTITUTE (replace with U+FFFD) or ENT_IGNORE (silently drop) is set. The default flags include ENT_SUBSTITUTE, so invalid UTF-8 normally becomes a replacement character rather than an empty string. An unrecognised encoding name makes PHP use the default encoding and emit a warning.
Example 1: the default conversion
With the default flags, both kinds of quotes are converted. A single quote becomes ' in HTML 4.01 mode.
$input = "<a href='test'>Tom & \"Jerry\"</a>";
echo htmlspecialchars($input), "\n";<a href='test'>Tom & "Jerry"</a>
Example 2: how flags change quotes
Quote flags matter most when the output goes inside an HTML attribute. ENT_COMPAT leaves single quotes alone, ENT_NOQUOTES leaves both alone, and ENT_HTML5 writes a single quote as '.
$text = "She said \"hi\" & it's fine";
echo json_encode([
'default' => htmlspecialchars($text),
'compat' => htmlspecialchars($text, ENT_COMPAT),
'noquotes' => htmlspecialchars($text, ENT_NOQUOTES),
'html5' => htmlspecialchars($text, ENT_QUOTES | ENT_HTML5),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"default":"She said "hi" & it's fine","compat":"She said "hi" & it's fine","noquotes":"She said \"hi\" & it's fine","html5":"She said "hi" & it's fine"}
Example 3: double_encode and invalid UTF-8
The first two lines show the effect of double_encode. The last two lines use bin2hex() to show the bytes returned for an invalid UTF-8 byte (\x80), first without and then with ENT_SUBSTITUTE. Without it, the result is an empty string, so the third line is blank.
$text = "Fish & Chips < 5";
echo htmlspecialchars($text), "\n";
echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false), "\n";
$bad = "atta\x80ck";
echo bin2hex(htmlspecialchars($bad, ENT_QUOTES, 'UTF-8')), "\n";
echo bin2hex(htmlspecialchars($bad, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')), "\n";Fish &amp; Chips < 5 Fish & Chips < 5 61747461efbfbd636b
Edge cases
- If neither
ENT_COMPAT,ENT_QUOTESnorENT_NOQUOTESis present in$flags, the behaviour isENT_NOQUOTES. If several are present,ENT_QUOTESwins, thenENT_COMPAT. Passing a custom flags value that omits a quote flag therefore stops converting quotes. - If several document-type flags are present,
ENT_HTML5has the highest precedence. If none is present,ENT_HTML401applies. ENT_IGNOREis discouraged because silently dropping bytes may have security implications. PreferENT_SUBSTITUTE.- The function does not translate other characters, such as accented letters or symbols. If you need every character that has a named entity converted, use
htmlentities(). - UTF-8, ISO-8859-1, ISO-8859-15, cp1251, cp1252, cp866 and KOI8-R are effectively equivalent for this function, because the characters it changes sit at the same positions, as long as the string is valid in that encoding.
Common mistakes
- Escaping on input instead of on output. Store the original text and escape it when you write it into HTML. Escaping early leads to double-escaped data and wrong results in other contexts.
- Using it for every context. The function prepares text for HTML content and quoted attributes. It does not make a value safe inside JavaScript, CSS or a URL. Those contexts need their own encoding.
- Dropping the quote flags. If you pass
ENT_NOQUOTESor a flags value without a quote flag, an attacker may close an attribute value with a quote. - Double-escaping. Running the function twice turns
<into&lt;. Escape once, at the last moment. Usedouble_encode: falseonly when the input is known to contain valid entities that you want to keep. - Mismatched encoding. Declare the real character set of the page and the input. Relying on a wrong
default_charsetcan give empty or garbled results. - Expecting it to remove tags. It makes tags visible as text. It does not delete them.
strip_tags()is a different tool with different trade-offs.
Related functions
htmlspecialchars_decode()converts the special entities back to characters.htmlentities()converts all characters that have named entities.get_html_translation_table()shows the translation table in use.strip_tags()removes HTML and PHP tags.nl2br()inserts<br>elements before newlines, and is usually applied after escaping.
Recap
htmlspecialchars() converts &, <, > and (by default) both quote types into entities. The 8.4 defaults are ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401. Always escape at output time, keep the encoding correct, leave double_encode at true unless you have a reason, and remember that it only covers HTML text and attribute contexts.
Tested examples
$input = "<a href='test'>Tom & \"Jerry\"</a>";
echo htmlspecialchars($input), "\n";
<a href='test'>Tom & "Jerry"</a>
$text = "She said \"hi\" & it's fine";
echo json_encode([
'default' => htmlspecialchars($text),
'compat' => htmlspecialchars($text, ENT_COMPAT),
'noquotes' => htmlspecialchars($text, ENT_NOQUOTES),
'html5' => htmlspecialchars($text, ENT_QUOTES | ENT_HTML5),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"default":"She said "hi" & it's fine","compat":"She said "hi" & it's fine","noquotes":"She said \"hi\" & it's fine","html5":"She said "hi" & it's fine"}
$text = "Fish & Chips < 5";
echo htmlspecialchars($text), "\n";
echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false), "\n";
$bad = "atta\x80ck";
echo bin2hex(htmlspecialchars($bad, ENT_QUOTES, 'UTF-8')), "\n";
echo bin2hex(htmlspecialchars($bad, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')), "\n";
Fish &amp; Chips < 5 Fish & Chips < 5 61747461efbfbd636b