A heredoc is a multiline string. Start it with <<<EOT, write the text on the following lines, and end it with EOT; on its own line. Variables inside are replaced as in a double-quoted string.
The identifier (EOT here) is a letter or underscore followed by letters, digits or underscores. People pick names that say what the text is: HTML, SQL, TEXT. Some editors, PhpStorm among them, highlight the body as that language when the name is HTML or SQL.
Build an HTML template with heredoc
Heredoc is the clearest way to write a block of HTML with values in it, because the markup keeps its shape and nothing needs escaping. The output is HTML, so the editor shows it on the Page tab.
Two details in that block: the array element is wrapped in braces, {$product['name']}, and the literal dollar sign before the price is escaped as \$. Change the name or add a tag and run it again.
Values from users must be escaped before they go into HTML. See htmlspecialchars for why and how.
Nowdoc: text without interpolation
A nowdoc puts the identifier in single quotes: <<<'EOT'. Nothing inside is replaced, so dollar signs and backslashes stay as written. Use it for code samples, regular expressions and any text that contains $.
Indent the closing marker
Since PHP 7.3 the closing marker may be indented, and that indentation is removed from every line of the body. This lets a heredoc inside a function or an if follow the code's indentation without the spaces ending up in the string.
The json_encode line makes the result visible character by character: 8 spaces were removed from each line, because the closing TEXT; is indented by 8.
A body line indented less than the closing marker is a parse error:
$html = <<<HTML
<p>Hello</p>
HTML;
PHP Parse error: Invalid body indentation level (expecting an indentation level of at least 4) in /home/index.php on line 3
Arrays, objects and method calls inside heredoc
Heredoc follows the same rules as double quotes. Simple variables work alone; for array keys in quotes, nested arrays, properties and method calls, use braces.
Heredoc as a function argument
Since PHP 7.3 the closing marker can be followed by more code on the same line, so a heredoc can be passed straight to a function or put in an array. With sprintf you get a template whose placeholders are filled later.
Common mistake: missing semicolon or extra text after the marker
The closing identifier must be written exactly as it was opened (same case), and whatever follows it on that line must make sense as PHP. EOT; ends a statement, EOT, continues an argument list. A letter or digit right after the marker (EOTX;) hides it, so the string runs to the end of the file and PHP reports syntax error, unexpected end of file. The opposite trap is a body line that starts with the identifier followed by a space or punctuation, such as EOT is the marker: it ends the string early, and the rest of that line is read as PHP (syntax error, unexpected identifier "is"). Pick an identifier that never appears at the start of a line in the text, such as HTML for markup or SQL for a query.
Frequently Asked Questions
What is a heredoc in PHP?
A way to write a multiline string without quotes: start with <<<EOT and a newline, write the text, and end with EOT; on its own line. Variables inside are replaced, exactly as in a double-quoted string, and you can use " and ' freely.
What is the difference between heredoc and nowdoc?
A heredoc (<<<EOT) replaces variables and escape sequences like a double-quoted string. A nowdoc (<<<'EOT', identifier in single quotes) does neither and keeps every $ and backslash as written, like a single-quoted string.
Can the closing heredoc marker be indented?
Yes, since PHP 7.3. The indentation of the closing marker is removed from every line of the body, so you can indent a heredoc to match your code. Every body line must be indented at least as far as the closing marker, or PHP stops with a parse error.
How do I call a function inside a heredoc?
Heredoc only interpolates variables, array elements, properties and method calls. Call the function first and store the result, or keep the function name or a closure in a variable and call it inside braces: $e = 'htmlspecialchars'; then {$e($text)}.