skip to content

Why do PHP style guides say to omit the closing ?> tag in files that contain only PHP code?

level: juniorimportance: must knowfreq 55%

answer

  1. text after ?> is output
  2. only one newline is swallowed
  3. stray whitespace before headers
  4. included files add their bytes too
  5. PSR-12: MUST be omitted

basics

~20 s

Anything after a closing ?> is output, and PHP swallows only the one newline right after the tag. A stray blank line in an included class file then corrupts responses or blocks later headers; omitting ?> removes the risk.

solid answer

~50 s

PHP treats everything outside `<?php ... ?>` as literal output. The closing tag swallows **only the single newline** that immediately follows it, so an extra blank line, a trailing space, or an editor-added newline after `?>` becomes **output** the moment the file is included. In a class or config file that output arrives before the application has sent its headers, so later header or cookie calls fail, and it corrupts non-HTML responses such as JSON, CSV or file downloads. The closing tag at the end of a file is **optional**, so the fix is simply not to write it: then there is no "outside" to leak from. PSR-12 makes this a rule: the closing `?>` **MUST** be omitted from files containing only PHP. Templates that switch between HTML and PHP still use `?>`, because there it is the point.

code

php · 6 lines
php
<?php
// config.php, correct: no closing tag, nothing can leak.
return [
    'currency' => 'EUR',
    'vat_rate' => 0.2,
];

go deeper

for a junior

Recall that text outside PHP tags is output and that pure-PHP files should end without ?>, as PSR-12 requires.

for a middle

Explain the one-newline rule and why stray bytes from an included file cause header warnings and corrupt JSON or downloads.

for a senior

Diagnose a corrupted response by hunting for leaked bytes in included files, including a BOM before <?php, and enforce the rule with a coding-standard check.

for a principal

Make tag hygiene a tooling concern: a formatter in CI removes the class of bug, instead of relying on reviewers to spot invisible whitespace.

## How PHP reads a file A `.php` file is not PHP code from top to bottom. PHP scans it for **tags**: - `<?php` opens PHP mode. It must be followed by whitespace (a space, tab or newline), otherwise the file has a syntax error. - `?>` closes PHP mode. Everything after it, up to the next opening tag, is **inline HTML**: bytes that PHP sends to the output unchanged, whatever they are. - The closing tag also acts as a statement terminator, so the last statement before `?>` needs no semicolon. That design is what lets PHP be embedded in HTML templates. It is also the source of the problem. ## The one-newline rule The manual's instruction-separation rules say the closing tag **includes the immediately trailing newline**, if there is one. Exactly one newline is swallowed. So: | File ends with | What PHP outputs after the tag | |---|---| | `?>` and nothing else | nothing | | `?>` plus one newline | nothing, the newline is swallowed | | `?>` plus two newlines | one newline | | `?>` plus a space and a newline | a space and a newline | Many editors add a final newline, some add two, and a merge can leave a blank line. The file still looks clean, but it now emits bytes. ## Why a few stray bytes matter The bytes are emitted when the file is **included**, which for a class, a config file or a helper usually happens long before the page is rendered. Consequences: 1. **Headers can no longer be changed.** Once output has been sent, calls that set headers or cookies fail with a "headers already sent" warning that points at the included file. Output buffering can hide this until the buffer fills; header handling is its own topic. 2. **Non-HTML responses break.** A newline before a JSON body, a CSV export, an image or a PDF download corrupts the payload. 3. **The source is hard to find.** The file that leaks is often not the one being edited, and a blank line is invisible in review. The same logic applies at the **start** of a file: anything before `<?php`, including a UTF-8 byte-order mark saved by an editor, is inline output too. The one exception is a `#!` shebang line at the top of a script run by the CLI, which PHP skips. ## The fix and the rule The manual states that the closing tag at the end of a file is optional and recommends omitting it when a file ends with PHP code. With no closing tag, the file never leaves PHP mode, so trailing whitespace is just whitespace inside PHP code and is ignored. Coding standards turned the advice into a rule: - **PSR-12** says: "The closing `?>` tag MUST be omitted from files containing only PHP." - Formatters and sniffers that implement PSR-12 or PER Coding Style flag or remove the tag automatically. ## Keeping a codebase clean Because a leaked newline is invisible, the fix belongs in tooling rather than review: - PHP CS Fixer has a `no_closing_tag` rule, part of its PER-CS rule sets, that removes the tag from PHP-only files; PHP_CodeSniffer's PSR-12 standard reports it. - `php -l` does **not** help: whitespace after `?>` is valid syntax, so the file lints cleanly. - A file that starts with a byte-order mark shows three extra bytes (`EF BB BF`) before `<?php` in a hex dump; configure editors to save PHP files as UTF-8 without a BOM. - When a warning names a file and line where output started, that location is the leak, even if the file looks empty there. ## Where ?> still belongs Omitting the tag is for **pure-PHP files**: classes, functions, config arrays, bootstrap scripts. Templates that mix HTML and PHP need `?>` to return to HTML, and there the one-newline rule is something to know rather than avoid: `<?= $total ?>` at the end of a line eats that line's newline, which can join lines in a plain-text email template. A good interview answer names the mechanism (output outside tags, one newline swallowed), the symptom (stray output before headers, broken downloads) and the standard (PSR-12 requires omitting it).

  • A JSON endpoint starts its body with an empty line; where do you look first?
    At files included before the response is written: a pure-PHP file that ends with `?>` followed by more than one newline or trailing spaces, or a file with bytes (often a UTF-8 BOM) before `<?php`. The stray bytes are inline output. Remove the closing tag, strip the BOM, and add a coding-standard check so it cannot come back.
  • Does the last statement before ?> need a semicolon?
    No. The closing tag implies a semicolon, so `<?php echo $x ?>` is valid. In a pure-PHP file with no closing tag every statement needs its semicolon, which is one more reason formatters treat the tag-less style as the norm for class and config files.

saying these in an interview costs you the question

  • PHP ignores all whitespace after ?> at the end of a file.
  • The closing ?> tag is required for a PHP file to parse.
  • Omitting ?> is only a style preference with no runtime effect.
  • Templates that mix HTML and PHP should also avoid ?>.
  • A UTF-8 byte-order mark before <?php is ignored by PHP.