In PHP, how do heredoc and nowdoc strings differ, and what does the flexible closing marker introduced in PHP 7.3 allow?
answer
- <<<EOT versus <<<'EOT'
- heredoc like double quotes, nowdoc like single
- closing marker may be indented
- that indentation is stripped from every line
- tabs and spaces cannot mix
basics
~20 sHeredoc (<<<EOT) is a multi-line double-quoted string that interpolates variables and escapes; nowdoc (<<<'EOT') is its literal, single-quoted twin. Since PHP 7.3 the closing marker may be indented, and that indentation is removed from every line.
solid answer
~40 sBoth open with `<<<`, an identifier and a newline, and end with the same identifier. **Heredoc** (`<<<EOT`, optionally `<<<"EOT"`) behaves like a double-quoted string without the quotes: `$vars`, `{$expr}` and escapes like `\t` work, and quotes need no escaping. **Nowdoc** (`<<<'EOT'`) behaves like single quotes and parses nothing, not even `\\`. Since PHP 7.3 the closing marker may be **indented** with spaces or tabs; exactly that much whitespace is stripped from the start of every body line, so the template can follow the code's indentation. A body line indented less than the marker is a `ParseError`, as is mixing tabs and spaces. The marker may also be followed by more code, such as `)` or `,`, on the same line. Choose a marker word that never starts a line of the body.
code
php · 20 lines<?php
declare(strict_types=1);
function reminderBody(string $name, DateTimeImmutable $slot): string
{
return <<<MAIL
Hello {$name},
your appointment is on {$slot->format('l, j F')} at {$slot->format('H:i')}.
- bring your insurance card
Reply "CANCEL" to cancel.
MAIL;
}
$sample = <<<'TPL'
Hello {$name}, fee: $40\n
TPL;
echo reminderBody('Ada', new DateTimeImmutable('2026-10-06 14:30'));
echo PHP_EOL, $sample, PHP_EOL; // Hello {$name}, fee: $40\n (nothing parsed)go deeper
Recall that heredoc works like double quotes and nowdoc like single quotes, and that nowdoc uses a quoted identifier.
Explain the PHP 7.3 flexible marker: indentation stripping, the ParseError for under-indented lines and mixed tabs and spaces, and code after the marker.
Pick marker words that cannot start a body line, use nowdoc for literal blocks, and remember heredoc templates still need output escaping.
Decide when inline heredoc templates should move into template files with an engine that escapes by context, especially for emails and HTML.
## Two multi-line string syntaxes For text that spans many lines, such as an email body, PHP offers two block syntaxes that begin with `<<<`, an identifier and a newline, and end at a line holding the same identifier: | | Heredoc | Nowdoc | |---|---|---| | Opening | `<<<EOT` or `<<<"EOT"` | `<<<'EOT'` | | Behaves like | a double-quoted string | a single-quoted string | | Variable interpolation | yes: `$name`, `{$slot->time}` | no | | Escape sequences (`\n`, `\t`, `\x41`) | yes | no; every character is literal | | Quotes inside | need no escaping | need no escaping | | Usable as a class constant or property default | only without variables | yes | The identifier follows the rules of any PHP label: letters, digits and underscores, not starting with a digit. `EOT`, `TEXT`, `MAIL` and `SQL` are common choices. ## Heredoc for templates with values A heredoc is the natural shape for an appointment-reminder email body: ```php $body = <<<MAIL Hello {$patient->firstName}, this is a reminder of your appointment on {$slot->format('l, j F')} at {$slot->format('H:i')} with {$doctor->name}. Reply "CANCEL" to cancel. MAIL; ``` Everything that works in double quotes works here, including the rule that method calls and quoted keys need curly braces. The newline before the closing marker is not part of the string. ## Nowdoc for literal blocks A nowdoc parses nothing, which makes it the right choice for text that contains `$` signs or backslashes meant literally: a sample template shown in documentation, a block of shell or SQL code to display, a regex with many backslashes. `<<<'TPL'` followed by `Hello {$name}` keeps the text `{$name}` exactly as written, so the same template can later be filled by a different mechanism. ## The flexible closing marker (PHP 7.3) Before PHP 7.3 the closing identifier had to start in the first column and could be followed only by a semicolon and a newline, which forced templates to break the code's indentation. Since 7.3: 1. **The closing marker may be indented** with spaces or tabs. 2. **Its indentation is removed** from the start of every line of the body, so the example above produces lines that start at `Hello`, not four spaces in. 3. **A body line indented less** than the closing marker is a `ParseError`: "Invalid body indentation level (expecting an indentation level of at least 4)". 4. **Tabs and spaces must not be mixed** in that indentation: `ParseError` "Invalid indentation - tabs and spaces cannot be mixed". 5. **Code may follow the marker** on the same line, so `sprintf(<<<EOT … EOT, $a)` or an array element ending in `EOT,` is valid. Body lines may be indented **more** than the marker; the extra whitespace is kept, which is how you indent a nested list inside an email. ## The marker-in-the-body trap With the flexible syntax, the closing identifier is recognised at the start of any line, even when it is only the beginning of a longer word. A body line starting with `MAILING list` inside a `<<<MAIL` block ends the string early and produces a `ParseError`. The rule of thumb: pick a marker word that never begins a line of the text. ## Mistakes that show up in review - **Mixing marker styles.** Code written for PHP 7.2 and earlier often puts the marker in column one; it still works, but mixing that style with indented markers in one file confuses readers. - **An editor converting indentation.** Reformatting a file from tabs to spaces or back can leave a heredoc with mixed indentation, which is a `ParseError`, not a warning. - **Expecting a trailing newline.** The line break before the closing marker is not included; append `PHP_EOL` or an empty line if the text must end with one. - **Using a heredoc for literal templates.** A `$` meant for a later templating pass is interpolated immediately; that text belongs in a nowdoc. ## Choosing between the four literal syntaxes - Short text, no values: single quotes. - Short text with values or `\n`: double quotes. - Multi-line text with values: heredoc. - Multi-line text that must stay literal: nowdoc. Neither heredoc nor nowdoc escapes anything for the output context. An HTML email built with a heredoc still needs each interpolated value escaped for HTML before it goes in.
- Why does a heredoc containing a variable fail as a class constant value, while a nowdoc works?Class constants and property defaults must be constant expressions known at compile time. A nowdoc, or a heredoc without variables, is a plain string literal, so it qualifies. A heredoc that interpolates `$name` needs a runtime value, so it is rejected in that position.
- What happens if one body line is indented less than the closing marker?Compilation fails with a `ParseError`, "Invalid body indentation level (expecting an indentation level of at least N)", where N is the marker's indentation. PHP will not guess how to strip a line that does not have that much leading whitespace. Blank lines are exempt, so empty lines inside the block are fine.
saying these in an interview costs you the question
- Nowdoc interpolates variables but not escape sequences
- The closing marker must always start in column one
- Indenting the closing marker adds that indentation to the string
- Heredoc bodies need escaped double quotes
- Mixing tabs and spaces in the indentation is only a style issue