HTML-Safe Output and Escaping

Sanitizing makes input fit a rule on arrival (filter_var(), Subsection 4.14.6). Escaping encodes a value for the context it is printed into, at output time. "Cleaned" input still breaks a page inside an attribute or a script, so store what the user typed and escape every value where you print it.

Escape for the context the value is printed into
Output context Escape with
HTML text, quoted attribute htmlspecialchars() with ENT_QUOTES
URL query value rawurlencode(), then HTML-escape the URL
Inline JavaScript data json_encode() with the JSON_HEX_* flags
SQL, shell Prepared statements (Databases with PDO), escapeshellarg() (Command Injection)
Escaping a comment, an attribute and a link (escape.php)PHP
<?php
function e(?string $v): string
{
    return htmlspecialchars($v ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
$comment = "<script>alert('x')</script>\n5 > 3 & \"ok\"";
$name = '" onmouseover="alert(1)';
$q = 'rock & roll/2026';
echo strip_tags('<a href="javascript:alert(1)">Click</a> <b>x</b>', '<a>'), "\n";
echo urlencode($q), ' ', rawurlencode($q), ' ', json_encode('</script>', JSON_HEX_TAG), "\n";
?>
<p><?= nl2br(e($comment)) ?></p>
<input value="<?= e($name) ?>">
<a href="/search?q=<?= e(urlencode($q)) ?>">More</a>
Output
<a href="javascript:alert(1)">Click</a> x
rock+%26+roll%2F2026 rock%20%26%20roll%2F2026 "\u003C\/script\u003E"
<p>&lt;script&gt;alert(&apos;x&apos;)&lt;/script&gt;<br />
5 &gt; 3 &amp; &quot;ok&quot;</p>
<input value="&quot; onmouseover=&quot;alert(1)">
<a href="/search?q=rock+%26+roll%2F2026">More</a>

Since PHP 8.1 the default flags include ENT_QUOTES | ENT_SUBSTITUTE; the e() helper adds HTML5 entities and makes the choice explicit. Call nl2br() after escaping. strip_tags() is no defense: it passed the javascript: link. Blade escapes {{ }} for you (Laravel), Subsections 4.17.5-4.17.7 cover cross-site scripting, and PHP 8.5's URI extension parses whole URLs (Subsection 4.18.5).